loops-l2.md
2.69 KB
L2 · Integration Loop
中间层。当所有 Unit passed 后开闸:跑集成测试 → 若失败则归因 → 改 SDD → 回 L1。L2 是「跨 Unit 契约」一致性的守门员。
状态机
L2 闸门触发条件
不是「所有 Unit 都跑过一次」,而是「所有 Unit 的当前 contract_hash 与依赖关系下,状态都是 passed」。这避免 SDD 改过之后某个旧版 Unit 还停留在 passed 而事实上契约已变。
function l2GateOpen(project) {
return project.units.every(u =>
u.status === "passed" &&
u.contract_hash === currentSDD(project).contractHashFor(u.id)
);
}
失败归因 — BugTriager 输出契约
BugTriager 必须输出一个机读 JSON,LoopController 据此自动决定回滚范围。Triager 不直接改文件,只发「建议」。
{
"run_id": "run-int-20260512-1031",
"root_cause": "auth-be returns 500 on invalid pwd, expected 401; auth-fe error handler relies on 401",
"category": "contract_mismatch",
"affected_units": ["auth-be", "auth-fe"],
"proposed_sdd_patch": {
"target_section": "#unit-auth-be",
"op": "update",
"text": "明确:登录失败必须 401 with {error:'invalid_credentials'}"
},
"confidence": 0.82
}
归因 → 回写 → 重置 三步
① 归因
BugTriager 读 stderr + diff + units.json + sdd.html,产 triage.json。confidence 时上报 L3 让人类裁决。
② 回写
Architect 接收 proposed_sdd_patch,用 section 级 DOM 操作改 sdd.html,归档旧版到 history/sdd-rev-N.html。
③ 重置
UnitRegistry 把 affected_units 的 status 改回 pending,并自动重算 contract_hash,触发 L1 新一轮。
L2 修复预算
同一项目每个 PRD rev 内最多触发 3 次 L2 修复轮回。超出后整个项目挂起,发 SSE 事件 project.l2_exhausted,等待人类(L3)发新需求或手动干预。
为什么是 3:实测 Claude Code Agent 三轮内还修不好的,要么是契约描述本身有歧义(PRD 缺陷),要么是技术选型有根本问题。再加轮次只会浪费 Token。
集成测试套件从哪来?
这是一个设计决策(见 风险与待决 · R3)。初版选 方案 A:
| 方案 | 谁写集成测试 | 优点 | 缺点 |
|---|---|---|---|
| A · 选中 | Architect 在生成 SDD 时同步生成 integration/spec.json |
少一个 Agent,集成测试与 SDD 同步演化 | Architect 职责变重 |
| B | 独立 IntegrationAuthor Agent | 关注点分离 | 多一个 Agent,多一次握手 |
| C | Integrator 自己写 | 读取代码后再写最贴合 | 容易写一个「永远会通过」的 trivial test |