arch-components.md
4.32 KB
三个新组件
整套改造的核心只有三个新对象:LoopController、DocStore、UnitRegistry。它们是新增 1500 行代码的主要载体。
① LoopController
路径:server/src/core/loop-controller.js · ~180 行
编排 L1 / L2 / L3 三层状态机。所有 Agent 调用统一从这里出。它本身无状态,所有状态从 UnitRegistry / DocStore 读。
关键方法
| 方法 | 调用时机 | 返回 |
|---|---|---|
runProject(pid) |
启动 / 续跑项目 | `'shipped' |
runUnit(unit) |
L1 内部 | UnitResult |
onHumanFeedback(pid, req) |
L3 入口(HTTP) | void · 异步触发 runProject |
onL2Failure(pid, runId) |
Integrator 返回 fail 后 | void |
getState(pid) |
Dashboard 轮询 | ProjectState 快照 |
主循环骨架
async function runProject(pid) {
while (true) {
// L1: pending → coding → testing → passed
const waves = scheduler.getExecutionGroups(unitRegistry.pending(pid));
for (const w of waves) await Promise.all(w.map(runUnit));
if (unitRegistry.anyBlocked(pid)) return 'blocked';
// L2 gate
if (!l2GateOpen(pid)) continue;
const r = await agentManager.callAgent('integrator', { pid });
if (r.ok) return 'shipped';
// L2 failed → triage → patch SDD → reset units
const tri = await agentManager.callAgent('bug-triager', r.failure);
if (tri.confidence < 0.5) {
sse.emit(pid, 'human_intervention_needed', tri); return 'l2_blocked';
}
await agentManager.callAgent('architect', { mode: 'patch', patch: tri.proposed_sdd_patch });
unitRegistry.reset(pid, tri.affected_units);
if (project.l2Rounds++ >= 3) return 'l2_exhausted';
}
}
② DocStore
路径:server/services/doc-store.js · ~140 行
负责 prd.html / sdd.html / units.json / integration/spec.json 的读写、归档、校验。所有写操作走它,没有 Agent 能直接 Write 这些文件。
API 面
module.exports = {
// 读
readPRD(pid), readSDD(pid), readUnits(pid), readIntegrationSpec(pid),
// 写(自动归档旧版)
writePRD(pid, html),
writeSDD(pid, html),
writeUnits(pid, json),
writeIntegrationSpec(pid, json),
// 校验
validateHTML(html), // 用 cheerio + 必含 section
validateUnitsSchema(json), // 用 typebox
// diff / 归档
diffPRD(pid, fromRev, toRev),
archive(pid, kind, oldHtml), // → history/{kind}-rev-N.html
};
HTML 校验规则
| 文档 | 必含元素 | 校验工具 |
|---|---|---|
| prd.html | 顶级 ` |
,至少 1
| cheerio |
| sdd.html | 每
含
+
` | cheerio | | units.json | schema 校验 + DAG 无环 | typebox + 拓扑排序 | | integration/spec.json | 每 case 的 involves 都在 units.json 中存在 | 自写 |
③ UnitRegistry
路径:server/services/unit-registry.js · ~120 行
Unit 状态机的 source of truth。状态转换都走它,不允许 Agent 自己改 units.json 的 status 字段。
API 面
module.exports = {
list(pid), // 全部 unit + 状态快照
pending(pid), passed(pid), blocked(pid),
l2GateOpen(pid), // 全 passed 且 hash 一致
// 状态转换
markCoding(pid, unitId),
markTesting(pid, unitId),
pass(pid, unitId, contractHash),
fail(pid, unitId, reason),
block(pid, unitId, reason),
reset(pid, unitIds), // L2 修复后
// diff (用于 L3 增量)
diffWithSDD(pid), // → { added, changed, removed }
};
状态转换合法性
module.exports = {
list(pid), // 全部 unit + 状态快照
pending(pid), passed(pid), blocked(pid),
l2GateOpen(pid), // 全 passed 且 hash 一致
// 状态转换
markCoding(pid, unitId),
markTesting(pid, unitId),
pass(pid, unitId, contractHash),
fail(pid, unitId, reason),
block(pid, unitId, reason),
reset(pid, unitIds), // L2 修复后
// diff (用于 L3 增量)
diffWithSDD(pid), // → { added, changed, removed }
};
| from \ to | pending | coding | testing | failed | passed | blocked |
|---|---|---|---|---|---|---|
| — | ✓ | |||||
| — | ✓ | |||||
| — | ✓ | ✓ | ||||
| ✓ | — | ✓ | ||||
| ✓ (L2 reset) | — | |||||
| ✓ (人类干预) | — |
非法转换 → 抛异常 + SSE 错误事件,便于排查。
三者协作
外部依赖
| 包 | 用途 | 是否新增 |
|---|---|---|
cheerio |
解析 / 校验 HTML | 新增 (~120KB) |
@sinclair/typebox |
JSON schema 校验 | 已存在 |
better-sqlite3 |
— | 已存在(不需要新表) |
不引入新数据库;项目状态全部落文件。Dashboard 用 SSE 推送,不需要轮询 DB。