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
@@ -29,7 +29,7 @@
29
29
  | 雇佣 | 只能由人工/主代理 `add_member` | **院士与常驻研究员均可自主雇佣临时工**(框架代为主持) |
30
30
  | 解雇 | `remove_member`(框架侧删除,子代理仅 interrupt) | **逻辑解雇 + 真实释放**(`drainContinuableChildren`),额度不占用终身配额 |
31
31
  | 公共规则 | 每轮提示里注入章程(曾在 §24.1-③ 因重复注入泄漏) | **章程写入员工 `persona`(持久、随简历恢复)**,结构性消除重复注入 |
32
- | 消息 | 手搓 `mailboxes.json` + `relayToGroup` | **移植持久邮箱**:pending−delivered、按目标串行投递、去重、queued 不重发 |
32
+ | 消息 | 手搓 `mailboxes.json` + `relayToGroup` | **移植持久邮箱**:pending−delivered、按目标串行投递、`inboxInjected`/`inboxSuppressed` 去重、**ack 只在发送成功后发生 ⇒ 未 ack 仍可重投**(同一轮内不重复 prepend) |
33
33
  | 任务板 | `Shared/taskboard.md` + `State/taskboard.json` | **移植 CAS 任务 DAG**:`task-<n>`、`revision` 比较交换、依赖满足才可认领 |
34
34
  | 等待 | `activityTimeoutMs` 心跳轮询 | **移植一次性活动等待**(`vibe_v5_wait`,不轮询) |
35
35
  | 状态持久化 | `State/*.json` 直写 fs(曾出 F-9 静默覆盖/丢写) | **加固 JSON 单文件** `State/<研究所>.v5state.json`:内存事件折叠 + 逐文件串行化写入整份最新快照;**绝不写宿主会话日志**(§8.1) |
@@ -121,7 +121,7 @@ v5 研究所服务(preset 内 vibe-math-v5.js,零 npm 依赖)
121
121
  │ ├── m 票布尔一致表决 + Verified/ 落盘 ← 官方无表决
122
122
  │ └── 人对研究所的干预通道(vibe_v5_* 与 /v5) ← 官方无
123
123
  └── 移植(把官方 agent-teams 的成熟设计照搬并改造)
124
- ├── 持久邮箱:pending − delivered、按目标串行 tail、in-flight 去重、queued 永不重发
124
+ ├── 持久邮箱:pending − delivered、按目标串行 tail、`inboxInjected`/`inboxSuppressed` 去重(同轮不重复 prepend)、**ack 仅发送成功后发生 ⇒ 未 ack 仍可重投**
125
125
  ├── CAS 任务 DAG:task-<n>、revision 比较交换、blockedBy 依赖校验、claim/release/... 动作矩阵
126
126
  ├── 一次性活动等待(免轮询)+ "无人在跑"快捷返回
127
127
  ├── provisioning → active | failed 的创建对账与崩溃恢复
@@ -481,7 +481,7 @@ flowchart TD
481
481
  | # | 移植/复用的 DSH 机制 | 官方源码位置 | v5 改造点 |
482
482
  |---|---|---|---|
483
483
  | M1 | **一次性活动等待**:按 TEAM id 注册 future-only 一次性 waiter,在任何状态提交或成员 `agent/status` 变化时唤醒,超时返回 | `agent-team/lib/index.js` `TeamActivity.wait` / `notify` | 改为按"研究所"键;暴露为 `vibe_v5_wait {timeout_ms}`,区间 10 000–3 600 000(照搬其校验);附加"无人在跑则立即返回 `noProgress`"的快捷(照搬 `wait_agent` 的 `no-active-peer`) |
