loops-l3.html 7.83 KB
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" />
<title>L3 · Human Feedback 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-l3.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>L3 · Human</span></div>
      <h1>L3 · Human Feedback Loop</h1>
      <p class="deck">最外层。接收人类的新需求 / 反馈 / 否决,把它<b>合并进 PRD</b>,再让 Architect 重算 SDD diff,仅对增量 Unit 触发 L1 + L2。</p>
    </header>
    <article class="body">

      <section>
        <h2>状态机</h2>
        <svg viewBox="0 0 1100 200" 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="70" width="140" height="60" rx="4" fill="#ECE4FB" stroke="#6E48C3"/><text x="90"  y="105" text-anchor="middle">human request</text>
            <rect x="220" y="70" width="140" height="60" rx="4" fill="#fff" stroke="#0E0E10"/><text x="290" y="95"  text-anchor="middle">PM Agent</text><text x="290" y="115" text-anchor="middle" font-size="10" fill="#3B3B40">+ patch PRD</text>
            <rect x="420" y="70" width="140" height="60" rx="4" fill="#fff" stroke="#0E0E10"/><text x="490" y="95"  text-anchor="middle">Architect</text><text x="490" y="115" text-anchor="middle" font-size="10" fill="#3B3B40">diff SDD</text>
            <rect x="620" y="70" width="140" height="60" rx="4" fill="#fff" stroke="#0E0E10"/><text x="690" y="95"  text-anchor="middle">unit diff</text><text x="690" y="115" text-anchor="middle" font-size="10" fill="#3B3B40">new / changed</text>
            <rect x="820" y="70" width="140" height="60" rx="4" fill="#E2F2EA" stroke="#0E8C5A"/><text x="890" y="105" text-anchor="middle">→ L1 (incr.)</text>
            <line x1="160" y1="100" x2="215" y2="100" stroke="#6E48C3" marker-end="url(#arr)"/>
            <line x1="360" y1="100" x2="415" y2="100" stroke="#0E0E10" marker-end="url(#arr)"/>
            <line x1="560" y1="100" x2="615" y2="100" stroke="#0E0E10" marker-end="url(#arr)"/>
            <line x1="760" y1="100" x2="815" y2="100" stroke="#0E0E10" marker-end="url(#arr)"/>
          </g>
        </svg>
      </section>

      <section>
        <h2>触发渠道</h2>
        <div class="grid-3">
          <div class="card l3">
            <h4>① REST</h4>
            <p><code>POST /api/devloop/projects/:pid/requirement</code><br/>JSON body 走 CLAUDE.md 推荐的 <code>-d @/tmp/req.json</code> 方式。</p>
          </div>
          <div class="card l3">
            <h4>② Web UI</h4>
            <p>DevLoopDashboard 右上角 <span class="kbd">+ NEW REQUIREMENT</span>,弹出 textarea。</p>
          </div>
          <div class="card l3">
            <h4>③ 自动信号</h4>
            <p>L1 <code>blocked</code> 或 L2 <code>exhausted</code> 时,LoopController 主动发 SSE,由前端弹「请补充信息」对话框。</p>
          </div>
        </div>
      </section>

      <section>
        <h2>增量 Unit 算法</h2>
        <p>Architect 对比新旧 <code>units.json</code>,标记三种变化:</p>
        <table class="dense">
          <thead><tr><th>变化类型</th><th>判断</th><th>处置</th></tr></thead>
          <tbody>
            <tr><td><b>added</b></td><td>新 unit.id 不在旧 list</td><td>status = pending,加入 L1 队列</td></tr>
            <tr><td><b>changed</b></td><td>同 id,<code>contract_hash</code> 变化</td><td>status 回退到 pending,加入 L1 队列(旧代码归档到 <code>history/</code>)</td></tr>
            <tr><td><b>removed</b></td><td>旧 id 不在新 list</td><td>整个 unit 目录归档到 <code>history/removed/</code>,从 DAG 摘除</td></tr>
            <tr><td><b>unchanged</b></td><td>id 在且 hash 不变</td><td>保持 passed,<b>无须重跑</b>。这是增量化的关键</td></tr>
          </tbody>
        </table>
      </section>

      <section>
        <h2>PRD 增量合并示例</h2>
        <p>PM Agent 不重写整个 PRD,只追加 <code>&lt;section data-rev=N&gt;</code>,并维护 doc 级 <code>data-rev</code> 自增。这样人类能直观看到「这一轮反馈引入了什么」。</p>
        <div class="tabs" data-tabs>
          <div class="tabbar" role="tablist">
            <button role="tab" aria-selected="true"  aria-controls="t-before">prd.html · rev 2</button>
            <button role="tab" aria-selected="false" aria-controls="t-after">prd.html · rev 3 (after feedback)</button>
          </div>
          <div class="tabpanel" id="t-before">
