plan-reproducibility.html 12.7 KB
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Claude Code 复现指南 · 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="plan-reproducibility.html">
<div class="shell">
  <aside id="sidebar"></aside>
  <main class="page">
    <header class="page-head">
      <div class="crumb"><span class="pill">07 · PLAN</span><span class="sep">/</span><span>Claude Code 复现指南</span></div>
      <h1>Claude Code 复现指南</h1>
      <p class="deck">把这套 PRD + SDD 喂给一个全新的 Claude Code session,让它从空仓库(仅有 LemonBoss 既有代码)重建整套 DevLoop 系统。本页含:执行 prompt 模板 + manifest 抽取脚本 + 验收清单。</p>
    </header>
    <article class="body">

      <section>
        <h2>三步法</h2>
        <svg viewBox="0 0 1100 220" width="100%" role="img">
          <defs><marker id="arrRP" 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">
            <rect x="40"  y="80" width="280" height="60" rx="6" fill="#F7E8D2" stroke="#C77C2C"/>
            <text x="180" y="105" text-anchor="middle" font-weight="700">① 抽取 build-manifest</text>
            <text x="180" y="125" text-anchor="middle" font-size="10">cheerio 扫 sdd-*.html → JSON</text>

            <rect x="400" y="80" width="280" height="60" rx="6" fill="#ECE4FB" stroke="#6E48C3"/>
            <text x="540" y="105" text-anchor="middle" font-weight="700">② 投喂 Claude Code</text>
            <text x="540" y="125" text-anchor="middle" font-size="10">manifest + 复现 prompt</text>

            <rect x="760" y="80" width="280" height="60" rx="6" fill="#E2F2EA" stroke="#0E8C5A"/>
            <text x="900" y="105" text-anchor="middle" font-weight="700">③ 自动开发 + 自验</text>
            <text x="900" y="125" text-anchor="middle" font-size="10">写代码 → 单测 → 集成</text>

            <g stroke="#0E0E10" fill="none" marker-end="url(#arrRP)">
              <line x1="320" y1="110" x2="395" y2="110"/>
              <line x1="680" y1="110" x2="755" y2="110"/>
            </g>
          </g>
        </svg>
      </section>

      <section>
        <h2>步骤 ① · 抽取 build-manifest.json</h2>
        <p>需要 node + cheerio。脚本 ~80 行:</p>
<pre class="code"><span class="c">// scripts/extract-manifest.js · NEW</span>
<span class="k">const</span> fs = <span class="n">require</span>(<span class="s">'fs'</span>);
<span class="k">const</span> path = <span class="n">require</span>(<span class="s">'path'</span>);
<span class="k">const</span> cheerio = <span class="n">require</span>(<span class="s">'cheerio'</span>);

<span class="k">const</span> docsDir  = path.<span class="n">resolve</span>(__dirname, <span class="s">'../docs/prd/newfunction'</span>);
<span class="k">const</span> manifest = <span class="p">{</span>
  meta: <span class="p">{</span> source: <span class="s">'docs/prd/newfunction'</span>, prdRev: <span class="s">'v0.2'</span> <span class="p">}</span>,
  model: <span class="p">[]</span>, view: <span class="p">[]</span>, control: <span class="p">[]</span>, process: <span class="p">[]</span>,
  units: <span class="p">[]</span>,
<span class="p">}</span>;

<span class="c">// 1. 从 sdd-{mvcp}.html 抽 4 象限</span>
<span class="k">for</span> (<span class="k">const</span> mvcp <span class="k">of</span> <span class="p">[</span><span class="s">'model'</span>, <span class="s">'view'</span>, <span class="s">'control'</span>, <span class="s">'process'</span><span class="p">]</span>) <span class="p">{</span>
  <span class="k">const</span> html = fs.<span class="n">readFileSync</span>(path.<span class="n">join</span>(docsDir, <span class="s">`sdd-</span><span class="v">${mvcp}</span><span class="s">.html`</span>), <span class="s">'utf8'</span>);
  <span class="k">const</span> $ = cheerio.<span class="n">load</span>(html);
  $(<span class="s">'[data-def-name]'</span>).<span class="n">each</span>((_, el) =&gt; <span class="p">{</span>
    manifest[mvcp].<span class="n">push</span>(<span class="p">{</span>
      name:     $(el).<span class="n">attr</span>(<span class="s">'data-def-name'</span>),
      path:     $(el).<span class="n">find</span>(<span class="s">'code.path'</span>).<span class="n">first</span>().<span class="n">text</span>() ||
                $(el).<span class="n">find</span>(<span class="s">'code.path.mono'</span>).<span class="n">first</span>().<span class="n">text</span>(),
      shape:    $(el).<span class="n">find</span>(<span class="s">'pre.shape'</span>).<span class="n">text</span>() ||
                $(el).<span class="n">find</span>(<span class="s">'pre.code.shape'</span>).<span class="n">text</span>(),
      contract: $(el).<span class="n">find</span>(<span class="s">'ul.contract li'</span>).<span class="n">map</span>((_,li) =&gt; $(li).<span class="n">text</span>()).<span class="n">get</span>(),
      tests:    $(el).<span class="n">find</span>(<span class="s">'ul.tests li'</span>).<span class="n">map</span>((_,li) =&gt; $(li).<span class="n">text</span>()).<span class="n">get</span>(),
    <span class="p">}</span>);
  <span class="p">}</span>);
<span class="p">}</span>

