dsh-vibe-math 2.7.2 → 2.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.en.md +11 -10
  2. package/README.md +13 -12
  3. package/cordis.patch.yml +112 -16
  4. package/docs/AUDIT-CHECKLIST.md +229 -3
  5. package/docs/COMPAT-AUDIT-ROUND2.md +35 -9
  6. package/docs/final-paper.md +2 -2
  7. package/docs/math-computation.md +13 -6
  8. package/docs/parameter-schema.md +37 -0
  9. package/docs/release-notes/README.en.md +1 -0
  10. package/docs/release-notes/README.md +1 -0
  11. package/docs/release-notes/RELEASE-NOTES-2.7.2.en.md +2 -2
  12. package/docs/release-notes/RELEASE-NOTES-2.7.2.md +3 -3
  13. package/docs/release-notes/RELEASE-NOTES-2.7.3.en.md +67 -0
  14. package/docs/release-notes/RELEASE-NOTES-2.7.3.md +67 -0
  15. package/docs/release-notes/TEMPLATE.md +6 -0
  16. package/docs/status-report-fields.md +167 -0
  17. package/docs/test-timing.md +8 -5
  18. package/installer.js +27 -17
  19. package/package.json +250 -203
  20. package/prompt-corpus-persona/persona-corpus.json +9 -9
  21. package/prompt-corpus-persona/persona-corpus.md +225 -33
  22. package/prompt-corpus-v2/formal-verify-v2.json +295 -115
  23. package/prompt-corpus-v2/formal-verify-v2.md +2287 -283
  24. package/prompt-corpus-v3/formal-verify-v3.json +105 -105
  25. package/prompt-corpus-v3/formal-verify-v3.md +213 -143
  26. package/prompt-corpus-v4/formal-verify-v4.json +18 -18
  27. package/prompt-corpus-v4/formal-verify-v4.md +49 -44
  28. package/prompt-corpus-v5/prompt-corpus-v5.json +82 -82
  29. package/prompt-corpus-v5/prompt-corpus-v5.md +451 -207
  30. package/scripts/update-doc-counts.mjs +71 -0
  31. package/tests/audit-artifact-docs.mjs +72 -0
  32. package/tests/audit-artifact-docs.mutants.mjs +58 -0
  33. package/tests/audit-installer-assertions.mutants.mjs +63 -0
  34. package/tests/audit-installer-compat.test.mjs +52 -4
  35. package/tests/audit-installer-policy.test.mjs +41 -8
  36. package/tests/audit-math-computation-contract.mjs +64 -8
  37. package/tests/audit-math-computation-contract.mutants.mjs +106 -0
  38. package/tests/audit-math-computation-parity.mjs +164 -3
  39. package/tests/audit-math-computation-sensitivity.mjs +4 -2
  40. package/tests/audit-package-membership.mjs +57 -0
  41. package/tests/audit-package-membership.mutants.mjs +28 -0
  42. package/tests/audit-participant-set-parity.mjs +160 -0
  43. package/tests/audit-path-discipline.mjs +83 -0
  44. package/tests/audit-path-discipline.mutants.mjs +51 -0
  45. package/tests/audit-persona-sensitivity.mjs +14 -0
  46. package/tests/audit-persona-surface.mutants.mjs +69 -0
  47. package/tests/audit-persona-surface.test.mjs +63 -22
  48. package/tests/audit-prompt-duplication.mjs +143 -0
  49. package/tests/audit-prompt-duplication.mutants.mjs +50 -0
  50. package/tests/audit-prompt-invariants.mjs +43 -5
  51. package/tests/audit-readme-bilingual.test.mjs +152 -0
  52. package/tests/audit-readme-counts.mjs +95 -0
  53. package/tests/audit-readme-counts.mutants.mjs +52 -0
  54. package/tests/audit-registration.mjs +78 -0
  55. package/tests/audit-status-report-fields.mjs +172 -0
  56. package/tests/audit-status-report-fields.mutants.mjs +64 -0
  57. package/tests/audit-v3-registration-parity.mjs +156 -0
  58. package/tests/audit-v5-integrity.mjs +300 -13
  59. package/tests/audit-v5-lean-abstention.mjs +392 -0
  60. package/tests/audit-v5-lean-abstention.mutants.mjs +106 -0
  61. package/tests/audit-v5-prompt-duplication.mjs +88 -0
  62. package/tests/audit-v5-prompt-duplication.mutants.mjs +34 -0
  63. package/tests/audit-v5-sensitivity.mjs +12 -7
  64. package/tests/e2e-identity-a6.mutants.mjs +65 -0
  65. package/tests/e2e-v5-round2.test.mjs +458 -13
  66. package/tests/formal-verify-v2.test.mjs +91 -43
  67. package/tests/formal-verify-v3.mutants.mjs +117 -0
  68. package/tests/formal-verify-v3.test.mjs +85 -39
  69. package/tests/formal-verify-v4.mutants.mjs +152 -0
  70. package/tests/formal-verify-v4.test.mjs +541 -9
  71. package/tests/formal-verify-v5.test.mjs +68 -9
  72. package/tests/helpers/doc-counts-format.mjs +41 -0
  73. package/tests/helpers/math-computation-fake-seam.mjs +4 -0
  74. package/tests/math-computation-a1.mutants.mjs +72 -0
  75. package/tests/math-computation-archive-rerun.mutants.mjs +65 -0
  76. package/tests/math-computation-discovery.mutants.mjs +76 -0
  77. package/tests/math-computation-shared.test.mjs +274 -5
  78. package/tests/math-computation-v2.test.mjs +767 -0
  79. package/tests/math-computation-v3.test.mjs +759 -0
  80. package/tests/math-computation-v4.mutants.mjs +133 -0
  81. package/tests/math-computation-v4.test.mjs +739 -0
  82. package/tests/prompt-v5-integrity.test.mjs +13 -2
  83. package/tests/run-tests.mjs +139 -9
  84. package/tests/run-tests.mutants.mjs +70 -0
  85. package/tests/selfdrive-v5.mjs +53 -1
  86. package/tests/selfdrive-v5.mutants.mjs +132 -0
  87. package/tests/v2-fix-probes.mutants.mjs +100 -0
  88. package/tests/v2-fix-probes.test.mjs +1039 -0
  89. package/tests/v2-list-agents-and-next-step.mutants.mjs +106 -0
  90. package/tests/v3-fix-probes.mutants.mjs +95 -0
  91. package/tests/v3-fix-probes.test.mjs +891 -0
  92. package/tests/v5-institute-fixes.mutants.mjs +190 -0
  93. package/vibe-math-v2/agent.cordis.yml +62 -6
  94. package/vibe-math-v2/math-computation.js +130 -41
  95. package/vibe-math-v2/math-engines.js +12 -12
  96. package/vibe-math-v2/vibe-math-v2.js +388 -113
  97. package/vibe-math-v2//345/256/236/347/216/260/346/226/271/346/241/210.md +5 -1
  98. package/vibe-math-v3/agent.cordis.yml +72 -6
  99. package/vibe-math-v3/math-computation.js +130 -41
  100. package/vibe-math-v3/math-engines.js +12 -12
  101. package/vibe-math-v3/vibe-math-v3.js +422 -126
  102. package/vibe-math-v4/agent.cordis.yml +44 -8
  103. package/vibe-math-v4/math-computation.js +130 -41
  104. package/vibe-math-v4/math-engines.js +12 -12
  105. package/vibe-math-v4/vibe-math-v4.js +196 -43
  106. package/vibe-math-v4//345/256/236/347/216/260/346/226/271/346/241/210.md +2 -0
  107. package/vibe-math-v5/agent.cordis.yml +46 -12
  108. package/vibe-math-v5/math-computation.js +130 -41
  109. package/vibe-math-v5/math-engines.js +12 -12
  110. package/vibe-math-v5/vibe-math-v5.js +1173 -218
  111. package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +80 -12
  112. package/vibe-math-v5//346/236/266/346/236/204/345/233/276.md +2 -2
@@ -61,7 +61,7 @@
61
61
  - [ ] 框头**类型**是否正确?(私信 ≠ 致全体表决者;督办 ≠ 分派;所办分派 ≠ 院士分派)
62
62
  - [ ] 系统/框架自己发出的反馈,**发送者**是否是框架自己(而不是"该成员发给自己",
63
63
  那会被自消息校验拒掉、静默失效)?
64
- - [ ] 一次提示词里,**同一条消息是否只出现一次**?(先 ack 再构造,否则会投递两遍)
64
+ - [ ] 一次提示词里,**同一条消息是否只出现一次**?(**先构造提示词、仅在发送成功后才 ack**(`if (ok && prompt.pending.length) await ackPending(prompt.pending)`);**同一轮内不重复 prepend** —— 去重靠 `inboxInjected`/`inboxSuppressed`,与 ack 时机无关。**这正是 G1 修好的缺陷**:在构造阶段就确认会丢消息,见 `tests/e2e-v5-round2.test.mjs` §[47])
65
65
  - [ ] 会议提示里"其他人的发言"是否只列**别人**、且用真实代号?
66
66
  - [ ] 表决提示里的对象、陈述、历史票是否与该对象真正对应?
67
67
  - [ ] 转述/中继的消息(提议开会、增聘、临时工意见、反对分派)是否署**真实发起人**?