484
- | M2 | **持久邮箱**:`pending = messages − delivered`;按目标串行 `dispatchTails`;`inFlightMessages` 去重;`'queued'` 已持久化,**绝不重发** | `TeamMailbox.send/dispatchOnce/recoverFor` | 保留语义;发送者改为"框架以根代理身份投递 + 正文署名"(因 DSH 邻接限制);增加"群聊摘要合批"(见 §11.2) |
484
+ | M2 | **持久邮箱**:`pending = messages − delivered`;按目标串行 `dispatchTails`。**未 ack ⇒ 仍可重投**:ack 只在发送**成功**后发生(`vibe-math-v5.js:6197-6208` 的 `wakeWithInbox`,`:6206` `if (ok && prompt.pending.length) await ackPending(...)`;prompt 构建阶段**只返回** pending,不再就地 ack —— 见 `:6192-6195` 的 G1 注释)。`delivered` 是**有上限的 LRU 窗口**、不是全量历史(`:312-313`),用于回答"这条投递过没有";同一轮内**不会重复 prepend**(`inboxInjected`/`inboxSuppressed`,失败后 `:6205` 清除注入标记以便下次重投)。**注意:`inFlightMessages` 在 v5 里已不存在(grep 0 命中)** | `TeamMailbox.send/dispatchOnce/recoverFor`、`ackDelivered`(`:1229`) | 保留语义;发送者改为"框架以根代理身份投递 + 正文署名"(因 DSH 邻接限制);增加"群聊摘要合批"(见 §11.2)。守卫:`tests/e2e-v5-round2.test.mjs` §[47](`:2130`,失败唤醒后消息**仍 pending**)。(口径复核于 `167b52d`) |
485
485
  | M3 | **CAS 任务板**:`task-<n>`、`revision` 比较交换、`blockedBy` DAG(missing/duplicate/cycle 三校验)、`ready` 派生、动作矩阵 `claim/release/edit/set_dependencies/complete/reopen/reassign/delete`、`writeScopes` 仅告警 | `TeamTaskBoard` | 保留全部语义;**新增**"解雇时自动释放其 owner 的任务"(官方明确 no auto-release,是缺口);`reassign` 归院士/所办 |
486
486
  | M4 | **创建对账**:先写 `provisioning` 记录并 flush,再创建 child;失败写 `failed`;恢复时把未终结的 `provisioning` 与 child 独立持久化的会话对账,判定 `active`/`failed` | `TeamRoster.spawnAdmitted` / `reconcileProvisioning` | 保留;增加 `hiredBy` / `dismissedAt` 等编制字段 |
487
487
  | M5 | **事件折叠式状态**:自有事件类型 + 校验折叠(官方把它折进会话日志投影) | `TeamJournal` + `projection`(key `agentTeam`) | **只移植折叠语义**:事件前缀仍为 `vibe5/`,但折叠结果写进研究所自己的加固 JSON(`State/<研究所>.v5state.json`)——**不注册投影单元、不 `session.append`**(§8.1) |
@@ -792,7 +792,7 @@ VibeMath/Projects/<project>/Institutes/<institute>/
792
792
  | `vibe_v5_say { to, text }` | 私信 | 只投递给 `to`,署名发送者,框头 `【研究所·私信 from <发送者>】` |
793
793
  | `vibe_v5_say { to:'voters', text }` | 只给有表决权者 | 只投给有表决权者,框头 `【研究所·致全体表决者 from <发送者>】`。**不得**降级成"私信"框头:那会让收件人以为自己被单独点名 |
794
794
 
795
- 投递走 M2 的邮箱纪律:**先持久化再投递**;`'queued'` 不重发;按目标串行;失败者保留队列等下次唤醒。
795
+ 投递走 M2 的邮箱纪律:**先持久化再投递**;**ack 只在发送成功后发生(未 ack ⇒ 仍可重投)**;按目标串行;失败者保留队列等下次唤醒。
796
796
 
797
797
  **框头必须反映真实来源(代词化正确性)**,这是本框架的硬要求:
798
798
 
@@ -823,18 +823,18 @@ VibeMath/Projects/<project>/Institutes/<institute>/
823
823
 
824
824
  ### 11.6 邮箱只投递一次
825
825
 
826
- 一次唤醒的提示词里,**同一条消息只能出现一次**。实现上必须**先 ack 邮件、再构造本轮提示词**:
826
+ 一次唤醒的提示词里,**同一条消息只能出现一次**。顺序是 **G1 修好的那一版**:**先构造、后 ack** —— 构造阶段只做去重注入(`inboxInjected` / `inboxSuppressed`),**仅发送成功才确认**(`if (ok && prompt.pending.length) await ackPending(prompt.pending)`),**同一轮内不重复 prepend**。**旧顺序已废弃**(确认先于构造 = 丢消息的形状;`tests/e2e-v5-round2.test.mjs` §[47] 断言失败的唤醒必须让消息**仍然 pending**):
827
827
 
