03 · SDD/MVCP 总览

SDD · MVCP 总览

SDD(System Design Document)用 MVCP 四象限组织:Model · View · Control · Process。每个象限是一篇独立页,每个定义带 name / path / shape / contract / tests 五字段。Claude Code 用 cheerio 即可机械抽取。

为什么是 MVCP 而不是 MVC

传统 MVC 三分对一个有大量「长进程 / 异步循环」的系统不够用。DevLoop 系统里:

  • L1/L2/L3 是长时运行的状态机
  • 每个 Agent 是长寿子进程(ACP session 复用)
  • SSE Broker 是持续推送通道

这些都不属于 Controller(即时响应请求),也不属于 Model(数据)。给它们一个独立象限 Process 才有归处。

四象限

M · MODEL 数据结构 · 文档 schema · 落盘格式 • prd.html · sdd.html 结构 • units.json · project.json · contract.json • runs/result.json · triage.json • Unit 六态机字典 • 全部 TypeBox schema → sdd-model.html V · VIEW UI 组件 · 路由 · i18n • DevLoopDashboard.vue 组件树 • 7 个子组件 props/slots/emits • router 路由表 • i18n keys 全清单 • 设计 token 引用 → sdd-view.html C · CONTROL 即时响应:API · 服务方法 · 路由 handler • REST 端点全签名(7 条) • LoopController 公共方法 • DocStore / UnitRegistry / SSEBroker 方法 • Agent callAgent 调用约定 • 中间件(auth · jsonGuard · sanitizer) → sdd-control.html P · PROCESS 长进程:状态机 · Loop · 后台任务 • L1 / L2 / L3 状态机详尽转换 • Agent 生命周期 (启动/复用/回收) • SSE 订阅 / 心跳 / 重连 • retryWithReflection 算法 • 清理 / 归档 / 预算守护 → sdd-process.html

每个定义的「五字段标准」

所有 MVCP 页里的每条定义都强制遵循下表结构,便于 Claude Code 用同一套抽取器处理:

字段类型含义抽取选择器
namestring · 必填唯一标识符 · 命名空间.对象名section[data-def-name]
pathstring · 必填预期落盘路径或文件位置[data-def-name] code.path
shapecode · 必填TypeBox / TS interface / Vue defineProps / SQL DDL[data-def-name] pre.shape
contractlist · 选填前置 / 后置条件 / 不变式[data-def-name] ul.contract li
testslist · 选填验收用例(Tester Agent 直接转 vitest)[data-def-name] ul.tests li

HTML 模板示例

<section data-def-name="DocStore.writePRD" data-mvcp="control">
  <h3>DocStore.writePRD(pid, html)</h3>
  <code class="path">server/services/doc-store.js</code>

  <pre class="shape">
async function writePRD(pid: string, html: string): Promise<{rev: number}>
  </pre>

  <ul class="contract">
    <li>前置:html 必须包含顶级 article[data-doc="prd"]</li>
    <li>后置:旧版本归档到 history/prd-rev-{rev}.html</li>
  </ul>

  <ul class="tests">
    <li>写入后 readPRD 能读回完全一致内容</li>
    <li>非法 HTML 抛 ValidationError</li>
  </ul>
</section>

本套文档因美观需要不完全遵守这个最严格的 HTML 模板,但每条定义至少含 name + path + shape。完全机械化版会在 P1 实施时由 Architect Agent 产出。

抽取流程

// Claude Code 抽取 build-manifest 的伪代码
const manifest = { model: [], view: [], control: [], process: [] };
for (const mvcp of ['model', 'view', 'control', 'process']) {
  const html = await fetch(`sdd-${mvcp}.html`).then(r => r.text());
  const $ = cheerio.load(html);
  $('[data-def-name]').each((_, el) => {
    manifest[mvcp].push({
      name:     $(el).attr('data-def-name'),
      path:     $(el).find('code.path').first().text(),
      shape:    $(el).find('pre.shape').text(),
      contract: $(el).find('ul.contract li').map((_,li) => $(li).text()).get(),
      tests:    $(el).find('ul.tests li').map((_,li) => $(li).text()).get(),
    });
  });
}
fs.writeFileSync('build-manifest.json', JSON.stringify(manifest, null, 2));

结果是一份 build-manifest.json,能直接喂给 Architect Agent 生成 units.json + Unit spec 文件。

四象限对应 Agent

象限谁负责写谁负责消费
ModelArchitectCoder(读 schema 生成数据访问代码)· DocStore(自身实现)
ViewArchitectCoder(fe 类 Unit 直接对应)
ControlArchitectCoder(be 类 Unit)· Integrator(跑端点)
ProcessArchitect所有 Agent 都要读懂 Process 才知道自己在生命周期哪一步