diff --git a/docs/superpowers/plans/2026-07-06-bilin-p5-workflow-knowledge.md b/docs/superpowers/plans/2026-07-06-bilin-p5-workflow-knowledge.md new file mode 100644 index 0000000..e60104e --- /dev/null +++ b/docs/superpowers/plans/2026-07-06-bilin-p5-workflow-knowledge.md @@ -0,0 +1,818 @@ +# 笔临 P5 作家工作流 + 知识库 MVP Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 交付 v0.6.0:驾驶舱 + 存稿/更新计划 + 章间衔接(可选 AI)+ 知识库 MVP(手动 CRUD / 审核 / 伏笔追踪)。 + +**Architecture:** schema v6 新增 `knowledge_entries` / `book_preferences` / `chapter_bridge_dismiss`;`KnowledgeService` 负责 CRUD 与遗忘判定;`CockpitService` 只读聚合字数与存稿;`ChapterBridgeService` 提取相邻章片段 + 可选 `AiClientService`;渲染层 `CockpitModal` / `ChapterBridgeModal` / 重写 `KnowledgePanel`。测试 **禁止 Mock** AI 集成/E2E 打真实 LM Studio。 + +**Tech Stack:** Electron 36、electron-vite、React 18、TypeScript、node:sqlite、Zustand、Jotai、i18next、Vitest、Playwright + +**Spec:** `docs/superpowers/specs/2026-07-06-bilin-p5-workflow-knowledge-design.md` + +**LM Studio 默认:** `baseUrl=http://127.0.0.1:1234`,`model=gemma-4-e4b-it` + +--- + +## File Map + +| 文件 | 职责 | +|------|------| +| `src/main/db/schema-v6.sql` | v6 DDL | +| `src/main/db/migrate.ts` | v5→v6 | +| `src/main/db/repositories/knowledge.repo.ts` | knowledge_entries CRUD | +| `src/main/db/repositories/book-prefs.repo.ts` | book_preferences KV | +| `src/main/db/repositories/bridge-dismiss.repo.ts` | chapter_bridge_dismiss | +| `src/main/services/knowledge.service.ts` | 审核、遗忘判定、bookmark 同步 | +| `src/main/services/cockpit.service.ts` | 驾驶舱聚合 | +| `src/main/services/chapter-bridge.service.ts` | 衔接文本 + AI | +| `src/main/db/repositories/chapter.repo.ts` | publish_status 读写 | +| `src/main/ipc/handlers/knowledge.handler.ts` | knowledge IPC | +| `src/main/ipc/handlers/cockpit.handler.ts` | cockpit IPC | +| `src/main/ipc/handlers/bridge.handler.ts` | bridge IPC | +| `src/main/ipc/register.ts` | 注册 handler | +| `src/shared/types.ts` | KnowledgeEntry / CockpitSummary 等 | +| `src/shared/ipc-channels.ts` | 新通道 | +| `src/preload/index.ts` | knowledge / cockpit / bridge API | +| `src/shared/electron-api.d.ts` | 类型 | +| `src/renderer/stores/useKnowledgeStore.ts` | 知识库状态 | +| `src/renderer/stores/useCockpitStore.ts` | 驾驶舱状态 | +| `src/renderer/components/knowledge/KnowledgePanel.tsx` | 重写 | +| `src/renderer/components/knowledge/KnowledgeEditorDialog.tsx` | 编辑对话框 | +| `src/renderer/components/cockpit/CockpitModal.tsx` | 驾驶舱 UI | +| `src/renderer/components/bridge/ChapterBridgeModal.tsx` | 衔接 UI | +| `src/renderer/components/layout/TopBar.tsx` | 驾驶舱按钮 | +| `src/renderer/components/layout/EditorLayout.tsx` | 发布状态、bridge 触发、statusbar | +| `src/renderer/components/settings/SettingsPage.tsx` | updateSchedule | +| `src/main/services/global-settings.ts` | 默认值扩展 | +| `public/locales/zh-CN/translation.json` | 文案 | +| `public/locales/en/translation.json` | 文案 | +| `tests/main/migrate-v6.test.ts` | UT-MIG-06 | +| `tests/main/knowledge.service.test.ts` | UT-KNOW-* | +| `tests/main/cockpit.service.test.ts` | UT-COCK-01 | +| `tests/main/chapter-bridge.test.ts` | UT-BRIDGE-01 / IT-BRIDGE-01 | +| `e2e/cockpit-knowledge.spec.ts` | E2E-COCK-* / KNOW-* / BRIDGE-* / STOCK-* | + +--- + +## Task 1: Schema v6 + 类型 + IPC 通道 + +**Files:** +- Create: `src/main/db/schema-v6.sql` +- Modify: `src/main/db/migrate.ts` +- Modify: `src/shared/types.ts` +- Modify: `src/shared/ipc-channels.ts` +- Create: `tests/main/migrate-v6.test.ts` +- Modify: `tests/main/migrate-v5.test.ts`(期望 version **6**) + +- [ ] **Step 1: 写迁移失败测试** + +`tests/main/migrate-v6.test.ts`: + +```typescript +import { describe, it, expect, afterEach } from 'vitest' +import { DatabaseSync } from 'node:sqlite' +import { migrate } from '../../src/main/db/migrate' +import type { SqliteDb } from '../../src/main/db/types' + +describe('migrate v6', () => { + let db: SqliteDb + afterEach(() => db?.close()) + + it('UT-MIG-06: applies v6 on fresh database', () => { + db = new DatabaseSync(':memory:') + migrate(db) + const version = db.prepare('SELECT MAX(version) AS v FROM schema_version').get() as { v: number } + expect(version.v).toBe(6) + const tables = db + .prepare("SELECT name FROM sqlite_master WHERE type='table'") + .all() as { name: string }[] + expect(tables.some((t) => t.name === 'knowledge_entries')).toBe(true) + expect(tables.some((t) => t.name === 'book_preferences')).toBe(true) + expect(tables.some((t) => t.name === 'chapter_bridge_dismiss')).toBe(true) + }) +}) +``` + +- [ ] **Step 2: 运行确认 FAIL** + +```bash +npm run test -- tests/main/migrate-v6.test.ts +``` + +- [ ] **Step 3: 实现 schema v6** + +`src/main/db/schema-v6.sql`(与 spec §3.1 一致)。 + +`src/main/db/migrate.ts`: + +```typescript +import schemaV6 from './schema-v6.sql?raw' +const CURRENT_VERSION = 6 +// ... +if (current < 6) { + db.exec(schemaV6) + db.prepare('INSERT INTO schema_version (version, description) VALUES (?, ?)').run(6, 'P5 knowledge/cockpit schema') +} +``` + +`src/shared/types.ts` 新增: + +```typescript +export type KnowledgeType = 'character' | 'foreshadow' | 'location' | 'relationship' | 'fact' +export type KnowledgeStatus = 'pending' | 'approved' | 'rejected' +export type ForeshadowStatus = 'buried' | 'partial' | 'resolved' +export type UpdateSchedule = 'daily' | 'alternate' | 'weekly' | 'none' + +export interface KnowledgeEntry { + id: string + type: KnowledgeType + title: string + content: string + importance: number + status: KnowledgeStatus + foreshadowStatus?: ForeshadowStatus + sourceChapterId?: string + lastMentionChapterId?: string + linkedBookmarkId?: string + createdAt: string + updatedAt: string +} + +export interface CreateKnowledgeInput { + type: KnowledgeType + title: string + content?: string + importance?: number + status?: KnowledgeStatus + foreshadowStatus?: ForeshadowStatus + sourceChapterId?: string + lastMentionChapterId?: string + linkedBookmarkId?: string +} + +export interface CockpitSummary { + totalWords: number + volumeWords: number + stockReadyCount: number + stockThreshold: number + foreshadowBuried: number + foreshadowResolved: number + foreshadowForgotten: number +} + +export interface ChapterBridgeResult { + previousChapterId: string | null + previousTail: string + currentHead: string + aiSuggestion?: string +} +``` + +扩展 `GlobalSettings`: + +```typescript +updateSchedule: UpdateSchedule // default 'none' +stockBufferThreshold: number // default 3 +``` + +扩展 `Chapter`: + +```typescript +publishStatus?: PublishStatus +``` + +`src/shared/ipc-channels.ts` 追加 spec §4.1 全部通道。 + +- [ ] **Step 4: 更新 migrate-v5 期望 version 6;运行 PASS** + +```bash +npm run test -- tests/main/migrate-v5.test.ts tests/main/migrate-v6.test.ts +``` + +- [ ] **Step 5: Commit** + +```bash +git add src/main/db/schema-v6.sql src/main/db/migrate.ts src/shared/types.ts src/shared/ipc-channels.ts tests/main/migrate-v6.test.ts tests/main/migrate-v5.test.ts +git commit -m "feat: add schema v6 for knowledge and cockpit workflow" +``` + +--- + +## Task 2: Repository 层 + +**Files:** +- Create: `src/main/db/repositories/knowledge.repo.ts` +- Create: `src/main/db/repositories/book-prefs.repo.ts` +- Create: `src/main/db/repositories/bridge-dismiss.repo.ts` +- Modify: `src/main/db/repositories/chapter.repo.ts` +- Create: `tests/main/knowledge.repo.test.ts` + +- [ ] **Step 1: UT-KNOW-01 失败测试** + +```typescript +it('UT-KNOW-01: CRUD and approve updates status', () => { + const db = new DatabaseSync(':memory:') + migrate(db) + const repo = new KnowledgeRepository(db) + const entry = repo.create({ type: 'foreshadow', title: '测灵石异常', foreshadowStatus: 'buried' }) + expect(entry.status).toBe('pending') + const approved = repo.update(entry.id, { status: 'approved' }) + expect(approved.status).toBe('approved') + db.close() +}) +``` + +- [ ] **Step 2: 实现 KnowledgeRepository** + +```typescript +export class KnowledgeRepository { + create(input: CreateKnowledgeInput): KnowledgeEntry { /* INSERT */ } + get(id: string): KnowledgeEntry | null + list(filter?: { status?: KnowledgeStatus; type?: KnowledgeType }): KnowledgeEntry[] + update(id: string, patch: Partial<...>): KnowledgeEntry + delete(id: string): void + countByStatus(status: KnowledgeStatus): number +} +``` + +`mapRow` 蛇形 ↔ 驼峰映射。 + +- [ ] **Step 3: BookPrefsRepository + BridgeDismissRepository** + +```typescript +// book-prefs.repo.ts +get(key: string): string | null +set(key: string, value: string): void + +// bridge-dismiss.repo.ts +isDismissed(chapterId: string): boolean +dismiss(chapterId: string): void +``` + +- [ ] **Step 4: ChapterRepository 扩展 publish_status** + +`mapRow` 增加 `publishStatus`;`update` patch 支持 `publishStatus`;新增: + +```typescript +setPublishStatus(id: string, publishStatus: PublishStatus): Chapter +countByPublishStatus(status: PublishStatus): number +getPreviousChapter(chapterId: string): Chapter | null // 按 sort_order 找上一章 +``` + +- [ ] **Step 5: 运行 PASS + Commit** + +```bash +npm run test -- tests/main/knowledge.repo.test.ts +git commit -m "feat: add knowledge and bridge repositories with publish status" +``` + +--- + +## Task 3: KnowledgeService + +**Files:** +- Create: `src/main/services/knowledge.service.ts` +- Create: `tests/main/knowledge.service.test.ts` + +- [ ] **Step 1: UT-KNOW-02 遗忘判定测试** + +```typescript +it('UT-KNOW-02: forgotten when sort_order gap >= 20', () => { + // 创建 25 章,在第 1 章埋 foreshadow approved buried + // latestOrder - mentionOrder >= 20 → forgotten count 1 +}) +``` + +- [ ] **Step 2: UT-KNOW-03 bookmark 同步测试** + +```typescript +it('UT-KNOW-03: createFromBookmark creates pending entry', () => { + const bm = bookmarkRepo.create(chapterId, 0, '测灵石发热', 'foreshadow') + const entry = service.createFromBookmark(bm.id) + expect(entry.status).toBe('pending') + expect(entry.linkedBookmarkId).toBe(bm.id) +}) +``` + +- [ ] **Step 3: 实现 KnowledgeService** + +```typescript +const FORGOTTEN_GAP = 20 + +export class KnowledgeService { + list(filter?: { status?: KnowledgeStatus; type?: KnowledgeType; forgottenOnly?: boolean }) + create(input: CreateKnowledgeInput): KnowledgeEntry + update(id: string, patch: Partial): KnowledgeEntry + delete(id: string): void + approve(id: string): KnowledgeEntry + reject(id: string): KnowledgeEntry + batchApprove(ids: string[]): number + createFromBookmark(bookmarkId: string): KnowledgeEntry + getForeshadowStats(): { buried: number; resolved: number; forgotten: number } + countPending(): number + + private isForgotten(entry: KnowledgeEntry): boolean { + if (entry.type !== 'foreshadow' || entry.status !== 'approved' || entry.foreshadowStatus !== 'buried') return false + const mentionId = entry.lastMentionChapterId ?? entry.sourceChapterId + if (!mentionId) return false + const mentionOrder = chapterRepo.get(mentionId)?.sortOrder ?? 0 + const latestOrder = Math.max(...chapterRepo.list().map((c) => c.sortOrder), 0) + return latestOrder - mentionOrder >= FORGOTTEN_GAP + } +} +``` + +- [ ] **Step 4: IT-KNOW-04 批量采纳** + +```typescript +it('IT-KNOW-04: batchApprove clears pending', () => { + // create 3 pending → batchApprove → countPending === 0 +}) +``` + +- [ ] **Step 5: 运行 PASS + Commit** + +```bash +npm run test -- tests/main/knowledge.service.test.ts +git commit -m "feat: add KnowledgeService with foreshadow forgotten logic" +``` + +--- + +## Task 4: CockpitService + GlobalSettings 扩展 + +**Files:** +- Create: `src/main/services/cockpit.service.ts` +- Modify: `src/main/services/global-settings.ts` +- Create: `tests/main/cockpit.service.test.ts` + +- [ ] **Step 1: UT-COCK-01 测试** + +```typescript +it('UT-COCK-01: getSummary counts words and ready stock', () => { + volRepo.create('卷一', 0) + chapterRepo.create(volId, 'A', 0, '

