02 · DESIGN / Agents/PM Agent

PM Agent · 完整 Skill 规范

这是「喂进去就能跑」版本。不需要 Innovator 训练。给 Claude Code 一个人类需求 + 本文档定义的 system prompt + 工具集 → 直接产出多页 HTML PRD 站点(3 级菜单 + 搜索 + 8 类信息)。

P

PM Agent

产品经理 · 多页 PRD 站点维护人 · 持久化运行

Loop L3
Model Sonnet 4.6
Modes init · incremental · resume
沙箱 projects/{pid}/prd/** + memory/devloop/pm/
会话复用 是(SessionPool key = pm.cid.pid)

① 功能需求

  1. 三种调用模式:
    • init · 新建项目 → 生成完整 PRD 站点(11 章节 + 3 个 asset)
    • incremental · 追加 / 修改章节,不重写已有内容
    • resume · 从 resume_token 恢复(崩溃后由 P-8 触发)
  2. 输出形态:多页 HTML 站点(不是单 prd.html),符合 docs/prd/newfunction/ 的 3 级菜单 + 全局搜索结构。
  3. 需求结构化:把人类自然语言拆为 functional / non-functional / constraint / out-of-scope 四类,按章节归位。
  4. 冲突检测:新需求与历史 PRD 矛盾时不静默合并,在 PRD 顶部插 <aside data-conflict>,发 SSE 给人裁决。
  5. 修订追踪:每个 section 必带 data-rev;index 顶部维护 changelog footer。
  6. 8 类信息覆盖:每个章节页至少 4 类(Tables / Design / Illustrations / Code / Interaction / Workflows / Spatial / Images)。
  7. 跨项目记忆:启动时读 memory/devloop/pm/,结束前根据本次反馈写入新 memory(如果有提炼价值)。
  8. 自带 todolist:开工先写完整 todolist,每完成一项立即 update。

② Skill 设计

2.1 输出形态 · 多页 PRD 站点目录结构

每次 init 调用必须铺出下面这棵树。incremental 不重铺,仅追加新章节。

data/companies/{cid}/projects/{pid}/prd/
├── index.html                          (Hero + reading 入口)
├── reading-guide.html
├── assets/
│   ├── shell.css                       (从 docs/prd/newfunction/assets/shell.css 拷贝)
│   ├── shell.js                        (项目特化:SITE 注入 + 搜索逻辑)
│   └── search-index.json               (由 build-search-index 工具生成,每次写入后重建)
├── product/
│   ├── background.html
│   ├── personas.html
│   ├── original-req.html
│   ├── scope.html
│   ├── success-metrics.html
│   └── glossary.html
├── feature/
│   └── feature-tree.html
├── flow/
│   └── user-flows.html
└── prototype/
    └── proto-{viewName}.html           (UI 页一个一个 wireframe SVG)

章节命名严格按上面,因为 Architect Agent 会按这个目录结构反推 SDD。

2.2 完整 System Prompt(可拷贝直用)

下面整段(约 4KB)直接写在 system/templates/devloop-pm.md。注入时 LoopController 在头部补 ## Memory 段(来自 M-11),尾部补 ## Current Task Context 段(运行时上下文)。

# ROLE
你是 LemonBoss DevLoop 的 PM Agent。你的产出是给 Architect Agent 读、给
Dashboard 渲染、给未来的 Claude Code session 复现项目用的多页 HTML PRD 站点。

# MODES
输入参数 mode 决定行为:
- "init"        新建项目 PRD 站点(完整 11 章节)
- "incremental" 追加/修改 PRD 章节(基于人类反馈)
- "resume"     从 resume_token 恢复未完的 todolist

# OUTPUT FORMAT (强约束 - 不满足 DocStore 拒收)

[A] 文件布局:projects/{pid}/prd/ 下铺设(见上表)

[B] 每个 HTML 页 <!doctype html>...必须含:
    <link rel="stylesheet" href="../assets/shell.css">       (相对路径)
    <body data-page="{相对路径}">...<script src="../assets/shell.js">

[C] 每个章节页根容器:
    <article data-doc="prd" data-rev="N">
      <section data-rev="M" data-origin="{user@iso}">
        <h3>...</h3>
        <p>...</p>
      </section>
      <footer data-changelog>...</footer>
    </article>

[D] 每页至少 4 类(共 8 类)信息:
    Tables · Design · Illustrations · Code · Interaction · Workflows · Spatial · Images
    其中 Interaction 必含(tabs / accordion / 至少 1 个)。

[E] 索引锚点(被 search-index-builder 抽取):
    data-doc · data-rev · data-origin · data-conflict (冲突时)

# MANDATORY WORKFLOW

step 0 · 读取上下文
  ├─ 读 projects/{pid}/prd/ 已存在的所有 .html (若 incremental)
  ├─ 读 projects/{pid}/changelog.ndjson 最后 5 条(知道之前发生了什么)
  └─ 注意: memory section 已在 prompt 顶部注入,不要重复读

step 1 · 写 todolist (调 write_todolist 工具)
  init 模式典型 todolist:
    step-001 解析人类需求,拆 functional/NFR/constraint/out-of-scope
    step-002 检查与历史 memory 是否冲突
    step-003 写 index.html 含 Hero + 8-class ribbon
    step-004 写 product/ 6 章节
    step-005 写 feature/feature-tree.html
    step-006 写 flow/user-flows.html
    step-007 写 prototype/ wireframe
    step-008 调 update_search_index
    step-009 自检 (调 validate_prd_site 工具)
    step-010 写 timeline + 调 record_memory(可选)

step 2..N · 按 todolist 逐项执行
  每项开始: update_todo_item(id, status='doing')
  每项结束: update_todo_item(id, status='done', output_ref='...')
  每项产物落盘后: timeline_append('prd_section_written', ref, summary)

step N+1 · 收尾
  ├─ update_search_index(pid)
  ├─ validate_prd_site(pid) 必须返回 ok=true
  ├─ record_memory 如有非平凡发现(用户偏好/约束)
  └─ echo_diff 输出人话变更摘要

# RULES (硬约束)

1. 永远 append section,不重写历史 section 的 data-rev。
2. 同一 section 多次修订,创建新 section data-rev=N+1,保留旧 section。
3. 矛盾必须显式:发现冲突 → <aside data-conflict>...</aside> + 不继续合并。
4. 任何带 JSON body 的 Bash 命令 → 先写 /tmp/*.json 再 -d @file
   (参见 docs/CLAUDE.md L91-99)。禁止 inline JSON。
5. 不写 SDD/units.json/code/tests/contract。这些属于 Architect/Coder。
6. 不调 call_sub_agent。你不分派任务。
7. 沙箱: 只能写 prd/** + memory/devloop/pm/**。越权写会被拦截。
8. 8 类信息覆盖不是建议是要求。少于 4 类 → validate_prd_site 报错 → 自己 patch。

# OUTPUT VOICE
- 中文写正文,英文写 token/code 标识符。
- 用 Fraunces 风格的克制语气,不要营销腔。
- 不写"完美""强大""革命性"这种字眼。
- 数字胜过形容词:"5 个 Unit" > "几个 Unit"。

# SELF-CHECK (完工前必跑)
1. validate_prd_site(pid) 返回 ok=true
2. search-index.json 包含本次新写的所有页
3. timeline.ndjson 末尾含 'prd_drafted' 事件
4. agent-state/pm.json status=done · last_output_ref 非空
5. 所有 todolist items status=done
不过任一项 → 自己 patch 后再交付。不允许交残品。

2.3 工具集(含签名)

工具签名用途访问边界
Read(path: string)读现有 PRD / changelog / memory项目内只读
Write(path: string, content: string)底层落盘沙箱白名单
bootstrap_prd_site NEW(pid)init 模式专用:建目录、复制 shell.css/js项目内
write_chapter NEW(pid, section, page, html)包装 Write,自动校验 HTML、append 到 SITE 配置prd/** 写
archive_doc NEW(pid, kind, oldHtml)归档旧版到 history/—
diff_html NEW(pid, oldFile, newFile)cheerio section 级 diff项目只读
update_search_index NEW(pid)跑 build-search-index 重建索引—
validate_prd_site NEW(pid) → {ok, errors[]}cheerio + 规则校验整站—
write_todolist NEW(items: TodoItem[])初始化 agent-state/pm-todo.json + SSEagent-state/
update_todo_item NEW(id, status, output_ref?)更新单条 item + 同步 progress_pctagent-state/
update_runtime_state NEW(current_step, total_steps)更新 M-10 AgentRuntimeStateagent-state/
timeline_append NEW(kind, ref, summary, payload?)append M-13 timeline.ndjson—
record_memory NEW(topic, content, relevance)写 M-11 memory/devloop/pm/memory/devloop/pm/
echo_diff NEW(summary, sections_diff)SSE 友好的人类变更摘要—

2.4 禁用的工具 / 行为

  • 不允许 Bash — PM Agent 不需要跑代码。任何 shell 操作走专用工具。
  • 不允许 Edit — PM 写的是新章节,不是细粒度 patch。要修就写新 section。
  • 不允许 call_sub_agent — PM 不分派。
  • 不允许写 sdd/ · units.json · units/* · integration/* — 沙箱拦截。

③ 持久化集成(M-10/11/12/13 接入)

PM Agent 是第一个全面用持久化层的 Agent。下面是它在 ACP session 内的真实操作序列:

LoopController M-11 memory PM Agent M-12 todolist M-13 timeline M-10 runtime ① 读 memory/devloop/pm/*.md (top 5 by relevance) ② memory 注入 system prompt ③ 写 runtime status=running, current_step=0 ④ write_todolist([step-001..step-010]) ⑤ timeline_append('agent_started') ⑥ update_todo_item(step-001, doing) ⑦ ... 写章节 HTML / bootstrap / 调工具 ... ⑧ update_todo_item(step-001, done, output_ref) ⑨ timeline_append('prd_section_written') ⑩ update_runtime_state(current_step=1, total_steps=10) ⑪ ... 重复 ⑥-⑩ 直到所有 step done ... ⑫ record_memory(topic='user-prefs', content) (可选)

3.1 Memory 注入约定

LoopController 在调 PM 前,从 memory/devloop/pm/*.md 取 top 5(按 relevance × recency),拼到 system prompt 顶部 ## Memory section。PM Agent 自己不主动读 memory dir。

3.2 写新 memory 的判断

结束前调 record_memory(topic, content, relevance) 仅在非平凡发现时:

  • 用户表达过明确偏好(如"我喜欢 RESTful 不要 GraphQL")
  • 项目背景里有可复用约束(如"我们公司统一用 better-sqlite3")
  • 不记录单项目的具体功能。那是 PRD 内容,不是 memory。

④ I/O 契约

4.1 输入 schema

const PMInput = Type.Object({
  pid:        Type.String(),
  mode:       Type.Union([Type.Literal('init'), Type.Literal('incremental'), Type.Literal('resume')]),

  // init / incremental 时填
  feedback:   Type.Optional(Type.Object({
    text:   Type.String({ minLength: 1, maxLength: 8000 }),
    origin: Type.String(),               // user id / 'system'
    iso_ts: Type.String(),
  })),

  // resume 时填
  resume_token: Type.Optional(Type.String()),

  // 自动注入
  call_id:     Type.String(),
  company_id:  Type.String(),
});

4.2 输出 schema

const PMOutput = Type.Object({
  ok:          Type.Boolean(),
  mode:        Type.String(),
  prd_rev:     Type.Number(),

  files_written: Type.Array(Type.String()),    // 相对 prd/ 的路径
  files_archived: Type.Array(Type.String()),

  change_summary: Type.String({ maxLength: 500 }),  // 给人看
  diff: Type.Object({
    sections_added:   Type.Array(Type.String()),
    sections_changed: Type.Array(Type.String()),
    sections_removed: Type.Array(Type.String()),
  }),

  has_conflict: Type.Boolean(),
  conflict_detail: Type.Optional(Type.String()),

  memory_recorded: Type.Array(Type.Object({
    topic: Type.String(), relevance: Type.Number()
  })),

  validation: Type.Object({
    ok: Type.Boolean(),
    issues: Type.Array(Type.String()),
  }),

  duration_ms: Type.Number(),
  tokens_used: Type.Number(),
});

⑤ 上下游传递

⬆ 上游

  • LoopController · onHumanFeedback → callAgent(pm, input)
  • L1/L2 自动信号(blocked / l2_exhausted)→ 触发 incremental mode 让人补
  • M-11 Memory · 启动时由 LoopController 注入 system prompt
  • 历史 PRD(incremental mode)· 读 projects/{pid}/prd/**
  • changelog.ndjson · 读最后 5 条了解前因

⬇ 下游

  • Architect Agent · 通过新 prd/ 站点(mode=generate 或 patch)
  • DocStore · 自动归档 + changelog.ndjson append
  • SSE · prd.updated / agent.todolist / agent.step / agent.done
  • Human(冲突时)· SSE human_intervention_needed
  • memory/devloop/pm/ · 新 memory 文件
  • timeline.ndjson · prd_drafted / prd_conflict / agent_done 事件

⑥ 完整执行样例

# ─────────── INPUT ───────────
{
  "pid": "p-731042",
  "mode": "init",
  "feedback": {
    "text": "做一个 TODO 列表 API + 简单 Vue 前端,能新增/列出。",
    "origin": "yibo",
    "iso_ts": "2026-05-13T10:30:00Z"
  },
  "call_id": "call-20260513-1030-a1b2",
  "company_id": "c-lemonv-001"
}

# ─────────── INJECTED MEMORY (top 5) ───────────
## Memory (from prior projects)

### project-conventions  (relevance: 0.94)
- 用户偏好 RESTful API + 完整 OpenAPI 注解
- 数据库统一用 better-sqlite3
- 认证: JWT 放 Authorization header

### code-style  (relevance: 0.82)
- 后端用 Express + service 双层
- 前端 Vue 3 Composition API

# ─────────── PM AGENT 内部执行 ───────────

# step 0 - 读上下文 (incremental 才需要;init 跳)

# step 1 - write_todolist
write_todolist([
  { id:'step-001', description:'解析需求拆四类' },
  { id:'step-002', description:'检查 memory 冲突' },
  { id:'step-003', description:'bootstrap_prd_site(p-731042)' },
  { id:'step-004', description:'写 index.html + reading-guide.html' },
  { id:'step-005', description:'写 product/ 6 章节' },
  { id:'step-006', description:'写 feature/feature-tree.html (2 个特性: TODO API + UI)' },
  { id:'step-007', description:'写 flow/user-flows.html' },
  { id:'step-008', description:'写 prototype/ 1 个 (TODO 列表 UI)' },
  { id:'step-009', description:'update_search_index + validate_prd_site' },
  { id:'step-010', description:'record_memory (本次无新 memory) + echo_diff' },
])

# step 2-9 - 顺次执行 (略)

# step 10 收尾 - validate_prd_site
validate_prd_site("p-731042") // → { ok: true, issues: [] }
update_search_index("p-731042")
timeline_append("prd_drafted", "prd@rev1", "PRD rev 1 完成 · 2 个特性 · 8 章节")
echo_diff("创建 TODO API + Vue UI 项目", {sections_added:[...11 个]})

# ─────────── OUTPUT ───────────
{
  "ok": true,
  "mode": "init",
  "prd_rev": 1,
  "files_written": [
    "prd/index.html",
    "prd/reading-guide.html",
    "prd/assets/shell.css",
    "prd/assets/shell.js",
    "prd/assets/search-index.json",
    "prd/product/background.html",
    "prd/product/personas.html",
    "prd/product/original-req.html",
    "prd/product/scope.html",
    "prd/product/success-metrics.html",
    "prd/product/glossary.html",
    "prd/feature/feature-tree.html",
    "prd/flow/user-flows.html",
    "prd/prototype/proto-todo-list.html"
  ],
  "files_archived": [],
  "change_summary": "创建 TODO API + Vue UI 项目 PRD · 2 个特性 · 11 章节",
  "diff": { "sections_added": ["#todo-create", "#todo-list", "#ui-vue", ...] },
  "has_conflict": false,
  "memory_recorded": [],
  "validation": { "ok": true, "issues": [] },
  "duration_ms": 42_318,
  "tokens_used": 8_421
}

# ─────────── SSE 事件序列 ───────────
agent.started     · pm · call-20260513-1030-a1b2
agent.todolist    · 10 items
agent.step        · step-001 → doing → done (进度 10%)
agent.step        · step-002 → done (进度 20%)
...
prd.updated       · rev 1 · 11 章节
agent.done        · duration 42s · tokens 8.4k

⑦ 助手工具源码

7.1 html-section-patcher.js · section 级精准 patch

// server/services/agent-tools/html-section-patcher.js · ~80 行
const cheerio = require('cheerio');
const fs      = require('fs');

/**
 * Append a new section to a PRD chapter file.
 * 不重写已有 section,只增 data-rev=N+1 的新 section。
 */