@@ -216,8 +216,8 @@ v2/v3/v4/v5 是**同构实现**(同一份契约、四份独立代码,刻意
216
216
  ② 建一个 `git worktree` 拿改动前的检出,同一套件在两种布局各跑一遍,归一化路径/临时目录/耗时后
217
217
  **逐行比对**(`_oneoff/layout-invariance.mjs`);③ 相对链接扫描 0 失效(`_oneoff/scan-links.mjs`)。
218
218
  - [ ] 讲"全套件 / 门禁 / 多少次全绿"时,是否区分了**随包发布面**与**仓库**?`package.json` 的 `files`
219
- 只发 `tests/` 的 **23 项**(其中 `.test.mjs` **11** 个),完整门禁(`node tests/run-tests.mjs`,
220
- 当前 **65 项 = 44 套件 + 21 探针/变体**)只在开发检出里成立。两边的清单见
219
+ 只发 `tests/` 的 **65** 项(**文件计数**;其中 `.test.mjs` **17** 个),完整门禁(`node tests/run-tests.mjs`,
220
+ 当前 **101 项作业(job count)= 44 套件 + 57 探针/变体**,由 `node tests/run-tests.mjs --counts` 派生)只在开发检出里成立。两边的清单见
221
221
  `docs/test-timing.md` §1.1;发布物里的 runner 会把缺失/跳过项**打印出来**(不会静默少跑),
222
222
  所以"安装用户照文档跑得到全套件"这类说法必须避免。
223
223
  - [ ] **runner 本身也要跑一遍**:直接跑套件通过 ≠ 并行 runner 通过(2.3.13 就出现过
@@ -516,3 +516,229 @@ DSH 的 **agent preset 交付方式**在 0.1.6 → 0.1.7 之间换过一次,
516
516
  要求:**分数 ≥80 且无 critical/high**。命令与安装方式见 §9 顶部。
517
517
  - Action 版本核对脚本已随之删除(没有工作流就没有可核对的 pin)。**若将来有人(包括我)真要引入工作流**:
518
518
  第三方 Action 必须钉 commit SHA 并保留版本注释,同时把本节的结论一并更新,并说明这次引入解决了什么原文中非做不可的问题。
519
+
520
+
521
+ ## 本周期新增守卫索引(守卫/章节 → 它钉住的不变量 → 证明它会咬的变异)
522
+
523
+ > 维护规则:只列**能在代码里指出来**的守卫;每行给出"不变量"与"变异"两列。**变异脚本多数已随包** —— 形式是 `tests/*.mutants.mjs`(单位:文件名)或套件内的 `--self-probe` / 变异段(由 runner 的 `VARIANTS` 注册为 **job**)。仍标 **dev-only** 的行指向 `_oneoff/auditR2/*`(**不在 `files[]`**),属**剩余待转换项**;转换完成时该行的 dev-only 标记必须一并删掉。
524
+
525
+ | 守卫 / 章节 | 钉住的不变量 | 证明它会咬的变异 |
526
+ |---|---|---|
527
+ | `tests/audit-path-discipline.mjs`(**probe,已随包发布**) | v4/v3 的成员可见文本不得声明 `Members/<x>/` 根;v5 必须带成员库根(两侧相反,是**有意**的) | harness = `tests/audit-path-discipline.mutants.mjs`:4 个族、`ALL FAMILIES BITE`(4/4) |
528
+ | `tests/audit-v5-sensitivity.mjs` | v5 提示词面/事件源的结构性变异必须让对应完整性守卫变红 | 该套件自身的"每变异一探针" |
529
+ | `tests/audit-v3-registration-parity.mjs` | v3 的工具注册与声明面一致 | 该套件自身(`run-tests.mjs:87-90` 以显式 args 运行它) |
530
+ | `tests/formal-verify-v4.test.mjs` §N5–§N9 | N5/N6 路径纪律;N9 行为证明:写到**文档化路径**的卡片必须被 `countArtifacts` 计入(自动会议基数) | 该套件自身的变异/行为断言(§N9 为真机行为证明) |
531
+ | `tests/audit-participant-set-parity.mjs` | 四个预设各**只有一个**参与集生产者;集合与"版本/期望"同时给出;v2/v3 的有意差异被钉住 | **随包** `node tests/audit-participant-set-parity.mjs --self-probe`(**6/6**:同一批谓词对**真实源码**为真、对**故意改坏的字符串**为假 —— 单位 = 谓词用例数,逐条打印 `green-now=… broken-goes-red=…`);该变体已注册为 runner job。原 `_oneoff/auditR2/participant-set-proof.mjs` 已删除(其内容即此自探针) |
532
+ | `tests/math-computation-shared.test.mjs` §23 / §23b | 装了但不在 PATH 的引擎可被发现;根来自**宿主注入**(不依赖本机 `ProgramFiles`);R/Octave/Julia/MATLAB 每 OS 根齐备 | **随包** `node tests/math-computation-discovery.mutants.mjs`(**5/5**:**副本对基线绿**(未变异的模块对)+ m3 丢掉已知安装目录查找 ⇒ **376/4**、b1 忽略宿主注入根 ⇒ **376/4**、b2 删掉 octave/julia 每 OS 根 ⇒ **378/2**、m4 假设包管理器存在 ⇒ **376/4**;每个都命中**具名** §23/§23b 断言,单位 = 具名断言标签)。原 dev-only 草稿 `round9-mutants.mjs`、`roundB-mutants.mjs` 已删除 |
533
+ | 同上 §26 | 编辑**原源文件**重跑 ⇒ 同一归档 + attempt≥2 + `scriptChanged`;指向**归档副本** ⇒ 新归档 + `fileIsArchivedScript`/`ARCHIVED_SCRIPT_RERUN` | **随包** `node tests/math-computation-archive-rerun.mutants.mjs`(**3/3**:**副本对基线绿** + a1 删掉 `ARCHIVED_SCRIPT_RERUN` 告警 ⇒ **379/1**、a2 让归档源永不被识别 ⇒ **378/2**;每个都命中**具名** §26 断言,单位 = 具名断言标签)。原 `roundA-mutants.mjs`(a1/a2)与 `round9-mutants.mjs` 的引用均已转换/删除 |
534
+ | 同上 §28 | **任何** spawn 都不得带未替换的 argv 占位符(matlab 的 `run('<script>')` 内嵌形态) | **随包** `node tests/audit-math-computation-parity.mjs --self-probe`(§7 四条:STRING-level 替换、未知占位符、已知占位符必须有实现、**旧写法产物必被谓词抓住**;单位 = 谓词用例数,逐条打印 `green-now=… broken-goes-red=…`);该变体已注册为 runner job |
535
+ | 同上 §29 | cli 策略来自**描述符**(`CLI_POLICY`),模块内不得再有硬编码比较;拒绝里带描述符值 | **随包** 同上 `--self-probe`(§8 四条:声明、被消费、无硬编码、拒绝里带描述符值)+ 敏感度探针 `cli-policy-ignored` |
536
+ | 同上 §30 | 商业模板的 `verify`/`verifyReason` **到达用户**;未声明者**不打印空槽** | **随包** `node tests/math-computation-shared.test.mjs --self-probe`(**case 3 / §30:2/2**:从被测模块里抽出**真实**的 `verifyFields(engine)` 助手,喂一个 stub 描述符表,删掉它唯一的 reason 行(或空槽守卫)后,**§30 自己的谓词** `carriesVerifyProvenance` / `hasNoVerifySlot` 必须翻假;单位 = 谓词用例数);该变体已注册为 runner job。原 `_oneoff/auditR2/verify-provenance-proof.mjs` 已删除 |
537
+ | `tests/audit-math-computation-parity.mjs` §7 / §8 | 描述符不得用未知占位符、每个占位符都有实现;cli 策略"声明一次 + 被消费";僵尸字段(`winPrefix`/`stdinArgv`/`defaultOn`)不得回归 | **随包** `--self-probe`(**8/8**,§7 与 §8 各四条);两个 dev-only 草稿(`descriptor-sweep-proof.mjs`、`cli-policy-proof.mjs`)已删除 |
538
+ | `tests/audit-installer-compat.test.mjs`(版本探测块 + `%s` 断言 + **A7 结构化断言**) | 多来源不一致时出现 `disagreement`(一致时**不出现**);日志行不得带 `%s`/`%d`;**版本与 source 断言在 `detectDshVersion` 的结构化返回值上**(不是"日志里出现过这个版本串"或"出现过 source 这个词"),日志只保留一条**行形状**检查 | **随包** `tests/audit-installer-assertions.mutants.mjs`(**5/5** 具名红:A7-version ⇒ compat 74/3、A7-source ⇒ 75/2)。早先的 `_oneoff/auditR2/installer-mutants.mjs` 已被它取代(**仅开发检出**,历史) |
539
+ | `tests/audit-installer-policy.test.mjs`(**A8 + 动作/语言约定**) | ① 措辞类断言换成**报告级**:`failedLog(logs).length === 0`(不是"日志里没有『备份失败』四个字");② **一个动作一行**:restore / cleanup 各**恰好一次**(`EXACTLY ONCE`);③ **一个动作一种语言**:任何面向用户的日志**标签**(`[dsh-vibe-math] ` 之后、首个 `:`/`:` 之前)**不得是纯 ASCII**(英文领域词夹在中文标签里、如 `preset 声明方式`,是合规的)——**源码扫描 + 运行期捕获日志两侧都查**;只保留**一条**明确标注的 `[wording smoke check — the ONE allowed]` | **随包** `tests/audit-installer-assertions.mutants.mjs`(**5/5** 具名红:A8-failure ⇒ policy 86/9、DL-label ⇒ 93/2、DL-duplicate ⇒ 94/1) |
540
+ | `tests/audit-persona-surface.test.mjs`(允许清单) | 新注册的工具必须**被提及或显式入允许清单**;允许清单项必须是真实工具 | **随包** `tests/audit-persona-surface.mutants.mjs`(**3/3**:真实树基线绿 + **`PERSONA_ROOT` 副本树基线绿**(证明该缝忠实、变异才是红因)+ 注册一个未被提及的工具 ⇒ 守卫**具名红**);另见本就随包的 `tests/audit-persona-sensitivity.mjs`(多探针,同一 `PERSONA_ROOT` 机制,含 `v2-phantom-tool-mentioned` 等) |
541
+ | `tests/audit-persona-sensitivity.mjs`(并发/原子写) | 语料写入**原子**(临时文件 + rename),并发读者不会读到撕裂文件;敏感度运行前先"预热"语料 | **随包**:① 原子写的不变量在 `tests/audit-persona-surface.test.mjs` 的 `writeAtomic`(`renameSync`)与 `tests/audit-installer-policy.test.mjs` §10「preset writes are ATOMIC」(失败写不得触碰目标)里被断言;② 敏感度守卫自己在 `:75` 记录"surface 的写入是原子的(临时文件 + rename),并发写者无法撕裂";③ 多探针 harness = `tests/audit-persona-sensitivity.mjs` 本身(`PERSONA_ROOT` 双方向 + SETUP-FAIL ≠ detection)。原 `_oneoff/auditR2/concurrency-proof.mjs` 的撕裂演示属**机制证据**(旧写法 100 撕裂 → 原子 0),已被上述断言取代 |
542
+
543
+ > **运行方式(本轮核实)**:`tests/run-tests.mjs:93-110` 收集 `tests/` 下**每一个** `.mjs`(仅跳过自身、`NEEDS_ARGS` 清单与 `replacedBare` 变体),因此 `audit-path-discipline.mjs` 是**门禁内的 probe**,不是"只能手动跑";未随包发布的守卫(见 `package.json#files` 的 tests 子集)在安装树里不可运行。
544
+
545
+
546
+ ## 什么随包发布、为什么(分割必须是**有意**的)
547
+
548
+ - **规则(已批准)**:**守卫与其证明 harness 一起发布** —— `tests/audit-*.mjs` 及其 `*.mutants.mjs` 是**可外部复核的契约证据**;**`_oneoff/**` 与 `tests/.audit-mutants/**` 永不随包发布**(开发暂存物与变异工作区,安装树里没有它们的位置)。
549
+ - **随包发布**:能在**已安装**的树里独立验证的守卫/审计 —— 全部 `tests/audit-*.mjs`(含 `audit-market-metadata`、`audit-readme-bilingual`、`audit-v3-registration-parity`、`audit-registration`、`audit-path-discipline` 及其 `audit-path-discipline.mutants.mjs`)、`math-computation-v{2,3,4}.test.mjs` 契约套件、`v2-fix-probes.test.mjs` / `v3-fix-probes.test.mjs`。
550
+ - **不随包发布**:需要**开发宿主或活的 DSH 会话**才有意义的 —— `e2e-*`、`selfdrive-*`,以及任何需要启动宿主/会话或读取 `_oneoff/` 暂存物的套件(安装树里没有这些前提,跑起来只会给假红/假绿)。
551
+ - **有意排除的例子(必须逐文件取证,不能从总数推断)**:`tests/audit-market-metadata.test.mjs` **不随包发布**——它检查的是**仓库里的图片产物**(`示例图/`),这些不在 tarball 内;随包会让**发布验证在解包后的包里失败**。该守卫**自己**就用一条断言钉住这个排除("this repository-only guard is NOT listed in package.json files")。
552
+ - 维护规则:新增守卫时先回答"它在**已安装**的树里有意义吗?";有意义就同时加进 `package.json#files`,否则写一行理由。
553
+
554
+ ### 附:十条"断不住"的断言特征(写新守卫前先对照)
555
+
556
+ > 这份清单从开发检出的 `开发检出里的审查台账(随包文档不含该路径)` **精简**而来(**不链接**它:随包文档不依赖 `开发检出`)。> 每条都配一个"如何证明它会咬"的要求:**先让守卫红一次**,再让它绿。
557
+
558
+ | # | 特征 | 症状 | 对策 |
559
+ |---|---|---|---|
560
+ | 1 | **空断言(vacuous)** | 两边取同一表达式/同一变量,恒真 | 让两边来自**不同来源**(代码 vs 规格/文档/常量) |
561
+ | 2 | **条件门(conditional gate)** | 前提(如 `if (x)`)不成立时整段被跳过,却仍算"通过" | 前提不存在时**显式失败**或记为 skip 并计数 |
562
+ | 3 | **匹配器过宽** | `includes("source")`、`/./`、`length > 0` 之类任何输入都能过 | 锚定**行形状**或断言**结构化字段** |
563
+ | 4 | **对象错位(wrong subject)** | 断言的是"附近的"东西而不是要保的东西(如断言了副本、断言了另一个函数) | 断言**产出该事实的那一处**,并写清主语 |
564
+ | 5 | **静默跳过** | 缺文件/缺环境时 `return`/`continue`,报告里看不出 | 跳过要**打印并计数**(本仓库 `NEEDS_ARGS` 是范例) |
565
+ | 6 | **只数数量** | 只断言"共 N 条",不看内容 | 数量 + **至少一条内容**/集合相等 |
566
+ | 7 | **固定文案** | 断言语义却钉死措辞,改写即红、静默降级却绿 | 断言**结构化字段/报告**,最多保留一条明确标注的文案冒烟检查 |
567
+ | 8 | **重复来源** | 生产者与断言读**同一**来源,同错同绿 | 交叉来源:代码 vs 语料 vs 文档;或独立推导 |
568
+ | 9 | **缺 harness** | 守卫存在但没有任何输入能让它变红("文档,不是守卫") | 每条守卫配一个变异/证明脚本,并在守卫索引里登记 |
569
+ | 10 | **环境依赖** | 只在某台机器/某次顺序下成立(真实引擎、临时目录、并发) | 夹具显式隔离环境;并发敏感的加并行压力证明 |
570
+
571
+ **验收标准**:每条新守卫都要回答"我怎么让它红一次";答不出来就不算守卫。
572
+
573
+
574
+ ## 守卫索引补遗(fallout #4)
575
+
576
+ **图例(红一次的要求)**:每一行都必须回答"我怎么让它**红一次**"。标 **in-repo** 的 harness 随包发布(`tests/**.mutants.mjs` 或套件内的变异/行为段,含 `--self-probe`),**包内读者可复现**;标 **dev-only** 的只能在本仓库的开发检出里跑(不在 `files[]`),**包内读者无法复现** —— 它们应被逐步转成 in-repo 形式。**当前随包的 `tests/*.mutants.mjs` 共 21 个**(计数单位 = 文件名,可由 `Get-ChildItem tests/*.mutants.mjs` / `ls tests/*.mutants.mjs` 复核;含本轮新增的 `audit-persona-surface.mutants.mjs`、`audit-installer-assertions.mutants.mjs`、`audit-status-report-fields.mutants.mjs` 等);仍指向 `_oneoff/auditR2/*` 的行是**剩余待转换项**。
577
+
578
+ | 守卫 / 位置 | 钉住的不变量 | 红一次(harness) |
579
+ |---|---|---|
580
+ | A1 v2/v3 skip 台账 + `mgrBranchExercised` | 跳过/未走到分支必须留痕,不能沉默 | in-repo:`tests/math-computation-a1.mutants.mjs`(**2/2** 族具名红,`skipped=[]`,`exit=0`;v2/v3 两个预设) |
581
+ | A6 name-set 快照 | 工具名集合变化必须被察觉 | in-repo:`tests/e2e-identity-a6.mutants.mjs`(**2/2** 族具名红,`skipped=[]`,`exit=0`;v2/v3 两个预设) |
582
+ | F1 v3 planner 集合相等 | planner 的两侧集合必须相等 | in-repo:`tests/v3-fix-probes.mutants.mjs`(**族数从族自报行现取**:本轮 **11/11** 具名红;族名以该文件为准,含 F2cap / F-A / F5 / F4c / F6a / F6b / F6c 等 —— **不要照抄固定数字**,跑一次即知) |
583
+ | F2 v3 id 契约 | id 形状/唯一性契约 | in-repo:`tests/v3-fix-probes.mutants.mjs`(同上:本轮 **11/11**;v2 对应族 `tests/v2-fix-probes.mutants.mjs` 本轮 **10/10**;两者都打印 `TOTAL WALL TIME` 供复核) |
584
+ | F3 v2 `list_agents` | 该工具必须存在且被提及 | **随包** `node tests/v2-list-agents-and-next-step.mutants.mjs`(**6/6**:三个**副本基线绿**(`MC_V2_PLUGIN` / `V2_PLUGIN` / `PERSONA_ROOT` 各一)+ **存在性**:改名注册 ⇒ `math-computation-v2` 具名红 `对照:vibe_math_list_agents 可调用`(单位 = 工具名)+ **被提及**:注册名改了而 persona 仍写旧名 ⇒ `audit-persona-surface` 具名红 `every registered tool is mentioned in the persona, or explicitly allow-listed`(单位 = 已注册工具名)) |
585
+ | F4 v2 require-gate 下一步 | 拒绝必须给出可执行的下一步 | **随包** `node tests/v2-list-agents-and-next-step.mutants.mjs` 的 **F4 用例**:删掉 `下一步:把该对象形式化到 Lean 通过` ⇒ `v2-fix-probes` 具名红 `★★ [A5] require-gate 反馈行携带下一步`(单位 = 拒绝点)。**同时修了缝**:`v2-fix-probes.test.mjs` 的三处**源码文本**断言原先直接读仓库文件(`V2_PLUGIN` 够不到),现统一走 `readPluginSrc()`(默认仍是仓库文件),因此文本断言与行为断言读**同一个** artifact |
586
+ | A5 v5 kind 多重集 + id 集 | 事件 kind 的多重集与 id 集不变 | 随包变异族(R14 实测):`A5-ids` ⇒ **计数断言 0 红**、**ID-SET 语义断言红**,整轮 **82 红**;`A5-kinds` ⇒ **计数断言 0 红**、**KIND 语义断言红**,整轮 **21 红**。**正确措辞**:计数断言**保持绿**、新加的**语义断言必然变红**,其余红是**同一次变异的下游级联**(下游用例引用 id 前缀/kind)—— 不要写成"只有 ID-SET/KIND 变红" |
587
+ | v5 L1 `leanPathContractOk` | Lean 路径契约(文档根 = 框架根) | **随包(此前的"显式有界"裁定已被取代 —— 判别证据已经存在)**:① 断言 `tests/e2e-v5-round2.test.mjs` §[53] 的 `★★ [v5 L1] a HEALTHY composition surfaces no Lean path contract note in report() diagnostics`(`:2420`;同组还有 `:2399` 符号锚、`:2401` "调用并入队具名提示"、`:2403` "下一次提交排空");② 变异族条目 `tests/v5-institute-fixes.mutants.mjs` 的 `v5 L1: the Lean tools short form resolves to the WRONG file (path contract broken)` —— **单点** `const viaShort = leanAbsPath(probe)`(插件 `:1024`)⇒ `leanAbsPath(probe + 'x')`;③ **实测两条**:修复树 `passed=418 failed=0 ALL GREEN`;**独立路线**(预设复制到 **ASCII 临时路径**、同一单点变异、`V5_PLUGIN=<copy>/vibe-math-v5.js node tests/e2e-v5-round2.test.mjs`)⇒ **`passed=417 failed=1`**,**唯一**失败就是上面那条具名断言,同时 stderr 出现检查器自己的 `Lean path contract BROKEN …`;族自报 **12/12**、`hangs=[]`、`skipped=[]`、`TOTAL WALL TIME 304581 ms`(复核方同修订 306599 ms)。**机制(为何此前会误判为有界)**:该提示只**入队** `pendingLoadNotes`(`:1147-1151`,上限 5),要等**下一次提交**由 `drainLoadNotes()` 折进 `loadProblemLog`/`stateCache.diagnostics` 才可见(`:1193` 处的调用)—— 所以判别面是 **`report()`**,断言需要那一步"再提交";初期只查 `status().persistence.loadProblem` 因而看不到它 |
588
+ | v5 L2 `defectTargetsThisReply` | `defect` 只能针对本轮对象 | **随包** `tests/audit-v5-lean-abstention.mutants.mjs` 的 **M2**(已存在,不是缺口):它变异的正是该谓词,并让 `tests/audit-v5-lean-abstention.mjs` 的**具名**断言 `S2: the same reply cannot conclude the object it declared defective` 变红。实测控制(本机复核过):`MUTANTS_ONLY='M2 enforcement disabled' node tests/audit-v5-lean-abstention.mutants.mjs` ⇒ `control (unmutated): exit=0 | passed=31 failed=0` + `ALL MUTANTS RED AS REQUIRED`,exit 0 |
589
+ | v4 N10 compaction agent-local 守卫 | compaction 只作用于本 agent | `formal-verify-v4.test.mjs` §N10(套件内行为断言) |
590
+
591
+ **包边界核对(实测,随批次变化)**:`package.json#files` 共 **173** 条(本版新增两份发布说明),其中 `_oneoff` 条目 **0 条**;随包 `tests/*.mutants.mjs` **24 个**(单位 = 文件名)。**这两类数字必须重新派生、不要照抄**:`node -e "console.log(Object.keys(require('./package.json').files).length)"` 与 `ls tests/*.mutants.mjs | wc -l`(Windows:`(Get-ChildItem tests/*.mutants.mjs).Count`)。**本轮之后索引里已无 live dev-only 行**:凡是提到 `_oneoff/auditR2/*` 的地方都明确标为**历史/已被取代**;如需新增证据,一律走 `tests/*.mutants.mjs` 或套件内 `--self-probe`(并在 runner 的 `VARIANTS` 里注册为 job)。
592
+
593
+
594
+ ### 自检的能力边界("自检通过" ≠ "兼容")
595
+
596
+ - `checkHostCapabilities`(以及安装器自检)只证明**服务/API 的存在与形状**,**不证明运行时行为语义**;F-4c/F-5/F-6 一类的语义缺陷对它是**结构性不可见**的。
597
+ - 因此**通过自检不得被读成兼容性结论**。安装器把这句话印在**用户可见的自检通过行**上(`installer.js` 的 `宿主自检通过(…)`),并由 `tests/audit-installer-compat.test.mjs` 的断言钉住(该行必须同时出现"形状"与"行为")。
598
+
599
+
600
+ ### 跑测日志的可诊断性("红必须点名断言")
601
+
602
+ - **约定**:**红必须点名断言**。`tests/run-tests.mjs` 在失败时把该套件的**具名失败行**(从 stdout+stderr 提取,无具名行时回退到最近 40 行原始输出)打印在**每条 `FAILED: <file> [suite|probe]` 行下面**,套件自身的最后一行的摘要不再是唯一线索;TOTAL 行在失败清单之前。
603
+ - **已关闭的反向缺陷(假阳性)**:提取器按行的**自有前缀**、**行首锚定**判定(`^\s*(FAIL\b|FAILURES:|✗|✘)`、断言条目 `^\s*[-*]\s+\S`、行首中止标记 `^\s*(SyntaxError|TypeError|ReferenceError|Error):`),并**显式排除 `^\s*ok\b`** —— 因此一条**通过**行即使把字面量 `FAIL - ` 写进消息里,也不会被当成失败(v2/v3 owner 曾因此让一次全绿看起来变红)。
604
+ - **怎么让它红一次(in-repo)**:`node tests/run-tests.mjs --self-check` 三个用例 —— ①失败子进程产出**具名**行;②**通过行内含 `FAIL - ` ⇒ 提取结果为空**;③真正的 ` FAIL - …` 行与 `TypeError:` 标记仍被提取;反向证明由随包的 `tests/run-tests.mutants.mjs` 给出(**2/2**:基线通过;把具名提取改成常量 ⇒ `--self-check` 变红)。
605
+
606
+
607
+ ### 补遗:v5 提示词双副本守卫 + 派生语料的规则
608
+
609
+ | 守卫 / 位置 | 钉住的不变量 | 红一次(harness) |
610
+ |---|---|---|
611
+ | `tests/audit-v5-prompt-duplication.mjs`(**已随包**) | 提示词恰好两份副本、**归一化后完全相同**,且 P1/P2/P3 六条子句在**两份**里都在 | 随包的 `tests/audit-v5-prompt-duplication.mutants.mjs`:对**一份**副本做单点编辑 ⇒ 具名红("the two prompt copies are IDENTICAL after normalisation …"、"P1 exception present in copy 1"、"P2 disclosure present in copy 1") |
612
+ | `tests/audit-v5-prompt-duplication.mutants.mjs`(**已随包**) | 上表的证明本身 | 它就是 harness;`node tests/run-tests.mjs` 自动发现两者(`tests/*.mjs` 枚举,非 `NEEDS_ARGS`) |
613
+
614
+ **派生语料的规则(协议级)**:`prompt-corpus-persona/*` **不是**需要被"重定向"的随包语料,而是**人格/提示词源的派生产物**;因此当人格源发生编辑时,**随后的一次门禁运行合法地重新生成它**(本轮重生成的语料里正好含新的 P2 披露文本)。规则:**把重生成与它的源编辑一起提交**;绝不把一个脏的派生语料留过提交;并且在有人正在编辑人格/提示词源时**不要跑全量门禁**。
615
+
616
+
617
+ ### 派生规则与探针卫生(镜像协议 §9.6 / §9.6b / §9.7)
618
+
619
+ **两个派生件,成对重生成(§9.6 / §9.6b)**
620
+ - `cordis.patch.yml` 是 `scripts/build-preset-rows.mjs` 的**生成物**(不是手写文件);任何一个 bundle 输入(如 `vibe-math-v*/agent.cordis.yml`)被编辑,就必须在**同一批**里重跑 `node scripts/build-preset-rows.mjs`。守卫的报错已经写明准确命令,保留这个措辞:
621
+ `cordis.patch.yml is exactly what scripts/build-preset-rows.mjs generates — hand-edited? regenerate with: node scripts/build-preset-rows.mjs`。
622
+ - `prompt-corpus-persona/*` 是**人格/提示词源的派生语料**(不是需要被"重定向"的随包源):人格源一改,随后一次门禁运行会**合法地重生成**它。
623
+ - **共同规则**:源编辑与它的派生件**一起提交**;绝不把脏的派生件留过提交;**不要**在有人正编辑源时跑全量门禁(改完再跑,让重生成随提交落地)。
624
+
625
+ **探针卫生两条(§9.7 ⑤ / ⑥)**
626
+ - ⑤ **重新实例化的探针必须从"seam 解析出的路径"复制**(即夹具实际解析到的那份),不能从硬编码/临时路径复制 —— 从错路径复制会让探针**空过**(vacuous),它证明的是"副本里没这东西",而不是"代码里没有"。
627
+ - ⑥ **断言真正的契约**,不要断言"它出现在这一节里":按小节 grep 只能证明**局部可见**,不能证明契约成立(同一条误判在本项目 5× 矩阵里被抓到过一次)。
628
+
629
+ ### 补遗二:参数化提示词双副本守卫(v2/v3/v4)
630
+
631
+ | 守卫 / 位置 | 钉住的不变量 | 红一次(harness) |
632
+ |---|---|---|
633
+ | `tests/audit-prompt-duplication.mjs`(**已随包**,对 v2/v3/v4 参数化) | 每套恰好两份副本;两份共享一段**不短于较短者 60%** 的前缀(v2 0.957 / v3 0.965 / v4 0.97),且 copy 1 **多出的行只能是 YAML 脚手架/宿主后缀**,绝不是提示词正文;待落地的 V2-1/V2-3 子句必须出现在**共享前缀**里(**红先**:默认运行在 V2-1/V2-3 编辑落地前**预期为红**——当前 9 passed / 15 failed;编辑落地并重生成派生件后转绿。只看对照时用 `PROMPT_DUP_SKIP_CLAUSES=1` ⇒ 9/0。**不要**为了让门禁变绿而削弱本守卫) | 随包的 `tests/audit-prompt-duplication.mutants.mjs`:每套对**单份**副本做一次编辑 ⇒ **三条具名红**(`share a substantial common prefix (32/70=0.46)`、`(13/85=0.15)`、`(59/101=0.58)`) |
634
+ | `tests/audit-prompt-duplication.mutants.mjs`(**已随包**) | 上表的证明本身 | 它即 harness;`run-tests.mjs` 的 `tests/*.mjs` 枚举自动发现两者(非 `NEEDS_ARGS`) |
635
+
636
+ > 与 v5 版的关系:`tests/audit-v5-prompt-duplication*.mjs` 钉 v5 的两份副本;本文件把同一不变量**参数化到 v2/v3/v4**。两族的"红一次"都由随包的 `*.mutants.mjs` 给出(shipped guard + shipped harness)。
637
+
638
+
639
+ ### 发布说明的可追溯性(追溯缺口是**记录**下来的,不是被改写的)
640
+
641
+ - **约定**:**发布说明应点名钉住每条修复的守卫**;历史说明若没点名,**以本守卫索引为可追溯性来源**,而不是回头改写已发布的说明。
642
+ - **判定口径(必须按这个定义,避免"过宽匹配")**:**具体引用** `tests/*.mjs`(或套件名)才算"点名了守卫";**散文里出现"守卫/guard"不算**。
643
+ - **已记录的缺口(由本文件按上述口径现算,不手写)**:35 个发布说明文件中有 **12** 个未点名任何守卫 —— 2.2.1、2.3.14、2.3.15、2.3.16、2.3.8、2.3.9、2.4.0、2.4.1、2.5.0、2.6.0、2.7.0.en、2.7.0。这些是**已知、有意不修**的追溯缺口(已发布说明属历史)。
644
+ - 缺口的原始记录见审查台账的 §12.0/§14(安装器脚本审查的调和记录,含 matcher 与 12 的核对)。
645
+
646
+
647
+ ### 补遗三:声明语法扫描(§9.7⑨,位于 `tests/audit-prompt-duplication.mjs`)
648
+
649
+ | 守卫 / 位置 | 钉住的不变量 | 红一次(harness) |
650
+ |---|---|---|
651
+ | `tests/audit-prompt-duplication.mjs`(**已随包**)的**声明语法扫描** | 锚定声明行(`^\s*-\s+`?<name>`?\s*=\s*\S` 及列表行):每个命中必须 **(a) 在另一份副本里也声明**,且 **(b) 在该预设的 JS 里可读为 `params.<name>`**,否则具名红;v4 的六个字段仍归 `must NOT be declared here` | **自我探针**:把 `- activityLogCap = 100` 注入**单份**副本 ⇒ v2/v3 一条具名红("declared in copy 1 … must also be declared in copy 2"),v4 **两条**(单副本 + "the JS never reads `params.activityLogCap`");三条漂移变异仍红(0.33 / 0.11 / 0.50) |
652
+
653
+ - **诚实标注:这个扫描在今天的提示词上是"构造性空跑"** —— 三个预设的 `declaredParams=0`,即真实提示词里**没有**可扫的声明行;它的"会咬"完全由**自我探针**证明(注入一行即红),将来任何"只写一份副本"或"`params.<name>` 不可读"的声明都会被它抓住。
654
+ - 参考计数(默认模式):本守卫 **66/0**;`audit-prompt-invariants` **157/0**。
655
+
656
+
657
+ ### 约定:文本门必须读代码,而不是注释
658
+
659
+ - **文本型断言("在源码里 grep 某个门")必须先 `stripComments` 再匹配**:一条**注释**里出现 `formalOn() && p.formal` 曾让 I8 在**真实门被删除**后仍然变绿(守卫看着在,实际上没在守)。
660
+ - 反例证据是随包的:`tests/audit-prompt-invariants.mjs --self-probe` 的变异"v5: the formal gate is REMOVED but a comment still quotes it (I8 must read code, not comments)" ⇒ 具名红(`v5 I8: the reply channel is gated on formalOn()`)。
661
+ - 同类检查(I1/I2 及 X5–X8 扫描器自身的守卫)也都在**去注释后的代码流**上运行;新增文本门请照此办理。
662
+
663
+
664
+ ### 约定:安装器面向用户的输出一律中文
665
+
666
+ - **规则**:`installer.js` 面向**用户**的输出(`logger.info/warn` 的每一行)一律**中文**;出现英文标签就是**缺陷**,除非在同一行注明理由(例如必须保留厂商/命令原文)。代码与约定必须一致,后来者不得重新引入英文标签。
667
+ - **依据**:R6 归因发现三条英文标签(`:764`/`:769`/`:791`)与一条信息行(`:787`)混在中文输出里;它们经 **logger 助手**(非 `console.*`)发出,所以按 `console.` grep 查不到 —— 排查时请按 `logger?.info?/warn?` 检索。
668
+ - **连带风险(已实测)**:本地化这类标签会波及**测试助手**(`audit-installer-policy` 的 `failedLog()` 之前匹配旧英文标签 ⇒ 91/1);改名/改文案必须**连带改守卫**,并把匹配收窄到"只证明失败"的证据(过宽的 alternation 曾把 `preset files:`/`preset baseline:` 当成失败 ⇒ 89/3)。
669
+
670
+
671
+ ### 跑测超时(门禁必须能终止)
672
+
673
+ - **默认 180 s/套件**(`GATE_SUITE_TIMEOUT_MS` 可覆盖):超时会**杀死子进程**并报**具名失败** —— `FAILED: <file> [suite|probe] (TIMEOUT after 180s)`,失败行下面照常打印该子进程捕获的 stdout/stderr 尾部;**挂起绝不能被当成"还在跑"**。
674
+ - **为什么是 180 s(两个实测数字,未来读者据此区分"慢但诚实"与"卡死")**:整门禁墙钟 **~200 s**;最慢的诚实套件 **135–144 s**(`audit-math-computation-sensitivity`)。180 s 能兜住偶发挂起,又不会误杀诚实套件。
675
+ - **命名覆盖(诚实但慢的套件,不是卡死)**:`run-tests.mjs` 的 `TIMEOUT_OVERRIDES` 给 `v2-fix-probes.mutants.mjs` 与 `v3-fix-probes.mutants.mjs` 各 **900 s**,理由是**实测**:v2 **≈43.1 s/族 × 10 族 ≈ 430 s**、v3 **≈23.5 s/族 × 11 族 ≈ 260 s**(两者内部已把**每个子进程封顶 120 s**,并报 `hangs=[]`,即"慢但诚实")。**默认仍是 180 s**,普通挂起照旧被快速抓住;只有这两个具名套件被放宽。
676
+ - **v5 家族的覆盖(门禁亲自抓到的校准缺口)**:`v5-institute-fixes.mutants.mjs` 实测**墙钟 201.9 s**(`hangs=[]`、`skipped=[]`、`ALL MUTANTS RED AS REQUIRED`)⇒ **超过 180 s 默认**,因此在 `TIMEOUT_OVERRIDES` 里给它同样的 **900 s**。对照:`formal-verify-v4.mutants.mjs` 实测 **70.9 s**,**无需**覆盖(余量充足)。**记录一次"写入方声明为假、被门禁抓住"的事故**:曾有声明称该 v5 家族已落在其覆盖之内,实际并未;门禁以具名失败 `FAILED: v5-institute-fixes.mutants.mjs [probe] (TIMEOUT after 180s)` 抓住了它 —— 这正是具名超时机制存在的意义(挂起≠还在跑)。
677
+ - **覆盖决策只引用族自己打印的 `TOTAL WALL TIME` 行**(**5/5 个变异族**现在都有这一行;下表**以 `92c60cc` 的树为准**;族会随增量**增删**、同一机器的读数也会有 ~1.5% 差异,所以引用时请取**同一次运行**里族自报的 `TOTAL WALL TIME` **与其 `N/N`**,不要用估算、外部读数或跨运行拼接):
678
+ | 族 | 具名红 | `hangs`/`skipped` | `TOTAL WALL TIME`(本次实测) | 覆盖 |
679
+ |---|---|---|---|---|
680
+ | `tests/v2-fix-probes.mutants.mjs` | 10/10 | `[]`/`[]` | **433601 ms (434 s)** | **900 s** |
681
+ | `tests/v3-fix-probes.mutants.mjs` | 11/11 | `[]`/`[]` | **254986 ms (255 s)** | **900 s** |
682
+ | `tests/v5-institute-fixes.mutants.mjs` | 11/11 | `[]`/`[]` | **≈300 s**(族自报:本轮 **298959 ms**;复核方 **299649 / 295216 ms** —— 约 1.5% 机器差异) | **900 s** |
683
+ | `tests/formal-verify-v4.mutants.mjs` | 7/7 | `[]`/`[]` | **87897 ms (88 s)** | 无(默认 180 s 足够) |
684
+ | `tests/formal-verify-v3.mutants.mjs` | 1/1 | `[]`/`[]` | **50834 ms (51 s)** | 无 |
685
+ **本表取自"逐族各跑一次",且 `N/N` 与毫秒数来自**同一次运行**的族自报行**:同一机器不同时刻会变(v5 261 s ↔ 264 s、v4 84 s ↔ 88 s ↔ 123 s),族条目也会被**增删**。**已发生的实例(worked example)**:v4 家族曾因 N19 两条增量变为 **9/9 / 123 s**,随后该增量被**回退**(其前提与两条随包守卫 T13/A1 互斥 —— 记为**实测边界**,不是缺陷),于是回到 **7/7 / 88 s**。同一族的读数因此可能一小时之差就不同 —— **请以你那次运行的族自报行为准,不要跨运行拼数字。**
686
+ **历史(外部整进程墙钟 / 估算,均已作废,仅作对照)**:v2 ≈430 s、v3 ≈260 s(**现已实测 434 s / 255 s,见上表**)、v5 208.5 s(更早 201.9 s)、v4 88.9 s / 75 s / 86 s、v3-new 45.5 s。
687
+ **同一台机器上不同时刻读数会差**(负载/排队;例如 v4 本次 123 s,更早 86 s / 75 s、外部读数 88.9 s)—— 这正是要引用**族自己那一行**的原因:它和该次门禁同条件。
688
+
689
+ - **三种"计数"不要混为一谈**(R14 提出、R19 F-C 修正):① **门禁 JOB 计数**(**唯一权威是 `node tests/run-tests.mjs --counts`**,`audit-readme-counts` 守着;本文件的行由派生器写入,**本节不复述数字**以免随作业数漂移,见 D1 行);② **变异族计数**(本表的 `N/N`,各族自报);③ **`audit-v5-integrity` 内部的 gate 计数**(本轮实测 **56**)。**三类都可以变** —— ① 变了跑 `scripts/update-doc-counts.mjs`,② 变了改本表,**③ 变了不需要改任何文档**(这一点不是"没人引用",而是"**没有任何随包文档把它的数值当作断言**";本节提到它只是说明区别,`audit-readme-counts` 只对 ① 做全 `docs/**` 的 `TOTAL <n>` 扫描,见该守卫的 F-C 断言)。
690
+ - **改限的规矩**:抬高**默认值或任何覆盖值**都必须**同时更新本行的全部实测数字并在提交信息里说明**;不许为了让门禁变绿而悄悄改(上面三条覆盖用例正是为了让这条规矩可验证)。
691
+ - **禁止**为了让门禁变绿而**悄悄抬高**这个值;确有套件变慢,请**同时**更新这里的两个数字与理由。
692
+ - **怎么让它红一次(in-repo,两个方向)**:① `node tests/run-tests.mjs --self-check` 有一条**真实路径**用例 —— 合成 sleep 作业、`timeoutMs=1000`、走同一个 `runSuite`/`failedLine`,断言 `timedOut=true, exit=null` 且失败行 `FAILED: (synthetic-sleeper) [probe] (TIMEOUT after 1s)`;② 随包的 `tests/run-tests.mutants.mjs` 用 `GATE_SUITE_TIMEOUT_MS=1000` 对**真实套件**做**正例**(具名 TIMEOUT + 非零退出)与**反例**(默认限制下同一套件不报 TIMEOUT、exit 0),因此该判据被双向校验(§9.7 ㉗)。
693
+
694
+
695
+ ### S6:版本策略只有一句措辞(`MATH_VERSION_POLICY`)
696
+
697
+ - **实现(三层,同一字面量)**:短句 `MATH_VERSION_POLICY_CLAUSE`(grep 锚点)是**唯一**的策略字面量;长句 `MATH_VERSION_POLICY` 由它**拼接派生**;`mathVersionPolicy(pinned)` 由长句派生**策略字段**(安装计划 `versionPolicy` 与运行/缺包路径同一处);两条**人类消息**同样由短句派生 —— `MATH_MISSING_PACKAGES` 的失败详情(`需要的包未安装:…`)与 `parsePackageSpec` 的 `unsupported version syntax` 提示。因此"一句策略"覆盖**策略字段 + 两条消息**三处表面,短句字面量全文只出现 **1** 次。
698
+ - **断言**:`tests/math-computation-shared.test.mjs` §34(**6** 条**行为**断言,从响应里读,grep 锚点 `S6: the install plan uses the SAME policy wording`);`tests/audit-math-computation-parity.mjs` §11(**4** 条**静态**断言)。
699
+ - **怎么让它红一次**:单点变异 —— 把安装计划处的助手调用改回旧措辞 ⇒ 共享 §34 **具名红**(实测 `370 passed / 3 failed`)。harness(**随包**):`node tests/math-computation-shared.test.mjs --self-probe`(S6 两个用例,与 §34 同一判据;`_oneoff/auditR2/s6-mutant.mjs` 只是同内容的早期 dev-only 草稿)。
700
+ - **parity §11 是"树级"断言(已标注,不冒充可变异证明)**:它读的是**仓库里的随包副本**,刻意**不接** env seam —— 否则会踩协议 ㉟(iii)"loaded-seam vs repo file"(守卫该看仓库文件却看了夹具)。因此它**不能**被 env-seam 变异变红;它的价值是"有人手改仓库副本就会红",与 parity 的字节一致检查同属一类。
701
+ - **数字(revision `ef54f4d` + 未提交内容哈希;数字不带 revision 不复现)**:shared **373/0** → 变异后 **370/3**;parity **107/0**;contract **200/0**;`--self-probe` **2/2**(各 372/1);`COPIES OK`。sha256 前缀:`math-computation.js` `8963ec58ba5eb547`、shared `d822d176a6adae06`、parity `440e308dbe4b07f5`。
702
+
703
+
704
+ #### S6 补充:派生必须"承重"(mutant 发现的紧度缺口)
705
+
706
+ - **缺口**:最初只断言"失败详情里**含有**该短句",而把短句**内联复制**回消息同样满足它 ⇒ 该断言**不承重**(mutant 案例 2 实测:改回内联后仍然 `376 passed / 0 failed`)。
707
+ - **修法**:承重断言放在**能变红的地方**(共享套件读 **env 解析出的**模块源码)——见 §35:短句在模块里**只出现 1 次**、`MATH_MISSING_PACKAGES` 详情**由常量构造**、`unsupported version syntax` 提示**由常量构造**、长句**由短句派生**。改回内联后:`occurrences=2` ⇒ `378 passed / 2 failed`,两条**具名红**("the policy clause exists as exactly ONE literal"、"the missing-package detail is built from the clause constant")。
708
+
709
+
710
+ ### README/计数一致性(D1):数字必须**派生**,不许手打
711
+
712
+ - **权威来源**:`node tests/run-tests.mjs --counts`(门禁自己那份 job 列表)+ `package.json#files`(随包 `tests/*.mjs` 数)。文档里的数字由 **`node scripts/update-doc-counts.mjs`** 生成(`--check` 只检查不改);新增测试文件会让守卫变红,**修法是跑一次 updater**,因此数字再也不可能被手打。
713
+ - **计数(实测)**:`TOTAL 101`(44 套件 + 57 探针/变体,**作业计数/job count**,不是文件计数);随包 `tests/*.mjs` = 65(**文件计数**:17 个套件 + 48 个探针/脚本)。
714
+ - **怎么让它红一次(in-repo)**:`tests/audit-readme-counts.mutants.mjs` —— 基线:守卫绿;**★ 篡改 README 里被引用的派生总数**(走 `COUNTS_README` seam,指向**绝对路径**的副本)⇒ 守卫**具名红**("README.md quotes the DERIVED totals")。守卫本体:`tests/audit-readme-counts.mjs`(**19+** 条断言:README/EN/test-timing/checklist 的派生形状、旧读数的消失,以及 **F-C 的全 `docs/**`(live 文档)`TOTAL <n>` 扫描** —— 每个 occurrence 必须等于 `--counts` 的派生值,冻结的 `docs/release-notes/**` 按名排除并在输出里报数)。
715
+ - **touch-anchor(claim-vs-tree 用)**:`--counts`、`update-doc-counts.mjs`、`README COUNTS:`、`README.md quotes the DERIVED totals`。
716
+
717
+
718
+ ### F-5 沙箱围栏根(guard row,实测于 `466a08b`+:`F-5/1c` 只从该提交起存在)
719
+
720
+ - **实现**:v4 侧 `policyRootCandidatesOf()`(把解析出的策略对象映射为**候选围栏根**,数组/字符串两种形状都收)、`fenceRootDriftNote()`(把"宿主根 vs 会话根"的差异做成**一次性具名告警**)、`warnedFenceDrift`(**one-shot** 标志,防刷屏)。行为要点:**包含关系**判定(会话根必须落在宿主根内)、**数组**策略逐个取候选、**不可解释的策略 ⇒ no-op**(不是死分支);`resolve({})` 的**回退调用被排除**在漂移比较之外;折叠仅在 **win32** 生效。
721
+ - **断言锚点**:`tests/formal-verify-v4.test.mjs:1929` 的 `section('N17 F-5 fence-root drift: containment, no-op boundary, one-shot naming both roots')`;用例命名见 `:1936` 的 NOTE(**case (0) = 宿主根本没有 `sandboxPolicy` 服务**;**case 1b = 服务存在但解析出的根不可解释(`{allow,deny}`)**)、`:1941`(F-5/i:无服务 ⇒ 策略路径从不运行、无告警)、`:1949`(F-5/1b:不可解释 ⇒ **no-op 且无告警**,属**文档化的**边界而非死分支),以及同节的 case ii/iii/iv(漂移比较被调用、包含判定、一次性命名两个根)。
722
+ - **怎么让它红一次(in-repo,随包)**:`node tests/formal-verify-v4.mutants.mjs` —— **族数从族自报行现取**(本轮 **8/8**,新增第 8 条 `N20-3b: a failed index write is silent again`,见下面 item-6 行)按名变红:`F-5/1b: treat \`allow\` as a fence root`、`F-5/ii: the drift comparison is never called`、`F-5/iii+iv: the containment predicate is disabled (always warn)`、`F-5/ii: the one-shot is dropped`、`N15/4: the compaction-failure warning is silenced`、`F-5/1c: an unknown session cwd is warned about`、**`N18: a failed current-project marker write is silent again`**、**`N20-3b: a failed index write is silent again`**;输出 `hangs=[]`、`skipped=[]`、`ALL MUTANTS RED AS REQUIRED`。**时间以族自报的 `TOTAL WALL TIME` 行为准**(本轮 **108296 ms**;同族在更早/不同条目数下的读数为 87897 / 86103 / 122608 ms 等 —— 机器与条目数都会变,**跑一次即知**)。**注意条目会回退**:曾出现的 `N19/F4`、`N19/F10` 两条**已被回退**(其前提与 T13/A1 两条随包守卫互斥,记为**实测边界**),因此该族曾在 9/9 与 7/7 之间变动 —— 引用时请用你那次运行的 `N/N`。本仓的 `TIMEOUT_OVERRIDES` 无需为它放宽(< 180 s 默认)。
723
+ - **"保持静默"的断言必须同时给出**"离被测分支最近的一步"的**活性证据**:这里能区分"分支真的跑了但选择不告警"与"分支压根没跑"的,是 **`resolve({session})` 的调用计数**(实测 `LIVENESS: resolve calls=11`),而**不是**"策略服务被查过"(同一个服务也被 `workspaceRoot()`/`getPolicy()` 读,查过 ≠ 走到这里),也**不是**"写入成功"(cwd 未知时同一次调用会因**无关的路径原因**返回 `ok:false`)。守卫:N17 的 `F-5/1c`(`tests/formal-verify-v4.test.mjs`);随包变异:`tests/formal-verify-v4.mutants.mjs` 的**第六条**。
724
+ - **两条文档化边界(有意行为,不是缺陷)**:① **策略里没有根字段 ⇒ no-op**(没有可比的东西,不告警);② **会话 cwd 未知 ⇒ 构造性相等 ⇒ 静默**(把"未知"当相等,避免用猜测刷告警)——**现在是断言而非仅文档**:`tests/formal-verify-v4.test.mjs:1981`(`* F-5/1c an UNKNOWN session cwd stays SILENT`)与 `:1982`(`* F-5/1c LIVENESS: the helper really ran with an unknown cwd (resolve calls=…, policy queries=…)`);随包家族的**第六条**即 `F-5/1c: an unknown session cwd is warned about`(**6/6** 族变红)。两者都必须在断言里保持为"**no warning**",改动它们等于改契约。
725
+ - **历史注记**:`docs/COMPAT-AUDIT-ROUND2.md` 的 §F-5 保留了 `2649a47` 的用例命名更正(case 1 / case 1b),那里是**历史**记录;本行是**可复核的守卫索引**。
726
+
727
+ ### v4 item-6 — Lean 库索引写失败的**具名一次**(guard row,实测于 `c8a1b92`)
728
+
729
+ - **不变量**:一次失败的 Lean 库索引写必须被**具名一次**,带上 `lib=` / `proved=`,并说出后果 **`STALE until the next successful rebuild`**;同时**工具仍返回扫描计数**(具名是**附加**,不是新的失败模式)。
730
+ - **具名点(唯一承担责任处)**:`vibe-math-v4/vibe-math-v4.js:1297` —— `console.error('vibe-math-v4: rebuildLeanLibIndexes: the Lean library index could NOT be written (lib=… proved=…) - the index is STALE u…')`。
731
+ - **调用点分类(六个)**:`:893` 是 `settleLeanJob` 的**包装器**(`warnedIndexRebuild` 一次性 + 自己的 `settleLeanJob: rebuildLeanLibIndexes failed` 消息);`:1006`(`writeLeanLibIndexesSafe`)、`:1402`、`:1446`、`:1460` 是**构造上 best-effort** 的包装(`catch(e){ /* best-effort */ }`)—— 所以具名责任集中在 `rebuildLeanLibIndexes` 内部那一处。
732
+ - **断言锚点**:`tests/formal-verify-v4.test.mjs:2074` 的 `section('N20-3b rebuildLeanLibIndexes: a failed INDEX WRITE is NAMED once (covers all six callers)')`;四条断言:`:2091` **反空过**(健康重建**静默**:`a healthy index rebuild is silent (the case is not vacuous)`)、`:2097` **恰好一次**(`a failed index write is NAMED exactly once (matched=… of … stderr lines)`)、`:2098` **后果**(`/STALE until the next successful rebuild/`)、`:2099` **附加性**(`r.ok === true`,仍返回扫描计数)。
733
+ - **怎么让它红一次(随包)**:`node tests/formal-verify-v4.mutants.mjs` —— 第 **8** 条族 `N20-3b: a failed index write is silent again`(单点:`if((_idxLibOk === false || _idxProvedOk === false) && !warnedIndexRebuild){` ⇒ `if(false){`)按名变红 `* N20-3b a failed index write is NAMED exactly once (matched=0 of 1 stderr lines)`。**族自报(本次运行)**:`8/8`、`hangs=[]`、`skipped=[]`、`ALL MUTANTS RED AS REQUIRED`、`TOTAL WALL TIME 108296 ms`(同修订复核方 110016 ms、作者 115082 ms —— 机器差异约 5%;**引用你那次运行的族行**)。
734
+ - **两条文档化边界**:① **settle 路径的套内捕获**:注入的索引写失败会被"最先触发的那个 rebuild 调用者"吃掉(作者实测 `armed=3 failed=1`,更早 `armed=9 failed=1` 且无告警)⇒ 套内无法判别该调用者;两个原始数字**作者实测、未被独立复现**(R17 标 still-unsure),但 R17 复现了边界所依赖的**结构**:单行 best-effort 包装、六个调用者都已被 `rebuildLeanLibIndexes` 内部的一次性具名覆盖、且每次后续 rebuild 都重写索引 ⇒ **自愈**,边界即立在这份结构证据上。② **best-effort 包装(`:1006`/`:1402`/`:1446`/`:1460`)**:构造上不具名,这是**有意行为**而非缺口。
735
+ - **已记录的守卫候选(本轮不派活)**:机械核对每个 `console.error('vibe-math-<preset>: …')` **具名点**是否**要么**在索引里有行(不变量 + 变异)**要么**在**显式豁免清单**里 —— 实现方式同 `audit-readme-counts` 的 seam(`COUNTS_*` 那套)。**记录而不立即实现**:它很可能一次性暴露一整类**未镜像的具名点**,值得独立一轮预算。
736
+
737
+ ### 补遗四:语义单元、分层的断言与"计数是线索"(v4 writer 的措辞 + R14)
738
+
739
+ - **F-3 / F-4c —— 计数是线索,判决要读在"语义单元"上**:`registerTool(` 有 **36** 个调用点(本仓实测),但**注册单元只有一个** —— 它们都经 `ctx.effect(() => tools.register({…}))`(`vibe-math-v4.js:4042-4047`),所以**没有任何调用点需要自带包装**。同理,失败的 mkdir 在 **`ensureDirs()` 内部**被一次性具名(`warnedMkdir` 声明于 `:353`,置位并告警于 `:507-508`),**覆盖所有调用方**,因此**不需要**任何调用方侧的返回值检查。判决要写成"**哪个单元**被统一/包装/检查了",并给出 `file:line`。
740
+ - **G-6 —— 分层,不重复(字段表已交付)**:**断言级**规则在 `tests/formal-verify-v4.test.mjs` 的 **SHIPPED ASSERTION RULES (C)**;**字段级**语义归 `docs/status-report-fields.md` 的**逐预设字段作用域表**(**name / scope / which-views / meaning** 四列,由源码**按 marker 派生**:v2 **20** 行、v3 **25** 行、v5 **43** 行取自 `status()` 的顶层键(含**简写属性**,R19 F-A),v4 **明确没有** `fieldScopes` 所以**不编造行**)。两处**不重复**同一句话。**归属(与 §14.9 同口径)**:字段级对账由 `tests/audit-status-report-fields.mjs` 拥有(doc↔source **双向**,打印 `frozen / doc-rows / exceptions` 三个计数;**简写属性感知**,否则会漏 8 个真实键);`tests/audit-v5-integrity.mjs` 只保留 frozen-key drift 检查。变异:`tests/audit-status-report-fields.mutants.mjs`(**3/3**:基线 + 删一行文档 ⇒ 具名红 + 源码加一个字段 ⇒ 具名红)。**作业计数不复述**:以本文件由派生器写入的基线条(`node tests/run-tests.mjs --counts`)为准 —— 复述即漂移(R19 F-C 实测)。
741
+ - **A2 —— 同样是分层**:**断言级**规则在同上 **SHIPPED ASSERTION RULES (D)**;**实现级**变异族在 `tests/v5-institute-fixes.mutants.mjs` 的 **G1/G2**(实测该文件含 G1×3、G2×2、G4×2 的族名)。
742
+ - **V3-G2 —— 第二条"同族门禁条件"(断言已落地并绿)**:`tests/formal-verify-v3.test.mjs` 在**模块作用域**记住第一轮槽位(`:328` `let gateSlots1 = null`,round-one 的 require 轮在块里,所以必须提到模块级;`:739` `gateSlots1 = slotsOf(batch) // measured ["0","1"]`),并断言**
743
+ `:812` `* V3-G2 the re-eligible reviews are the SAME slots that fired in round one (before=… after=…)` **与** `:813` `* V3-G2 and they are still DISTINCT slots`**(`:807` 的注释写明"`.length >= 2` 是**计数**,语义单元是**同一批槽位**")。修复树 ALL GREEN(该套件 41.3 s)。
744
+ - **V3-G2 的实测边界(无判别性变异 ⇒ 不发布该族,这是测量结论而非疏漏)**:本 revision 下**不存在**严格保计数的变异 —— 继承旧子任务在本树里是**惰性**的(已结论的 verify 任务早已被删除),而**完全禁用回填**会超过 300 s 子进程上限、被分类为 **`HANG`(不是具名红)**。两个候选变异都被逐一实测后**否决**,记录在 `tests/formal-verify-v3.mutants.mjs` 的 `MEASURED BOUNDARY (V3-G2)`(`:78`)、`TWO CANDIDATE MUTANTS WERE THEN MEASURED - BOTH REJECTED`(`:86`)与 `RECORDED BOUNDARY`(`:93`)。因此该族**保持 1/1**,**故意**不含 V3-G2 条目 —— 一个会挂起的族会拖垮门禁,而不是证明什么。
@@ -5,7 +5,7 @@
5
5
  方法:**活体契约目录**(Host Inspect `Service.listService` / `Event.listEvents` / `Tool.listTools`)+ 宿主包源码逐行核对 + 可执行复现脚本
6
6
 
7
7
  > 本文件替代 `COMPAT-AUDIT-0.1.5-rc.2.md` 作为当前兼容性结论。
8
- > 上一轮审计提出的 R1–R7 **从未被修复**(在 `FIX-REPORT-2026.md` 中检索 R1–R7 零命中),本文件逐条重新独立验证。
8
+ > 上一轮审计提出的 R1–R7 **从未被修复**(在 `FIX-REPORT-2026.md`(**该文件不存在**:已发布报告留下的**悬空引用**;权威记录在本文与审计台账,不在仓库里) 中检索 R1–R7 零命中),本文件逐条重新独立验证。
9
9
 
10
10
  ---
11
11
 
@@ -125,7 +125,10 @@
125
125
  `runShell` 的返回值**在 `ensureDirs` 的调用点无人检查**(`:905/:925/:1051` 都是 `await ensureDirs()` 后继续)。
126
126
  **后果**:若 `powershell`/`sh` 不可用或返回非 0,目录不会被创建,而唯一的兜底是 `fs.writeText` 自动建父目录;一旦某条写入路径不经过 `fs.writeText`,落盘就会失败且**没有任何报错**。属"mock 覆盖不到、实机才暴露"的一类。
127
127
 
128
- ### F-5 `getPolicy()` 回退会静默改变沙箱围栏根(中)
128
+ ### F-5 `getPolicy()` 回退会静默改变沙箱围栏根(中)
129
+
130
+ > **断言用例的准确命名(`2649a47` 的更正)**:**case 1 = 宿主根本没有 `sandboxPolicy` 服务**(策略路径从不运行);**case 1b = 服务存在但围栏根不可解释(`{allow,deny}`)⇒ 退化为 no-op**。早先 F-5 行里"无可比根"的说法**过宽**,以此处为准。
131
+
129
132
 
130
133
  `v2:140`、`v3:206`、`v4:113` 三处同构:
131
134
 
@@ -302,15 +305,24 @@ e2e-f1-agent-detach CONFIRMED (drives the real AgentRegistry)
302
305
 
303
306
  ### 未修(需你决策,风险与取舍已说明)
304
307
 
305
- | 编号 | 说明 | 为何未动 |
306
- |---|---|---|
307
- | **F-4b** | 插件行在 `compaction` 隔离组**外** → 取到宿主根 compaction,预设阈值不生效 | 修法是**把插件行移进该组**,但会同时改变它对组内其它服务的可见性;**需先确认这是否为有意设计** |
308
- | **F-4c** | `ensureDirs()` 依赖外部二进制且返回值无人检查 | 属"实机才暴露"一类;改动会影响启动路径,建议与你的实机验证一起做 |
309
- | **F-7** | v2/v3 用全局 `setInterval` 而非 `ctx.interval` | 已核实**不是功能故障**(在 `ctx.effect` 内且正确清理),仅是未用宿主 timer 服务 |
308
+ | 编号 | 说明 | 为何未动 | 状态 |
309
+ |---|---|---|---|
310
+ | **F-4b** | 插件行在 `compaction` 隔离组**外** → 取到宿主根 compaction,预设阈值不生效 | 修法是**把插件行移进该组**,但会同时改变它对组内其它服务的可见性;**需先确认这是否为有意设计** | **待你决策**(风险与取舍已说明) |
311
+ | **F-4c** | `ensureDirs()` 依赖外部二进制且返回值无人检查 | 属"实机才暴露"一类;改动会影响启动路径,建议与你的实机验证一起做 | **已关闭**(§14.2:已在后续批次修复,见 §7 的回归测试) |
312
+ | **F-7** | v2/v3 用全局 `setInterval` 而非 `ctx.interval` | 已核实**不是功能故障**(在 `ctx.effect` 内且正确清理),仅是未用宿主 timer 服务 | **非缺陷**(已核实,无需改动) |
313
+
314
+ **同一清单的其余状态**(F-1…F-11 全表,按编号;本表只列需要动作的三条):
315
+
316
+ - **已修复并各有回归测试**:F-1 / F-2 / F-3 / F-4 / F-5 / F-8(见 §7)——**F-4c 现已并入这一组**(§14.2)。
317
+ - **已知限制(不修)**:F-8。
318
+ - **已有守卫**:F-9 —— `tests/audit-corrupt-file-guard.test.mjs` 钉住该行为。
319
+ - **待定**:F-10 / F-11(尚未决策,机制未验证)。
320
+ - **非缺陷**:F-7。
321
+ - **本清单是唯一权威的"未修 / 需决策"入口**;按**标题文本**定位本表,不要引用行号(行号会漂移)。已发布说明里的悬空引用见文末"已知且有意不修的悬空引用"一节。
310
322
 
311
323
  - **两个并行深度审计未能返回**(长时间运行后由我中止,无输出)。其覆盖范围(服务 API 逐参数核对、R1–R7 逐条复核)**已由我本人独立完成**,结论即本文件 §1/§2。
312
- - 未逐条验证上一轮 `FIX-REPORT-2026.md` 第四节列出的约 20 条审计编号(v4 D1/D2/D3/D7、v3 D17/D18/M14/M2 等)——这些**只是编号转述**,需先验证机制再改。
313
- - **修复已实施**(见 §7):F-1/F-2/F-3/F-4/F-5/F-8 完成并各有回归测试;F-4b/F-4c/F-7 见 §7 末表,需你决策或留待实机验证。
324
+ - 未逐条验证上一轮 `FIX-REPORT-2026.md`(**该文件不存在**:已发布报告留下的**悬空引用**;权威记录在本文与审计台账,不在仓库里) 第四节列出的约 20 条审计编号(v4 D1/D2/D3/D7、v3 D17/D18/M14/M2 等)——这些**只是编号转述**,需先验证机制再改。
325
+ - **修复已实施**(见 §7):F-1/F-2/F-3/F-4/F-5/F-8 完成并各有回归测试;F-4b/F-7 见 §7 末表,需你决策或留待实机验证。**F-4c 已关闭**:产品改动在 `6aff85a`(`ensureDirs()`/`removeFile()` 失败走 `warnShellOnce`,有界一次),守卫断言与具名红变异(`tests/v3-fix-probes.mutants.mjs` 的 F4c 家族,删掉该告警即红)均已提交——本行原先把 F-4c 与 F-4b/F-7 并列在"需决策",属**陈旧条目**,现更正。
314
326
 
315
327
  ---
316
328
 
@@ -326,3 +338,17 @@ e2e-f1-agent-detach CONFIRMED (drives the real AgentRegistry)
326
338
 
327
339
  > **后续轮次**:DSH 0.2.0 的适配审计与四宿主实测记录见 [`COMPAT-AUDIT-ROUND3.md`](COMPAT-AUDIT-ROUND3.md)
328
340
  > (预设交付形态换代、会话事件白名单、子代理数量上限、工具名抛错、压缩语义等)。
341
+
342
+
343
+ ---
344
+
345
+ ## 已知且有意不修的悬空引用(traceability gap, option (a))
346
+
347
+ - `FIX-REPORT-2026.md` **在本仓库中不存在**。**四处**引用:本文件两处(其上方已就地标注),以及**已发布**的 `docs/release-notes/RELEASE-NOTES-2.0.22.md:74`/`:84`。
348
+ - **决定**:已发布的发布说明属于**历史**,不修改(用户立场:不动已发布产物);因此这两处保持原样,作为**已知、有意不修**的缺口记录在此。权威记录以本文件与审计台账为准。
349
+ - 若将来要消除该缺口,正确做法是**补一份** `FIX-REPORT-2026.md` 或在本文件里给出这些编号的完整转述,而**不是**改写已发布说明。
350
+
351
+ ## 未修清单的入口(按标题定位,不按行号)
352
+
353
+ - 本文件的 **`### 未修(需你决策,风险与取舍已说明)`** 一节(F-1…F-11;F-7 非缺陷、F-8 已知限制、F-9 已有 `tests/audit-corrupt-file-guard.test.mjs`、F-10/F-11 待定、**F-4c 已关闭**)是唯一权威的"未修 / 需决策"清单。
354
+ - **按标题文本定位,不写行号**:行号会随编辑漂移,标题是稳定的;链接目标在本仓库内,且本文件随包发布(`files[]`),因此指针不会指向包外。
@@ -27,7 +27,7 @@
27
27
  | `paperLanguage` | enum `zh`\|`en` | `zh` | 四套 | 论文语言,同时决定 LaTeX 模板(`ctexart`/`article`)与引擎优先顺序 |
28
28
  | `paperCompilePdf` | boolean | `true` | 四套 | 检测到 LaTeX 时是否编译 `paper.pdf` |
29
29
  | `paperLatexCommand` | string | `''` | 四套 | 强制指定**一个**引擎可执行文件;空 = 按语言自动探测。指定但解析不到时按"未检测到"降级 |
30
- | `paperEditor` | enum | v4 `office`;v5 `academician` | **仅 v4/v5** | 定稿代表。v4:`office`(会话根/人类侧)或 `resident:<id>`;v5:`academician` 或 `office`(见 §7) |
30
+ | `paperEditor` | enum | v4 `office`;v5 `academician`(**封闭集合 `office|academician`**,越界取值返回 `V5_INVALID_ARGUMENT`) | **仅 v4/v5** | 定稿代表。v4:`office`(会话根/人类侧)或 `resident:<id>`;v5:`academician` 或 `office`(见 §7) |
31
31
 
32
32
  归一化:**枚举**的非法值一律回退到上表默认值(绝不回退到更强的档位);**未知键不会直通参数层**
33
33
  (v2/v3 的参数归一化只遍历已知默认键,v4 把被忽略的键列在返回的 `ignored` 里,v5 的 set-schema 是闭集,
@@ -80,7 +80,7 @@
80
80
  | `paper.pdf` | 仅在编译器**真的跑通**时出现 |
81
81
  | `paper.meta.json` | 总是:参数、触发原因、运行 id、`finalizedAt`、编译状态与引擎、v5 的咨询计数等 |
82
82
  | `paper.log.md` | 总是:谁写了什么、谁提了什么意见、如何采纳(人读审计链) |
83
- | `paper.lock.json` | 总是:该论文目录的**写锁**(持有者 / 时间 / 运行 id),并发的复写与重编译据此串行化(round-3 补记;README 的 final-paper 一节同样列出) |
83
+ | `paper.lock.json` | **仅 v2/v3**:该论文目录的**写锁**(持有者 / 时间 / 运行 id),并发的复写与重编译据此串行化。**v4/v5 不写此文件**:v4 的论文复写只在本进程内串行化,且**共享文件写锁未实现**(`vibe_v4_claim_write` 返回 `{ok:true}` 但不保留,见其工具说明);v5 无此锁。README 的 final-paper 一节按同一口径列出 |
84
84
 
85
85
  宿主 fs 只支持**文本写**(没有 `writeBytes`),所以 `paper.pdf` **只能由编译器子进程**在论文目录内产生,
86
86
  插件只做存在性校验,绝不自己写二进制,也**绝不覆盖已存在的 pdf**。
@@ -1,6 +1,6 @@
1
1
  # 数学计算工具(`math_computation`)—— 契约与用法
2
2
 
3
- > 四个预设(v2/v3/v4/v5)共用同一个工具。规格与冻结件在开发检出里:`_oneoff/math-computation-spec-v3.md`(rev-3)、`_oneoff/mc-P1-ready/INTERFACE-FREEZE.md`、`mc-P1-ready/{engines.md,tool-schema.json,receipt.md,prompts.md,guards.md}`。
3
+ > 四个预设(v2/v3/v4/v5)共用同一个工具。规格与冻结件**只在开发检出里**(`_oneoff/**` **不随包发布**,`files[]` 不含它;随包读者请看本文件正文的契约与穷举表):`_oneoff/math-computation-spec-v3.md`(rev-3)、`_oneoff/mc-P1-ready/INTERFACE-FREEZE.md`、`mc-P1-ready/{engines.md,tool-schema.json,receipt.md,prompts.md,guards.md}`。
4
4
  > 本文件只讲**已实现的行为**与**明确不能保证的东西**,不含性能或效果承诺。
5
5
 
6
6
  ## 1. 它是什么
@@ -44,7 +44,7 @@ math_computation {
44
44
  **"没有 subprocess 服务"与"没有引擎"是两件事**(避免误导用户去装 python):
45
45
  - 宿主若能声明自己没有 subprocess 服务(预设可选提供 `hasSubprocess:()=>false`),`run` 与 `probe` 一律返回 `MATH_NO_SUBPROCESS` + `next.kind:'note'`;
46
46
  - 宿主不提供该声明时:命令**解析不到** ⇒ `MATH_ENGINE_NOT_FOUND`(附逐 OS 安装指引);能解析但 `spawn` 拿不到句柄(返回 `null`)⇒ `MATH_NO_SUBPROCESS`。
47
- - **机读词汇表(穷举;权威副本在 `_oneoff/mc-P1-ready/tool-schema.json#refusalVocabulary`,由 parity 守卫盯着,改名即红)**:
47
+ - **机读词汇表(穷举;权威副本**只在开发检出**里:`_oneoff/mc-P1-ready/tool-schema.json#refusalVocabulary`(**不随包发布**),由 parity 守卫盯着,改名即红)**:
48
48
  - `code`(11 个):`MATH_NOT_AVAILABLE` `MATH_ENGINE_NOT_FOUND` `MATH_ENGINE_LICENSE_REQUIRED` `MATH_ENGINE_UNUSABLE` `MATH_MISSING_PACKAGES` `MATH_TIMEOUT` `MATH_NONZERO_EXIT` `MATH_ENGINE_BAD_ARGV` `MATH_REFUSED` `MATH_INVALID_ARGUMENT` `MATH_NO_SUBPROCESS`;
49
49
  - `next.kind`(7 个):`user-install`(用户自装指引)、`agent-install`(代装计划/计划就绪)、`vendor`(商业引擎厂商指引)、`enable`(`mathComputation:'off'` 的启用建议)、`engine-override`(bad-argv ⇒ 用 `mathEngineOverride` 覆盖模板)、`reason`(机读拒绝原因)、`note`(补充说明,如无 subprocess);
50
50
  - `next.reason`(**13** 个,仅当 `next.kind==='reason'`):`policy`、`engine-not-allowed`、`missing-cli-command`、`expr-not-supported`、`mode-not-supported`、`path-outside-project`、`file-not-found`、`system-scope-unsupported`、`plan-token-mismatch`、`archive-missing`、`receipt-unparsable`、`no-subprocess`、**`unsupported-version-syntax`**(包规格写法不被支持:版本求解交给包管理器,见 §5)。
@@ -70,8 +70,8 @@ math_computation {
70
70
  回执与返回体都带 **`scriptPath`**(归档脚本周相对路径)与 **`scriptHash`**(脚本字节的 sha256),所以"这次跑的到底是哪份代码"是可查的:
71
71
 
72
72
  1. `mode:'code'` 把源码落成 `Computation/<id>/script.<ext>`;agent **可以用普通文件工具打开并编辑它**。
73
- 2. 编辑后用 `mode:'file'` 指向该脚本重跑,会写出**一份新回执**(新 attempt、新 `scriptHash`)。
74
- 3. **旧回执对修改后的代码无效**。`mode:'file'` 的回执按**路径**归属同一个 archive id(不是按内容),所以"同一文件编辑后重跑"落在同一 id 的**新 attempt**,并显式给出:
73
+ 2. 要拿到"改过代码"的证据,编辑**你最初运行的那个源文件**,再用 `mode:'file'` 指向**同一个源路径**重跑:归档 id 以**源路径**为键,所以这会写出**同一归档 id 的新 attempt**(新 `scriptHash`);重跑**归档副本本身**(`receipt.scriptAbs` 那个路径)则是**另一份新归档**(新 id、attempt 1、无历史,见 §3.5 下方说明),**不会**给出"同一归档的新 attempt"。
74
+ 3. **旧回执对修改后的代码无效**。`mode:'file'` 的回执按**路径**归属 archive id(不是按内容),所以"同一文件编辑后重跑"落在同一 id 的**新 attempt**,并显式给出:
75
75
  - `scriptChanged:true`:该文件当前哈希与**上一份回执**记录的 `scriptHash` 不一致(旧回执不再代表当前代码);
76
76
  - `scriptChangedDuringRun:true`:文件在**本次运行期间**被改动(典型是另一个成员同时在编辑)——运行前后各取一次哈希才可能发现;
77
77
  - 两者都同时进入 `warnings[]`(`SCRIPT_CHANGED_SINCE_LAST_RECEIPT` / `SCRIPT_CHANGED_DURING_RUN`)与 `receipt.md`,**绝不静默**。
@@ -121,11 +121,12 @@ python 走 pip 时仍有真实的系统模板(不带 `--user`);conda/mamba
121
121
  - **拒绝的写法**:空格、`;`、`|`、`&`、`$`、反引号、`@`、括号等 shell 危险或管理器不认识的语法 ⇒ `MATH_INVALID_ARGUMENT` + `next.reason='unsupported-version-syntax'`(**不会**把可疑字符串丢给 shell)。
122
122
 
123
123
  - **冻结参与集(freeze/prune)规则(v4,round-9 P2/D4 的设计决定)**:验证/会议开始时把当时的名册**快照**下来(`rosterSnapshot`)并记录 `rosterVersion`,所有视图(`status()` 的 `consensus`/`meeting`/`verify`、以及内部的 `allSpoke`/`allVoted` 判据)**一律读快照**,不再从活的 `residents` 重新推导。三条配套语义:① **移除成员时同时从冻结集里剔除它**(`removeMember`),否则在飞投票永远凑不齐、验证/会议会卡死——v4 既有契约是"移除即释放等待"(`e2e-v4-fixes` T26/T31 钉住这一点);② **新增成员不进入已冻结的集合**(冻结的意义);③ **成员集真的变化时 `rosterVersion` 递增**,让任何视图都能看出自己读到的是否已过期。**约定**:没有进行中的验证/会议时,`status().frozenParticipants` 为**显式 `null`**(不是空数组、也不是旧的活名册),`rosterVersion` 仍然给出当前值。
124
+ - **读法(推荐)**:`status().frozen` 是**原子**三元组 `{version, participants, kind}`——`participants` 是快照本身,`version` 是**冻结时记录、并在每次名册变动时重新盖章**的版本(绝不是"活计数器配旧集合"),`kind` ∈ `'verify'`/`'meeting'`(`vibe-math-v4.js:3609`;六条守卫见 `tests/formal-verify-v4.test.mjs:1582-1596`,其中包含"`frozen.version` 必须等于快照记录的版本"与"hire/fire 后活计数器严格递增")。**`frozenParticipants` 已标注 deprecated**(`:3610`):它是 `frozen.participants` 的别名,只为兼容旧读取者保留;新代码请读 `frozen`,不要把别名与 `status().rosterVersion`(活计数器)混用。
124
125
  - **N1:编辑脚本后的"变更检测"到底对什么生效**。归档 id **以源路径为键**(`mode:'file'` 用 `file:<项目内相对路径>`,`mode:'code'/'expr'` 用脚本文本):
125
126
  - 编辑**同一个源文件**再跑 ⇒ **同一个归档 id**、`attempt ≥ 2`(落在 `Computation/<id>/attempts/<n>/`)、`scriptChanged:true`、`previousReceipt{runId,attempt,scriptHash}` 指向上一 attempt,且**上一 attempt 的脚本与回执绝不改写**。这是 P2a 承诺的核对流程,已在真机与共享套件里双向验证。
126
127
  - 指向**另一个源路径**(例如上一 attempt 的**副本**、或新写的文件)⇒ 按设计就是**新归档**:新 id、`attempt:1`、`scriptChanged:false`、`previousReceipt:null`,且 `sourceHashBefore == sourceHashAfter`(文件在跑之前就已经是编辑后的内容)。**这不是变更检测失效**,而是"该 id 从来没有历史回执"。
127
128
  - 运行响应现在**显式带 `runId` 与 `attempt`**(此前只有 `receipt.runId`,调用方根本无法核对"是不是同一个归档")。
128
- - `mode:'file'` 指向 `Computation/…` 的**归档脚本本身**目前会被路径守卫拒绝(源必须位于受允许的项目目录);响应里的 `fileIsArchivedScript` 与 `ARCHIVED_SCRIPT_RERUN` 警告是**防御性**的,用于将来放开该路径时如实解释"为何这里没有历史"。
129
+ - `mode:'file'` 指向 **`Computation/…` 的归档脚本副本**是**允许**的:`projectRel()` 只做词法归一(拒绝绝对路径与越出项目根的 `..`,**不限制目录名**),所以该路径会被正常读取并执行。但按设计那是**另一份新归档**:新 id、attempt 1、无 `previousReceipt`、`scriptChanged:false`,旧 attempt 不会被覆盖。响应会用 **`fileIsArchivedScript:true`** 与警告码 **`ARCHIVED_SCRIPT_RERUN`** 说明"这不是同一归档的新 attempt",避免把"新归档无历史"误读成"变更检测失效"。
129
130
  - **关于 nonce**:归档 id **不含任何随机数**,给定 (engine, mode, 键, packages) 完全确定;若你在某个路径里看到 nonce 样的后缀,那是**调用方自己的工作目录命名**,不是模块产生的。
130
131
  - **N4:`project` 字段与 receipt 路径里的项目名是两个东西**。`project` 是**配置层的项目标识**(未配置项目时由工作区路径派生,所以会出现 `C-Users-…-vmself6` 这种 slug,它是**显示/配置值**);而 receipt/归档路径一律用 `Projects/<当前项目名>`(默认 `default`)。两者不一致是**层次不同**,不是命名错误;改名会破坏既有状态与归档路径,故只在此说明。
131
132
  - **冻结视图的版本语义(v4 P2)**:冻结视图(`status().consensus`、`report().verify`)报的是**快照当时的 `rosterVersion`**,而 `status().rosterVersion` 是**当前活计数器**;成员集变化后两者**应当不同**——这个差值正是"你读到的是过期参与集"的信号。断言方式:两个冻结视图彼此相等,活计数器 ≥ 快照版本(`tests/formal-verify-v4.test.mjs` 的 D4 跨视图用例)。
@@ -140,13 +141,17 @@ python 走 pip 时仍有真实的系统模板(不带 `--user`);conda/mamba
140
141
  - **找到可用引擎就报它的真实版本**(`engineInfo.path` + `version`),**不再**给安装指引;**只有**一个可用引擎都找不到时,才按 OS 给安装指引——而且是对**请求的那个引擎**(不是"第一个允许的引擎")。`op:'probe'` 的 `engine`/`engineInfo` 就是**请求的**引擎;请求的引擎缺失时,`code`/`message`/`next` 全部指向它(历史上这里会回显 `python`)。
141
142
  - **`mathEngineOverride` **不是**发现手段**:它只按引擎名覆盖 **argv 模板**(如 `{python:{versionArgv,scriptArgv,evalArgv,packageProbe}}`),**不能指定一个可执行文件**。所以 `MATH_ENGINE_NOT_FOUND` 的正解是:装上引擎、把解释器放进 PATH、让上面的 DSH 运行时被扫到,或用 `engine:'cli'` + `cli.command` 显式给命令。
142
143
  - **round-9 响应字段(真机发现后新增,文档与实现同步)**:
144
+ - 引擎发现顺序:PATH → 宿主自带的运行时(`runtimeRoots` + `listDirAbs`)→ **各操作系统的已知安装目录**(`mathInstallRoots(engine, env, win)`,覆盖 R / Octave / Julia / MATLAB 与 Windows 上的 Python;Maple / Wolfram 布局不固定,未纳入)。**存在性一律靠列目录证明**。宿主若提供可选的 **`installRoots(engine)`**,则**以它为准**(不再读模块进程的 `ProgramFiles`/`LOCALAPPDATA`),测试与自定义布局因此都不依赖机器环境。
143
145
  - `probe` 返回 `configured`(配置里启用的引擎全集)与 **`absent[]`**(每个缺席引擎带 **`why`**:`not-found-on-this-machine` / `needs-a-caller-supplied-command`),message 也会点名"已配置但本机未发现:…"——可用性行**不再静默地只报一部分**(F3)。
144
146
  - `run` 的成功响应与 `MATH_MISSING_PACKAGES` 失败**都**带 **`versionPolicy`** 与 **`constraintsNotEnforced:[…]`**:明确"版本约束只按 base name 查存在、从不校验",并点名本次**未校验**的约束(无约束时为空数组)——杜绝"静默丢弃约束"(F7)。
145
- - **`MATH_TIMEOUT` 与 `MATH_NONZERO_EXIT` 的证据字段一致**:都带 `argv`、`receipt`、`exit`(超时为 `null`)与 `stderr`(同样的截断上限),超时另有 `timedOut:true`(F5)。
147
+ - **`MATH_TIMEOUT` 与 `MATH_NONZERO_EXIT` 的证据字段一致**:都带 `argv`、`receipt`、`exit`(超时为 `null`)与 `stderr`(同样的截断上限),超时另有 `timedOut:true`(F5)。**超时的部分输出还进持久回执**:`receipt.json` 带 **`partialStdout`**/**`partialStderr`**(各取**最后 2000 字符**,`math-computation.js:1089-1090`;响应里的 `stdout`/`stderr` 走同一来源,`:1131-1132`),所以事后只看回执也能看到超时前产生了什么。
148
+ - **商业模板的"需人工确认"来源会到用户手里(item 2 实测)**:描述符里的 `verify` / `verifyReason`(例如 maple 的"CLI 拼写随版本变化,需在持证机器上确认")现在会出现在 **`probe` 的每引擎条目与 `engineInfo`**,以及**厂商指引 `next`**(`vendor` 载荷、以及引擎未找到时的安装指引)里;字段**只在描述符声明了 `verify` 时**出现,未声明的引擎**不打印空槽**。共享套件 §30 断言"理由到达响应"与"无声明则无空槽",`_oneoff/auditR2/verify-provenance-proof.mjs` 以同一谓词证明单点去除理由会变红。
146
149
  - **`engine:'cli'` 的三条行为**:① **包预检按 `cli.command` 的族**执行(`python*`/`Rscript`/`octave`/`julia` 用对应描述符的探测);命令族**不可识别** ⇒ **跳过**预检并给出 `PACKAGE_PRECHECK_SKIPPED` 警告(**不误报缺包、不阻塞**逃生口)。② `mode:'code'`/`'file'` 时归档脚本的**绝对路径会自动追加到 argv 末尾**(回执里 `cli.scriptAppended: true`;`cli.argv` 仍是调用方给的 argv)——否则会掉进 REPL、`exit=0` 而 stdout 为空,读起来像成功。**但若调用方 argv 自带程序槽(`-c`/`-m`/`-e`/`--eval`/`--command`),则调用方 argv 优先、不追加**(否则脚本会被当作多余参数,例如 `python -c 'print(1)' script.txt` 会把脚本喂给 `sys.argv[1]`);此时回执给 `cli.scriptAppended:false` + `cli.scriptSkipped` 说明原因。③ `engineInfo.version` 取自**真实的版本探测**(族可识别时),不再是 `"unknown"`。
147
150
  - **真实宿主上的"空 spawn"与失败可见性(round-7 实测)**:真实会话里宿主的 `subprocess` 通路**在会话早期可能对一次 spawn 返回空**(`exit=null`)。因此:版本探测**最多尝试 3 次**(20s / 45s / 45s + 退避),且只在**看起来像冷启动/空返回**(`timedOut`、`exit===null`、`spawned=false`)时重试——确定性失败(非零退出、输出无法解析)**不重试**。仍然失败时返回 `MATH_ENGINE_UNUSABLE` 并携带**机读诊断** `probe: {argv, exit, timedOut, ms, spawned, stderrTail, attempts, retried, retryDiag}`;**引擎已找到就绝不再给"去装引擎"的指引**(那会误导),改为 `next.kind:'note'`。包预检同样重试;若预检**始终跑不起来**,**不判缺包、不阻塞**,给出 `PACKAGE_PRECHECK_UNKNOWN` 警告(附 attempts/argv)——"探测失败"永远不会被读成"包没装"。
148
151
  - **包存在性探测的 argv 形态**:python 的探针代码遍历 `sys.argv[1:]`,所以包名按**每个一个 argv 项**传入(`<pkgs...>`);R/Julia/Octave 的模板把逗号串插进代码(`<pkgs>`/`__PKGS__`)。真机上曾经把 `numpy,pandas` 当成**一个**包名,导致"已装的包也报缺"。
149
152
  - **探针 argv 里不得出现 shell 元字符(round-7 实测)**:真实宿主的 subprocess 通路会**拒绝**含 `|`(以及 `%`)的 argv——表现为一次**空 spawn**(`exit=null`),而不是报错。因此所有探测/许可证代码都改成**不含 `|`/`%`** 的形式(每包一行 `名称 ok|missing`),解析器两种形态都接受(`名称:状态` 与 `名称 状态`)。**新增/修改任何探针模板时都要保持这条**。
153
+ - **argv 模板的占位符替换是"字符串级"的(审查实测)**:`<script>`/`<expr>` 会在**任意位置**被替换(`String(a).split('<script>').join(abs)`),因此像 matlab 的 `run('<script>')` 这种**内嵌**写法也能正确展开;此前用的是**整元素相等**判断,matlab 实际收到的是**字面量** `run('<script>')`,脚本从未执行且不报错。共享套件现在遍历**整张描述符表**断言"任何 spawn 都不得带未替换的占位符",parity 守卫断言"描述符不得使用未知占位符、且每个用到的占位符都有实现"。
154
+ - **残余不确定性(如实记录)**:r 与 wolfram 的 **run argv** 尚未在夹具中被逐字观测(这两个引擎的 run 在该夹具下被拒绝),它们的 argv 依赖**同一段**字符串级替换代码;若要逐字证据,需在装有该引擎的机器上各跑一次 `mode:'code'`(或扩展夹具让其 run 通过)。
150
155
  - **响应必须永远可 JSON 序列化(round-7 回归)**:诊断对象曾被写成自引用(`retryDiag` 指向父对象),导致 `JSON.stringify(response)` 抛 `Converting circular structure to JSON`,调用方**看不到任何结果**。现在诊断经 `jsonSafeDiag()` 投影为纯字段(`argv/exit/timedOut/ms/spawned/stderrTail/attempts/retried/retryDiag`),共享套件有一条**横切不变量**:`probe`/`run`/`receipt`/`install` 的 13 种响应(成功与失败)都必须能 `JSON.stringify`。**新增任何响应字段都要过这条。**
151
156
  - **探测的 cwd 必须"一定存在"(round-7 真实根因)**:版本探测/包预检/许可探测曾用 `projectRoot()` 作 cwd;**全新会话里该目录还不存在**,而宿主对"cwd 不存在"的 spawn 返回 `spawned:true` + `exit:null`(**重试无用**——同 cwd 同结果),于是新会话第一次 `probe` 会得到"引擎存在但不可用"。现在这三类探测统一走 `probeCwd(H)`:优先宿主可选字段 `probeCwd`,否则 **OS 临时目录**(`TEMP`/`TMPDIR`/`TMP`),最后才回退到项目根;**真正的 `run` 仍用项目根**(run 会先写回执,写盘本身会创建目录树,所以它一直是好的)。
152
157
 
@@ -185,3 +190,5 @@ python 走 pip 时仍有真实的系统模板(不带 `--user`);conda/mamba
185
190
  ## 9. 参数配置在哪改
186
191
 
187
192
  `vibe_math_set_params`(v2/v3)、`vibe_v4_set`(v4)、`vibe_v5_set`(v5)以及各自的控制面:v2/v3 是 `vibe_math_setup`(交互式 schema)/`vibe_math_status`/`vibe_math_template`/`vibe_math_save_settings`,v4 是 `vibe_v4_configure`/`vibe_v4_status`(**没有** `template`/`setup`),v5 是 `vibe_v5_configure`/`vibe_v5_status`(同样没有 `template`/`setup`);六个参数名在四套、README 双语参数表与本文中拼写一致(由 `tests/audit-math-computation-parity.mjs` 与 `audit-math-computation-contract.mjs` 盯着)。
193
+
194
+ - **安装失败的判别(不新增失败码)**:`install` 失败用 **`op:'install'` + `installedSoFar`(已成功的命令 argv)+ `timedOut`** 区分,**不**新增失败码(`MATH_FAILURE_CODES` 属冻结接口,扩展需单独的接口变更批次);成功路径同样给出 `installedSoFar`,调用方无需特判。
@@ -0,0 +1,37 @@
1
+ # 参数机读 schema(`vibe_math_set_params`)与 `PARAM_PROPS_EXTRA` 例外表
2
+
3
+ **真源**:调度参数的描述与类型以 `PARAM_SCHEMA`(`vibe-math-v2.js:307` / `vibe-math-v3.js` 同段)为唯一真源;
4
+ 工具面 `vibe_math_set_params` 的 `parameters` 由 `paramProps()` 从该真源**派生**(两处注册点共用同一派生,杜绝手写字面量两份近似重复)。
5
+
6
+ **派生规则**:源条目 `type: 'enum'` **或** 带 `options: [...]` ⇒ 派生为 `{ type: 'string', enum: options }`(仅 `type === 'enum'` 才算 enum 会漏掉 `leanInitiative`/`mathInstallScope` 这类"string + options"条目);`description` 仅在源里**非空**时才带上(源缺描述时**在源里补**,绝不在 schema 字面量里编词)。
7
+
8
+ **例外表 `PARAM_PROPS_EXTRA`**:源无法表达、必须逐字沿用现状的条目。**该集合必须与本文档逐条相等**,由断言钉住(两侧任一方向漂移 ⇒ 具名红),并配单点变异(删掉一条例外 ⇒ 红)。
9
+
10
+ ## v2(11 条例外)
11
+
12
+ | 键 | 例外原因 |
13
+ |---|---|
14
+ | `solverToolAllow` `solverToolDeny` `verifierToolAllow` `verifierToolDeny` `leanArgs` `mathEngines` `mathPackages` | 现状为 `type: 'array'` + `items`;`PARAM_SCHEMA` 不建模数组类型,逐字沿用以免丢失 `items` |
15
+ | `solverAllowNetwork` `verifierAllowNetwork` `solverAllowScripts` `verifierAllowScripts` | 现状为 `oneOf`(布尔或字符串);源无 `oneOf` 概念,逐字沿用。**这 4 条只在 v2 存在**:v2 允许用字符串 `''` 表达“未设置”,而 v3 用普通 `boolean`(`{ type: 'boolean' }`)——所以“统一两预设”的清理若把 v2 改成 boolean,就是**尚未批准的 agent 面向契约变更** |
16
+
17
+ ## v3(9 条例外)
18
+
19
+ | 键 | 例外原因 |
20
+ |---|---|
21
+ | `solverToolAllow` `solverToolDeny` `verifierToolAllow` `verifierToolDeny` `leanArgs` `mathEngines` `mathPackages` `leanSearchPaths` | 同上:`type: 'array'` + `items`,源不建模数组 |
22
+ | `mode` | **枚举顺序有意与源不同**:字面量为 `['manual','auto']`,`PARAM_SCHEMA` 为 `['auto','manual']`。采用源序属 **agent 面向契约变更,尚未批准**;将来若批准,改为采源序即可(一行翻转),并从此表移除 |
23
+
24
+ ## 覆盖度("带真源描述的描述符数")
25
+
26
+ | 预设 | 现状 | stage 1(例外沿用 + 真源派生) | stage 2(在源补描述后) |
27
+ |---|---|---|---|
28
+ | v2 | 0 / 49 | 45 / 49 | **49 / 49** |
29
+ | v3 | 0 / 63 | 60 / 63 | **63 / 63** |
30
+
31
+ stage 2 待补描述的源条目:v2 = `leanAsync`、`leanSearchPaths`、`mathMode`、`finalPaper`;v3 = `leanAsync`、`mathMode`、`finalPaper`。
32
+
33
+ ## 守卫
34
+
35
+ `tests/v2-fix-probes.test.mjs` / `tests/v3-fix-probes.test.mjs`:
36
+ ① 机读 `description` == 真源逐字;② 两处注册点的 `parameters` 逐字一致;③ **例外集合 == 本表**(含上表的键名清单)。
37
+ 单点变异(把某条描述改回 `''`、或删掉一条例外)⇒ 具名红,由 `tests/v2-fix-probes.mutants.mjs` / `tests/v3-fix-probes.mutants.mjs` 家族承接。
@@ -8,6 +8,7 @@ Chinese counterpart of this index is [`README.md`](./README.md) (中文索引).
8
8
 
9
9
  | Version | Key change | Notes |
10
10
  |---|---|---|
11
+ | 2.7.3 | Patch: the v5 mailbox acknowledges **only after a successful send** (unacknowledged mail is no longer lost), round numbering is correct on founding and on resume, a failed required paper-artifact write is named once with an idempotent refill, a refused task CAS leaves no trace, the member path contract and v4's Lean library-index write failure are named; eight stale "ack first" passages were corrected and a prose↔code pairing guard now prevents them from returning. | [中文](./RELEASE-NOTES-2.7.3.md) · [English](./RELEASE-NOTES-2.7.3.en.md) |
11
12
  | 2.7.2 | Patch: completes engine discovery and evidence on real hosts (engines installed but not on `PATH` are found, no unrunnable install command, `probe` names the absent engines, complete timeout evidence, the constraint policy is stated, archive identity and participant-set semantics are checkable). | [中文](./RELEASE-NOTES-2.7.2.md) · [English](./RELEASE-NOTES-2.7.2.en.md) |
12
13
  | 2.7.1 | Patch: honesty about substitutions, install-policy fixes, and seven defects found by the first real end-to-end run (probe echo, bundled-runtime discovery, `cli` precheck, `mode:'code'`, real versions, metacharacter-free probe argv, fresh-workspace cwd). | [中文](./RELEASE-NOTES-2.7.1.md) · [English](./RELEASE-NOTES-2.7.1.en.md) |
13
14
  | 2.7.0 | Added Lean **incremental** formal verification (async compile + `import` reuse) and the **`math_computation`** tool for all four presets. | [中文](./RELEASE-NOTES-2.7.0.md) · [English](./RELEASE-NOTES-2.7.0.en.md) |
@@ -7,6 +7,7 @@
7
7
 
8
8
  | 版本 | 关键变化 | 说明 |
9
9
  |---|---|---|
10
+ | 2.7.3 | 补丁:v5 邮袋改为「发送成功后才确认」(未确认消息不再丢失)、入会与恢复的轮次编号正确、论文必需产物写失败具名告警且补偿幂等、任务 CAS 被拒不留痕、成员库路径契约与 v4 Lean 索引写失败具名;文档中 8 处旧顺序已改正并新增文↔码配对守卫。 | [中文](./RELEASE-NOTES-2.7.3.md) · [English](./RELEASE-NOTES-2.7.3.en.md) |
10
11
  | 2.7.2 | 补丁:真实引擎发现与证据补齐(不在 PATH 的引擎可发现、不给出跑不了的安装命令、`probe` 点名缺席引擎、超时证据完整、约束口径明说、归档身份与名册口径可核对)。 | [中文](./RELEASE-NOTES-2.7.2.md) · [English](./RELEASE-NOTES-2.7.2.en.md) |
11
12
  | 2.7.1 | 补丁:替代方案诚实性、安装口径修复,以及首次真机端到端跑出的七个缺陷(`probe` 回显、宿主自带运行时发现、`cli` 预检、`mode:'code'`、真实版本号、探测 argv 无元字符、全新工作区的 cwd)。 | [中文](./RELEASE-NOTES-2.7.1.md) · [English](./RELEASE-NOTES-2.7.1.en.md) |
12
13
  | 2.7.0 | 新增 Lean **增量**形式化(异步编译 + `import` 复用)与四套预设的 **`math_computation`** 数学计算工具。 | [中文](./RELEASE-NOTES-2.7.0.md) · [English](./RELEASE-NOTES-2.7.0.en.md) |
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## Overview
6
6
 
7
- - **More reliable discovery on real hosts**: discovery now runs PATH → runtimes shipped by the host → **the known per-OS install locations**, so an engine installed in its default directory but never added to `PATH` (R, for example) is found.
7
+ - **More reliable discovery on real hosts**: discovery runs PATH → runtimes shipped by the host → **the known per-OS install locations**, so an engine installed in its default directory but never added to `PATH` (R, for example) is found. The engines with known default locations are **R / Octave / Julia / MATLAB** (plus Python on Windows); Maple and Wolfram have no stable default layout and are not covered. A host may also supply the candidate roots through the optional `installRoots` interface, so discovery no longer depends on the plugin process's environment variables.
8
8
  - **No more unrunnable install commands**: `MATH_ENGINE_NOT_FOUND` only offers an executable command when that package manager really exists on this machine; otherwise it gives the suggested command, the official location and the reason.
9
9
  - **`probe` no longer reports only the good news**: besides the engines that resolved, it names the **configured but not found** engines and why.
10
10
  - **Complete failure evidence**: `MATH_TIMEOUT` carries the same fields as `MATH_NONZERO_EXIT`, and a timeout's partial output is available in **both the response and the receipt**.
@@ -20,7 +20,7 @@
20
20
  - Constraint policy fields: both a successful response and a "missing packages" failure carry **`versionPolicy`** and **`constraintsNotEnforced`** (the constraints that were **not** checked, or an empty array).
21
21
  - `MATH_TIMEOUT` responses carry **`stdout`**/**`stderr`** (capped); the receipt JSON carries **`partialStdout`**/**`partialStderr`**.
22
22
  - `MATH_ENGINE_NOT_FOUND`'s `next` carries **`suggestedCommand`**, **`packageManager`**, **`packageManagerAvailable`** and **`note`** (plus `vendorUrl` where the engine has one).
23
- - The `run` response carries **`fileIsArchivedScript`**, and emits an **`ARCHIVED_SCRIPT_RERUN`** warning when `mode:'file'` targets an archived script.
23
+ - The `run` response carries **`fileIsArchivedScript`**: when `mode:'file'` targets an **archived copy**, it also emits the **`ARCHIVED_SCRIPT_RERUN`** warning explaining that this is a **new archive by design** (new id, attempt 1, no history). To get a **new attempt of the same archive with change detection**, edit the **original source file** and re-run `mode:'file'` pointing at **that same source path**.
24
24
  - v4: `status()` carries **`rosterVersion`** and **`frozenParticipants`**; the `consensus` / `meeting` / `verify` views carry their own `rosterVersion`.
25
25
  - v4: the `paper` view carries **`dirProjected`** / **`dirSource`** / **`readSideEffect`**, saying whether the path is a projection or real state.
26
26
  - v2/v3: decision and sub-agent interrupt diagnostics carry **`code`** and **`next`** (for example `VIBE_MATH_DECISION_NOT_FOUND`, with the current queue state); v4 member-level calls carry **`V4_NO_SUCH_RESIDENT`** with the current roster and a next step.