<span class="c">// 2. 从 prd-feature-tree.html 抽 Unit 列表(粗)</span>
<span class="c">// 这步实际由 Architect Agent 做 P1 时的拆分;脚本只做最简取出 file tree 中的 Unit id</span>

fs.<span class="n">writeFileSync</span>(<span class="s">'build-manifest.json'</span>, <span class="n">JSON.stringify</span>(manifest, <span class="v">null</span>, <span class="v">2</span>));
console.<span class="n">log</span>(<span class="s">`✓ extracted </span><span class="v">${manifest.model.length + manifest.view.length + manifest.control.length + manifest.process.length}</span><span class="s"> definitions`</span>);</pre>
        <p>跑一下:</p>
        <pre class="code">cd lemonboss
node scripts/extract-manifest.js
<span class="c"># → build-manifest.json (~30KB)</span></pre>
      </section>

      <section>
        <h2>步骤 ② · 复现 Prompt 模板</h2>
        <p>把以下 prompt 喂给一个全新的 Claude Code session(在空 lemonboss 仓库根目录启动)。</p>
<pre class="code"><span class="c"># 复现 prompt · 直接粘到 claude code</span>

我手上有一份 LemonBoss DevLoop 系统的完整 PRD + SDD(HTML 形式),位于
docs/prd/newfunction/ 下,共 41 页。我已用脚本抽出 build-manifest.json。

请你扮演 Architect + Coder + Tester 三个角色,完成以下工作:

【阶段 A · 拆 Unit】
1. 读 docs/prd/newfunction/prd-feature-tree.html,确认 34 个 Unit
2. 读 build-manifest.json,把 MVCP 四象限的定义映射到 Unit
3. 在 data/companies/c-repro/projects/p-init/units.json 落地 DAG

【阶段 B · 实现每个 Unit】
对每个 Unit 顺次:
1. 读对应 Unit 的所有 MVCP 定义(manifest 里的 model/view/control/process)
2. 按 path 字段落盘,按 shape 字段写代码骨架
3. 按 contract 列表实现行为
4. 按 tests 列表写 vitest 用例(后端 supertest + 内存 SQLite,前端 @vue/test-utils)
5. 跑 vitest,红了反思(参考 docs/prd/newfunction/loops-l1.html 的 retryWithReflection
   章节),重写直到绿

【阶段 C · 集成】
1. 读 docs/prd/newfunction/sdd-process.html 实现 LoopController.runProject
2. 启动后端 + 前端,cd 到 docs/prd/newfunction,用 docs 自己做 demo 项目跑一遍
3. 注入「contract_mismatch」演示自愈