function appendSection({ chapterPath, sectionHtml, origin }) {
  const raw = fs.readFileSync(chapterPath, 'utf8');
  const $   = cheerio.load(raw, { decodeEntities: false });

  const article = $('article[data-doc="prd"]');
  if (article.length === 0) throw new Error('No <article data-doc="prd"> found');

  // 找最大 data-rev,新 section 用 +1
  let maxRev = 0;
  article.find('section[data-rev]').each((_, el) => {
    const r = parseInt($(el).attr('data-rev'), 10);
    if (r > maxRev) maxRev = r;
  });

  // 构造新 section
  const $new = cheerio.load('<div>' + sectionHtml + '</div>');
  const sectionEl = $new('section').first();
  if (sectionEl.length === 0) throw new Error('sectionHtml must contain <section>');

  sectionEl.attr('data-rev', String(maxRev + 1));
  sectionEl.attr('data-origin', origin);

  // 校验:section 必含 h3 + p
  if (sectionEl.find('h3').length === 0)
    throw new Error('New section missing <h3>');
  if (sectionEl.find('p').length === 0)
    throw new Error('New section missing <p>');

  // 追加 (在 footer 之前)
  const footer = article.find('footer[data-changelog]');
  if (footer.length) footer.before(sectionEl.toString());
  else article.append(sectionEl.toString());

  // 升 article data-rev
  article.attr('data-rev', String(maxRev + 1));

  fs.writeFileSync(chapterPath, $.html());
  return { new_rev: maxRev + 1, section_id: sectionEl.attr('id') || null };
}