hello world

') + chapterRepo.setPublishStatus(chId, 'ready') + const summary = service.getSummary(activeVolumeId) + expect(summary.totalWords).toBeGreaterThan(0) + expect(summary.stockReadyCount).toBe(1) +}) +``` + +- [ ] **Step 2: 实现 CockpitService** + +```typescript +export class CockpitService { + constructor( + private db: SqliteDb, + private settings: GlobalSettingsService + ) {} + + getSummary(activeVolumeId?: string): CockpitSummary { + const chapters = new ChapterRepository(this.db).list() + const totalWords = chapters.reduce((s, c) => s + stripHtml(c.content).length, 0) + const volumeWords = activeVolumeId + ? chapters.filter((c) => c.volumeId === activeVolumeId).reduce(...) + : totalWords + const stockReadyCount = new ChapterRepository(this.db).countByPublishStatus('ready') + const stats = new KnowledgeService(this.db).getForeshadowStats() + const { stockBufferThreshold } = this.settings.get() + return { totalWords, volumeWords, stockReadyCount, stockThreshold: stockBufferThreshold, ...stats } + } + + shouldShowOnOpen(): boolean { + return new BookPrefsRepository(this.db).get('cockpitSeen') !== 'true' + } + + markSeen(): void { + new BookPrefsRepository(this.db).set('cockpitSeen', 'true') + } +} +``` + +- [ ] **Step 3: global-settings defaults** + +```typescript +updateSchedule: 'none', +stockBufferThreshold: 3, +``` + +`get()` merge 时补默认值。 + +- [ ] **Step 4: 运行 PASS + Commit** + +```bash +npm run test -- tests/main/cockpit.service.test.ts +git commit -m "feat: add CockpitService and extend global settings" +``` + +--- + +## Task 5: ChapterBridgeService + +**Files:** +- Create: `src/main/services/chapter-bridge.service.ts` +- Create: `tests/main/chapter-bridge.test.ts` + +- [ ] **Step 1: UT-BRIDGE-01 文本提取测试** + +```typescript +it('UT-BRIDGE-01: extracts tail and head', () => { + const prev = chapterRepo.create(volId, '上一章', 0, '

