立项策划:裁决 M1C-1 三条开工前置,前向兼容粒度另拆 M1C-0b
Project CI / Repository checks (pull_request) Successful in 1m5s
Project CI / Frontend tests (pull_request) Successful in 2m54s
Project CI / Backend tests (pull_request) Successful in 3m59s
Project CI / Native shell tests (pull_request) Failing after 11m27s

对 2026-08-14「M1C-0 合入复核」留下的三条前置逐条读实代码后定稿:

① barrier:补,且必须是独立的第六个计数 user_revision_pending_count。
   不能并进 repair_required_count——它带 repair_of_delegation_id.is_none()
   只算原始委派,而用户第 2 次修订的父节点自身就是 repair 节点,并进去会让
   第 2 次及以后的修订全部不阻塞。判据形状照 user_input_required_count。
   另有四处逐字段读 barrier 的调用点不走 is_clear(),加字段不会自动传播。

② 正向一致性:复用 EvidenceReady 的三条客观证据约束,并把该处 if/else-if
   链改成穷尽 match;但不得更严——该函数在每次读取时都跑,过严会把写入方的
   一个 bug 变成 delivery 永久读不出来。同时订正:这不是既有漏洞,唯一派生点
   产不出该变体,约束的是 M1C-1 引入的第二个写入方。

③ 前向兼容粒度:订正 08-14 自己写的「二选一」——「单条跳过并告警」不安全,
   barrier 是计数,跳过一条损坏的 Dispatched 记录会让 Supervisor 在仍有未完成
   委派时收束,把可用性故障换成正确性故障。改走第三条路(损坏仍整体锁死,
   前向不兼容解析进显式 Unknown 并最大化阻塞),并另开 M1C-0b 承接——该改动
   触及 master 已发布机制的所有读路径,不得塞进 M1C-1 的 diff。