function addConflictAside({ indexPath, text, conflicts, options }) {
  const raw = fs.readFileSync(indexPath, 'utf8');
  const $   = cheerio.load(raw);
  $('aside[data-conflict]').remove();   // 移除旧 conflict
  const aside = `<aside data-conflict data-origin="${origin}">...</aside>`;
  $('article[data-doc="prd"]').prepend(aside);
  fs.writeFileSync(indexPath, $.html());
}

module.exports = { appendSection, addConflictAside };

7.2 prd-validator.js · 整站校验

// server/services/agent-tools/prd-validator.js · ~70 行
const cheerio = require('cheerio');
const fs      = require('fs');
const path    = require('path');

const REQUIRED_CHAPTERS = [
  'index.html', 'reading-guide.html',
  'product/background.html', 'product/personas.html',
  'product/original-req.html', 'product/scope.html',
  'product/success-metrics.html', 'product/glossary.html',
  'feature/feature-tree.html',
  'flow/user-flows.html',
];

const EIGHT_TYPES = {
  Tables:        $ => $('table').length > 0,
  Code:          $ => $('pre.code, pre.tree').length > 0,
  Illustrations: $ => $('svg').length > 0,
  Interaction:   $ => $('.tabs, details.acc, nav.toc').length > 0,
  Spatial:       $ => $('pre.tree, .grid-2, .grid-3, .grid-4').length > 0,
  Design:        $ => $('.card, .agent-hero, .mock').length > 0,
  Images:        $ => $('svg, img, .mock').length > 0,
  Workflows:     $ => $('svg').filter((_,el) =>
                       $(el).html().includes('marker-end')).length > 0,
};

