dsh-vibe-math 2.3.16 → 2.4.0

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 (32) hide show
  1. package/README.en.md +49 -46
  2. package/README.md +48 -45
  3. package/cordis.patch.yml +1123 -10
  4. package/docs/AUDIT-CHECKLIST.md +45 -0
  5. package/docs/COMPAT-AUDIT-ROUND2.md +3 -0
  6. package/docs/COMPAT-AUDIT-ROUND3.md +139 -0
  7. package/docs/generate_framework_diagram_v5.mjs +27 -27
  8. package/docs/release-notes/RELEASE-NOTES-2.4.0.md +172 -0
  9. package/docs/test-timing.md +2 -1
  10. package/installer.js +89 -21
  11. package/package.json +33 -9
  12. package/preset-declaration.js +62 -0
  13. package/prompt-corpus-v5/prompt-corpus-v5.json +7 -7
  14. package/prompt-corpus-v5/prompt-corpus-v5.md +8 -129
  15. package/scripts/build-preset-rows.mjs +146 -0
  16. package/tests/audit-installer-compat.test.mjs +9 -3
  17. package/tests/audit-preset-rows.test.mjs +185 -0
  18. package/tests/audit-v5-integrity.mjs +49 -3
  19. package/tests/e2e-v5-round2.test.mjs +137 -67
  20. package/tests/formal-verify-v5.test.mjs +43 -35
  21. package/tests/prompt-v5-integrity.test.mjs +49 -47
  22. package/tests/selfdrive-v5.mjs +42 -55
  23. package/vibe-math-v2/vibe-math-v2.js +6 -2
  24. package/vibe-math-v3/vibe-math-v3.js +6 -2
  25. package/vibe-math-v4/vibe-math-v4.js +191 -13
  26. package/vibe-math-v5/agent.cordis.yml +8 -8
  27. package/vibe-math-v5/preset.yml +1 -1
  28. package/vibe-math-v5/vibe-math-v5.js +215 -91
  29. package/vibe-math-v5//345/256/236/347/216/260/346/226/271/346/241/210.md +56 -44
  30. package/vibe-math-v5//346/236/266/346/236/204/345/233/276.md +24 -22
  31. package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v5-en.svg +14 -14
  32. package//347/244/272/344/276/213/345/233/276//346/241/206/346/236/266/345/233/276-v5.svg +14 -14
@@ -14,7 +14,7 @@
14
14
  | `vibe-math-v4/agent.cordis.yml`(300 行) | v4 的完整提示词与人设。v5 章程在其基础上改写为"研究所规章"。 |
15
15
  | `@deepseek-ai/dsh-experimental-agent-team@0.1.5-rc.2` 源码 | 已完整通读(`lib/index.js` 75 KB + 全部 `.d.ts`)。**移植来源**。 |
16
16
  | `@deepseek-ai/dsh-experimental-tool-agent-team@0.1.5-rc.2` 源码 | 已完整通读。其 9 个工具的 schema 与 `team:policy` 做法是 v5 工具面的参照。 |
17
- | 本机 DSH 运行时实测 | DSH `0.1.5-rc.2`;`subagents` / `agents` / `sessionProjections` / `fs` / `tools` / `commands` / `timer` / `compaction` 均已挂载并取到精确契约。 |
17
+ | 本机 DSH 运行时实测 | DSH `0.1.5-rc.2`;`subagents` / `agents` / `sessionProjections` / `fs` / `tools` / `commands` / `timer` / `compaction` 均已挂载并取到精确契约。(v5 **不使用** `sessionProjections`,见 §8.1。) |
18
18
  | 隔离 `DSH_HOME` 实测 | 官方 agent-teams bundle **确实可挂载**(已用 `--dump-config` + 真启动 + 反例对照证明)。该结论仅作为"官方路径可行"的存档,**v5 不采用该路径**。 |
19
19
 
20
20
  ---
@@ -32,7 +32,7 @@
32
32
  | 消息 | 手搓 `mailboxes.json` + `relayToGroup` | **移植持久邮箱**:pending−delivered、按目标串行投递、去重、queued 不重发 |
33
33
  | 任务板 | `Shared/taskboard.md` + `State/taskboard.json` | **移植 CAS 任务 DAG**:`task-<n>`、`revision` 比较交换、依赖满足才可认领 |
34
34
  | 等待 | `activityTimeoutMs` 心跳轮询 | **移植一次性活动等待**(`vibe_v5_wait`,不轮询) |
35
- | 状态持久化 | `State/*.json` 直写 fs(曾出 F-9 静默覆盖/丢写) | **移植会话日志投影**(host-only projection unit):原子、有序、可回放、免 fs 竞态 |
35
+ | 状态持久化 | `State/*.json` 直写 fs(曾出 F-9 静默覆盖/丢写) | **加固 JSON 单文件** `State/<研究所>.v5state.json`:内存事件折叠 + 逐文件串行化写入整份最新快照;**绝不写宿主会话日志**(§8.1) |
36
36
  | 裁定 | 全体常驻一致(真/假) | **≥ m 名有表决权者一致给出布尔 1 或 0**(m 可调,默认 `min(3, 有表决权人数)`) |
37
37
  | 主代理 | hands-off | **hands-off 的"对外接口/所办"**:汇报 + 自然语言控制,**不参与研究** |
38
38
  | 依赖 | 无(preset 内单文件) | **无(preset 内单文件)**——不引入任何 npm 包 |
@@ -59,7 +59,7 @@
59
59
  9. **可信度分级不变**:只有 `Verified/` 绝对可信;其余皆为经验性参考。
60
60
  10. **上下文自治**:员工上下文达阈值自动压缩;压缩后规章不丢(因为规章在 `persona`,不在对话里)。
61
61
  11. **可控**:人/主代理随时可干预(留言、提要求、开会、增开关闭成员、暂停/恢复/终止)。
62
- 12. **持续化**:全部状态随会话日志持久化,支持断点续跑;一所一会话,天然隔离。
62
+ 12. **持续化**:全部状态持久化在研究所自己的加固 JSON 文件 `State/<研究所>.v5state.json` 里,支持断点续跑;一所一会话、一份工作区,天然隔离。**绝不写宿主会话日志**——写入会让用户自己的会话再也恢复不了(§8.1)。
63
63
 
64
64
  ---
65
65
 
@@ -96,9 +96,10 @@ subagents.drainContinuableChildren(parent: Agent, childIds) // ★ 真正"释
96
96
  subagents.listChildren(parentSessionId, signal) -> SubagentListEntry[]
97
97
  // entry.activity: 'running' | 'inactive'; entry.mode: 'one-shot' | 'continuable'
98
98
  agents.get(id) / agents.list() / agents.roots()
99
- sessionProjections.register({ key, stateSchema, init, apply, stateVersion }) -> disposer
100
- sessionProjections.stateOf(session, key) // ★ host-only 单元:不进客户端快照,但始终被 checkpoint
101
- session.append(type, data) + sessions.flush(session)
99
+ fs.resolve / fs.stat / fs.readText / fs.writeText / fs.listDir
100
+ // ★ 状态持久化只用 fs 自己写 State/<研究所>.v5state.json。
101
+ // sessionProjections.register/stateOf 与 session.append/sessions.flush 一律【不使用】:
102
+ // append 无法把事件标为 ignorable,宿主因此拒绝载入该会话(§8.1)。
102
103
  ```
103
104
 
104
105
  **两个决定性推论:**
@@ -125,7 +126,7 @@ v5 研究所服务(preset 内 vibe-math-v5.js,零 npm 依赖)
125
126
  ├── 一次性活动等待(免轮询)+ "无人在跑"快捷返回
126
127
  ├── provisioning → active | failed 的创建对账与崩溃恢复
127
128
  ├── 路径规范化(write scope)与稳定错误码
128
- └── 严格的会话日志投影(自有事件 + host-only 投影单元)
129
+ └── 加固 JSON 状态文件(自有事件折叠 + 逐文件串行化写入,见 §8)
129
130
  ```
130
131
 
131
132
  ---