【约束】
- 严格遵守 docs/prd/newfunction/agents-coder.html 的沙箱规则
- 任何带 JSON body 的 curl 必须先写 /tmp/*.json 再 -d @file
  (参见 CLAUDE.md L91-99)
- 不动 server/services/evolution-loop.js(参见 arch-mapping.html PATCH 说明)
- 不动 packages/acp-core/ · core/master-session.js · core/agent-manager.js

【验收】
完成后跑 npm test,所有 vitest 必须通过。然后跑:
  curl -X POST http://localhost:3010/api/devloop/projects -d @/tmp/test.json
  (/tmp/test.json 单行紧凑:
   {"company_id":"c-001","name":"todo","requirement":"做个 TODO API"})
应在 60 秒内看到 prd.html + sdd.html + units.json 三份文档生成。

开始吧。</pre>
      </section>

      <section>
        <h2>步骤 ③ · 验收清单</h2>
        <p>Claude Code 跑完后,人类只需对照以下 12 条逐项检查:</p>
        <table>
          <thead><tr><th>#</th><th>检查点</th><th>判定</th></tr></thead>
          <tbody>
            <tr><td>1</td><td><code>server/src/core/loop-controller.js</code> 存在且导出 loopController</td><td>✓ require 不报错</td></tr>
            <tr><td>2</td><td><code>server/services/doc-store.js</code> 存在</td><td>✓</td></tr>
            <tr><td>3</td><td><code>server/services/unit-registry.js</code> 存在</td><td>✓</td></tr>
            <tr><td>4</td><td><code>server/services/sse-broker.js</code> 存在</td><td>✓</td></tr>
            <tr><td>5</td><td><code>server/routes/devloop.js</code> 存在且 7 个端点全</td><td>✓ <code>curl /api/health</code> + grep</td></tr>
            <tr><td>6</td><td><code>system/agents/devloop-{pm,architect,coder,tester,integrator,triager}/prompt.md</code> 全部存在</td><td>✓ ls 应见 6 个</td></tr>
            <tr><td>7</td><td><code>system/agents/_registry.json</code> 含 devloop-* 六条</td><td>✓ grep</td></tr>
            <tr><td>8</td><td><code>web/src/views/DevLoopDashboard.vue</code> + 7 个子组件</td><td>✓ ls web/src/components/devloop/</td></tr>
            <tr><td>9</td><td>npm test 全绿</td><td>✓</td></tr>
            <tr><td>10</td><td>POST /api/devloop/projects 60s 内生成 3 份文档</td><td>✓ file 存在</td></tr>
            <tr><td>11</td><td>SSE 流持续推 unit.state 事件</td><td>✓ curl -N 看到</td></tr>
            <tr><td>12</td><td>整套 DevLoop 能<b>对自己做增量</b>(用 DevLoop 给 DevLoop 加新需求)</td><td>✓ 元-自举测试</td></tr>
          </tbody>
        </table>
      </section>

      <section>
        <h2>典型失败模式与诊断</h2>
        <table class="dense">
          <thead><tr><th>失败</th><th>诊断</th><th>修</th></tr></thead>
          <tbody>
            <tr><td>缺某个 MVCP 定义</td><td>build-manifest.json 该项空</td><td>检查对应 SDD 页是否漏 data-def-name</td></tr>
            <tr><td>shape 抽不到</td><td>HTML class 不规范</td><td>用 pre.shape 不是 pre.code 也不是 pre.code.shape,统一</td></tr>
            <tr><td>Claude Code 写出的代码风格与 LemonBoss 不一致</td><td>prompt 缺约束</td><td>在复现 prompt 顶部 require 它先读 CLAUDE.md</td></tr>
            <tr><td>contract.json 与 SDD 不一致</td><td>Architect 没遵循 hashContract 算法</td><td>看 docs/prd/newfunction/api-contracts.html 第 4 节</td></tr>
            <tr><td>L2 修复死循环</td><td>l2_budget 没设</td><td>project.json 默认值校验</td></tr>
            <tr><td>SSE 不断重连</td><td>心跳没发</td><td>看 sse-broker.js 25s setInterval</td></tr>
          </tbody>
        </table>
      </section>

      <section>
        <h2>「文档驱动」承诺</h2>
        <div class="callout ok">
          <b>这套 PRD + SDD 的一致性是项目本身的健康指标。</b>
          <p style="margin:6px 0 0">如果未来某次实际代码与文档脱节超过 20%(脚本扫描 unit id 对齐率),系统会发 SSE 警告,提示要么改文档要么改代码。文档永远是 source of truth;代码是执行结果。</p>
        </div>
      </section>

      <section>
        <h2>下一步</h2>
        <p>完成本页所述复现一次,相当于:</p>
        <ul>
          <li>✓ 验证 PRD + SDD 的<b>可执行性</b>(不只是写给人看)</li>
          <li>✓ 验证 Claude Code + LemonBoss ACP 的<b>能力上限</b></li>
          <li>✓ 给团队和投资人一个明确的<b>能力证明</b>(demo 时直接放复现录屏)</li>
        </ul>
        <p>复现成功的录屏 + 时间戳 + manifest hash 应保存在 <code>docs/prd/newfunction/repro-evidence/</code>,作为项目交付物的一部分。</p>
      </section>

    </article>
    <nav class="prev-next"></nav>
    <footer class="site"><div><b>Section</b>07 · Plan</div><div><b>Page</b>plan-reproducibility.html</div></footer>
  </main>
</div>
<script src="assets/shell.js"></script>
</body>
</html>