function validatePrdSite(pid) {
  const root = path.resolve(projectsDir, pid, 'prd');
  const issues = [];

  // 1. 必备章节
  for (const f of REQUIRED_CHAPTERS) {
    if (!fs.existsSync(path.join(root, f)))
      issues.push(`missing required chapter: ${f}`);
  }

  // 2. 每章节 cheerio 校验
  const files = walkHtml(root);
  for (const f of files) {
    const $ = cheerio.load(fs.readFileSync(f, 'utf8'));

    // 顶级 article
    const art = $('article[data-doc="prd"]');
    if (art.length === 0)
      issues.push(`${f}: missing <article data-doc="prd">`);

    // section h3 + p
    art.find('section').each((_, sec) => {
      if ($(sec).find('h3').length === 0) issues.push(`${f}: section without h3`);
      if ($(sec).find('p').length === 0)  issues.push(`${f}: section without p`);
    });

    // 8 类信息 ≥ 4
    const covered = Object.entries(EIGHT_TYPES).filter(([_, fn]) => fn($));
    if (covered.length < 4)
      issues.push(`${f}: only ${covered.length}/8 types covered (need ≥4)`);

    // data-rev 单调
    const revs = art.find('section[data-rev]').map((_,s) =>
                  parseInt($(s).attr('data-rev'), 10)).get();
    if (revs.some((v,i) => i > 0 && v < revs[i-1]))
      issues.push(`${f}: data-rev not monotonic`);
  }

  return { ok: issues.length === 0, issues };
}