' + '甲'.repeat(400) + '

') + const cur = chapterRepo.create(volId, '本章', 1, '

开头内容

') + const result = service.getBridgeSync(cur.id) // 无 AI 同步方法 + expect(result.previousTail.length).toBeLessThanOrEqual(300) + expect(result.previousTail.length).toBeGreaterThan(50) + expect(result.currentHead).toContain('开头') +}) +``` + +- [ ] **Step 2: 实现 ChapterBridgeService** + +```typescript +export class ChapterBridgeService { + shouldPrompt(chapterId: string): boolean { + if (dismissRepo.isDismissed(chapterId)) return false + const ch = chapterRepo.get(chapterId) + if (!ch || stripHtml(ch.content).length >= 200) return false + return chapterRepo.getPreviousChapter(chapterId) !== null + } + + extract(chapterId: string): ChapterBridgeResult { + const prev = chapterRepo.getPreviousChapter(chapterId) + const cur = chapterRepo.get(chapterId)! + const prevText = prev ? stripHtml(prev.content) : '' + const curText = stripHtml(cur.content) + return { + previousChapterId: prev?.id ?? null, + previousTail: prevText.slice(-300), + currentHead: curText.slice(0, 200) + } + } + + async getBridge(chapterId: string, withAi: boolean): Promise { + const base = this.extract(chapterId) + if (!withAi || !base.previousTail) return base + try { + const suggestion = await this.aiClient.chat([ + { role: 'user', content: `你是连载小说编辑。上一章末尾:「${base.previousTail}」。本章开头:「${base.currentHead || '(尚未撰写)'}」。用 2-3 句话指出衔接是否顺畅,并给出一个开头方向建议。不要续写正文。` } + ]) + return { ...base, aiSuggestion: suggestion.trim() } + } catch { + return base + } + } + + dismiss(chapterId: string): void { + dismissRepo.dismiss(chapterId) + } +} +``` + +- [ ] **Step 3: IT-BRIDGE-01(240s LM Studio)** + +```typescript +it('IT-BRIDGE-01: getBridge returns aiSuggestion from LM Studio', async () => { + // 两章有内容 → getBridge(id, true) → aiSuggestion.length > 10 +}, 240_000) +``` + +- [ ] **Step 4: 运行 PASS + Commit** + +```bash +npm run test -- tests/main/chapter-bridge.test.ts +git commit -m "feat: add ChapterBridgeService with optional AI suggestion" +``` + +--- + +## Task 6: IPC + Preload + +**Files:** +- Create: `src/main/ipc/handlers/knowledge.handler.ts` +- Create: `src/main/ipc/handlers/cockpit.handler.ts` +- Create: `src/main/ipc/handlers/bridge.handler.ts` +- Modify: `src/main/ipc/handlers/chapter.handler.ts` +- Modify: `src/main/ipc/register.ts` +- Modify: `src/preload/index.ts` +- Modify: `src/shared/electron-api.d.ts` + +- [ ] **Step 1: 实现 handlers** + +参照 `auto.handler.ts` 模式,`wrap(() => ...)`,`BookRegistryService.getDb(bookId)`。 + +`knowledge.handler.ts`:`list` / `create` / `update` / `delete` / `approve` / `reject` / `batchApprove` / `createFromBookmark` / `stats` + +`cockpit.handler.ts`:`getSummary(bookId, volumeId?)` / `markSeen` / `shouldShowOnOpen` + +`bridge.handler.ts`:`get(bookId, chapterId, withAi?)` / `dismiss` / `shouldPrompt` + +`chapter.handler.ts` 新增: + +```typescript +IPC.CHAPTER_SET_PUBLISH_STATUS → chapterRepo.setPublishStatus(chapterId, status) +``` + +- [ ] **Step 2: register.ts** + +```typescript +registerKnowledgeHandlers(books, settings) +registerCockpitHandlers(books, settings) +registerBridgeHandlers(books, settings, aiClient) +``` + +- [ ] **Step 3: preload 命名空间** + +```typescript +knowledge: { list, create, update, delete, approve, reject, batchApprove, createFromBookmark, stats }, +cockpit: { getSummary, markSeen, shouldShowOnOpen }, +bridge: { get, dismiss, shouldPrompt }, +chapter: { /* 现有 */ setPublishStatus }, +``` + +- [ ] **Step 4: build 验证** + +```bash +npm run build +``` + +- [ ] **Step 5: Commit** + +```bash +git commit -m "feat: add knowledge cockpit bridge IPC and preload" +``` + +--- + +## Task 7: KnowledgePanel UI + +**Files:** +- Create: `src/renderer/stores/useKnowledgeStore.ts` +- Create: `src/renderer/components/knowledge/KnowledgeEditorDialog.tsx` +- Modify: `src/renderer/components/knowledge/KnowledgePanel.tsx` +- Modify: `src/renderer/styles/layout.css` + +- [ ] **Step 1: useKnowledgeStore** + +```typescript +interface KnowledgeState { + entries: KnowledgeEntry[] + stats: { buried: number; resolved: number; forgotten: number } + tab: 'pending' | 'all' | 'foreshadow' + load: (bookId: string) => Promise + setTab: (tab: ...) => void + create / update / delete / approve / batchApprove + openForgottenFilter: () => void // tab=foreshadow + forgottenOnly +} +``` + +- [ ] **Step 2: KnowledgeEditorDialog** + +Radix Dialog 或现有 `.modal-overlay` 模式;字段:type select、title、content textarea、importance 1-5、foreshadowStatus(type=foreshadow 时)、sourceChapter select、「直接采纳」checkbox。 + +- [ ] **Step 3: 重写 KnowledgePanel** + +布局见 spec §6.4;保留 `naming-check-open`;Tab 切换过滤列表;`data-testid` 与 spec 一致。 + +- [ ] **Step 4: CSS `.knowledge-panel` / `.kb-item` 对齐 ui_pc.html** + +- [ ] **Step 5: Commit** + +```bash +git commit -m "feat: implement KnowledgePanel with review workflow UI" +``` + +--- + +## Task 8: CockpitModal + TopBar + 首次打开 + +**Files:** +- Create: `src/renderer/stores/useCockpitStore.ts` +- Create: `src/renderer/components/cockpit/CockpitModal.tsx` +- Modify: `src/renderer/components/layout/TopBar.tsx` +- Modify: `src/renderer/stores/useBookStore.ts`(openBook 后检查 cockpit) +- Modify: `src/renderer/App.tsx` 或 `EditorLayout.tsx`(挂载 CockpitModal) + +- [ ] **Step 1: useCockpitStore** + +```typescript +loadSummary(bookId, volumeId?) +open / close +markSeen(bookId) +shouldShowOnOpen(bookId): Promise +``` + +- [ ] **Step 2: CockpitModal** + +加载 summary 展示卡片;按钮: +- `cockpit-resume-edit` → close + 恢复 lastChapterId +- `cockpit-bridge-check` → close + 打开 ChapterBridgeModal +- `cockpit-forgotten-foreshadow` → close + `useKnowledgeStore.openForgottenFilter()` + 切 rightPanel knowledge + +- [ ] **Step 3: TopBar** + +替换 `comingSoon` 为 `data-testid="topbar-cockpit"` → `useCockpitStore.open()` + +- [ ] **Step 4: 首次打开书籍** + +`useBookStore.openBook` 成功后: + +```typescript +if (await ipcCall(() => window.electronAPI.cockpit.shouldShowOnOpen(bookId))) { + await ipcCall(() => window.electronAPI.cockpit.markSeen(bookId)) + useCockpitStore.getState().open() +} +``` + +- [ ] **Step 5: Commit** + +```bash +git commit -m "feat: add CockpitModal and first-open behavior" +``` + +--- + +## Task 9: ChapterBridgeModal + EditorLayout 集成 + +**Files:** +- Create: `src/renderer/components/bridge/ChapterBridgeModal.tsx` +- Modify: `src/renderer/components/layout/EditorLayout.tsx` +- Modify: `src/renderer/stores/useAppStore.ts`(bridgeOpen 状态,可选) + +- [ ] **Step 1: ChapterBridgeModal** + +打开时 `bridge.get(bookId, chapterId, true)`;展示 tail/head/aiSuggestion;checkbox dismiss + `bridge-start-writing` 关闭。 + +- [ ] **Step 2: EditorLayout 自动触发** + +`switchTarget` 到 chapter 后: + +```typescript +const should = await ipcCall(() => window.electronAPI.bridge.shouldPrompt(chapterId)) +if (should) setTimeout(() => setBridgeOpen(true), 500) +``` + +- [ ] **Step 3: 驾驶舱手动触发** + +CockpitModal `cockpit-bridge-check` 设置 `bridgeChapterId` 为当前章并打开 modal。 + +- [ ] **Step 4: Commit** + +```bash +git commit -m "feat: add ChapterBridgeModal with auto prompt on new chapter" +``` + +--- + +## Task 10: 发布状态 + 存稿提醒 + 设置 + +**Files:** +- Modify: `src/renderer/components/layout/EditorLayout.tsx` +- Modify: `src/renderer/stores/useBookStore.ts` +- Modify: `src/renderer/components/settings/SettingsPage.tsx` + +- [ ] **Step 1: 章节 publish 循环按钮** + +在 `chapter-item` 内增加小图标按钮 `data-testid={`chapter-publish-${ch.id}`}`,点击循环 `draft → ready → published`,调用 `chapter.setPublishStatus`,刷新 chapters。 + +- [ ] **Step 2: StatusBar 存稿警告** + +EditorLayout `#editor-statusbar` 内: + +```typescript +const summary = await cockpit.getSummary(bookId, activeVolumeId) +if (settings.updateSchedule !== 'none' && summary.stockReadyCount < summary.stockThreshold) { + // 渲染 data-testid="status-stock-warning" +} +``` + +书籍打开时 load summary;publish 变更后 refresh。 + +- [ ] **Step 3: SettingsPage** + +新增「更新计划」区块:`updateSchedule` select、`stockBufferThreshold` number input (1-20)。 + +- [ ] **Step 4: Commit** + +```bash +git commit -m "feat: add publish status UI stock warning and update schedule settings" +``` + +--- + +## Task 11: 地标同步 + i18n + +**Files:** +- Modify: `src/renderer/components/editor/TipTapEditor.tsx` 或 landmark 插入流程 +- Modify: `public/locales/zh-CN/translation.json` +- Modify: `public/locales/en/translation.json` + +- [ ] **Step 1: 插入 foreshadow 地标时 checkbox** + +在 insert landmark 对话框(或插入后 toast 行动)增加「同步到知识库」;勾选则 `knowledge.createFromBookmark(bookmarkId)`。 + +若当前无独立 insert UI,在 `LandmarkModal` 或 TipTap landmark 命令回调中加 post-create hook。 + +- [ ] **Step 2: i18n 键** + +`cockpit.*`、`bridge.*`、`knowledge.*`、`publish.status.draft/ready/published`、`settings.updateSchedule.*`、`stock.warning` + +- [ ] **Step 3: Commit** + +```bash +git commit -m "feat: add foreshadow landmark sync and P5 i18n strings" +``` + +--- + +## Task 12: E2E 测试 + +**Files:** +- Create: `e2e/cockpit-knowledge.spec.ts` + +- [ ] **Step 1: 无 AI 门槛测试** + +```typescript +test('E2E-COCK-01: cockpit modal shows stats', async () => { /* openBookWithContent → topbar-cockpit → cockpit-total-words visible */ }) +test('E2E-COCK-02: first open shows cockpit once', async () => { /* 开书两次 */ }) +test('E2E-PUB-01: publish ready increases stock count', async () => { /* chapter-publish click → cockpit-stock-count */ }) +test('E2E-KNOW-01: create approve knowledge entry', async () => { /* knowledge-new → pending tab → approve */ }) +test('E2E-BRIDGE-01: new chapter triggers bridge modal', async () => { /* 新章 → bridge-previous-tail visible */ }) +test('E2E-STOCK-01: low stock warning in statusbar', async () => { /* set threshold + schedule in settings, 0 ready chapters */ }) +``` + +- [ ] **Step 2: LM Studio 测试(300s)** + +```typescript +test('E2E-BRIDGE-02: bridge shows AI suggestion', async () => { + test.setTimeout(300_000) + // 两章有内容 → 手动打开 bridge → bridge-ai-suggestion 非空 +}) +test('E2E-KNOW-02: foreshadow landmark sync', async () => { /* 插入 foreshadow + sync */ }) +``` + +- [ ] **Step 3: 运行 E2E** + +```bash +npm run test:e2e -- e2e/cockpit-knowledge.spec.ts +``` + +- [ ] **Step 4: Commit** + +```bash +git commit -m "test: add P5 cockpit knowledge and bridge E2E specs" +``` + +--- + +## Task 13: v0.6.0 交付 + +**Files:** +- Modify: `package.json` +- Modify: `README.md` +- Modify: `src/renderer/components/settings/SettingsPage.tsx`(版本号展示) +- Modify: `docs/superpowers/plans/2026-07-06-bilin-p5-workflow-knowledge.md`(勾选完成项) + +- [ ] **Step 1: 版本号 → `0.6.0`** + +- [ ] **Step 2: README 功能概览追加 P5** + +```markdown +- 驾驶舱:全书/本卷字数、存稿缓冲、伏笔摘要、快速入口(P5) +- 知识库 MVP:手动条目、审核流、伏笔追踪、地标同步(P5) +- 章间衔接:上下章片段 + 可选 AI 建议(P5) +- 更新计划:发布状态、存稿阈值提醒(P5) +``` + +- [ ] **Step 3: 全量测试** + +```bash +npm run build +npm run test +npm run test:e2e +``` + +- [ ] **Step 4: Commit** + +```bash +git commit -m "feat: ship v0.6.0 with cockpit knowledge and chapter bridge" +``` + +--- + +## Spec Coverage Checklist + +| Spec 要求 | Task | +|-----------|------| +| schema v6 三表 | Task 1 | +| Knowledge CRUD / 审核 / 遗忘 | Task 2–3, 7 | +| Cockpit 聚合 + 首次打开 | Task 4, 8 | +| ChapterBridge + AI | Task 5, 9 | +| publish_status UI | Task 10 | +| updateSchedule / 存稿提醒 | Task 4, 10 | +| 地标 foreshadow 同步 | Task 11 | +| IPC / preload | Task 6 | +| i18n | Task 11 | +| 全部测试 ID | Task 1–5, 12 | +| v0.6.0 DoD | Task 13 |