828
828
  ```
829
829
  pending = pendingFor(member) // ① 取待投递
830
- inbox = composeInbox(pending) // ② 组成框头块
831
- ackPending(pending) // ③ 先销账
832
- base = normalPrompt(member) // ④ 再构造(此时 pendingFor 已为空)
830
+ inbox = composeInbox(pending) // ② 组成框头块(注入标记去重;此处不销账)
831
+ base = normalPrompt(member) // ③ 构造本轮提示词(同一轮内不重复 prepend)
833
832
  prompt = inbox + base
833
+ ok = await wakeMember(member, prompt, kind) // ④ 发送
834
+ if (ok && pending.length) await ackPending(pending) // ⑤ 仅发送成功才销账
834
835
  ```
835
836
 
836
- 若顺序反了,`normalPrompt` 内嵌的状态块会再次列出同一批消息,成员会在同一条提示词里**读到两遍**
837
- (实测缺陷)。因此 §11.2 的合批与状态块的 `[新到的消息/通知]` 是**同一条链路**,不能各自投递。
837
+ **为什么不重复 prepend 不再依赖销账时机**:`promptFor` 用 `inboxInjected` / `inboxSuppressed` 记住"本次唤醒已经注入过",所以 `normalPrompt` 内嵌的状态块不会再列出同一批消息;而**把销账提到构造阶段正是 G1 修掉的丢消息形状**(发送失败时消息已被销账 ⇒ 永久丢失)。因此 §11.2 的合批与状态块的 `[新到的消息/通知]` 仍是**同一条链路**,只是销账点必须落在**发送成功之后**。
838
838
 
839
839
  ### 11.2 合批(吸取 v4 §27/§28"邮箱被长链饿死"的教训)
840
840
 
@@ -1063,7 +1063,7 @@ Progress/,不要从头再来",**不得**复用 `【入职首轮】` 与"你
1063
1063
 
1064
1064
  #### 14.5.7 一次提示词只投递一条消息
1065
1065
 
1066
- 见 §11.6。**先 ack、后构造**。
1066
+ 见 §11.6。**先构造、后 ack**:构造阶段只去重注入(`inboxInjected` / `inboxSuppressed`),**仅发送成功才确认**(`await ackPending(`),**同一轮内不重复 prepend**。
1067
1067
 
1068
1068
  #### 14.5.8 提示词完整性的测试基线(**本框架的强制交付物**)
1069
1069
 
@@ -1299,6 +1299,14 @@ M3 绕过 `normalizeMathParams`、M4 `projectRoot` 改成 workspace 根、M5 提
1299
1299
  **触发**:① 自动——收口判定处进入(幂等:`inst().paper` 已是 finalized 就直接走补写路径);
1300
1300
  ② 手动——`/v5 paper [lang=zh|en] [format=both|md|tex] [editor=office|academician] [force]` 或
1301
1301
  `vibe_v5_paper`(`editor=` 是**一次性**覆盖,不写回持久参数)。
1302
+
1303
+ **`/v5` 子命令的真实参数形状**(C4:CLI hint 与 fallback usage 曾比实现窄,现已与实现逐项一致):
1304
+ `configure <研究所名> <问题…>`|`start`|`resume`|`pause`|`stop`|`status`|`report`|`members`|
1305
+ `message <收件人|all> <正文…>`|`meeting <议程…>`|`hire <用途> <初始任务…>`(两者都必填,与
1306
+ `vibe_v5_hire` 一致)|`fire <成员id> [理由…]`|`add [方向…]`|`remove <成员id>`|
1307
+ `set <键>=<值> …`(缺 `=` 会显式报错,不再静默 no-op)|
1308
+ `paper [lang=zh|en] [format=both|md|tex] [editor=office|academician] [force]`。
1309
+ 无参 `configure` 是**真 no-op**(不再把已结题研究所的 `phase` 重置为 idle)。
1302
1310
  **自动触发恒用 `academician`**:所办是根会话、没有唤醒路径,自动流程必须能无人值守完成;`office`