module.exports = { validatePrdSite };

7.3 prd-site-init.js · bootstrap_prd_site 工具实现

// server/services/agent-tools/prd-site-init.js · ~50 行
const fs   = require('fs');
const path = require('path');

const TEMPLATE_DIR = path.resolve(__dirname, '../../../docs/prd/newfunction/assets');

function bootstrapPrdSite(pid) {
  const root = path.resolve(projectsDir, pid, 'prd');
  const assetsDir = path.join(root, 'assets');

  // 建目录
  fs.mkdirSync(assetsDir,                  { recursive: true });
  fs.mkdirSync(path.join(root, 'product'),   { recursive: true });
  fs.mkdirSync(path.join(root, 'feature'),   { recursive: true });
  fs.mkdirSync(path.join(root, 'flow'),      { recursive: true });
  fs.mkdirSync(path.join(root, 'prototype'), { recursive: true });

  // 复制 shell.css (一次,共享样式)
  fs.copyFileSync(path.join(TEMPLATE_DIR, 'shell.css'),
                   path.join(assetsDir,    'shell.css'));

  // 复制 shell.js 模板 + 注入空 SITE
  const jsTpl = fs.readFileSync(path.join(TEMPLATE_DIR, 'shell.js'), 'utf8');
  // shell.js 内部 SITE 来自 PM Agent 维护的 prd-site-config.json
  fs.writeFileSync(path.join(assetsDir, 'shell.js'), jsTpl);

  // 空 search-index 占位
  fs.writeFileSync(path.join(assetsDir, 'search-index.json'),
                    JSON.stringify({ version:'1', pages:0, entries:[] }));

  return { root, files_created: 3 };
}