@@ -140,7 +141,7 @@ v5 研究所服务(preset 内 vibe-math-v5.js,零 npm 依赖)
140
141
  | **常驻研究员** | `r-<n>` | N(默认 3) | 与研究所同寿 | 建所时创建;此后可由院士提议增聘常驻(需人/主代理确认,见 §10.4) |
141
142
  | **临时工** | `t-<n>` | 0…上限 | **任务期内** | **由院士或任一位在册常驻研究员自主雇佣** |
142
143
 
143
- > `n` 为**会话内单调计数器**(分职位独立),一经分配**永不复用**(沿用 DSH"名字不朽"原则,避免档案串号)。全部编号持久在投影状态里。
144
+ > `n` 为**会话内单调计数器**(分职位独立),一经分配**永不复用**(沿用 DSH"名字不朽"原则,避免档案串号)。全部编号持久在 `State/<研究所>.v5state.json` 状态里。
144
145
 
145
146
  ### 4.2 主代理(会话根代理)= 研究所的"对外接口 / 所办"
146
147
 
@@ -216,7 +217,7 @@ flowchart TD
216
217
  MTG[会议召集与纪要]
217
218
  TASK[任务板 CAS·DAG]
218
219
  VOTE[表决箱 m 票布尔一致]
219
- PERSIST[会话日志投影<br/>状态权威源]
220
+ PERSIST[加固 JSON 状态文件<br/>State/&lt;研究所&gt;.v5state.json<br/>状态权威源]
220
221
  MIRROR[人可读镜像文件]
221
222
  WAIT[一次性活动等待]
222
223
  LIFE[分级保活·看门狗]
@@ -483,7 +484,7 @@ flowchart TD
483
484
  | M2 | **持久邮箱**:`pending = messages − delivered`;按目标串行 `dispatchTails`;`inFlightMessages` 去重;`'queued'` 已持久化,**绝不重发** | `TeamMailbox.send/dispatchOnce/recoverFor` | 保留语义;发送者改为"框架以根代理身份投递 + 正文署名"(因 DSH 邻接限制);增加"群聊摘要合批"(见 §11.2) |
