Bladeren bron

fix(v7): 格式对齐 spec 0.9——卷纲子目录/卷摘要文件名/信息差字段名

- 卷纲:大纲/第NN卷.md → 大纲/卷纲/第NN卷.md(OutlineReader 读侧、persist 写侧、fixture、断言)
- 卷摘要:卷摘要/NN.md → 卷摘要/第NN卷.md(与时间线/卷纲同形态)
- 信息差字段:谁知道/读者知道 → 知情人/读者已知(spec §4.3;重建器/SecretWriter/fixture/测试,全库旧名零残留)

边界回顾 #1/#2/#3;任务 07-02-v7-boundary-code-fixes 阶段 A
lingfengQAQ 20 uur geleden
bovenliggende
commit
d593f0e774

+ 2 - 2
v7/src/cache/rebuilder.js

@@ -201,8 +201,8 @@ async function scanSecrets(repoPath, db) {
         insertStmt.run(
           id,
           id,
-          JSON.stringify(fm.谁知道 || []),
-          fm.读者知 ? 1 : 0,
+          JSON.stringify(fm.知情人 || []),
+          fm.读者知 ? 1 : 0,
           fm.登记章 || 1,
           JSON.stringify(fm.关键词 || []),
           filePath

+ 5 - 5
v7/src/state-machine/persist.js

@@ -38,14 +38,14 @@ export async function persistDraftOutline(ctx, { 细纲 }) {
   }
 }
 
-/** 序1 建书 → book.yaml + 大纲/总纲.md + 大纲/第01卷.md + .gitignore + git init + core.quotepath */
+/** 序1 建书 → book.yaml + 大纲/总纲.md + 大纲/卷纲/第01卷.md + .gitignore + git init + core.quotepath */
 export async function persistCreateBook(ctx, { book, 总纲, 卷纲 }) {
   try {
     const gitignore = await buildGitignore(ctx.repoPath, ['.cache/', '工作区/'])
     const written = await writeAtomicBatch(ctx.repoPath, [
       { path: 'book.yaml', content: serializeYAML(book) },
       { path: path.join('大纲', '总纲.md'), content: 总纲 },
-      { path: path.join('大纲', '第01卷.md'), content: 卷纲 },
+      { path: path.join('大纲', '卷纲', '第01卷.md'), content: 卷纲 },
       { path: '.gitignore', content: gitignore },
     ])
     // P0-2:书仓库工程化(spec quality §3.3 钉死建书流程负责 git init + core.quotepath)
@@ -58,15 +58,15 @@ export async function persistCreateBook(ctx, { book, 总纲, 卷纲 }) {
   }
 }
 
-/** 序4 卷复盘 → 定稿/摘要/卷摘要/NN.md + 大纲/第{卷号+1}卷.md(+ 可选伏笔条目) */
+/** 序4 卷复盘 → 定稿/摘要/卷摘要/NN.md + 大纲/卷纲/第{卷号+1}卷.md(+ 可选伏笔条目) */
 export async function persistVolumeReview(ctx, { 卷号, 卷摘要, 下卷卷纲, 伏笔条目 = [] }) {
   try {
     const files = []
     const nn = String(卷号).padStart(2, '0')
-    files.push({ path: path.join('定稿', '摘要', '卷摘要', `${nn}.md`), content: 卷摘要 })
+    files.push({ path: path.join('定稿', '摘要', '卷摘要', `${nn}.md`), content: 卷摘要 })
     if (下卷卷纲) {
       const next = String(卷号 + 1).padStart(2, '0')
-      files.push({ path: path.join('大纲', `第${next}卷.md`), content: 下卷卷纲 })
+      files.push({ path: path.join('大纲', '卷纲', `第${next}卷.md`), content: 下卷卷纲 })
     }
     for (const e of 伏笔条目) {
       const body = `---\n${serializeYAML(e.frontMatter || {})}\n---\n${e.body || ''}`

+ 2 - 2
v7/src/storage/adapters/OutlineReader.js

@@ -34,7 +34,7 @@ export class OutlineReader {
    */
   async readVolumeOutline(volumeNum) {
     const volStr = String(volumeNum).padStart(2, '0')
-    const volumePath = path.join(this.repoPath, '大纲', `第${volStr}卷.md`)
+    const volumePath = path.join(this.repoPath, '大纲', '卷纲', `第${volStr}卷.md`)
 
     try {
       const content = await fs.readFile(volumePath, 'utf8')
@@ -49,7 +49,7 @@ export class OutlineReader {
    * @returns {Promise<number[]>}
    */
   async listVolumes() {
-    const outlineDir = path.join(this.repoPath, '大纲')
+    const outlineDir = path.join(this.repoPath, '大纲', '卷纲')
 
     try {
       const files = await fs.readdir(outlineDir)

+ 1 - 1
v7/src/storage/adapters/SecretWriter.js

@@ -14,7 +14,7 @@ export class SecretWriter {
   /**
    * 写信息差到 定稿/设定/信息差/<id>.md(front matter 走防呆序列化)。
    * @param {string} id - 文件名干(如 "信息差-021-血书真相")
-   * @param {object} frontMatter - 强度/谁知道[]/读者知道/登记章/关键词[]
+   * @param {object} frontMatter - 强度/知情人[]/读者已知/登记章/关键词[]
    * @param {string} content - 正文(## 描述 / ## 内容 段)
    * @returns {Promise<{ok: boolean, filePath: string, error: string}>}
    */

+ 2 - 2
v7/test/commands/read-secret.test.js

@@ -11,11 +11,11 @@ after(async () => {
   await cleanup()
 })
 
-test('read-secret --基本信息 返回 JSON(含读者知/关键词)', async () => {
+test('read-secret --基本信息 返回 JSON(含读者知/关键词)', async () => {
   const r = await run(['信息差-001'], { 基本信息: true }, ctx)
   assert.equal(r.ok, true)
   const data = JSON.parse(r.output)
-  assert.equal(data.读者知, false)
+  assert.equal(data.读者知, false)
   assert.ok(Array.isArray(data.关键词))
 })
 

+ 0 - 0
v7/test/fixtures/sample-book/大纲/第01卷.md → v7/test/fixtures/sample-book/大纲/卷纲/第01卷.md


+ 2 - 2
v7/test/fixtures/sample-book/定稿/设定/信息差/信息差-001-灭门真凶.md

@@ -1,9 +1,9 @@
 ---
 强度: 高
 状态: 进行
-谁知道:
+知情人:
   - 林晚
-读者知: false
+读者知: false
 登记章: 1
 关键词:
   - 玉佩

+ 1 - 1
v7/test/mechanical-check/check.test.js

@@ -14,7 +14,7 @@ const 文风铁律 = `---
 节奏优先。
 `
 const 名册 = '| 正名 | 别名 | 类型 | 首现章 |\n|--|--|--|--|\n| 林晚 | 晚晚 | character | 1 |\n'
-const 信息差 = '---\n读者知: false\n登记章: 1\n关键词:\n  - 玉佩\n---\n## 内容\n秘密。\n'
+const 信息差 = '---\n读者知: false\n登记章: 1\n关键词:\n  - 玉佩\n---\n## 内容\n秘密。\n'
 
 // 组装一个含 front matter + 正文的草稿,并放进受控临时仓库
 function files(draftBody, { fm } = {}) {

+ 3 - 3
v7/test/state-machine/persist.test.js

@@ -69,7 +69,7 @@ test('persistCreateBook(序1)→ 写 book.yaml + 总纲 + 第一卷卷纲',
     assert.equal(r.ok, true)
     assert.match(await read(root, 'book.yaml'), /剑起青云/)
     assert.match(await read(root, '大纲/总纲.md'), /逆袭/)
-    assert.match(await read(root, '大纲/第01卷.md'), /入门/)
+    assert.match(await read(root, '大纲/卷纲/第01卷.md'), /入门/)
   } finally { await cleanup() }
 })
 
@@ -78,8 +78,8 @@ test('persistVolumeReview(序4)→ 写卷摘要 + 下卷卷纲', async () =>
   try {
     const r = await persistVolumeReview(ctx, { 卷号: 1, 卷摘要: '第一卷收束。', 下卷卷纲: '# 第2卷\n新地图。' })
     assert.equal(r.ok, true)
-    assert.match(await read(root, '定稿/摘要/卷摘要/01.md'), /收束/)
-    assert.match(await read(root, '大纲/第02卷.md'), /新地图/)
+    assert.match(await read(root, '定稿/摘要/卷摘要/01.md'), /收束/)
+    assert.match(await read(root, '大纲/卷纲/第02卷.md'), /新地图/)
   } finally { await cleanup() }
 })
 

+ 1 - 1
v7/test/storage/adapters/SecretReader.test.js

@@ -10,7 +10,7 @@ const fixtureRoot = path.join(__dirname, '../../fixtures/sample-book')
 test('SecretReader.readBasicInfo 读信息差 front matter', async () => {
   const r = await new SecretReader(fixtureRoot).readBasicInfo('信息差-001')
   assert.equal(r.ok, true)
-  assert.equal(r.data.读者知, false)
+  assert.equal(r.data.读者知, false)
 })
 
 test('SecretReader.readContent 读 ## 内容 段', async () => {

+ 2 - 2
v7/test/storage/adapters/SecretWriter.test.js

@@ -7,11 +7,11 @@ test('SecretWriter.write 写信息差(防呆 front matter + 内容)', async
   const root = await makeRepo()
   try {
     const w = new SecretWriter(root)
-    const fm = { 强度: '高', 谁知道: ['林晚'], 读者知道: false, 登记章: 152, 关键词: ['血书'] }
+    const fm = { 强度: '高', 知情人: ['林晚'], 读者已知: false, 登记章: 152, 关键词: ['血书'] }
     const r = await w.write('信息差-021-血书真相', fm, '## 内容\n血书写着真凶名讳。')
     assert.equal(r.ok, true)
     const content = await read(root, '定稿/设定/信息差/信息差-021-血书真相.md')
-    assert.match(content, /读者知: false/)
+    assert.match(content, /读者知: false/)
     assert.match(content, /关键词:\n {2}- 血书/)
     assert.match(content, /## 内容/)
     assert.match(content, /血书写着真凶名讳/)