1303
1311
  只从手动路径进入,此时流程停在 `awaiting-editor` 等人工驱动。
1304
1312
 
@@ -1515,7 +1523,7 @@ Lean 工具另外会返回**非错误码**的失败原因(都属于"可读结
1515
1523
  建所(1 院士 + N 研究员,各自 brainstorm 出方向);`Member` 记录与不可变身份;`provisioning → active|failed` 对账;`vibe_v5_status`/`list`/`wait`;`vibe_v5_configure`/`start`/`pause`/`resume`/`stop`;**院士的 L1 全所视图 `vibe_v5_overview`**(编制 + 各成员 Progress 摘要 + 任务板 + 停滞告警)。
1516
1524
 
1517
1525
  ### P3 · 通信
1518
- 邮箱(pending−delivered、按目标串行、去重、queued 不重发);`vibe_v5_say`(群聊/私信/表决者);摘要合批;以根代理身份中继并署名;`vibe_v5_wait`。
1526
+ 邮箱(pending−delivered、按目标串行、`inboxInjected`/`inboxSuppressed` 去重、**ack 仅发送成功后发生 ⇒ 未 ack 仍可重投**);`vibe_v5_say`(群聊/私信/表决者);摘要合批;以根代理身份中继并署名;`vibe_v5_wait`。
1519
1527
 
1520
1528
  ### P4 · 任务板
1521
1529
  CAS + DAG + 动作矩阵;`priority` 与 `vibe_v5_prioritize`;**`vibe_v5_assign`(院士分派)与 `vibe_v5_nudge`(点名督办)**;人读镜像;认领/被分派唤醒与暂停门控;解雇时回收 owner。
@@ -1612,6 +1620,11 @@ FIFO 提议队列 + 去重窗口;独立初评(快照后清空本轮票);
1612
1620
  | 负值/NaN 时长使看门狗立即放弃(§30-T41) | 未兜底 | `posMs(v, def)` 兜底所有时长读取 |
1613
1621
  | `idSafe` 路径穿越(§30-T39) | id 未消毒 | 统一消毒(§18) |
1614
1622
  | `State` 文件并发丢写 / 损坏静默覆盖(§27 writeJson、F-9) | 直写 JSON | **加固 JSON 单文件**:逐文件串行化 + 迟到写者落下整份最新快照;版本不符/损坏的快照载入时丢弃;镜像只写不读 |
1623
+ | 成员可见**路径基准** ≠ 代码读写者基准 —— **v5**(`LIB_SPEC`) | v5 的读者/写者用 `Members/<id>/Progress/progress.md`;而文案声明"库根 = 项目根"且**推荐直接用 fs** | v5 成员可见路径**一律带成员库根** `Members/<你>/…`;基准只说一条规则("相对路径按会话 cwd 解析 ⇒ 必须用带库根的完整路径");断言**两半都钉**:带根声明 ≥N 且无旧形 `(Progress\|Propos\|Methods\|Subproblems)/<你>/` |
1624
+ | 同一不变量、**相反的基准** —— **v4**(`residentLibraries`) | v4 的读者/写者位于**项目根**:`Progress/<rId>/progress.md`(用户实拍 `<项目根>/Progress/r-1/progress.md` 正是 v4 的**正确**形态);成员可见文本**不得**声明 `Members/<x>/…` | 断言由 `tests/audit-path-discipline.mjs`(v4 半)与 `tests/formal-verify-v4.test.mjs` 的 N5/N6 钉住。v4 与 v5 的路径纪律**相反**,所以**必须分两行写** —— 合并成一条必然把其中一侧写错 |
1625
+ | 成员域工具失败只回裸 `V5_MEMBER_NOT_FOUND`(DEFECT 1) | 无诊断、无下一步 | `memberDiagnosis` 四分支:① 未启动 → `next{kind:start, tool:vibe_v5_start}`;② running 但 0 在册 → `{kind:staff, tool:vibe_v5_add_researcher}`;③ **无身份/所办(会话根不是成员)** → `{kind:member-call, tool:vibe_v5_members}` 且 message 必须解释"所办/成员"之别;④ 名册无此人 → `{kind:roster, tool:vibe_v5_members}`。测试:分支 1/3 **行为级**钉,分支 2/4 **结构级**钉(工具面不可达,注释写明原因) |
1626
+ | `no calling member:` 类拒绝只给 code+message | 未适用同一"code + 状态 + next"契约 | 6 处统一补 `next{kind:'member-call', tool:'vibe_v5_members'}`;断言用通式扫描:每条拒绝之后必须出现 `next{kind:` |
1627
+ | 文档承诺"`off` 是真正的无操作"却在**投递的**提示词里留形式化子句 + 悬空前向引用(v4 `verifyPrompt`) | 只在配置路径上成立,未钉**投递文本** | 子句与其指针同时门控 `formalOn()`(指针直接删除);断言驱动**投递文本**:`off` 下无 `formal`/`形式化段`,`encourage` 下必须出现形式化块 |
1615
1628
  | `reports`/`meetings` 无界膨胀(§29-T37) | 全量存内存 | 状态文件只存索引 + 上限环形;全文只落磁盘 |
1616
1629
  | 唤醒信号硬编码 60 s < 超时 120 s(§18) | 常量写死 | 统一取 `activityTimeoutMs` |
1617
1630
 
@@ -1658,3 +1671,58 @@ FIFO 提议队列 + 去重窗口;独立初评(快照后清空本轮票);
1658
1671
  > 2. **任何上下文压缩之后规章依然生效**(因为它在 persona,不在对话里)。v4 曾为此写了一套
1659
1672
  > "压缩后重申核心规则"的补丁,并因此产生了"规则重申每轮重复泄漏"的真实缺陷
1660
1673
  > (v4 §24.1-③);v5 由架构消除,不需要那个补丁。
1674
+
1675
+ ## §14.9 `status()` / `report()` 字段与作用域(review-6/7 的机器面文档对应物)
1676
+
1677
+ > F12(status/report 评审):`status()` 暴露的机器面字段必须在这里有对应物,并写明**作用域**(耐久=从状态文件派生;会话=进程内重建、重启归零),否则操作者会把会话值当成耐久事实。
1678
+ >
1679
+ > **本节的覆盖范围(F3 的文档半边;复核于 `77a3588`)**:本节只逐一覆盖**下面列出的**字段(行为与理由层面);`status()` 的**完整顶层键集合**由**字段级**文档 [`docs/status-report-fields.md`](../../docs/status-report-fields.md) 的 **`### v5 的字段与作用域`** 表逐条覆盖(name / scope / which-views / meaning 四列,随包发布)—— 本节与那张表是**分工**关系,不是重复。
1680
+ >
1681
+ > **归属(两边都必须这样说)**:字段级对账由 **`tests/audit-status-report-fields.mjs`** 拥有(doc↔source **双向**,并打印 **`frozen / doc-rows / exceptions`** 三个计数,因此不可能空过);**`tests/audit-v5-integrity.mjs` 只保留它的 frozen-key drift 检查**。**本节不写"还有 N 个键没覆盖"这类字面数字** —— 那个数字就是守卫打印的 `frozen - doc-rows - exceptions`(当前 43 − 43 − 0 = 0),需要时**从守卫取**,不要手抄(手抄正是 F-F 的缺陷)。
1682
+
1683
+ | 字段 | 作用域 | 含义 / 注意 |
1684
+ |---|---|---|
1685
+ | `phase` / `runId` / `params` / `lastProgressAt` | 耐久 | 状态文件里的权威值;`autoDone` 是 `phase==='solved'` 的**会话镜像**(S1 修复:载入与同步两处都镜像) |
1686
+ | `running` | 会话 | 本次会话是否在推进;重启后为 false(不代表研究所"停摆") |
1687
+ | `quorum`(含 `m`/`voters`/`rosterVersion`) | 耐久派生 | 单快照视图;`rosterVersion` 用于判断某个旧视图是否过期 |
1688
+ | `members[]` | 混合 | `id/kind/phase/direction/hiredBy/error` 耐久;`rounds`/`busy`/`contextPct`/`childId` **会话**(重启归零) |
1689
+ | `failedMembers` + `failedMembersNote` | 耐久 | 永久 `phase:'failed'` 的成员与补救路径(`resume` 只重试 live-child 上限类) |
1690
+ | `chat.{pending,delivered}` + `chatScope` | 耐久 | `pending` 是**全所**未确认投递数(非"我的未读");`delivered` 是**封顶账本** |
1691
+ | `officeRequests` / `Shown` / `Cap` / `Dropped` / `Truncated` | 耐久 | 只有所办能批准的请求;超过 `OFFICE_REQUEST_CAP=10` 时**丢最旧并计数**,不再伪装成完整清单 |
1692
+ | `persistence.{writeFailures,prematureReads,loadSettled,loadProblem}` | 会话(+ 载入问题字符串) | 写失败次数、首次载入前被读取次数、是否已载入、状态文件被拒的原因;`report()` 有对应行 |
1693
+ | `leanNotices` + `leanNoticesScope` | 会话 | `【形式化结果】` 公告只投发起者一次;耐久事实在 `Formal/Jobs/<jobId>.json` 与 `vibe_v5_lean_job {jobId}` |
1694
+ | `verified` / `verifiedTrue` / `concludedFalse` / `undecided` + `verifiedNote` | 耐久 | `verified` 是历史名(= 已定论,**含判假**);精确语义用后三个字段 |
1695
+ | `fieldScopes` | — | 本表在运行时的机器可读版本(session/durable 两个列表) |
1696
+ | `meetingOpen` | 耐久 | 已开始但未 `finalizeMeeting` 的会议标记;`report()` 把它从"历史会议"里剔除并显式告警 |
1697
+ | `report()` 的 `## ⚠ 状态诊断` | 混合 | 三个来源:顶层 `diagnostics`(被跳过的事件)、会话载入问题日志、研究所层"忽略的键";每行都带真实 `type`/`error`,**不会**出现 `undefined` |
1698
+ | `phaseFactLine()` | — | `report()` 与 `State/README.md` 共用的"阶段/运行中/已结题"单一句式(F13:同一事实只有一个组合处) |
1699
+
1700
+ ## §14.10 G-6:容量拒绝的成员"排队并在下一轮重试"(对齐 v4);G-7:沙箱围栏根的自检
1701
+
1702
+ ### G-6 —— 被 live-child 上限拒绝的成员:排队 + 自动重试(1 轮内完成)
1703
+
1704
+ - **此前的实际行为(证据而非假设)**:`vibe_v5_resume` **已经**会重建"被上限拒绝"的成员——`vibe-math-v5.js` 里 `queued = inst().members.filter(m => /active child limit|maxActiveSubagents/.test(m.error))` 并在同一轮 `spawnMember`,返回值 `respawned` 计数(对应 `tests/e2e-v5-round2.test.mjs` 的断言 "a slot freed by firing a member lets resume rebuild a capped member")。但**调度器自己从不重试**:不调用 `resume` 就没有任何动静 ⇒ 与 v4(`pendingSpawns` + 每个心跳 `retryPendingSpawns()`)相比,v5 是"手动才动"。
1705
+ - **本轮的机制(对齐 v4)**:
1706
+ 1. 拒绝时**就地落耐久标记**:成员记录写 `phase:'failed'` **且** `failReason:'activation-limit'`(`spawnMember` 的 catch、`vibe_v5_hire` 的临时工分支、`addResearcher` 的常驻研究员分支三处)。
1707
+ 2. 队列**派生自耐久状态**,不是第二个真相源:`pendingSpawnMembers()` = `isCapRefusedMember(m) && !m.childId`;`isCapRefusedMember()` 同时被 `resume` 的 `queued` 过滤复用 ⇒ 两个路径对"哪些失败是延后工作"永远一致(普通 provider 失败绝不被复活)。
1708
+ 3. 每个调度轮次开头调用 `retryPendingSpawns()`(**位置在 `schedulePass` 的 `dbg.passes += 1`(`vibe-math-v5.js:6320`)与 `loadSettled` 等待之后、任何"选活/调度"之前** —— `:6329`;早先写成"`schedulePass` 首行"是**措辞**不准:首行是 `dbg.passes += 1`。**语义("在任何调度/选择之前重试")成立**;若将来加守卫,请断言**位置语义**(重试先于选活/派发),不要钉"字面首行"):容量未释放时该次重试**不询问宿主**(`spawnMember` 从已记录的天花板直接拒绝,见 `hostChildLimit` 的 skip-before-spawn),零成本;容量释放后成功创建 ⇒ 成员转 `active`、列表自动清空。(措辞复核于 `167b52d`)
1709
+ 4. **停止规则而非会话计数**:只有"非容量类 provisioning 错误"会停止该成员的自动重试(并 `notice` 所办);没有"会话内最多 N 次"的硬上限——那会让长时间运行的会话**永久丢掉**延后工作。
1710
+ - **可观测性**:`status().pendingSpawns[] = {id, kind, attempts, error}` + `pendingSpawnsNote`;`report()` 一行 `- 待补建成员:r-2(常驻研究员,已重试 N 次)…`(仅在非空时输出)。`fieldScopes` 同步:`pendingSpawns[].attempts` 属 session,队列本身属 durable(派生)。
1711
+ - **证明**:`_oneoff/g6-pending-spawns.mjs` —— 控制组 **18/0**:上限=1 时 3 人 founding ⇒ 1 活 + 2 条 `pendingSpawns`(`failReason` 与 cap 文本齐备、`report()` 有该行);释放一个位置后**恰好重建一个**、另一个仍排队(`attempts` 递增);继续释放 ⇒ **队列清空**且**每个成员恰好创建一次**(`perMember={"r-1":1,"r-2":1,"r-3":1}`)。变异:删掉 `schedulePass()` 里的 `retryPendingSpawns()` 调用(单站点)⇒ 具名红(`★ G-6b: ONE freed slot rebuilds exactly ONE deferred member…`、`★ G-6b: the rebuilt member is ACTIVE with a child`)。
1712
+
1713
+ ### G-7 —— 沙箱策略的"可写根"与会话工作目录不一致时必须可见(安全相关)
1714
+
1715
+ - **背景(安装器复核 ROUND2)**:宿主 `dsh-sandbox-policy` 的 `catch(e){} → resolve({})` 兜底可能把写围栏从会话工作区**静默放宽**到进程 cwd/配置根。v5 消费 `sandboxPolicy` 的方式:`sandboxPolicyOf()`(惰性 `ctx.get`,1 处)、`workspaceRoot()` 在**会话 cwd 缺失时**退回 `sp.workspaceRoot`(1 处)、`getPolicy()` 把 `sp.resolve({session})` 的结果**原样**交给 `fs.writeText`(2 处调用)——即 v5 **不覆盖**宿主围栏。
1716
+ - **v5 能看见的字段**:`policy.workspaceRoot`(可写根的声明)。因此本轮做的是**比较 + 具名告警**,不是拒绝写:`checkSandboxPolicyRoot()` 把 `policy.workspaceRoot` 与会话 cwd 规范化后比较(每会话只查一次,`sandboxPolicyChecked`),不一致则记录 `sandboxPolicyMismatch = {at, expected, policyRoot, note}`、打印一次具名 `console.error`、并在 `status().persistence.sandboxPolicyMismatch` 与 `report()` 的 `- ⚠ 沙箱策略根与会话工作目录不一致:…` 暴露。**不拒绝写入的理由**:v5 无法改变宿主围栏,拒绝只会让合法工作静默停止持久化;告警把"可能落在预期之外的树"这个事实交给操作者。
1717
+ - **证明**:`_oneoff/g7-sandbox-root.mjs` 控制组 **9/0**(策略根=会话根 ⇒ 无字段、无 ⚠;漂移到 `other-root` ⇒ 字段含两个根 + 后果说明、具名 `console.error` 恰好一次、`report()` ⚠ 行、无字面 `undefined`)。变异:把记录条件改成 `if (false)`(单站点)⇒ 具名红。
1718
+
1719
+ ### 一条自带检查的边界(复核者措辞)
1720
+
1721
+ - **随包自检证明的是服务/API 的"形状",不是运行时的"语义"**:`selfdrive`/`audit-*` 能证明工具被注册、参数被接受、返回值有该字段、失败码是类型化的;它**不能**证明宿主在真实故障路径上(例如 `sandboxPolicy.resolve` 的内部 catch)表现得与形状一致。因此 **"self-check passed" 不得读作 "compatible"**:兼容性要靠本文件这类"故意制造漂移/失败"的对抗夹具(G-7 的 mismatch、G-6 的容量拒绝、L2 的缺陷强制弃权)来证明。
1722
+
1723
+ ### G-6b —— 每一条"容量拒绝"路径都必须落耐久原因(不留文本匹配兜底)
1724
+
1725
+ - **问题**:founding 的两处 catch(院士 `vibe-math-v5.js` 的 `spawnMember` 之后、研究员分支)会**重写** `spawnMember` 自己已经写好的失败记录,而那一版**没有** `failReason` ⇒ 该路径的分类就只剩"匹配错误文本"(`isCapRefusedMember()` 的兜底),正是本轮要消除的"两条路可能分歧"。另外 `status().members[]`/`failedMembers[]` 也不暴露该字段,操作者无法直接审计队列。
1726
+ - **修法**:两处 founding catch 与其它三处一样写 `failReason: isActivationLimitReached(e) ? 'activation-limit' : 'other'`;`status().members[]` 与 `status().failedMembers[]` 增加 `failReason`(公开面可审计,不必再按错误文本反推)。
1727
+ - **守卫(可测的"没有兜底也能工作")**:新增文档化的测试缝 `V5_SPAWN_RETRY_STRICT=1` —— 它让 `isCapRefusedMember()` **只认耐久 `failReason`**、完全关闭错误文本匹配(读在每次调用)。同一场景跑两遍(normal / STRICT):派生队列必须**逐一相同**且等于失败集合;实现见 `_oneoff/g6b-failreason.mjs`(控制组 **6/0**,同时校验 `status()` 公开面与**状态文件**里的 `failReason`;变异"删掉 researcher 分支的新写入"(单站点)⇒ 具名红)。
1728
+ - **边界**:`reconcileProvisioning()` 里"重启找不到子会话"导致的 `failed` **不是**容量拒绝,因此不带该标记(它靠 `Object.assign({}, m, …)` 保留原有标记,不会被误分类为待补建)。
@@ -119,7 +119,7 @@ sequenceDiagram
119
119
  participant L as 状态文件(State/&lt;研究所&gt;.v5state.json)
120
120
 
121
121
  S->>S: schedulePass:按优先级挑一个成员
122
- S->>S: 先 ack 收件箱,再构造本轮提示词
122
+ S->>S: 构造本轮提示词(收件箱:先构造、后 ack —— 仅发送成功才 `await ackPending(`)
123
123
  S->>M: 【轮次】+ 状态块(编制/m/我的任务/新消息)+ 回执契约
124
124
  M->>M: 读同事的库 · 推导 · 写自己的 Progress/ 与卡片
125
125
  M-->>S: subagent/end + 单个 JSON 回执
@@ -323,7 +323,7 @@ flowchart LR
323
323
  end
324
324
  subgraph R["每轮提示词(临时,短)"]
325
325
  ST["状态块<br/>&#91;状态&#93; 你是 &lt;代号&gt;(职位)|轮次|m|表决者数<br/>&#91;在册&#93; … &#91;未就位&#93; …<br/>&#91;任务板&#93; … &#91;新到的消息/通知&#93; …"]
326
- ASK["本轮问句(常规 / 心跳 / 会议 / 表决)<br/>+ 收件箱(先 ack 再构造,绝不重复投递)"]
326
+ ASK["本轮问句(常规 / 心跳 / 会议 / 表决)<br/>+ 收件箱(**先构造、后 ack**:构造阶段只做去重注入 `inboxInjected`/`inboxSuppressed`;**仅发送成功才确认** `await ackPending(`;**同一轮内不重复 prepend**)"]
327
327
  SPEC["回执契约(按职位裁剪)<br/>say / progress / record / propose_verify / verdict<br/>propose_meeting / task_* / reject_assign / input<br/>hire / fire / vote_solved / solved / contextPct / compacted"]
328
328
  end
329
329
  P --> MEM["成员(子会话)"]