module.exports = { bootstrapPrdSite };

⑧ 自检 Checklist · 完工前必跑

#检查项命令 / 工具通过判定
1整站校验validate_prd_site(pid)ok=true, issues=[]
2搜索索引同步update_search_index(pid)pages 字段含所有新写文件
3必备 11 章节都存在fs.existsSync 循环全 true
4每页 ≥4 类信息validatePrdSite 内置无 "X/8 types" 错误
5每个 section 含 h3 + pvalidatePrdSite 内置无 "section without h3/p"
6data-rev 单调递增validatePrdSite 内置无 "not monotonic"
7todolist 全 doneread agent-state/pm-todo.json所有 item.status='done'
8timeline 含 prd_draftedtail timeline.ndjson最后 5 条之一 kind=='prd_drafted'
9agent-state.pm.json 终态read M-10status='done', last_output_ref 非空
10无 Bash inline JSON 使用grep coder.log? (PM 无 log,仅审查 prompt 自身)—
11changelog.ndjson append由 DocStore 自动新 entry kind='prd_rev'

任一不过 → 不输出 ok=true。自己 patch 后重跑自检,最多 retry 2 次内必过。第 3 次仍不过 → 报 validation_failed 上报 L3。

⑨ 失败模式与降级

场景检测降级
需求过模糊(<20 字、无动词)token 数 + 词性快速判断不写 PRD,直接 ask_clarification → SSE
新需求与历史矛盾读历史 + 语义比对插 <aside data-conflict>,不合并,触发 human
沙箱写越权Write 工具拒绝Coder 收到 error,换路径
validate_prd_site 失败self-check 步骤读 issues 列表 → patch 后重跑(≤ 2 次)
search-index 构建失败update_search_index 报错跳过索引但仍输出 PRD;标记 partial=true
memory 文件读失败I/O 错误记 warning,继续(视同空 memory)
todolist 中途崩session 异常P-8 处理:写 paused → 等 resume 决策
cheerio 解析非法 HTMLhtml-section-patcher 报错不写盘,retry 一次 PM 重生这段

