arch-workflow.html
8.54 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" />
<title>运行时工作流 · 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="arch-workflow.html">
<div class="shell">
<aside id="sidebar"></aside>
<main class="page">
<header class="page-head">
<div class="crumb"><span class="pill">04 · ARCHITECTURE</span><span class="sep">/</span><span>运行时工作流</span></div>
<h1>运行时工作流</h1>
<p class="deck">一次端到端的执行序列。横向是参与者,纵向是时间。第 1-4 步是 L3 初始化,第 5-9 步是 L1,第 10-13 步是 L2,第 14-15 步回到 L3。</p>
</header>
<article class="body">
<section>
<h2>端到端时序图</h2>
<svg viewBox="0 0 1100 760" width="100%" role="img" aria-label="端到端时序图">
<defs><marker id="arrW" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L10,5 L0,10 Z" fill="#0E0E10"/></marker></defs>
<g font-family="JetBrains Mono" font-size="11" fill="#0E0E10">
<g font-size="12" font-weight="700">
<text x="60" y="30">Human</text>
<text x="200" y="30">DevLoop API</text>
<text x="360" y="30">PM</text>
<text x="480" y="30">Architect</text>
<text x="610" y="30">LoopController</text>
<text x="790" y="30">Coder + Tester</text>
<text x="955" y="30">Integrator</text>
</g>
<g stroke="#dcd5c4" stroke-dasharray="2 4">
<line x1="80" y1="40" x2="80" y2="740"/>
<line x1="240" y1="40" x2="240" y2="740"/>
<line x1="380" y1="40" x2="380" y2="740"/>
<line x1="520" y1="40" x2="520" y2="740"/>
<line x1="680" y1="40" x2="680" y2="740"/>
<line x1="840" y1="40" x2="840" y2="740"/>
<line x1="990" y1="40" x2="990" y2="740"/>
</g>
<line x1="80" y1="70" x2="235" y2="70" stroke="#6E48C3" stroke-width="1.5" marker-end="url(#arrW)"/>
<text x="90" y="64">① POST /devloop/requirement</text>
<line x1="240" y1="100" x2="375" y2="100" stroke="#0E0E10" marker-end="url(#arrW)"/>
<text x="250" y="94">② invoke PM</text>
<line x1="380" y1="130" x2="515" y2="130" stroke="#0E0E10" marker-end="url(#arrW)"/>
<text x="390" y="124">③ prd.html v1 → Architect</text>
<line x1="520" y1="160" x2="675" y2="160" stroke="#0E0E10" marker-end="url(#arrW)"/>
<text x="530" y="154">④ sdd.html + units.json</text>
<rect x="680" y="190" width="310" height="220" fill="#E2F2EA" fill-opacity=".35" stroke="#0E8C5A" stroke-dasharray="4 3"/>
<text x="700" y="208" fill="#0E8C5A" font-weight="700">L1 · per Unit (parallel)</text>
<line x1="680" y1="240" x2="835" y2="240" stroke="#0E0E10" marker-end="url(#arrW)"/>
<text x="690" y="234">⑤ spawn coder(unit)</text>
<line x1="840" y1="270" x2="680" y2="270" stroke="#0E0E10" marker-end="url(#arrW)"/>
<text x="690" y="264">⑥ code commit</text>
<line x1="680" y1="300" x2="835" y2="300" stroke="#0E0E10" marker-end="url(#arrW)"/>
<text x="690" y="294">⑦ spawn tester(unit)</text>
<line x1="840" y1="330" x2="680" y2="330" stroke="#C7341A" stroke-dasharray="3 2" marker-end="url(#arrW)"/>
<text x="690" y="324" fill="#C7341A">⑧ test fail → retry coder</text>
<line x1="840" y1="380" x2="680" y2="380" stroke="#0E8C5A" marker-end="url(#arrW)"/>
<text x="690" y="374" fill="#0E8C5A">⑨ test pass → unit passed</text>
<rect x="680" y="430" width="310" height="120" fill="#F7E8D2" fill-opacity=".4" stroke="#C77C2C" stroke-dasharray="4 3"/>
<text x="700" y="448" fill="#C77C2C" font-weight="700">L2 · gate: all passed</text>
<line x1="680" y1="470" x2="985" y2="470" stroke="#0E0E10" marker-end="url(#arrW)"/>
<text x="690" y="464">⑩ run integration</text>
<line x1="990" y1="500" x2="680" y2="500" stroke="#C7341A" stroke-dasharray="3 2" marker-end="url(#arrW)"/>
<text x="690" y="494" fill="#C7341A">⑪ fail + failure-report</text>
<line x1="680" y1="530" x2="515" y2="530" stroke="#0E0E10" marker-end="url(#arrW)"/>
<text x="525" y="524">⑫ patch sdd.html</text>
<line x1="520" y1="570" x2="680" y2="570" stroke="#0E0E10" marker-end="url(#arrW)"/>
<text x="530" y="564">⑬ reset affected units → loop L1</text>
<line x1="680" y1="620" x2="80" y2="620" stroke="#6E48C3" marker-end="url(#arrW)"/>
<text x="90" y="614" fill="#6E48C3">⑭ notify human · request next feedback (L3)</text>
<line x1="80" y1="680" x2="235" y2="680" stroke="#6E48C3" stroke-width="1.5" marker-end="url(#arrW)"/>
<text x="90" y="674" fill="#6E48C3">⑮ new requirement / sign-off</text>
</g>
</svg>
</section>
<section>
<h2>同一时刻三个 Loop 的并发关系</h2>
<p>L1 内部允许并行(多 Unit),但 <b>L2 始终串行</b>(一个项目同时只能有一个 Integrator 跑)。L3 触发会等待当前 L2 跑完再合并 PRD。</p>
<table class="dense">
<thead><tr><th>场景</th><th>处理</th></tr></thead>
<tbody>
<tr><td>L1 跑了一半,L3 新需求到达</td><td>当前 L1 跑完再合并(保证状态一致),合并后增量 reset</td></tr>
<tr><td>L2 跑了一半,L3 新需求到达</td><td>L2 跑完再合并;若 L2 失败也照常合并(人类可能本来就是为了改这个)</td></tr>
<tr><td>L2 跑了一半,又收到一个 L3 反馈</td><td>队列化,按时间序处理</td></tr>
<tr><td>多项目并行</td><td>每项目独立 LoopController 实例,互不干扰</td></tr>
</tbody>
</table>
</section>
<section>
<h2>SSE 事件清单</h2>
<p>Dashboard 通过 <code>GET /api/devloop/projects/:pid/stream</code> 订阅。事件类型:</p>
<table class="dense">
<thead><tr><th>event</th><th>触发时机</th><th>payload 关键字段</th></tr></thead>
<tbody>
<tr><td><code>prd.updated</code></td><td>PM 写 PRD 完成</td><td>rev, changeSummary</td></tr>
<tr><td><code>sdd.updated</code></td><td>Architect 写 SDD 完成</td><td>rev, mode (generate/patch)</td></tr>
<tr><td><code>unit.state</code></td><td>UnitRegistry 状态转换</td><td>unit_id, from, to, retries</td></tr>
<tr><td><code>agent.stream</code></td><td>Agent stdout/stderr 流式</td><td>agent, unit_id, chunk</td></tr>
<tr><td><code>integration.result</code></td><td>Integrator 返回</td><td>ok, failures[]</td></tr>
<tr><td><code>triage.result</code></td><td>BugTriager 返回</td><td>category, confidence, affected_units</td></tr>
<tr><td><code>human_intervention_needed</code></td><td>confidence < 0.5 或 blocked</td><td>reason, suggestion</td></tr>
<tr><td><code>project.shipped</code></td><td>L2 通过</td><td>final_rev</td></tr>
<tr><td><code>project.exhausted</code></td><td>L2 修复预算耗尽</td><td>last_triage</td></tr>
</tbody>
</table>
</section>
<section>
<h2>错误与中止</h2>
<p>LoopController 顶层 <code>try/catch</code>:</p>
<ul>
<li><b>Agent 调用超时(> 10min):</b>kill 进程,retry 一次,再失败置 Unit blocked。</li>
<li><b>DocStore 写失败(磁盘满 / 权限):</b>整个项目挂起,SSE 告警人类。</li>
<li><b>未捕获异常:</b>记录到 <code>stream.log</code>,发 <code>fatal</code> 事件,<b>不</b>自动重启(避免雪崩)。</li>
</ul>
</section>
</article>
<nav class="prev-next"></nav>
<footer class="site"><div><b>Section</b>04 · Architecture</div><div><b>Page</b>arch-workflow.html</div></footer>
</main>
</div>
<script src="assets/shell.js"></script>
</body>
</html>