loops-l2.html
7.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" />
<title>L2 · Integration Loop · LemonBoss PRD</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,500;9..144,700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="assets/shell.css" />
</head>
<body data-page="loops-l2.html">
<div class="shell">
<aside id="sidebar"></aside>
<main class="page">
<header class="page-head">
<div class="crumb"><span class="pill">02 · LOOPS</span><span class="sep">/</span><span>L2 · Integration</span></div>
<h1>L2 · Integration Loop</h1>
<p class="deck">中间层。当所有 Unit passed 后开闸:跑集成测试 → 若失败则<b>归因 → 改 SDD → 回 L1</b>。L2 是「跨 Unit 契约」一致性的守门员。</p>
</header>
<article class="body">
<section>
<h2>状态机</h2>
<svg viewBox="0 0 1100 240" width="100%" role="img">
<defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto"><path d="M0,0 L10,5 L0,10 Z" fill="#0E0E10"/></marker></defs>
<g font-family="JetBrains Mono" font-size="12" fill="#0E0E10">
<rect x="20" y="90" width="140" height="60" rx="4" fill="#F7E8D2" stroke="#C77C2C"/><text x="90" y="125" text-anchor="middle">run-integration</text>
<rect x="220" y="30" width="140" height="60" rx="4" fill="#FCE7E2" stroke="#C7341A"/><text x="290" y="55" text-anchor="middle">fail</text><text x="290" y="75" text-anchor="middle" font-size="10" fill="#3B3B40">stderr + diff</text>
<rect x="220" y="150" width="140" height="60" rx="4" fill="#fff" stroke="#0E8C5A" stroke-width="2"/><text x="290" y="185" text-anchor="middle">ship</text>
<rect x="420" y="30" width="140" height="60" rx="4" fill="#fff" stroke="#0E0E10"/><text x="490" y="55" text-anchor="middle">triage</text><text x="490" y="75" text-anchor="middle" font-size="10" fill="#3B3B40">BugTriager</text>
<rect x="620" y="30" width="140" height="60" rx="4" fill="#fff" stroke="#0E0E10"/><text x="690" y="55" text-anchor="middle">patch SDD</text><text x="690" y="75" text-anchor="middle" font-size="10" fill="#3B3B40">Architect</text>
<rect x="820" y="30" width="140" height="60" rx="4" fill="#E2F2EA" stroke="#0E8C5A"/><text x="890" y="55" text-anchor="middle">reset units</text><text x="890" y="75" text-anchor="middle" font-size="10" fill="#3B3B40">→ L1</text>
<line x1="160" y1="110" x2="215" y2="60" stroke="#C7341A" marker-end="url(#arr)"/>
<line x1="160" y1="130" x2="215" y2="180" stroke="#0E8C5A" marker-end="url(#arr)"/>
<line x1="360" y1="60" x2="415" y2="60" stroke="#0E0E10" marker-end="url(#arr)"/>
<line x1="560" y1="60" x2="615" y2="60" stroke="#0E0E10" marker-end="url(#arr)"/>
<line x1="760" y1="60" x2="815" y2="60" stroke="#0E0E10" marker-end="url(#arr)"/>
<path d="M890,90 Q890,170 160,160" fill="none" stroke="#0E8C5A" stroke-dasharray="4 3" marker-end="url(#arr)"/>
</g>
</svg>
</section>
<section>
<h2>L2 闸门触发条件</h2>
<p>不是「所有 Unit 都跑过一次」,而是「所有 Unit 的<b>当前 contract_hash 与依赖关系下,状态都是 passed</b>」。这避免 SDD 改过之后某个旧版 Unit 还停留在 passed 而事实上契约已变。</p>
<pre class="code"><span class="k">function</span> <span class="n">l2GateOpen</span>(<span class="v">project</span>) <span class="p">{</span>
<span class="k">return</span> project.units.<span class="n">every</span>(u =>
u.<span class="n">status</span> === <span class="s">"passed"</span> &&
u.<span class="n">contract_hash</span> === <span class="n">currentSDD</span>(project).<span class="n">contractHashFor</span>(u.<span class="n">id</span>)
);
<span class="p">}</span></pre>
</section>
<section>
<h2>失败归因 — BugTriager 输出契约</h2>
<p>BugTriager 必须输出一个机读 JSON,LoopController 据此自动决定回滚范围。Triager 不直接改文件,只发「建议」。</p>
<pre class="code"><span class="p">{</span>
<span class="n">"run_id"</span>: <span class="s">"run-int-20260512-1031"</span>,
<span class="n">"root_cause"</span>: <span class="s">"auth-be returns 500 on invalid pwd, expected 401; auth-fe error handler relies on 401"</span>,
<span class="n">"category"</span>: <span class="s">"contract_mismatch"</span>,
<span class="n">"affected_units"</span>: <span class="p">[</span><span class="s">"auth-be"</span>, <span class="s">"auth-fe"</span><span class="p">]</span>,
<span class="n">"proposed_sdd_patch"</span>: <span class="p">{</span>
<span class="n">"target_section"</span>: <span class="s">"#unit-auth-be"</span>,
<span class="n">"op"</span>: <span class="s">"update"</span>,
<span class="n">"text"</span>: <span class="s">"明确:登录失败必须 401 with {error:'invalid_credentials'}"</span>
<span class="p">}</span>,
<span class="n">"confidence"</span>: <span class="v">0.82</span>
<span class="p">}</span></pre>
</section>
<section>
<h2>归因 → 回写 → 重置 三步</h2>
<div class="grid-3">
<div class="card l2">
<h4>① 归因</h4>
<p>BugTriager 读 stderr + diff + units.json + sdd.html,产 <code>triage.json</code>。<b>confidence < 0.5</b> 时上报 L3 让人类裁决。</p>
</div>
<div class="card l2">
<h4>② 回写</h4>
<p>Architect 接收 <code>proposed_sdd_patch</code>,用 section 级 DOM 操作改 <code>sdd.html</code>,归档旧版到 <code>history/sdd-rev-N.html</code>。</p>
</div>
<div class="card l2">
<h4>③ 重置</h4>
<p>UnitRegistry 把 <code>affected_units</code> 的 status 改回 <code>pending</code>,并自动重算 <code>contract_hash</code>,触发 L1 新一轮。</p>
</div>
</div>
</section>
<section>
<h2>L2 修复预算</h2>
<p>同一项目<b>每个 PRD rev</b> 内最多触发 3 次 L2 修复轮回。超出后整个项目挂起,发 SSE 事件 <code>project.l2_exhausted</code>,等待人类(L3)发新需求或手动干预。</p>
<div class="callout warn">
<b>为什么是 3:</b>实测 Claude Code Agent 三轮内还修不好的,要么是契约描述本身有歧义(PRD 缺陷),要么是技术选型有根本问题。再加轮次只会浪费 Token。
</div>
</section>
<section>
<h2>集成测试套件从哪来?</h2>
<p>这是一个设计决策(见 <a href="plan-risks.html">风险与待决 · R3</a>)。初版选 <b>方案 A</b>:</p>
<table class="dense">
<thead><tr><th>方案</th><th>谁写集成测试</th><th>优点</th><th>缺点</th></tr></thead>
<tbody>
<tr><td><b>A · 选中</b></td><td>Architect 在生成 SDD 时同步生成 <code>integration/spec.json</code></td><td>少一个 Agent,集成测试与 SDD 同步演化</td><td>Architect 职责变重</td></tr>
<tr><td>B</td><td>独立 IntegrationAuthor Agent</td><td>关注点分离</td><td>多一个 Agent,多一次握手</td></tr>
<tr><td>C</td><td>Integrator 自己写</td><td>读取代码后再写最贴合</td><td>容易写一个「永远会通过」的 trivial test</td></tr>
</tbody>
</table>
</section>
</article>
<nav class="prev-next"></nav>
<footer class="site"><div><b>Section</b>02 · Loops</div><div><b>Page</b>loops-l2.html</div></footer>
</main>
</div>
<script src="assets/shell.js"></script>
</body>
</html>