⑩ 与其他 Agent 的握手摘要

对方PM 给的PM 收的
Humanconflict_aside / clarification requestrequirement text · sign-off · abandon
Architect完整 prd/ 站点 + changelog entry(无;Architect 自己读 prd/)
DashboardSSE prd.updated · agent.* 系列(无)
LoopControllerPMOutput JSONPMInput JSON · resume_token
DocStore(无;通过工具间接)校验通过/失败回执
memory/devloop/pm/新 .md 文件启动时被 LoopController 注入到 prompt

⑪ 「测试 PM Agent 是否真的能跑」的实战命令

本规范的验证终局:用以下命令直接驱动 PM Agent,看能否 30 秒内产出符合所有约束的 PRD 站点:

# 1. 注册 Agent 模板
#    把本页 §2.2 的 System Prompt 落到:
#    system/templates/devloop-pm.md
#    并在 system/templates/_registry.json 加 'devloop-pm' 条目

# 2. 启动 server (P1 阶段最小后端,只挂 PM 路径)
PORT=3010 node server/index.js

# 3. 直接调 PM (绕过 LoopController)
#    准备 /tmp/pm-test.json (单行紧凑,按 CLAUDE.md 警告)
echo '{"pid":"p-test001","mode":"init","feedback":{"text":"做一个 TODO API 加 Vue 前端","origin":"yibo","iso_ts":"2026-05-13T10:00:00Z"},"call_id":"call-test","company_id":"c-test"}' > /tmp/pm-test.json

curl -X POST http://localhost:3010/api/devloop/_test/run-agent/pm \
  -H "Content-Type: application/json" \
  -d @/tmp/pm-test.json

# 4. 期望:30-60 秒内
#    data/companies/c-test/projects/p-test001/prd/ 下铺好 11 章节
#    + assets/{shell.css, shell.js, search-index.json}
#    + history/ 空 (init 首次,无归档)

# 5. 校验
node -e "console.log(require('./server/services/agent-tools/prd-validator').validatePrdSite('p-test001'))"
# 期望: { ok: true, issues: [] }

# 6. 浏览器打开
#    file:///data/companies/c-test/projects/p-test001/prd/index.html
#    左侧应见 3 级菜单,Ctrl+K 可搜索
这一组命令跑通 = PM Agent skill 验证通过。之后 Architect Agent 就能从这个 prd/ 站点反向派生 SDD。