另记两条比 08-14 更糟的事实:锁死半径是整个项目目录而非单个 run;
.json.previous 备份只在 primary NotFound 时回退,损坏但存在的 primary 不回退。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 05:57:12 +00:00
parent f45db359bd
commit deae1e08ce
2 changed files with 20 additions and 3 deletions
@@ -1,5 +1,17 @@
# 决策记录
## 2026-08-15 M1C-1 开工前三条裁决:barrier 独立计数、正向校验的上限、前向兼容粒度另拆 `M1C-0b`
对本文件 2026-08-14「`M1C-0` 合入复核」留下的三条前置逐条裁决,全部读实代码后定稿。**其中第三条订正了 08-14 自己的表述**:原文写的「二选一:给枚举加单条容错,或接受回滚锁死」是伪二选一——「单条跳过并告警」这个选项对 delivery 不安全,已作废。
- **裁决一:补 barrier,且必须是独立的第六个计数 `user_revision_pending_count`。** 不补的后果是 `M1C-1` 写入方一落地,Supervisor 就能在用户修订尚未派出时收束用户任务(收束门 `static_delegate_completion_blocker_at_locked`)。不复用现有两个计数的理由是硬的:`repair_required_count``repair_of_delegation_id.is_none()`,只算原始委派——因 depth≤1,返工的返工本就不该阻塞;而**用户第 2 次修订的父节点自身就是 repair 节点**,并进去会让第 2 次及以后的修订全部不阻塞,恰好在最需要处失效。`user_input_required_count` 的语义是「等用户回答」,与「用户已发话、等 Supervisor 派发」不同,`detail()` 文案会说反。判据形状照 `user_input_required_count``ClaimedByParent` + 该 status + 无活跃 child**不带** `repair_of.is_none()`)——它正是仓库里「轮次无上限」概念的既有先例。**连带**:除 `is_clear()``detail()` 外,另有四处调用点逐字段读 barrier 而不走 `is_clear()``autonomous_policy.rs` 两处、`runtime_tools/delivery.rs` 两处),加字段不会自动传播,每处单独裁决。这是 `M1C-0` 那个失败模式的同构版本,载体从 enum 变体换成 struct 字段,编译器同样沉默。
- **裁决二:正向一致性锁死在「只能从 `EvidenceReady` 改写而来」,不得更严。** 复用 `EvidenceReady` 的三条客观证据约束(终态 `completed`、无缺失产物、`verification_required``verified_revision` 存在);产物覆盖校验与「不得携带 `user_input_questions`」已对所有状态生效,不需改。**重点是实现形状**:把 `validate_static_delegate_structured_result` 里按 `contract_status` 的 if/else-if 链改成穷尽 `match`、不留 `_`——同一失败形状在本仓库已出现两次,靠纪律没拦住。**反向风险**:该函数不是入口过滤器,`read_static_delegate_delivery_at``validate_static_delegate_delivery_record` 让它每次读取都跑;过严等于把写入方的一个 bug 变成「该 delivery 永久读不出来」,直接触发裁决三的锁死,故不得再加 `EvidenceReady` 自己都没有的条款(例如要求 `error``None`)。**另订正 08-14 前置二可能引起的误读**:它不是既有漏洞——专业 Agent 自证「用户要求修订」今天不可达,唯一派生点 `build_static_delegate_structured_result_at` 只能产出三个旧变体,claim 回执的 `structuredResult` 从 delivery 拷贝,均为 Runtime 侧;本条约束的是 `M1C-1` 引入的**第二个写入方**(审批命令)。
- **裁决三:「单条跳过并告警」作废,走第三条路,并另开 `M1C-0b`。** 单条跳过不安全的原因很具体:`list_static_delegate_deliveries_at` 喂的是 barrier 的**计数**,跳过一条损坏的 `Dispatched` 记录会让 `waiting_count` 少 1、barrier 变 clearSupervisor 在仍有未完成委派时收束——把可用性故障换成了正确性故障,比锁死更糟。对照组说明危险面很精确:谱系重放不受影响,缺节点走「上游缺失」出口返回 `(u32::MAX, u32::MAX)` 哨兵,本就 fail closed。**第三条路是把「解析失败」拆两类**:损坏(截断/非 UTF-8/超限)状态未知,维持整体锁死不动;前向不兼容(结构良好、只有一个 enum 值不认识)是已知的未知,解析进显式 `Unknown` 但最大化阻塞(进 barrier、返工门无条件拒、谱系按最保守的「其它」分类,只会拒不会放)。不违反 `M1C-0` 条里「不得静默降级为 `NeedsRepair`」——那条禁的是把记录当正常记录继续走流程,`Unknown` 是显式隔离。半径从「整个项目静态委派面不可用(做游戏链路一起挂)」降到「只冻结携带该状态的那条 lineage」。
- **两条比 08-14 记录更糟的事实。** 锁死半径是**整个项目目录**——`list_static_delegate_deliveries_at(root)` 先读全目录再按 parent run 过滤,任何一条无关 run 的损坏记录毒化所有 run 的 barrier`.json.previous` 备份救不了——`read_agent_runtime_json_sidecar_with_max_bytes` 只在 primary **NotFound** 时才回退,损坏但存在的 primary 不回退。
- **实现坑。** `#[serde(other)]` 用不了:它只允许在 internally/adjacently tagged 枚举上,而 `StaticDelegateContractStatus` 是序列化成纯字符串的 unit-variant 枚举;需自定义 `Deserialize`,且 `Serialize` 必须原样回写原始字符串,否则旧版本任何一次读-改-写都会把未知值抹掉。
- **拆包与门禁。** ①② 进 `M1C-1`(它们约束的正是 `M1C-1` 新增的写入方);③ 拆 `M1C-0b`,与 `M1C-0` 同形状——无写入方、纯读路径、对既有记录零行为变化可证;塞进审批闭环的 diff 就是重犯第 23.8 节「拆包纪律」自己写的错。`M1C-1` 门禁因此为二选一:`M1C-0b` 先落,或直接上线并在 release note 明写「用过策划审批后回滚旧版本会让该项目静态委派面不可用」;**建议前者**,多机/版本不一致不需要用户主动回滚就会发生。`WP1` 的 depth≤1 结论仍未被推翻。
- 关联文档:`docs/technical/【技术方案】立项策划AgentFast GDD-2026-08-10.md` 第 23.7 节「落地约束」裁决一/二/三与「回归必须覆盖」、第 23.8 节 `M1C-0b` / `M1C-1` 行与「拆包纪律」。
## 2026-08-14 M1C-0 合入复核:三条 M1C-1 前置、一条文档订正、一条已排除假设
合入 `M1C-0`(见本文件同日条)后对该包做整组复核。**结论:包本身可合,「今天行为零变化」成立且可证**;但新变体在 `M1C-1` 写入之日会同时点亮三个默认值,而这三个默认值都不是裁决出来的,是「给用 `==` 比较(而非穷尽 `match`)的 durable enum 加变体,编译器不报,新变体静默落到作者没想过的那一侧」的结果——与本文件「修复 M1A-2 引入的回归」条同源,是同一失败模式的第二次出现。(复核完成后 `M1B-2` 已合入;其对 `delegation.rs` 的改动是 rustfmt 换行、无语义变化,本条结论不受影响。)