diff --git a/docs/superpowers/specs/2026-07-07-bilin-p53-p61-goals-injection-design.md b/docs/superpowers/specs/2026-07-07-bilin-p53-p61-goals-injection-design.md new file mode 100644 index 0000000..4ea1b75 --- /dev/null +++ b/docs/superpowers/specs/2026-07-07-bilin-p53-p61-goals-injection-design.md @@ -0,0 +1,421 @@ +# 笔临 P5.3 + P6.1 写作目标补全 + 知识注入历史 设计规格 + +**版本**:1.0 +**日期**:2026-07-07 +**范围**:P5.3(§8 补全)番茄钟、连更里程碑徽章、达标通知;P6.1 知识库条目注入历史与反向链接 +**前置**:P5.2 写作日志 + AI 知识抽取已交付(v0.8.0,`a39e06f`);P6 AI 知识上下文(v0.7.0) +**参考**:`design/readme.md` v1.1 §8 / §5.5.7、`docs/superpowers/specs/2026-07-07-bilin-p52-writing-logs-extraction-design.md`、`docs/superpowers/specs/2026-07-07-bilin-p6-ai-context-design.md` + +**目标版本**:v0.9.0 + +--- + +## 1. 背景与目标 + +### 1.1 阶段定位 + +v0.8.0 已交付 `writing_logs` 日更追踪、驾驶舱热力图/连更、状态栏日更进度,以及 AI 章节知识抽取与 P6 注入链路。但 `design/readme.md` §8 仍缺 **番茄钟**、**7/30/100 天连更里程碑徽章** 与 **达标/完成通知**;P6 spec 延后的 **注入历史 / 反向链接** 尚未落地,作者无法在知识库条目上查看「这条知识曾在哪些写作流中被 AI 使用」。 + +本阶段(v0.9.0)双线交付: + +- **P5.3**:补齐 §8 剩余能力,强化日更作者的正反馈与专注工具 +- **P6.1**:在知识库条目上记录并展示写作流(交互/自动/向导)上下文确认时的注入历史 + +### 1.2 已确认决策(Brainstorming 2026-07-07) + +| 维度 | 决策 | +|------|------| +| 交付版本 | **v0.9.0** 单版交付 P5.3 + P6.1 | +| 架构方案 | **主进程服务分层**:`PomodoroService` + `AchievementService` + `GoalNotificationService` + `KnowledgeInjectionService` | +| 统计范围 | **当前书籍**(与 P5.2 一致:连更、里程碑、番茄字数增量、成就均按 `bookId`) | +| 通知 | **Toast + 可选 Electron 系统通知**;设置页可关闭系统通知与目标通知 | +| 番茄钟 UI | **状态栏紧凑控件**(▶/⏸ + 剩余时间);驾驶舱展示今日番茄完成次数 | +| 番茄时长 | 设置页 **15 / 25 / 45 分钟** 三档,默认 25 | +| 里程碑 UI | **庆祝 + 持久展示**:达成时 Toast/系统通知;驾驶舱「成就区」展示已解锁 7/30/100 天徽章 | +| P6.1 范围 | **仅知识库条目**(不含设定条目 AI 注入历史) | +| P6.1 记录场景 | **交互 / 自动 / 向导** 三模式 `confirmContext` 且 `knowledgeIds` 非空;**不含 AI 对话助手** | +| 注入历史 UI | **列表最近 3 条预览 + 编辑对话框完整时间线** | +| 切换书籍(番茄进行中) | **自动取消番茄**并 Toast 提示 | + +### 1.3 验收标准 + +用户能完成: + +1. 在状态栏启动 **15/25/45 分钟**番茄钟,暂停/取消/完成后看到剩余时间变化;完成时 Toast 显示本次新增字数 +2. `enableSystemNotifications` 开启时,番茄完成与里程碑解锁可收到 **系统通知**(权限被拒则降级 Toast) +3. `dailyWordGoal > 0` 且当日首次达标时收到 Toast(及可选系统通知);同一自然日不重复提醒 +4. 连更达到 **7 / 30 / 100 天**(基于 P5.2 `streakDays`)时:庆祝通知 + 驾驶舱成就区永久展示对应徽章;每档仅庆祝一次 +5. 交互/自动/向导 **确认知识上下文**后,被注入的 `approved` 知识条目在 **KnowledgePanel** 卡片显示最近 3 条注入记录 +6. 打开 **KnowledgeEditorDialog** 可查看该条目完整注入历史(时间、模式、关联章节) +7. 注入记录失败 **不阻断** 写作流确认步骤 + +### 1.4 本规格不实现(明确排除) + +| 项 | 归属 | +|----|------| +| 预计完本、写作速度仪表盘、时段热力图 | §9 延后 | +| 设定条目「被 AI 注入的轮次」 | P6.2 | +| AI 对话助手注入历史 | 本阶段排除(用户确认 C) | +| 番茄钟自定义任意分钟数、休息间隔、番茄统计全屏页 | 后续 | +| 里程碑 365 天等扩展档位 | 后续 | +| 语义 embedding、注入内容 diff | 后续 | +| 导入导出、章节模板 | P7 / §10 | + +--- + +## 2. 架构 + +### 2.1 进程模型 + +``` +┌────────────────────────────────────────────────────────────────────┐ +│ 主进程 │ +│ · PomodoroService(倒计时、开始/暂停/取消、完成字数差分) │ +│ · AchievementService(7/30/100 里程碑解锁检测与持久化) │ +│ · GoalNotificationService(Toast IPC + Notification,受设置控制) │ +│ · WritingLogService(已有,番茄快照/差分、streakDays) │ +│ · KnowledgeInjectionService(写入/查询 injection logs) │ +│ · CockpitService 扩展(成就区、pomodoroTodayCount) │ +│ · interactive/auto/wizard confirmContext 挂钩 injection 记录 │ +└──────────────────────────────┬─────────────────────────────────────┘ + │ IPC +┌──────────────────────────────▼─────────────────────────────────────┐ +│ 渲染进程 │ +│ · EditorLayout 状态栏:番茄控件 + 日更进度(已有) │ +│ · CockpitModal:成就区徽章 + 今日番茄次数 │ +│ · SettingsPage:番茄时长、通知开关 │ +│ · KnowledgePanel:注入历史最近 3 条预览 │ +│ · KnowledgeEditorDialog:完整注入历史列表 │ +└────────────────────────────────────────────────────────────────────┘ +``` + +### 2.2 模块边界 + +| 模块 | 职责 | 依赖 | +|------|------|------| +| `PomodoroService` | 单实例番茄状态机;完成时计算 `wordDelta` | `WritingLogService`, `GlobalSettings` | +| `AchievementService` | 读 `streakDays`,写 `writing_milestones`,触发庆祝 | `WritingLogService`, `GoalNotificationService` | +| `GoalNotificationService` | 统一发送 goal-met / milestone / pomodoro 通知 | `GlobalSettings`, `Notification`(Electron) | +| `KnowledgeInjectionService` | batch 写入、按 entry 查询、recent N | 每书 `KnowledgeInjectionRepository` | +| `CockpitService` | 聚合 `achievements[]`、`pomodoroTodayCount` | 上述服务 | + +**原则**: + +- 番茄钟状态在主进程,保证最小化/失焦时计时准确、完成时可发系统通知 +- 注入记录与 confirmContext **同事务意图**:build 成功即写 log;写 log 失败仅 `console.error`,不抛给 IPC +- 成就不回溯:仅在未来 `streakDays` 首次跨档时解锁 + +--- + +## 3. 数据模型 + +### 3.1 全局 userData:扩展 `writing_logs.sqlite` + +在现有 `writing_logs` 表基础上 **同库追加**(`WritingLogRepository.migrate()` 扩展): + +```sql +CREATE TABLE IF NOT EXISTS writing_milestones ( + book_id TEXT NOT NULL, + milestone TEXT NOT NULL, -- 'streak_7' | 'streak_30' | 'streak_100' + unlocked_at TEXT NOT NULL DEFAULT (datetime('now')), + PRIMARY KEY (book_id, milestone) +); + +CREATE TABLE IF NOT EXISTS pomodoro_daily ( + date TEXT NOT NULL, + book_id TEXT NOT NULL, + completed_count INTEGER NOT NULL DEFAULT 0, + total_words INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (date, book_id) +); +``` + +`pomodoro_daily` 在每次番茄 **完成** 时 `completed_count += 1`,`total_words += wordDelta`。 + +### 3.2 每书 schema v8 + +新文件 `schema-v8.sql`: + +```sql +CREATE TABLE IF NOT EXISTS knowledge_injection_logs ( + id TEXT PRIMARY KEY, + knowledge_entry_id TEXT NOT NULL, + flow_mode TEXT NOT NULL, -- 'interactive' | 'auto' | 'wizard' + flow_id TEXT NOT NULL, + chapter_id TEXT, + injected_at TEXT NOT NULL DEFAULT (datetime('now')), + FOREIGN KEY (knowledge_entry_id) REFERENCES knowledge_entries(id) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS idx_injection_entry_time + ON knowledge_injection_logs (knowledge_entry_id, injected_at DESC); +``` + +`migrate.ts`:`CURRENT_VERSION = 8`;v7→v8 仅 `exec(schemaV8)`。 + +### 3.3 TypeScript 类型(`src/shared/types.ts`) + +```typescript +export type PomodoroDuration = 15 | 25 | 45 +export type WritingMilestone = 'streak_7' | 'streak_30' | 'streak_100' +export type InjectionFlowMode = 'interactive' | 'auto' | 'wizard' + +export interface PomodoroState { + running: boolean + paused: boolean + remainingSec: number + bookId: string | null + startedAt: string | null +} + +export interface WritingAchievement { + milestone: WritingMilestone + unlockedAt: string +} + +export interface KnowledgeInjectionLog { + id: string + knowledgeEntryId: string + flowMode: InjectionFlowMode + flowId: string + chapterId?: string + injectedAt: string +} + +export interface CockpitSummary { + // 已有字段… + achievements?: WritingAchievement[] + pomodoroTodayCount?: number +} +``` + +扩展 `GlobalSettings`: + +```typescript +pomodoroDurationMinutes?: PomodoroDuration // default 25 +enableSystemNotifications?: boolean // default false +enableGoalNotifications?: boolean // default true +``` + +--- + +## 4. 服务行为 + +### 4.1 PomodoroService + +- **start(bookId)**:若已有运行中番茄且 `bookId` 不同,先 cancel 再 start;记录 `wordSnapshot = WritingLogService.getStats(bookId).todayWords` +- **pause / resume / cancel**:标准状态机;cancel 清空快照 +- **tick**:主进程 `setInterval(1s)` 递减;`remainingSec === 0` 触发 `complete()` +- **complete()**: + - `wordDelta = max(0, todayWords_now - wordSnapshot)` + - 更新 `pomodoro_daily` + - `GoalNotificationService.notifyPomodoroComplete(bookId, wordDelta)` + - 重置状态 + +渲染进程每 1s 通过 IPC `pomodoro:getState` 同步 UI(或由主进程 push `POMODORO_TICK` 事件,实现时二选一,计划阶段选定 push 以减少轮询)。 + +### 4.2 AchievementService + +- 在 **WritingLogService 写入后** 或 **Cockpit getSummary** 时调用 `checkMilestones(bookId)`: + - 读 `streakDays`(需 `dailyWordGoal > 0`) + - 若 `streakDays >= 7` 且 DB 无 `streak_7` → insert + notify + - 同理 30、100 +- `listAchievements(bookId)` → `WritingAchievement[]` 按 milestone 排序 + +### 4.3 GoalNotificationService + +| 事件 | Toast | 系统通知(`enableSystemNotifications`) | +|------|-------|----------------------------------------| +| 日更首次达标 | ✓(`enableGoalNotifications`) | 可选 | +| 里程碑解锁 | ✓ | 可选 | +| 番茄完成 | ✓ | 可选 | + +Toast 经现有 `useAppStore.showToast` 模式:主进程 `webContents.send(GOAL_NOTIFICATION, payload)` 或复用通用 notification IPC。 + +系统通知权限被拒:catch 并忽略,不抛错。 + +### 4.4 KnowledgeInjectionService + +挂钩点(均在 `buildFlowContextPayload` **成功返回之后**): + +- `interactive.handler` → `INTERACTIVE_CONFIRM_CONTEXT` +- `auto.handler` → `AUTO_CONFIRM_CONTEXT` +- `wizard.handler` → `WIZARD_CONFIRM_CONTEXT` + +```typescript +recordInjection(bookId, { + knowledgeIds: binding.knowledgeIds, + flowMode: 'interactive' | 'auto' | 'wizard', + flowId, + chapterId?: string // flow 关联章或 registry meta lastChapterId +}) +``` + +对每个 `knowledgeId`:校验 entry 存在且 `status === 'approved'`(防御性;UI 本不应注入 pending)。 + +查询: + +- `listRecentForEntry(entryId, limit=3)` +- `listForEntry(entryId, limit=50)` 供编辑对话框 + +--- + +## 5. UI 规格 + +### 5.1 状态栏番茄控件 + +- 位置:`EditorLayout` `#editor-statusbar`,日更进度右侧 +- 控件:`data-testid="pomodoro-control"` +- idle:▶ 按钮,点击 start +- running:⏸ + `MM:SS` + ✕取消 +- paused:▶ 继续 + ✕取消 +- 无 `currentBookId` 时禁用 + +### 5.2 驾驶舱成就区 + +- 位置:`CockpitModal` 写作统计区下方 +- `data-testid="cockpit-achievements"` +- 展示已解锁徽章(7🔥 / 30🔥 / 100🔥 或 i18n 文案);未解锁灰显或隐藏 +- `data-testid="cockpit-pomodoro-today"`:今日完成番茄次数 + +### 5.3 设置页 + +| 控件 | testid | 说明 | +|------|--------|------| +| 番茄时长 | `settings-pomodoro-duration` | select: 15/25/45 | +| 系统通知 | `settings-system-notifications` | checkbox,默认 false | +| 目标通知 | `settings-goal-notifications` | checkbox,默认 true | + +### 5.4 知识库注入历史 + +**KnowledgePanel 列表项**: + +- `data-testid="knowledge-injection-preview-{entryId}"` +- 最多 3 行:`{date} · {flowMode} · {chapterTitle}` + +**KnowledgeEditorDialog**: + +- 折叠区「注入历史」`data-testid="knowledge-injection-history"` +- 完整列表,按时间倒序;章节名可点击跳转(调用现有 `switchTarget`) + +--- + +## 6. IPC 通道 + +```typescript +POMODORO_START: 'pomodoro:start', +POMODORO_PAUSE: 'pomodoro:pause', +POMODORO_RESUME: 'pomodoro:resume', +POMODORO_CANCEL: 'pomodoro:cancel', +POMODORO_GET_STATE: 'pomodoro:getState', +ACHIEVEMENT_LIST: 'achievement:list', +KNOWLEDGE_LIST_INJECTIONS: 'knowledge:listInjections', +GOAL_NOTIFICATION: 'goal:notification', // main → renderer push +``` + +preload / `electron-api.d.ts` 同步扩展。 + +--- + +## 7. i18n 键(节选) + +```json +"pomodoro.start": "开始番茄钟", +"pomodoro.pause": "暂停", +"pomodoro.cancel": "取消", +"pomodoro.complete": "番茄钟完成!本次写作 {{words}} 字", +"pomodoro.switchedBook": "已切换书籍,番茄钟已取消", +"goal.dailyMet": "今日目标已达成 {{current}} / {{goal}} 字", +"achievement.streak7": "连更 7 天", +"achievement.streak30": "连更 30 天", +"achievement.streak100": "连更 100 天", +"achievement.unlocked": "解锁成就:{{name}}", +"cockpit.achievements": "写作成就", +"cockpit.pomodoroToday": "今日番茄 {{count}} 次", +"knowledge.injectionHistory": "注入历史", +"knowledge.injectionPreview": "最近注入", +"knowledge.injectionEntry": "{{date}} · {{mode}} · {{chapter}}", +"settings.pomodoroDuration": "番茄钟时长(分钟)", +"settings.systemNotifications": "启用系统通知", +"settings.goalNotifications": "启用目标达成提醒" +``` + +--- + +## 8. 测试要求 + +### 8.1 单元 / 集成 + +| 用例ID | 场景 | 预期 | +|--------|------|------| +| UT-POM-01 | start → complete,todayWords +50 | wordDelta=50,`pomodoro_daily` 更新 | +| UT-POM-02 | 切换 bookId start | 旧番茄 cancel,新番茄 running | +| UT-ACH-01 | streakDays=7 首次检测 | `writing_milestones` 写入 streak_7 | +| UT-ACH-02 | streak_7 已存在 | 不重复 notify | +| UT-INJ-01 | recordInjection 3 ids | 3 rows inserted | +| IT-INJ-01 | interactive confirmContext with knowledgeIds | DB 有对应 logs | +| IT-GOAL-01 | addToday 达标首次 | notification payload 发送一次 | + +### 8.2 E2E(对齐 design/readme §8.2) + +| 用例ID | 场景 | 预期 | +|--------|------|------| +| E2E-GOAL-01 | 日更 3000 目标写 3500 | 进度变绿,达标 Toast | +| E2E-GOAL-02 | 连更 7 天(测试数据 seed) | 成就区显示 7 天徽章 | +| E2E-GOAL-03 | 启动番茄并完成(测试环境可缩短 duration mock 或 settings=15 + 加速 hook 仅限 E2E) | 完成 Toast 含字数 | +| E2E-INJ-01 | 向导 confirm 知识上下文 | Panel 预览可见注入记录 | + +**E2E 番茄加速**:主进程识别 `BILIN_E2E=1` 时允许 `POMODORO_TEST_SEC=3` 环境变量覆盖时长(仅 E2E,不暴露给生产 UI)。 + +### 8.3 原则 + +- 注入历史 IT/E2E **不需要** AI 调用,可纯 DB 断言 +- 含通知的 E2E 断言 Toast DOM,不断言 OS 通知栏 + +--- + +## 9. 文件映射 + +| 文件 | 职责 | +|------|------| +| `src/main/db/schema-v8.sql` | `knowledge_injection_logs` | +| `src/main/db/migrate.ts` | v8 | +| `src/main/db/repositories/writing-milestone.repo.ts` | 全局 milestones | +| `src/main/db/repositories/pomodoro-daily.repo.ts` | 全局 pomodoro 日统计 | +| `src/main/db/repositories/knowledge-injection.repo.ts` | 每书 injection logs | +| `src/main/services/pomodoro.service.ts` | 番茄状态机 | +| `src/main/services/achievement.service.ts` | 里程碑 | +| `src/main/services/goal-notification.service.ts` | 通知 | +| `src/main/services/knowledge-injection.service.ts` | 注入记录 | +| `src/main/services/cockpit.service.ts` | 扩展 summary | +| `src/main/ipc/handlers/pomodoro.handler.ts` | 番茄 IPC | +| `src/main/ipc/handlers/achievement.handler.ts` | 成就 IPC | +| `src/main/ipc/handlers/knowledge.handler.ts` | 扩展 listInjections | +| `src/main/ipc/handlers/interactive|auto|wizard.handler.ts` | confirmContext 挂钩 | +| `src/renderer/components/layout/EditorLayout.tsx` | 番茄控件 | +| `src/renderer/components/cockpit/CockpitModal.tsx` | 成就区 | +| `src/renderer/components/knowledge/KnowledgePanel.tsx` | 注入预览 | +| `src/renderer/components/knowledge/KnowledgeEditorDialog.tsx` | 完整历史 | +| `src/renderer/components/settings/SettingsPage.tsx` | 新设置 | +| `tests/main/pomodoro.test.ts` | UT-POM-* | +| `tests/main/achievement.test.ts` | UT-ACH-* | +| `tests/main/knowledge-injection.test.ts` | UT/IT-INJ-* | +| `e2e/writing-goals.spec.ts` | E2E-GOAL-* | +| `e2e/knowledge-injection-history.spec.ts` | E2E-INJ-* | + +--- + +## 10. Definition of Done(v0.9.0) + +- [ ] §8 番茄钟 + 里程碑 + 通知按 §1.3 验收 +- [ ] P6.1 注入历史按 §1.3 第 5–6 条验收 +- [ ] schema v8 + 全局 sqlite 扩展迁移 +- [ ] `npm run build` 通过 +- [ ] `npm run test` 全绿(新增 UT/IT) +- [ ] E2E-GOAL-01~03、E2E-INJ-01 通过 +- [ ] `package.json` / README → v0.9.0 +- [ ] i18n zh-CN / en 键齐全 + +--- + +*本规格为笔临 P5.3 + P6.1 子项目。§9 个人写作分析、设定条目注入历史等在后续 spec 增量修订。*