<pre class="code"><span class="k">&lt;article</span> <span class="n">data-doc</span>=<span class="s">"prd"</span> <span class="n">data-rev</span>=<span class="s">"2"</span><span class="k">&gt;</span>
  <span class="k">&lt;section</span> <span class="n">data-rev</span>=<span class="s">"1"</span><span class="k">&gt;</span>
    <span class="v">用户名密码登录,会话 7 天有效</span>
  <span class="k">&lt;/section&gt;</span>
  <span class="k">&lt;section</span> <span class="n">data-rev</span>=<span class="s">"2"</span><span class="k">&gt;</span>
    <span class="v">个人主页:显示用户名 / 邮箱</span>
  <span class="k">&lt;/section&gt;</span>
<span class="k">&lt;/article&gt;</span></pre>
          </div>
          <div class="tabpanel" id="t-after" hidden>
<pre class="code"><span class="k">&lt;article</span> <span class="n">data-doc</span>=<span class="s">"prd"</span> <span class="n">data-rev</span>=<span class="s">"3"</span><span class="k">&gt;</span>
  <span class="k">&lt;section</span> <span class="n">data-rev</span>=<span class="s">"1"</span><span class="k">&gt;</span>...<span class="k">&lt;/section&gt;</span>
  <span class="k">&lt;section</span> <span class="n">data-rev</span>=<span class="s">"2"</span><span class="k">&gt;</span>...<span class="k">&lt;/section&gt;</span>
  <span class="k">&lt;section</span> <span class="n">data-rev</span>=<span class="s">"3"</span> <span class="n">data-origin</span>=<span class="s">"yibo@2026-05-12T10:31"</span><span class="k">&gt;</span>
    <span class="r">+</span> <span class="v">新增:支持 GitHub OAuth 登录</span>
    <span class="r">+</span> <span class="v">NFR:3rd-party token 刷新 ≤ 5s</span>
  <span class="k">&lt;/section&gt;</span>
<span class="k">&lt;/article&gt;</span></pre>
          </div>
        </div>
      </section>

      <section>
        <h2>什么时候算「项目结束」</h2>
        <p>L3 没有自动退出条件。结束信号来自人类的显式动作:</p>
        <ul>
          <li><b>显式 sign-off:</b><code>POST /api/devloop/projects/:pid/sign-off</code> → 项目 status 置 <code>shipped-final</code>,停止接收新需求。</li>
          <li><b>归档:</b>30 天无人类反馈,自动归档但保留所有产物。</li>
          <li><b>放弃:</b><code>POST .../abandon</code>,整个项目目录加 <code>.abandoned</code> 标记,从 Dashboard 列表移除。</li>
        </ul>
      </section>

    </article>
    <nav class="prev-next"></nav>
    <footer class="site"><div><b>Section</b>02 · Loops</div><div><b>Page</b>loops-l3.html</div></footer>
  </main>
</div>
<script src="assets/shell.js"></script>
</body>
</html>