484
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` 归院士/所办 |
485
486
  | M4 | **创建对账**:先写 `provisioning` 记录并 flush,再创建 child;失败写 `failed`;恢复时把未终结的 `provisioning` 与 child 独立持久化的会话对账,判定 `active`/`failed` | `TeamRoster.spawnAdmitted` / `reconcileProvisioning` | 保留;增加 `hiredBy` / `dismissedAt` 等编制字段 |
486
- | M5 | **严格会话日志投影**:自有事件类型 + 校验折叠;事件**不进模型历史** | `TeamJournal` + `projection`(key `agentTeam`) | 改为自有 key(host-only 单元,无 `wire`),事件前缀 `vibe5/` |
487
+ | M5 | **事件折叠式状态**:自有事件类型 + 校验折叠(官方把它折进会话日志投影) | `TeamJournal` + `projection`(key `agentTeam`) | **只移植折叠语义**:事件前缀仍为 `vibe5/`,但折叠结果写进研究所自己的加固 JSON(`State/<研究所>.v5state.json`)——**不注册投影单元、不 `session.append`**(§8.1) |
487
488
  | M6 | **路径规范化**:反斜杠归一、去 `./` 与前导 `/`、拒绝绝对路径/盘符/`.`/`..` 段 | `normalizeWriteScope` | 与 v4 的 `idSafe` 合并,用于成员 id、卡片 id、任务 write scope |
488
489
  | M7 | **稳定错误码**:`TEAM_*` 系列,分类明确 | 全套 `TeamError` | 照搬为 `V5_*`(见 §15.3) |
489
490
  | M8 | **按 Agent 作用域注册工具与提示段** | `tool-agent-team` `maybeInstall` | 采用 v4 的"会话作用域注册 + `exec.agent` 路由"(已审计验证),并额外用 `persona` 承载规章 |
@@ -501,27 +502,36 @@ flowchart TD
501
502
  | G5 | 会议 | 框架召集、收集、轮换发言顺序、写纪要、落任务/触发验证 |
502
503
  | G6 | m 票布尔表决 + `Verified/` | 框架计票、独立初评 → 公开辩论 → 落盘或留库附概率 |
503
504
  | G7 | 人对研究所的干预面 | `vibe_v5_*` 工具 + `/v5` 斜杠命令 |
504
- | G8 | 编制/职称/职权 | 自有投影状态 + 职权矩阵校验 |
505
+ | G8 | 编制/职称/职权 | 自有状态(`State/<研究所>.v5state.json`)+ 职权矩阵校验 |
505
506
 
506
507
  ---
507
508
 
508
- ## 8. 数据模型(会话日志投影)
509
+ ## 8. 数据模型(加固 JSON 状态文件)
509
510
 
510
- ### 8.1 为什么用投影而不是 `State/*.json`
511
+ ### 8.1 为什么用加固 JSON 文件,而不是宿主会话日志投影
511
512
 
512
- v4 用 fs 直写 JSON,为此在审计中付出过真实代价:**F-9 损坏文件被静默覆盖(数据丢失)**、`writeJson` 并发丢写、跨进程 `resume` 用陈旧磁盘快照覆盖内存态。会话日志投影**在构造上**消除这一整类问题:
513
+ **首版设计用的是投影**:v5 注册一个 host-only 投影单元(key `vibeMathV5`),把 `vibe5/*` 事件 `session.append` 进会话日志,再读回折叠结果。它确实带来了"追加即有序、可精确回放、由 DSH 负责跨进程恢复"的好处,沿用自官方 agent-teams 的 `TeamJournal`。
513
514
 
514
- - 事件**追加即有序**,不存在半写文件;
515
- - 状态由 `init + fold(events)` 得出,**可精确回放**;
516
- - 跨进程恢复由 DSH 的投影缓存负责,**不再需要 v4 的 `processEpoch` 启发式判断**;
517
- - 自有事件不是 `SurfaceEventType`,**不进入模型历史**(零上下文成本);
518
- - `register()` 的 disposer 挂在 preset fiber 上,停止/更新自动清理。
515
+ **这条路已被 DSH 0.2.0 审计推翻,原因是它会让用户自己的会话再也恢复不了:**
519
516
 
520
- > **P0 门槛 G-1**:必须先用一个最小实验证明——preset 内插件能 `register()` 一个 **host-only** 投影单元,自定义事件能 `session.append` 并经 **进程重启 + 会话恢复**正确折叠。若此门槛不通过,退回到"v4 加固版 fs 存储"(`writeJson` 串行化 + `assertWritable` 写前校验 + 原子替换),架构其余部分不变。
517
+ - `Session.append(type, data, …)` 组装的信封是 `{type, seq, time, data, …surfaceMetadata}`——它**无法把事件标记为 `ignorable`**(证据:安装的宿主 `dsh-session/lib/index.js`)。
518
+ - DSH 的会话持久化在载入时校验事件类型:**日志里出现已知集合之外的事件类型、且 `ignorable !== true` 时,直接拒绝载入该会话**并抛 `SessionFormatUnsupportedError`(证据:`dsh-session-persistence/lib/index.js` → `validateStoredEvents`;该规则在 `0.1.5-rc.2`、`0.1.7-rc.2`、`0.2.0-rc.2` 均存在)。
519
+ - 两条合起来的**具体后果**:只要研究所往用户会话里追加过一条 `vibe5/*` 事件,**用户自己的那次会话就 `resume` 不了**——这不是"多占一点日志",而是把用户的工作会话弄坏。研究所状态再"零 token 成本",也不能以宿主会话的可用性为代价。
521
520
 
522
- ### 8.2 投影键与状态
521
+ **现在的实现**:`makeFileBackend`(`installBackend` 装的唯一后端)把状态写进**研究所自己的加固 JSON 文件** `State/<研究所>.v5state.json`,并保留投影设计里真正有价值的性质:
523
522
 
524
- `key = 'vibeMathV5'`,`stateVersion = 1`,**host-only**(不传 `wire`)。
523
+ - **唯一权威**:状态只在内存里由 `init + fold(events)` 得出,整份快照写到那个文件;**宿主会话日志一个字节都不写**(测试直接钉住"日志零事件")。
524
+ - **写入串行化**:每个文件的写入排在一条 `chain` 上,且 `JSON.stringify` 推迟到执行时刻——迟到的写者落下的**永远是完整的最新状态**,不可能用陈旧子集覆盖(这正是 v4 §27 `writeJson` 丢写缺陷的教训)。
525
+ - **坏文件不致命**:载入时只接受版本匹配的快照,损坏/半写的文件被忽略并回到初始状态,不会抛进调度循环;"读不到"与"读到垃圾"都不会闩锁(§8.3)。
526
+ - **人可读镜像仍然只写不读**(§8.4)。
527
+
528
+ > **P0 门槛 G-1(结论:投影方案被推翻)**:当初用它验证"preset 内插件能 `register()` 一个 host-only 投影单元 + 自定义事件,且经进程重启 + 会话恢复正确折叠"。前几条当时通过,但它与"**用户会话必须保持可恢复**"直接冲突(上面的 `ignorable` 规则),因此整个投影路径被删除:`sessionProjections` 依赖、投影注册、投影后端、`STATE_SCHEMA`、`projectionsOf()` 全部移除,`inject` 也从 `['subagents','agents','fs','tools','commands','timer','sessions']` 收窄为 `['subagents','agents','fs','tools','commands','timer']`。持久化门槛的判据相应改为:① 状态文件是唯一权威;② **进程重启**后编制/任务/邮箱/验证精确恢复;③ 宿主会话日志保持**零事件**。
529
+
530
+ ### 8.2 状态文件与版本
531
+
532
+ `State/<研究所>.v5state.json`(`<研究所>` = `vibe_v5_configure` 的 `institute`,默认 `institute`),`version = 1`。
533
+
534
+ 它**不是投影单元**:没有注册、没有 `wire`、没有 `stateSchema` 契约——就是一份普通(但加固写入)的 JSON,读到版本不匹配的快照即丢弃。
525
535
 
526
536
  ```ts
527
537
  interface V5State {
@@ -584,11 +594,13 @@ interface Member {
584
594
 
585
595
  共 **12 类**事件。`vibe5/queue`(验证队列)、`vibe5/counters`(代号与编号)与 `vibe5/progress`(进展心跳)各占一类。
586
596
 
597
+ > **这些事件只进内存折叠,不进任何日志**:每次提交就是"`applyV5Event(state, {type, data})` 折一次 + 把折叠后的整份快照写进 `State/<研究所>.v5state.json`"。事件名沿用 `vibe5/` 前缀是为了与投影时代的记录对齐,它们**不再是宿主会话日志里的事件类型**(那正是让会话不可恢复的做法,§8.1)。
598
+
587
599
  **折叠纪律(吸取官方教训)**:官方在遇到非法事件时会 `state.failure = ...` **永久闩锁**,此后任何读取都抛错。v5 **不闩锁**:非法事件被**跳过并计入 `diagnostics`**,状态继续前进。理由——研究所的可用性优先于对日志的洁癖,且事件由我们自己的代码产生,非法即代码缺陷,应由测试而非运行时闩锁来发现。
588
600
 
589
601
  ### 8.4 人可读镜像
590
602
 
591
- 投影是权威源,但人(与主代理)需要可读视图。框架在关键节点把投影**单向**镜像为 Markdown:
603
+ 状态文件是权威源,但人(与主代理)需要可读视图。框架在关键节点把状态**单向**镜像为 Markdown:
592
604
 
593
605
  ```
594
606
  VibeMath/Projects/<project>/Institutes/<institute>/
@@ -597,7 +609,7 @@ VibeMath/Projects/<project>/Institutes/<institute>/
597
609
  Shared/Chat/<day>.md # 群聊记录(追加)
598
610
  Shared/Meetings/<mid>.md # 会议纪要全文
599
611
  Shared/Debates/<target>.md # 辩论录全文
600
- State/README.md # 声明"权威状态在会话日志里,本目录仅镜像,勿手改"
612
+ State/README.md # 声明"权威状态是本目录的 <研究所>.v5state.json(不在任何宿主会话日志里);其余仅镜像,勿手改"
601
613
  ```
602
614
 
603
615
  镜像**只写不读**,丢失或损坏都不影响正确性——这本身就是对 v4 F-9 一类数据丢失事故的结构性防护。
@@ -824,7 +836,7 @@ prompt = inbox + base
824
836
  ③ 真正召开:向参与者逐个发会议提示(含**此前已发言者的发言**,使讨论真实发生)
825
837
  ④ 发言顺序**每轮随机轮换**(v4 §24.1-④:避免固定第一位发言者看不到别人)
826
838
  ⑤ 收口条件 = **全部参与者都已发言**(按实时成员集判定,不按"被问过")
827
- ⑥ 写 Shared/Meetings/<mid>.md 全文;投影只存索引
839
+ ⑥ 写 Shared/Meetings/<mid>.md 全文;状态文件只存索引
828
840
  ⑦ 汇总会议中的 propose_task / claim_task / propose_verify / voteSolved:
829
841
  落任务板、触发验证、记停止表决
830
842
  ⑧ 看门狗:lastInputAt 超 recoverStallMs → 放弃该会议(记 abandoned),回到自组织
@@ -851,7 +863,7 @@ prompt = inbox + base
851
863
  2. **`reassign` 提升为院士/所办专属**(官方是 Lead 专属;v5 的 Lead 是所办,但"协调研究所内部工作"应属院士职权)。
852
864
  3. **新增 `priority` 字段与 `vibe_v5_prioritize`**:优先级由院士牵头设定(§4.4-L3),表达"全所资源往哪倾斜"。它**不改变**任务的依赖与认领规则,只是一条排序信号。
853
865
  4. **新增 `vibe_v5_assign`(院士专用)**:把任务**分派**给指定成员——即 §4.4-L2 的机制化。实现上等价于"以院士身份对任意 active 成员执行 reassign + 自动唤醒被分派者",但额外要求院士填写 `why`(分派理由)与 `acceptance`(验收标准),二者写入任务详情。被分派者可执行、也可据理反对(`params.memberMayRejectAssign`)——**反对不阻塞执行**,但框架会把反对意见连同理由**广播给院士与全所**,避免"秘密抗命"。
854
- 5. **人可读镜像**:每次变更后重建 `Shared/TaskBoard.md`(只写不读,最坏情况短暂滞后,权威源仍是投影)。
866
+ 5. **人可读镜像**:每次变更后重建 `Shared/TaskBoard.md`(只写不读,最坏情况短暂滞后,权威源仍是 `State/<研究所>.v5state.json`)。
855
867
 
856
868
  **认领/被分派即唤醒**:成员 `claim` 或被 `assign` 后,框架按 M1 唤醒它开工;但**暂停期不唤醒**(照搬 v4 §29-T36 的门控),恢复后自然接续。
857
869
 
@@ -935,7 +947,7 @@ pass,而该 pass 又立刻唤醒同一个任务所有者 —— 一个未完
935
947
  | 子代理 → 会话路由 | `childOwner: Map<childId, rootAgentId>`,由 `subagent/start` 捕获 |
936
948
  | 成员 → 成果库 | 每人独立目录 `Members/<id>/`,**只写自己、只读他人** |
937
949
  | 共享文件写 | 框架独占写(成员不直接写 `Shared/`、`Verified/`、`Problems/`) |
938
- | 进程 | 投影随会话日志持久化;**不再需要 `processEpoch` 启发式** |
950
+ | 进程 | 状态随 `State/<研究所>.v5state.json` 持久化(启动/恢复时先读文件);**不再需要 `processEpoch` 启发式** |
939
951
 
940
952
  > 注意:v4 的 `claim_write/release_write` 在 v5 中**直接删除**——因为成员不写共享文件(共享文件由框架独占写),成员各自目录无冲突。少一套永不被真正使用的锁机制。
941
953
 
@@ -943,7 +955,7 @@ pass,而该 pass 又立刻唤醒同一个任务所有者 —— 一个未完
943
955
 
944
956
  ```
945
957
  vibe_v5_resume:
946
- ① 从投影读出研究所状态(编制、任务、邮箱、会议、辩论、验证)
958
+ ① 从 `State/<研究所>.v5state.json` 读出研究所状态(编制、任务、邮箱、会议、辩论、验证)
947
959
  ② 对每个 phase='provisioning' 的成员做 §10.3 对账
948
960
  ③ 对每个 active 但 childId 不在 agents 中的成员:childId 置空,重新 startContinuable
949
961
  并以"其 Progress/ 摘要 + 规章 persona"重种化
@@ -951,7 +963,7 @@ vibe_v5_resume:
951
963
  ⑤ 恢复进行中的会议/验证并**刷新看门狗时钟**,然后 scheduleNext()
952
964
  ```
953
965
 
954
- 因为状态来自日志投影,"跨进程"与"同进程 abort 后 resume"**走同一条代码路径**——v4 曾为此写了 `needRespawn`、`processEpoch`、`autoDone` 拒绝复活等一堆特判(§17/§18/§27-T24),v5 由架构消除。
966
+ 因为状态来自同一个 JSON 状态文件,"跨进程"与"同进程 abort 后 resume"**走同一条代码路径**——v4 曾为此写了 `needRespawn`、`processEpoch`、`autoDone` 拒绝复活等一堆特判(§17/§18/§27-T24),v5 由架构消除。
955
967
 
956
968
  ### 14.5 提示词分配的正确性(身份 / 编制 / 交互不变式)
957
969
 
@@ -975,7 +987,7 @@ vibe_v5_resume:
975
987
  成员在被写进编制之前不能被描述。因此 `spawnMember` 的顺序是**硬性**的:
976
988
 
977
989
  ```
978
- ① 把成员以 phase='active' 提交进投影(此时它已在 activeMembers() 里)
990
+ ① 把成员以 phase='active' 提交进状态文件(此时它已在 `activeMembers()` 里)
979
991
  ② 置 busy,禁止调度器唤醒半成品
980
992
  ③ 构造 persona 与入职提示词(二者都从 activeMembers() 取编制、m、有表决权者数)
981
993
  ④ 创建它的资料库目录、写编制镜像
@@ -1105,8 +1117,8 @@ Progress/,不要从头再来",**不得**复用 `【入职首轮】` 与"你
1105
1117
  对象留在原库、可形式化后重新提议。`blocked` 就是"根据实现难度决定不做"的落点——
1106
1118
  决定权在代理,但**决定必须显式、可审计**(`note` 必填),不允许静默跳过。
1107
1119
 
1108
- **状态**:逐对象的形式化记录存在**投影**里(新事件类型 `vibe5/formal`,见 §8.3),
1109
- 因此它和 `verdicts` 一样:零 token 成本、随会话日志 checkpoint、跨进程恢复后仍在。
1120
+ **状态**:逐对象的形式化记录存在**状态文件**里(事件类型 `vibe5/formal`,见 §8.3),
1121
+ 因此它和 `verdicts` 一样:零 token 成本、随 `State/<研究所>.v5state.json` 落盘、跨进程恢复后仍在。
1110
1122
  `status().formal` / `report()` 的「Lean 形式化」小节是所办审计"我们到底拿到了严格证明,
1111
1123
  还是只有共识"的入口。
1112
1124
 
@@ -1220,7 +1232,7 @@ runaway 的编译器会在框架已经报告超时之后继续存活;`done`
1220
1232
  |---|---|
1221
1233
  | `vibe_v5_configure { project?, institute?, problem?, params? }` | 建所/配置(**不启动**) |
1222
1234
  | `vibe_v5_start { problem?, researcherCount?, academician?, seedDirections? }` | 开工 |
1223
- | `vibe_v5_set { … }` | 调参(持久化进投影) |
1235
+ | `vibe_v5_set { … }` | 调参(持久化进 `State/<研究所>.v5state.json`) |
1224
1236
  | `vibe_v5_pause / vibe_v5_resume / vibe_v5_stop` | 暂停 / 恢复 / 终止 |
1225
1237
  | `vibe_v5_message { to\|all, content }` | 以所办身份留言 |
1226
1238
  | `vibe_v5_meeting { agenda, kind }` | 召集会议 |
@@ -1303,15 +1315,14 @@ Lean 工具另外会返回**非错误码**的失败原因(都属于"可读结
1303
1315
  > 交付物:`Vibe-Mathematics/vibe-math-v5/`(`agent.cordis.yml`、`preset.yml`、`vibe-math-v5.js`、`实现方案.md`)+ `installer.js` 增加 v5 + `package.json` `files` + README 增 V5 小节。
1304
1316
 
1305
1317
  ### P0 · 可行性门槛(先做,可能推翻设计)
1306
- - **G-1 投影门槛**:写一个最小 preset 插件,`register()` 一个 host-only 投影单元 + `session.append` 自定义事件,验证:① 注册成功且不报"process-global service";② 折叠正确;③ **进程重启 + 会话恢复**后状态正确;④ 自定义事件不进入模型历史(`deriveMessages()` 不含它)。
1307
- - 若①失败 → 检查是否误传 `wire`;若仍失败 → 退回"v4 加固版 fs 存储"。
1308
- - ⚠ `stateSchema` 必须是带 `.parse()` 的对象。投影内部只调用 `def.stateSchema.parse(row.val)`,因此可用**零依赖**的手写结构校验器(`{ parse(v){ …throw… ; return v } }`)替代 zod,避免 preset 目录无法解析 `zod` 的问题(Node 解析不会走到 `profiles/node_modules`)。
1318
+ - **G-1 持久化门槛(结论:投影方案被推翻,改用加固 JSON)**:先用最小实验证明"状态能被可靠持久化并在重启后正确恢复"。首版按投影设计验证过:① 注册成功且不报"process-global service";② 折叠正确;③ 进程重启 + 会话恢复后状态正确;④ 自定义事件不进入模型历史(`deriveMessages()` 不含它)。①–③ 当时通过,但 DSH 0.2.0 审计发现根子上不成立:`Session.append` 无法把事件标为 `ignorable`,而宿主拒绝载入含未知事件类型的日志(`SessionFormatUnsupportedError`),于是**用户自己的会话无法 resume**。最终落地为加固 JSON 文件(§8.1),并把门槛判据改成:① 状态文件是唯一权威;② 重启后编制/任务/邮箱/验证精确恢复;③ 宿主会话日志保持**零事件**(`e2e-v5-round2`、`selfdrive-v5` 各有一条断言钉住)。
1319
+ - 这条推翻了原设计的一条底层假设,因此**"投影单元 + 事件类型 + init/apply"只保留了 `init/apply` 折叠**,`register`/`wire`/`stateSchema` 一并删除;读文件时只校验版本。
1309
1320
  - **G-2 persona 门槛**:验证 `startContinuable({request:{persona}})` 的 persona 在**冷恢复**后仍生效。
1310
1321
  - **G-3 邻接门槛**:验证框架以根代理身份可向任意成员投递、且**兄弟互发会被拒**(确认中继设计的必要性)。
1311
1322
  - **G-4 释放门槛**:验证 `drainContinuableChildren` 对已 `interrupt` 的成员可成功释放,且释放后再次投递会冷恢复(据此实现"永不再投递"的自我约束)。
1312
1323
 
1313
- ### P1 · 脚手架与投影
1314
- `vibe-math-v5/` 三件套;投影单元 + 事件类型 + `init/apply`;`V5State` 折叠与 `diagnostics`;`idSafe`/`normalizeWriteScope`;错误码。
1324
+ ### P1 · 脚手架与状态文件
1325
+ `vibe-math-v5/` 三件套;事件类型 + `init/apply` 折叠 + 加固 JSON 写入(`State/<研究所>.v5state.json`,逐文件串行化);`V5State` 折叠与 `diagnostics`;`idSafe`/`normalizeWriteScope`;错误码。
1315
1326
 
1316
1327
  ### P2 · 编制与生命周期
1317
1328
  建所(1 院士 + N 研究员,各自 brainstorm 出方向);`Member` 记录与不可变身份;`provisioning → active|failed` 对账;`vibe_v5_status`/`list`/`wait`;`vibe_v5_configure`/`start`/`pause`/`resume`/`stop`;**院士的 L1 全所视图 `vibe_v5_overview`**(编制 + 各成员 Progress 摘要 + 任务板 + 停滞告警)。
@@ -1366,7 +1377,7 @@ FIFO 提议队列 + 去重窗口;独立初评(快照后清空本轮票);
1366
1377
  | 兄弟互发被 DSH 拒绝 | 设计中已规避:**一切经框架中继**(§11.1) |
1367
1378
  | 官方 `maxMembers` 类终身配额 | v5 自建编制,额度按**在册**计,换人不受限 |
1368
1379
  | 成员不能真删除(若 G-4 失败) | 退化为**逻辑解雇**:标记 `dismissed` + 停止投递 + 回收任务;功能不减,仅 roster 残留历史 |
1369
- | 投影单元注册失败(G-1) | 退回 v4 加固版 fs 存储;架构其余不变 |
1380
+ | 状态文件写入失败/损坏(G-1 的另一面) | 载入时丢弃版本不符或损坏的快照并回到初始状态;写入串行化保证迟到者落下的是完整最新状态;镜像只写不读 |
1370
1381
  | 非法事件 | **不闩锁**(不同于官方):跳过 + 记 `diagnostics` |
1371
1382
  | 共识永达不成 | 对象留库附平均概率 + 辩论录;不阻塞其它工作;不强行裁决 |
1372
1383
  | `P < m`(人太少无法定论) | 明确公告并建议增聘常驻研究员;不静默停摆 |
@@ -1379,7 +1390,8 @@ FIFO 提议队列 + 去重窗口;独立初评(快照后清空本轮票);
1379
1390
  | 伪造发送者 | 框架以根代理身份中继,**署名取自 `exec.agent` 解析出的真实成员**,成员无法伪造他人署名 |
1380
1391
  | 路径穿越(成员 id / 卡片 id) | `idSafe` 消毒(保留中文等无害字符,替换分隔符/控制字符,去首尾点与横线),在唯一入口处统一施加 |
1381
1392
  | 多会话/多项目 | 一所一会话;项目内 `Institutes/<id>/` 隔离 |
1382
- | 会话日志膨胀 | 自有事件不进模型历史;`messages/delivered` 按去重窗口压缩;会议全文只落磁盘、投影只存索引 |
1393
+ | 用户会话不可恢复 | **绝不写宿主会话日志**:研究所的 `vibe5/*` 事件只进内存折叠,落盘只落研究所自己的 `State/<研究所>.v5state.json`(§8.1)。测试直接钉住"日志零事件" |
1394
+ | 状态文件膨胀 | `messages/delivered` 按去重窗口压缩;会议/辩论全文只落磁盘、状态文件只存索引 |
1383
1395
 
1384
1396
  ---
1385
1397
 
@@ -1395,7 +1407,7 @@ FIFO 提议队列 + 去重窗口;独立初评(快照后清空本轮票);
1395
1407
  | `contextPct` 被钳成 `[0,1]` 与阈值 66 比较恒假(§17) | 单位不一致 | 统一按 **0–100 百分比**(`clPct`) |
1396
1408
  | 非全票不写回平均概率(§18) | 只写辩论录 | §9.2 明确"留库概率 = 全组平均",写回来源卡 |
1397
1409
  | 方法型验证被误标为"命题"(§18) | 未按 targetKind 分类 | `Verified/<kind>/<id>.md`,来源卡类型随 targetKind |
1398
- | 同进程 abort→resume 不重建成员(§18) | `processEpoch` 只判跨进程 | **架构消除**:状态来自日志投影,重启与 abort 走同一路径 |
1410
+ | 同进程 abort→resume 不重建成员(§18) | `processEpoch` 只判跨进程 | **架构消除**:状态来自同一个 JSON 状态文件,重启与 abort 走同一路径 |
1399
1411
  | 验证只算"投过票的人是否一致" → 2/4 被判全体一致(§21-1) | 分母错误 | §9.2 明确分母是 `E`,且 `|B| ≥ m` 是硬门槛;测试钉"m−1 票不进" |
1400
1412
  | 会议按"被问过"而非"已发言"收口(§21-2) | 收口条件错 | 按 `inputs` 覆盖实时成员集判定 |
1401
1413
  | 背景每轮重复注入(§21-3) | 章程写在提示里 | **章程进 persona**,结构性消除 |
@@ -1412,8 +1424,8 @@ FIFO 提议队列 + 去重窗口;独立初评(快照后清空本轮票);
1412
1424
  | 暂停期间认领会僵尸唤醒(§29-T36) | 门控只覆盖 end | 认领记录照常入板,**唤醒加暂停门控** |
1413
1425
  | 负值/NaN 时长使看门狗立即放弃(§30-T41) | 未兜底 | `posMs(v, def)` 兜底所有时长读取 |
1414
1426
  | `idSafe` 路径穿越(§30-T39) | id 未消毒 | 统一消毒(§18) |
1415
- | `State` 文件并发丢写 / 损坏静默覆盖(§27 writeJson、F-9) | 直写 JSON | **会话日志投影**(架构消除);镜像只写不读 |
1416
- | `reports`/`meetings` 无界膨胀(§29-T37) | 全量存内存 | 投影只存索引 + 上限环形;全文只落磁盘 |
1427
+ | `State` 文件并发丢写 / 损坏静默覆盖(§27 writeJson、F-9) | 直写 JSON | **加固 JSON 单文件**:逐文件串行化 + 迟到写者落下整份最新快照;版本不符/损坏的快照载入时丢弃;镜像只写不读 |
1428
+ | `reports`/`meetings` 无界膨胀(§29-T37) | 全量存内存 | 状态文件只存索引 + 上限环形;全文只落磁盘 |
1417
1429
  | 唤醒信号硬编码 60 s < 超时 120 s(§18) | 常量写死 | 统一取 `activityTimeoutMs` |
1418
1430
 
1419
1431
  ---
@@ -1427,7 +1439,7 @@ FIFO 提议队列 + 去重窗口;独立初评(快照后清空本轮票);
1427
1439
  | Q3 | 临时工是否应可**跨使用期**长期留用?(现为任务期,雇主不主动解雇则续存) | 保留至雇主解雇或全所停止;框架在"长期空闲"时提醒雇主考虑解雇 |
1428
1440
  | Q4 | 常驻研究员的增聘是否允许院士**单独**决定? | 否,院士只能提议,由所办/人批准(§10.4) |
1429
1441
  | Q5 | 是否需要在浏览器里做研究所面板(roster/任务板/会议)? | 暂不做(需引入官方 UI 包)。留作 P11 可选 |
1430
- | Q6 | 是否接受把研究所事件追加进**用户会话日志**(不进模型历史,但会进日志文件)? | 接受(这是 DSH 官方的做法,也是免 fs 竞态的关键)。若不接受 → 退回 fs 存储 |
1442
+ | Q6 | 是否接受把研究所事件追加进**用户会话日志**(不进模型历史,但会进日志文件)? | **已否决(DSH 0.2.0 审计)**。`Session.append` 无法把事件标为 `ignorable`,而宿主会话持久化拒绝载入含未知事件类型的日志(`SessionFormatUnsupportedError` → `validateStoredEvents`),实测后果是**用户自己的会话无法 resume**。研究所状态只写加固 JSON 文件(§8.1) |
1431
1443
  | Q7 | 院士的分派强度:成员据理反对后应**照常执行**,还是**暂挂待裁决**? | 照常执行 + 广播反对理由(避免进度被任意反对卡死)。若你希望更强制衡,可改为"反对即暂挂该任务并由会议裁决" |
1432
1444
 
1433
1445
  ---
@@ -36,8 +36,8 @@ flowchart TB
36
36
  end
37
37
 
38
38
  subgraph L3["💾 状态与产物"]
39
- PROJ["会话日志的 host-only 投影单元<br/>键 vibeMathV5<br/>12 类事件 · 纯折叠 applyV5Event"]
40
- FS["人可读产物<br/>Members/&lt;id&gt;/* · Shared/* · Verified/ · Problems/"]
39
+ STATE["唯一权威状态源:加固 JSON 文件<br/>State/&lt;研究所&gt;.v5state.json<br/>12 类事件 · 纯折叠 applyV5Event"]
40
+ FS["人可读产物(镜像,勿手改)<br/>Members/&lt;id&gt;/* · Shared/* · Verified/ · Problems/ · State/README.md"]
41
41
  end
42
42
 
43
43
  RULE{{"求真门槛<br/>布尔一致 且 布尔票 ≥ m = min(quorumCap, 有表决权人数)<br/>否则留库 + 平均概率 + 辩论录"}}
@@ -58,7 +58,7 @@ flowchart TB
58
58
  VOTE --- SCHED
59
59
  CTX --- SCHED
60
60
  STAFF --- SCHED
61
- SCHED <-->|"append 事件 / 折叠读状态"| PROJ
61
+ SCHED <-->|"commit 事件 / 折叠读状态"| STATE
62
62
  SCHED <-->|"读写产物"| FS
63
63
  VOTE --> RULE
64
64
  RULE -->|"定论"| FS
@@ -71,7 +71,7 @@ flowchart TB
71
71
  class OFF human
72
72
  class ACAD,RES,TMP house
73
73
  class MSG,MTG,TASK,VOTE,CTX,STAFF,SCHED frame
74
- class PROJ,FS data
74
+ class STATE,FS data
75
75
  class RULE gate
76
76
  ```
77
77
 
@@ -110,14 +110,14 @@ sequenceDiagram
110
110
  autonumber
111
111
  participant S as 调度器(框架代码)
112
112
  participant M as 成员(continuable 子会话)
113
- participant L as 会话日志投影(vibeMathV5)
113
+ participant L as 状态文件(State/&lt;研究所&gt;.v5state.json)
114
114
 
115
115
  S->>S: schedulePass:按优先级挑一个成员
116
116
  S->>S: 先 ack 收件箱,再构造本轮提示词
117
117
  S->>M: 【轮次】+ 状态块(编制/m/我的任务/新消息)+ 回执契约
118
118
  M->>M: 读同事的库 · 推导 · 写自己的 Progress/ 与卡片
119
119
  M-->>S: subagent/end + 单个 JSON 回执
120
- S->>L: append 事件(message / progress / verdict / …)
120
+ S->>L: commit 事件(整份快照串行写入;读前必先 load)
121
121
  S->>S: handleReply:发言 / 入库 / 任务 / 提议 / 投票 / 雇佣
122
122
  S->>S: 记 contextPct;必要时发压缩指令
123
123
  S->>S: scheduleNext()(蹦床:同一时刻只有一个调度 pass)
@@ -273,36 +273,38 @@ flowchart TB
273
273
 
274
274
  ---
275
275
 
276
- ## 7. 状态:纯折叠 + 双后端
276
+ ## 7. 状态:纯折叠 + 唯一后端(加固 JSON 文件)
277
277
 
278
278
  ```mermaid
279
279
  flowchart LR
280
- subgraph EV["11 类事件"]
281
- E["institute · member · task · message · delivered ·<br/>meeting · debate · verdict · queue · counters · progress"]
280
+ subgraph EV["12 类事件"]
281
+ E["institute · member · task · message · delivered ·<br/>meeting · debate · verdict · formal · queue · counters · progress"]
282
282
  end
283
283
  EV --> FOLD["applyV5Event(纯函数折叠,只此一份)"]
284
284
  FOLD --> ST["V5State<br/>{ institutes, order, diagnostics }"]
285
- ST --> R1["读:stateOf(session, 'vibeMathV5')"]
285
+ ST --> R1["读:先 ready()(load),再 state()"]
286
286
  ST --> R2["未知/损坏事件 → 跳过 + 记入 diagnostics<br/>(可用性优先,绝不因一条坏事件卡死全场)"]
287
287
 
288
- subgraph B1["主后端(宿主有 sessionProjections)"]
289
- P1["事件只追加进会话日志<br/>不进模型上下文 = 零 token 成本"]
290
- P2["checkpoint 由 DSH 负责;restore 时<br/>从快照 + 日志尾部重新折叠"]
291
- end
292
- subgraph B2["回退后端(宿主没有该服务)"]
293
- F1["加固 JSON:State/&lt;institute&gt;.v5state.json<br/>同一份折叠 · 串行写 · 写前必 load"]
288
+ subgraph B1["唯一后端:加固 JSON 文件"]
289
+ P1["State/&lt;研究所&gt;.v5state.json<br/>事件只在内存里折叠,读前必先 load"]
290
+ P2["每次提交把整份快照经一条 promise 链<br/>串行写入:后写者不会用旧子集覆盖新状态"]
294
291
  end
295
292
  R1 --> B1
296
- R1 --> B2
297
293
 
298
294
  style FOLD fill:#eaf3fd,stroke:#0b6fb8
299
295
  style P1 fill:#eef7f1,stroke:#1f7a52
300
296
  style P2 fill:#eef7f1,stroke:#1f7a52
301
- style F1 fill:#fdf5e2,stroke:#b26a00
302
297
  ```
303
298
 
304
- **为什么用投影**:v4 的 `State/*.json` 直写带来"损坏静默覆盖 / 并发丢写 / 跨进程陈旧快照"一整类问题。
305
- v5 的副作用只是往会话日志追加事件,跨进程与同进程恢复**走同一条代码路径**。
299
+ **为什么不再用会话日志投影**:DSH 的会话持久化**拒绝加载**日志里带未知事件类型的会话 ——
300
+ 除非写入方把它标成 `ignorable: true`,而 `Session.append` **无法设置**该字段。所以把 `vibe5/*`
301
+ 事件追加进根会话,等于让**用户自己的会话无法恢复**。研究所状态因此只落在
302
+ `State/<研究所>.v5state.json` 这一个加固 JSON 文件里;`sessionProjections` 单元注册、投影后端与
303
+ `sessions.flush` 调用都已删除,插件也不再注入 `sessions`。
304
+
305
+ **为什么仍然安全**:v4 的 `State/*.json` 直写带来"损坏静默覆盖 / 并发丢写 / 跨进程陈旧快照"
306
+ 一整类问题。v5 用同一份纯折叠 + 整份快照串行写入 + 写前必 load 消掉它们,跨进程与同进程
307
+ 恢复**走同一条代码路径**(都是"读文件 → 折叠")。
306
308
 
307
309
  ---
308
310
 
@@ -403,8 +405,8 @@ flowchart LR
403
405
  │ └─ State-of-institute.md # 各成员对"是否已解决"的判断快照
404
406
  ├─ Verified/<类型>/<id>.md # 定论(只读;只有它能被当作已确立)
405
407
  └─ State/
406
- ├─ README.md # 说明"权威状态在会话日志投影里,不是这里"
407
- └─ <研究所>.v5state.json # 仅当宿主缺 sessionProjections 时的回退权威源
408
+ ├─ README.md # 说明"权威状态就是本目录的 <研究所>.v5state.json"
409
+ └─ <研究所>.v5state.json # 唯一权威状态源(纯折叠快照;读前必 load,整份串行写入)
408
410
  ```
409
411
 
410
412
  **可信分层**:只有 `Verified/` 与标注"已验证·真/假"的卡片**绝对可信**;
@@ -112,30 +112,30 @@
112
112
  <rect x="248" y="880" width="1410" height="38" rx="10" fill="#ffffff" stroke="#0b6fb8" stroke-width="1.6"/>
113
113
  <text x="953" y="903.1" font-size="11.5" font-weight="700" fill="#10202e" text-anchor="middle">Scheduler priority: meeting/verification (mutually exclusive) → queued verification → meetings → active tasks (throttled by activityTimeoutMs) → mail → chat digest → meeting on stall → heartbeat</text>
114
114
  <rect x="40" y="964" width="790" height="196" rx="14" fill="#eef7f1" stroke="#1f7a52" stroke-width="2"/>
115
- <text x="58" y="991" font-size="15" font-weight="700" fill="#125a3c">Source of truth: host-only projection cell of the session log (key vibeMathV5)</text>
116
- <text x="58" y="1011" font-size="12" fill="#5d7788">The only side effect is appending events to the session log, outside the model context (zero token cost)</text>
115
+ <text x="58" y="991" font-size="15" font-weight="700" fill="#125a3c">Source of truth: hardened JSON file State/&lt;institute&gt;.v5state.json</text>
116
+ <text x="58" y="1011" font-size="12" fill="#5d7788">the same pure applyV5Event fold · serial whole-snapshot writes · load before every read (zero token cost)</text>
117
117
  <rect x="62" y="1020" width="360" height="124" rx="10" fill="#ffffff" stroke="#1f7a52" stroke-width="1.6"/>
118
118
  <text x="242" y="1046.3" font-size="12" font-weight="700" fill="#10202e" text-anchor="middle">applyV5Event (pure fold, the only copy)</text>
119
- <text x="242" y="1062.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">11 kinds of events: institute / member / task /</text>
119
+ <text x="242" y="1062.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">12 kinds of events: institute / member / task /</text>
120
120
  <text x="242" y="1078.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">message / delivered / meeting / debate /</text>
121
- <text x="242" y="1094.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">verdict / queue / counters / progress</text>
121
+ <text x="242" y="1094.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">verdict / formal / queue / counters / progress</text>
122
122
  <text x="242" y="1110.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">unknown/bad event → skipped and recorded in diagnostics:</text>
123
123
  <text x="242" y="1126.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">availability first, one bad event never stalls the field</text>
124
124
  <rect x="434" y="1020" width="374" height="124" rx="10" fill="#ffffff" stroke="#1f7a52" stroke-width="1.6"/>
125
- <text x="621" y="1046.3" font-size="12" font-weight="700" fill="#10202e" text-anchor="middle">checkpoint / restore / resume</text>
126
- <text x="621" y="1062.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">the projection is checkpointed with the session log; restore</text>
127
- <text x="621" y="1078.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">re-folds snapshot + log tail → across and within processes</text>
128
- <text x="621" y="1094.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">one process recovery takes the same code path</text>
129
- <text x="621" y="1110.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">fallback: host without sessionProjections → hardened</text>
130
- <text x="621" y="1126.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">JSON: State/&lt;institute&gt;.v5state.json</text>
125
+ <text x="621" y="1046.3" font-size="12" font-weight="700" fill="#10202e" text-anchor="middle">the only backend: hardened JSON file</text>
126
+ <text x="621" y="1062.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">never read before load; each commit writes the whole</text>
127
+ <text x="621" y="1078.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">snapshot through a serial chain, so a late writer cannot</text>
128
+ <text x="621" y="1094.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">land a stale subset; across and within processes recovery</text>
129
+ <text x="621" y="1110.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">takes one code path: read file → fold. Institute events</text>
130
+ <text x="621" y="1126.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">never enter the host session log; DSH refuses unknown ones</text>
131
131
  <rect x="852" y="964" width="828" height="196" rx="14" fill="#eef7f1" stroke="#1f7a52" stroke-width="2"/>
132
- <text x="870" y="991" font-size="15" font-weight="700" fill="#125a3c">File plane: human-readable artifacts (everything outside the projection is only a mirror)</text>
133
- <text x="870" y="1011" font-size="12" fill="#5d7788">The authority for consensus is the projection; files are readable artifacts, breaking them by hand does not damage it</text>
132
+ <text x="870" y="991" font-size="15" font-weight="700" fill="#125a3c">File plane: readable artifacts (all except State/&lt;institute&gt;.v5state.json are mirrors)</text>
133
+ <text x="870" y="1011" font-size="12" fill="#5d7788">The authority is State/&lt;institute&gt;.v5state.json; everything else is a readable artifact — hand edits change nothing</text>
134
134
  <rect x="874" y="1020" width="784" height="124" rx="10" fill="#ffffff" stroke="#1f7a52" stroke-width="1.6"/>
135
135
  <text x="1266" y="1055.1" font-size="11.5" font-weight="700" fill="#10202e" text-anchor="middle">Members/&lt;id&gt;/Progress|Propos|Methods|Subproblems/</text>
136
136
  <text x="1266" y="1070.6" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">only the owner can write, everyone can read (cross-reading is encouraged)</text>
137
137
  <text x="1266" y="1086.1" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">Shared/Chat/*.md · Shared/Meetings/&lt;mt-id&gt;.md · Shared/Debates/&lt;obj&gt;.md</text>
138
- <text x="1266" y="1101.6" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">Shared/TaskBoard.md · Institutes.md (roster mirror) · State/README.md (says the authority is not here)</text>
138
+ <text x="1266" y="1101.6" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">Shared/TaskBoard.md · Institutes.md (roster mirror) · State/README.md (says the authority is the sibling v5state.json)</text>
139
139
  <text x="1266" y="1117.1" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">Problems/&lt;id&gt;.md (original problem) · Problems/conclusion.md (conclusion) · Verified/&lt;type&gt;/&lt;id&gt;.md (conclusion, read-only)</text>
140
140
  <path d="M 127 290 L 127 700 L 226 700" fill="none" stroke="#5b4bb8" stroke-width="2" marker-start="url(#bothHuman)" marker-end="url(#bothHuman)"/>
141
141
  <text x="127" y="336" font-size="12" font-weight="700" fill="#5b4bb8" text-anchor="middle" paint-order="stroke" stroke="#ffffff" stroke-width="4" stroke-linejoin="round">Tool surface</text>
@@ -156,7 +156,7 @@
156
156
  <line x1="556" y1="400" x2="578" y2="400" stroke="#b26a00" stroke-width="1.8" stroke-dasharray="7 5" marker-start="url(#bothHouse)" marker-end="url(#bothHouse)"/>
157
157
  <line x1="556" y1="430" x2="578" y2="430" stroke="#b26a00" stroke-width="1.8" stroke-dasharray="7 5" marker-start="url(#bothHouse)" marker-end="url(#bothHouse)"/>
158
158
  <line x1="400" y1="940" x2="400" y2="964" stroke="#1f7a52" stroke-width="2" marker-start="url(#bothData)" marker-end="url(#bothData)"/>
159
- <text x="392" y="956" font-size="10.5" font-weight="400" fill="#1f7a52" text-anchor="end" paint-order="stroke" stroke="#ffffff" stroke-width="4" stroke-linejoin="round">append / fold / stateOf</text>
159
+ <text x="392" y="956" font-size="10.5" font-weight="400" fill="#1f7a52" text-anchor="end" paint-order="stroke" stroke="#ffffff" stroke-width="4" stroke-linejoin="round">load / commit / fold</text>
160
160
  <line x1="1280" y1="940" x2="1280" y2="964" stroke="#1f7a52" stroke-width="2" marker-start="url(#bothData)" marker-end="url(#bothData)"/>
161
161
  <text x="1288" y="956" font-size="10.5" font-weight="400" fill="#1f7a52" text-anchor="start" paint-order="stroke" stroke="#ffffff" stroke-width="4" stroke-linejoin="round">read/write artifacts</text>
162
162
  <text x="52" y="1186" font-size="12" font-weight="700" fill="#10202e" text-anchor="start">Legend</text>
@@ -112,30 +112,30 @@
112
112
  <rect x="248" y="880" width="1410" height="38" rx="10" fill="#ffffff" stroke="#0b6fb8" stroke-width="1.6"/>
113
113
  <text x="953" y="903.1" font-size="11.5" font-weight="700" fill="#10202e" text-anchor="middle">调度器优先级:进行中的会议或验证(二者互斥,永不同时)→ 队列中的验证 → 暂存会议 → 已认领/被分派的在办任务(按 activityTimeoutMs 节流)→ 加急邮件 → 群聊摘要 → 停滞自动开会 → 兜底心跳</text>
114
114
  <rect x="40" y="964" width="790" height="196" rx="14" fill="#eef7f1" stroke="#1f7a52" stroke-width="2"/>
115
- <text x="58" y="991" font-size="15" font-weight="700" fill="#125a3c">状态权威源:会话日志的 host-only 投影单元(键 vibeMathV5)</text>
116
- <text x="58" y="1011" font-size="12" fill="#5d7788">副作用只是往会话日志追加事件 —— 不进模型上下文(零 token 成本),checkpoint / restore 交给 DSH</text>
115
+ <text x="58" y="991" font-size="15" font-weight="700" fill="#125a3c">状态权威源:加固 JSON 文件 State/&lt;institute&gt;.v5state.json</text>
116
+ <text x="58" y="1011" font-size="12" fill="#5d7788">同一份纯折叠 applyV5Event · 整份快照经串行 promise 链写入 · 读前必先 load —— 事件不进模型上下文(零 token 成本)</text>
117
117
  <rect x="62" y="1020" width="360" height="124" rx="10" fill="#ffffff" stroke="#1f7a52" stroke-width="1.6"/>
118
118
  <text x="242" y="1046.3" font-size="12" font-weight="700" fill="#10202e" text-anchor="middle">applyV5Event(纯折叠,只此一份)</text>
119
- <text x="242" y="1062.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">11 类事件:institute / member / task /</text>
119
+ <text x="242" y="1062.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">12 类事件:institute / member / task /</text>
120
120
  <text x="242" y="1078.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">message / delivered / meeting / debate /</text>
121
- <text x="242" y="1094.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">verdict / queue / counters / progress</text>
121
+ <text x="242" y="1094.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">verdict / formal / queue / counters / progress</text>
122
122
  <text x="242" y="1110.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">未知或损坏事件 → 跳过并记入 diagnostics:</text>
123
123
  <text x="242" y="1126.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">可用性优先,绝不因一条坏事件卡死全场</text>
124
124
  <rect x="434" y="1020" width="374" height="124" rx="10" fill="#ffffff" stroke="#1f7a52" stroke-width="1.6"/>
125
- <text x="621" y="1046.3" font-size="12" font-weight="700" fill="#10202e" text-anchor="middle">checkpoint / restore / resume</text>
126
- <text x="621" y="1062.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">投影随会话日志一起 checkpoint;restore</text>
127
- <text x="621" y="1078.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">时从快照 + 日志尾部重新折叠 → 跨进程与</text>
128
- <text x="621" y="1094.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">同进程恢复走同一条代码路径</text>
129
- <text x="621" y="1110.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">回退:宿主无 sessionProjections 时改用</text>
130
- <text x="621" y="1126.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">加固 JSON:State/&lt;institute&gt;.v5state.json</text>
125
+ <text x="621" y="1046.3" font-size="12" font-weight="700" fill="#10202e" text-anchor="middle">唯一后端:加固 JSON 文件</text>
126
+ <text x="621" y="1062.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">读之前必先 load;每次提交把整份快照经</text>
127
+ <text x="621" y="1078.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">一条串行 promise 链写入(后写者不会用旧</text>
128
+ <text x="621" y="1094.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">子集覆盖新状态);跨进程与同进程恢复走</text>
129
+ <text x="621" y="1110.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">同一条代码路径:读文件 → 折叠</text>
130
+ <text x="621" y="1126.3" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">研究所事件绝不写进宿主会话日志(DSH 拒载未知事件)</text>
131
131
  <rect x="852" y="964" width="828" height="196" rx="14" fill="#eef7f1" stroke="#1f7a52" stroke-width="2"/>
132
- <text x="870" y="991" font-size="15" font-weight="700" fill="#125a3c">文件面:人可读产物(投影之外的一切都只是镜像)</text>
133
- <text x="870" y="1011" font-size="12" fill="#5d7788">共识的权威在投影;文件是工作区与可读产物,手工改坏不会破坏研究所</text>
132
+ <text x="870" y="991" font-size="15" font-weight="700" fill="#125a3c">文件面:人可读产物(除 State/&lt;institute&gt;.v5state.json 外都只是镜像)</text>
133
+ <text x="870" y="1011" font-size="12" fill="#5d7788">权威状态是 State/&lt;institute&gt;.v5state.json;文件是工作区与可读产物,手工改坏不会破坏研究所</text>
134
134
  <rect x="874" y="1020" width="784" height="124" rx="10" fill="#ffffff" stroke="#1f7a52" stroke-width="1.6"/>
135
135
  <text x="1266" y="1055.1" font-size="11.5" font-weight="700" fill="#10202e" text-anchor="middle">Members/&lt;id&gt;/Progress|Propos|Methods|Subproblems/</text>
136
136
  <text x="1266" y="1070.6" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">只有本人可写,人人可读(跨读被鼓励)</text>
137
137
  <text x="1266" y="1086.1" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">Shared/Chat/*.md · Shared/Meetings/&lt;mt-id&gt;.md · Shared/Debates/&lt;obj&gt;.md</text>
138
- <text x="1266" y="1101.6" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">Shared/TaskBoard.md · Institutes.md(编制镜像)· State/README.md(说明此处非权威)</text>
138
+ <text x="1266" y="1101.6" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">Shared/TaskBoard.md · Institutes.md(编制镜像)· State/README.md(说明权威=同目录 v5state.json)</text>
139
139
  <text x="1266" y="1117.1" font-size="10.5" font-weight="400" fill="#2c4557" text-anchor="middle">Problems/&lt;id&gt;.md(原问题)· Problems/conclusion.md(结题)· Verified/&lt;类型&gt;/&lt;id&gt;.md(定论,只读)</text>
140
140
  <path d="M 127 290 L 127 700 L 226 700" fill="none" stroke="#5b4bb8" stroke-width="2" marker-start="url(#bothHuman)" marker-end="url(#bothHuman)"/>
141
141
  <text x="127" y="336" font-size="12" font-weight="700" fill="#5b4bb8" text-anchor="middle" paint-order="stroke" stroke="#ffffff" stroke-width="4" stroke-linejoin="round">工具面</text>
@@ -156,7 +156,7 @@
156
156
  <line x1="556" y1="400" x2="578" y2="400" stroke="#b26a00" stroke-width="1.8" stroke-dasharray="7 5" marker-start="url(#bothHouse)" marker-end="url(#bothHouse)"/>
157
157
  <line x1="556" y1="430" x2="578" y2="430" stroke="#b26a00" stroke-width="1.8" stroke-dasharray="7 5" marker-start="url(#bothHouse)" marker-end="url(#bothHouse)"/>
158
158
  <line x1="400" y1="940" x2="400" y2="964" stroke="#1f7a52" stroke-width="2" marker-start="url(#bothData)" marker-end="url(#bothData)"/>
159
- <text x="392" y="956" font-size="10.5" font-weight="400" fill="#1f7a52" text-anchor="end" paint-order="stroke" stroke="#ffffff" stroke-width="4" stroke-linejoin="round">append / fold / stateOf</text>
159
+ <text x="392" y="956" font-size="10.5" font-weight="400" fill="#1f7a52" text-anchor="end" paint-order="stroke" stroke="#ffffff" stroke-width="4" stroke-linejoin="round">load / commit / fold</text>
160
160
  <line x1="1280" y1="940" x2="1280" y2="964" stroke="#1f7a52" stroke-width="2" marker-start="url(#bothData)" marker-end="url(#bothData)"/>
161
161
  <text x="1288" y="956" font-size="10.5" font-weight="400" fill="#1f7a52" text-anchor="start" paint-order="stroke" stroke="#ffffff" stroke-width="4" stroke-linejoin="round">读写产物</text>
162
162
  <text x="52" y="1186" font-size="12" font-weight="700" fill="#10202e" text-anchor="start">图例</text>