---
name: harness-plan
description: "将需求转化为设计文档+实施计划+完整测试场景表，必须在编码前完成。仅当用户显式调用 /harness-plan 或明确要求进入 Harness 规划阶段时使用；不得因用户描述需求就自动触发。"
---
<!-- generated by harness_deploy.py; core=453b5642e5b9134d; agent=codebuddy; do not edit -->
# harness-plan — 需求规划

## Purpose

确定 change 后必须运行 `python <skills-root>/scripts/harness_gate.py classify --change <id> --stage plan --json`，并把脚本返回的 risk tier、默认阶段、条件阶段和必需验证写入计划；不得凭模型印象另建风险分级。

需求 → 设计文档 → 任务拆分 → 测试场景表（编码/测试唯一真相源）。项目已绑定远端平台时，阶段 1 只执行下表中的远端查询命令；远端不可用则记录 issue 并继续，不做本地回退。

## When to Use

仅当用户显式调用 `/harness-plan`（或明确说"用 harness 规划这个需求"）时执行。用户只是描述需求、提问或讨论方案时，**不得**自动进入本 skill。

**单阶段原则**：本 skill 只负责 plan 阶段。plan finalize + verify 完成后必须停止并交还用户，仅提示 `plannedPhases` 中的真实下一阶段；禁止自动调用其他阶段 skill。

## 统一读取协议

0. **脚本在 `<skills-root>/scripts/` 共享，不在每个 skill 子目录下** — 实际形态是 `.codebuddy/skills/scripts/harness_*.py`（`.claude`/`.cursor`/`.codex` 同理），**没有** `.../skills/harness-<phase>/scripts/`。plan、run/test、archive 三份执行日志里都先猜成后者、报 `No such file` 再靠 Search 找回来；照第一种写法直接用
1. **`.harness/changes/<change-name>/` 是唯一真相源** — 所有输入从该目录读取，产物写入对应子目录
2. **change-name 优先从 frontmatter 读取** — `plans/*-design.md`、`spec/*-design.md`、`plans/*-plan.md` 的 YAML `change-name`
3. **frontmatter 缺失时兼容旧格式** — 从路径推断，标记 `🟡 legacy-plan`，不失败
4. **design** — 设计真相源按序取第一个存在的：`plans/<change>-design.md`（v2 发布产物，哈希绑定）→ `spec/<change>-design.md`（legacy 手写）。两份**同时存在**时以 `plans/` 为准，并记 `🟡 WARN 设计文档双份`——v2 发布的那份才受完整性门禁保护，读手写的那份等于绕过校验
5. **plan** — 任务真相源：`plans/<change>-plan.md`
6. **implementation-detail** — 自适应执行参考；legacy 缺失 🟡WARN，不阻断
7. **test-scenarios** — 测试真相源：`plans/<change>-test-scenarios.md`
8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --json`。首次 Plan 捕获时，脚本把当时的 Git HEAD 写入不可变 `changeBase`；后续阶段只刷新 `git.head` 与各段指纹，不得用当前 HEAD 覆盖 `changeBase`。只有迁移旧 change 且已能证明真实计划起点时，才在首次补录时传 `--base <baseCommit>`。`changedSegments=[]` 时复用已有 profile/rules/map/change/code 指纹，不再重复跑全量 `harness-sync` 或代码库扫描。`knowledge` 段仅为兼容空段，知识始终按需远端查询；快照不能替代源码读取和验证门禁。

状态目录分层：新路径优先，旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
> 片段：read-protocol · plan 额外写 `meta/worktree.json`、`meta/change-context.json`

## Worktree 门禁

读取 `meta/worktree.json`（兼容旧路径 `worktree.json`）：

| 条件 | 行为 |
|------|------|
| `requested=false` | 主目录执行 |
| `requested=true` + path 存在 | 切换 worktree 执行 |
| `requested=true` + path 不存在 | **必须创建**（run）或 **停止**（test/review/submit） |

**严禁** `requested=true` 时静默回主目录。创建失败 → 停止或 blocking user confirmation 降级（须 🟡WARN + 用户确认）。

**状态与代码分离**：代码/编译/测试在 `worktreeRoot`；`stateDir`（logs/events/ledger/reports）写回 `.harness/changes/<change-name>/`。

创建命令与 JSON 模板 → 各 skill `reference.md`（plan 写决策，run 创建/更新）。
> 片段：worktree-gate · plan 在**设计审批包**写入 `worktree.json`（模板 → `reference.md`）

## 原生规划协议

内化为 `protocols.md`：`clarification-protocol`、`decision-grilling-protocol`、`implementation-planning-protocol`。不运行时调用 Superpowers/grill-me。

<!-- @section-id plan.delegate -->
## 执行路由（inline 优先）

- **阶段 3 探索默认 inline**：主会话直接使用 CodeGraph/Read，简单修复和常规跨文件变更不得为“隔离上下文”额外启动 agent。
- **仅高复杂度探索考虑委派**：涉及多个独立模块、陌生大型代码库或可并行的独立调查时，才执行一次 `python <skills-root>/scripts/harness_preflight.py check-agents --skills-root <skills-root> --agent harness-explorer --json`。只有 `executionMode=delegated` 才委派；`executionMode=inline` 是正常路径，静默继续；`unavailable` 只记录安装问题后 inline。
- **阶段 7.5 evaluator**：仅 `--adversarial` 或 auth/支付/迁移/并发等高风险规划启用；需要固定 agent 时才预检。无论预检 inline、spawn 失败、空返回或只有元数据，都立即由主会话完成同一对抗检查，`fallbackPolicy=inline-no-retry`。
- 日志记录 `executionMode=inline|delegated`；只有真实定义损坏或 spawn 失败才记 issue。正常 inline 不得显示“subagent 不可用”告警。

## Workflow 概要

| 阶段 | 动作 |
|------|------|
| 0 | git status；脏工作区 → baseline 隔离 + `decision`，不询问。环境体检并入 0.5 的引导命令，不再单独跑 doctor |
| 0.5 | 先定英文 `change-name`（kebab-case，`^[a-z0-9]+(-[a-z0-9]+)*$`）与一次性中文展示标题（建议 6～24 个可见字符，保留必要产品名），然后**一条命令完成引导**：<br>`python <skills-root>/scripts/harness_context.py bootstrap-plan --project . --change <cn> --executor <tool> --title "<中文标题>" --json`<br>它按序做完 doctor → 建 change 骨架 → prepare → state capture（首次把当时 HEAD 固定为不可变 `changeBase`）→ classify（落 `meta/gate-policy.json`）→ 生成合规 `plan_<uuid>` run-id 并追加 `phase.start`，返回紧凑摘要：`runId`/`attempt`/`tier`/`defaultPhases`/`conditionalPhases`/`requiredValidations`/`changeBase`/`head`/`executionRoot`。**重跑复用同一 run-id、不重复写 `phase.start`**（换 run-id 会让 finalize 按生命周期身份 fail-closed）。finalizer 必须复用该 `runId`/`attempt`。返回里的 `legacyBootstrap: true` 只是「本次引导前还没有任何交接凭证」的标记（首个阶段的正常状态），不是项目结构需要迁移 |
| 0.5b | 仅在引导失败需要单步排查时用等价分解——**每条的参数都是必填，少一个就是白跑一轮**：<br>`harness_runtime.py doctor --project . --change-dir ".harness/changes/<cn>" --agent <claude-code\|codebuddy\|codex\|cursor> --json`<br>`harness_context.py prepare --project . --change <cn> --phase plan --executor <tool> --title "<中文标题>" --json`<br>`harness_state.py capture --project . --change-dir ".harness/changes/<cn>" --json`<br>`harness_gate.py classify --change <cn> --stage plan --json`<br>`harness_events.py append --change-dir ".harness/changes/<cn>" --phase plan --type phase.start --run-id plan_<uuid> --attempt 1`<br>⚠️ `--change-dir` 一律是 `.harness/changes/<cn>`（状态目录），**不是** prepare 返回的 `executionRoot`（代码执行根，无 worktree 时等于项目根）——填错会把 gate-policy/events 写到项目根 |
| 0.6 | 用引导返回的 `tier`/`defaultPhases`/`requiredValidations` 生成 `plannedPhases`，向用户用中文说明可选阶段；确认后运行 `harness_context.py configure-plan --project . --change <cn> --phases "plan,execute,...,archive" --operator <tool> --reason "<中文原因>" --json`。无 Git 或不需要提交时不得加入 `submit`；快速迭代默认 `plan,execute,archive`。省略项由脚本写入 `skippedPhases`，不得伪造阶段事件。<br>⚠️ 无风险信号时 classify 默认 `standard`（`plan,execute,submit,archive` + compile/unitTest/unitTestFull），**不再默认 full**。需要 review 阶段与 apiTest 的高风险变更，在计划文档里写「风险等级: full」或用 `classify --tier-override full` 显式升档 |
| 1 | **条件触发，不每次必查**：需求涉及历史取舍/设计原因、兼容或升级边界、疑似与既有变更重复，或用户要求延续既有方案时，直接执行一次 `npx hunter-harness knowledge query "<用户需求原文>" --limit 10 --json`；全新独立需求跳过本步直接进阶段 2（2026-09 审查实测：无条件查询的自然语言原文 8/8 零命中，强制执行只有成本没有收益）。这是唯一执行入口，不扫描技能目录、不查找其他脚本；失败记 `issue` 并继续，不建立本地索引或离线回退。**查询结果（含 0 条）必须落事件**：`harness_events.py append --phase plan --type command --command "knowledge query" --note "count=<n>" --json`（`command` 类型事件只接受 `command`/`exit_code`/`duration_ms`/`note` 字段，多给 `--name`/`--message` 会被 `EVENT_FIELD_NOT_ALLOWED` 拒绝；query 回执的 `receipt.index_generation` 反映索引代数；0 条时可先跑 `npx hunter-harness knowledge status --json`（0.4.11+）区分「job 未跑 / 失败 / 结果为空」） |
| 2 | 歧义优先检查 + 复杂度分级；先确认会改变实现方向的语义歧义 |
| 3 | 按复杂度执行有预算的代码探索；简单修复不得扩散到无关模块 |
| 4 | **设计审批包** blocking user confirmation；确认事件早于 approved 设计文档和 `meta/worktree.json` |
| 5–6 | **v2（默认）**：任务与场景只沉淀进 `meta/plan-evidence-input.json`（自然输入，字段定稿时点见 `reference.md` 阶段 8 v2 表）；`plans/*.md` 四份由 finalize 派生，**不得手写**——手写的会被派生渲染覆盖，只是白写。|
| 7.5 | 仅 `--adversarial` 对抗评审 |
| 8 | **v2 路径（新 change 优先）**：优先一条命令收口——`npx hunter-harness plan publish --input <meta/plan-evidence-input.json>`（内部完成 evidence-pack → 基线/attempt 自动记账 → finalize；评审收据因重建过期且 findings 未变时加 `--renew-review` 自动续签）。assurance/高风险计划首次发布前先跑 `plan review-record` 把收据写回 pack 并把 pack 的 `adversarial_review` 拷回输入文件（重跑时透传）。三步分解链 `plan evidence-pack` → `plan review-record` → `plan finalize` 保持可用（契约见 `reference.md` 阶段 8 v2 路径）。`risk_signals`/`machine.capabilities`/`context.attempt`/`expected_baseline` 都可省略（推荐）：evidence-pack 按 affected_paths 与 git status 推断信号与能力、按发布历史派生 attempt 与基线，推导值在 `derived` 块回显；capabilities 由命令探测真实仓库状态，阶段 0.6 的 plannedPhases 会被读取。发布成功后把 `harness_context.py close` 的 `--to-phase` 取 `plannedPhases` 中 plan 的真实后继，不得写死。原子发布、派生清单计数对账、完整生命周期、render → `checklist.md` |

change-name 范围变更 → 提示重命名或记 🟡WARN（→ `reference.md`）

## P0 执行可信度规则

- 命令结果不得靠猜测；普通 Bash 被拒 → 立即改用等价 PowerShell 重试一次
- 仅 PowerShell 成功且有明确证据（构建/git/测试输出、文件存在、exit 0）时可标 ✅OK；否则 ❌FAIL 或 🟡WARN
- 禁止把 hook 拒绝、静态验证、无输出、用户跳过说成成功 → 详见 [[../protocols/powershell-protocol.md|powershell-protocol]]、[[../protocols/evidence-based-reporting-protocol.md|evidence-based-reporting-protocol]]

## 生成内容语言约定

- sync/ingest 等生成的文档、规则、知识条目、架构说明一律**优先使用中文**撰写（标识符、命令、代码、API 字段名保持原文）
- 面向平台展示的标题/摘要/正文默认中文；仅当用户明确要求或目标系统强制时才用英文
> 片段：p0-trust

## 关键规则

| 规则 | 要点 |
|------|------|
| 产物路径 | 只写 `.harness/changes/<cn>/`；禁止 superpowers 输入 |
| 设计真相源 | **v2** = `plans/<cn>-design.md`（finalize 派生、哈希绑定、八 target 之一）；**legacy** = `spec/<cn>-design.md`。同一 change 只有一份设计权威，禁止两处并存导致漂移；下游读取顺序见 `shared/read-protocol.md` |
| Change 标题 | 首次 Plan 同时确定英文 `change-name` 与中文展示标题；英文名保持目录和机器标识不变，中文标题由 `prepare --title` 持久化，后续阶段只复用、不重新生成 |
| 阶段计划 | Plan 必须持久化 `plannedPhases`；固定从 plan 开始、以 archive 结束。Execute、Review、Submit 可按项目策略省略；高风险必需项只能转为“提前结束且不可发布”，不得伪装通过 |
| 产品边界 | `ownership.productPaths` 必须覆盖计划会修改的源文件、测试文件和构建入口；只写目录前缀或精确文件，禁止 `**` 通配。finalize 前对照任务表补齐，避免归档阶段才发现边界缺口 |
| 空目录 | 不得为“预留目录”生成 `.gitkeep`；只有产品明确需要跟踪空目录时才能创建，并在计划中说明业务原因 |
| 设计审批包 | 一次 blocking user confirmation 含 worktree（读 `harness.json` `defaultWorktree`）。必须同时展示 **in_scope 与 out_of_scope 两个列表**——只展示"做什么"会让范围误判活到发布之后，代价是整份计划 republish |
| 引用即追问 | 需求引用了外部设计文档章节（贴段落、指 `### Bn`、说"之前设计的时候如…"）时，阶段 2 必须确认该章节是否纳入本次范围，落到 in_scope 或 out_of_scope；引用 ≠ 纳入，也 ≠ 排除 |
| 阶段 8 | 唯一入口：`hunter-harness plan publish`（编排收口，内部走 evidence-pack → finalize）或分解链 `plan evidence-pack` → `plan finalize`（证据包 → 八 target + journal committed + plan-events.ndjson）。assurance/高风险计划先跑 `plan review-record` 把对抗评审收据写回证据包（`PLAN_REVIEW_REQUIRED` 报错里的 `expected_review.input_hash` 是公开契约），不得手拼哈希；重建后 findings 未变时 `plan review-record --renew` 续签即可，不重跑评审。失败不得手工补终态；历史 legacy 收据只读（`harness_plan_finalize.py verify`） |
| 发布路径 | **只有 v2**（`plan evidence-pack` → `plan finalize`）；legacy staging/finalize/republish 已于 0.3.0 移除，历史 legacy 收据保持可读（`harness_plan_finalize.py verify`）。v2 的场景契约已补齐 `priority`/`owner_phase` 与可执行测试三元，派生的 `meta/scenario-manifest.json` 能被 execute 门禁与 `harness_ledger.py record` 解包消费，证据闭环走得通。P0/P1 场景必须给全可执行三元（要么整组给全、要么整组省略；省略则 manifest 降为 schemaVersion 1，关门绑不上结构化执行收据） |
| 发布后改产物 | 改 `meta/plan-evidence-input.json` 后重跑 `plan publish`——`expected_baseline`（上次 manifest 哈希/generation）与 `context.attempt` 递增都由命令从 journal/plan-events 自动记账，不再手填；收据过期加 `--renew-review`。分解链下重跑 `plan evidence-pack` 同样自动记账（输入省略 attempt/baseline 即推荐写法，推导值在 `derived` 块回显）。**绝不手改 `meta/scenario-manifest.json`**（派生物，手改必致 `ARTIFACT_HASH_DRIFT`）→ `reference.md`「发布后修订计划」 |
| v2 输入骨架 | 不要猜 `plan-evidence-input.json` 结构：`npx hunter-harness plan evidence-pack --print-template` 给出**一个字不改就能通过 evidence-pack** 的骨架（结构合法；`change_key`/`run_id` 仍须换成真实身份才能 finalize），逐项替换即可。结构不符时命令返回 `PLAN_EVIDENCE_INPUT_INVALID`，带 `field_path` 与 `problems[]`（缺失/多余键、枚举取值）——按 problems 改完重跑；**不得**为找契约去反编译 `dist/bin.js` 或翻 npx 缓存。带不了 `<>` 的占位字段与硬约束清单 → `reference.md` 阶段 8 v2 路径 |
| Plan 结束 | **禁止**询问执行模式；只提示 `/harness-execute` |
| 知识查询 | 阶段 1 失败不得假装已读历史，也不得改用本地索引或其他执行入口 |
| 读命令输出 | `--json` 输出是完整结构，**不得**接 `\| tail -N` / `\| head -N` 后据此判断——截断的 JSON 解析不了，只会逼出一次补读。输出太长时读命令写下的文件（classify → `meta/gate-policy.json`），或用 `bootstrap-plan` 的紧凑摘要 |
| 歧义优先检查 | 否定、对比、动作对象或范围存在多种合理解释时，最小取证后先给推荐理解并一次一问；确认前不深挖错误方向 |
| 简单修复探索预算 | 预计不超过 2 个代码文件、且不涉及认证/安全/迁移/并发/API 契约重设时，最多 1 次合并 CodeGraph 查询 + 1 次定向补查、1 个用户澄清问题；无关发现只记非阻断说明 |
| 精简产物 | 简单修复只保留实现所需的设计、任务、边界和测试；禁止在 spec/plan/detail/scenarios 中重复同一背景和结论 |
| 测试执行成本 | 场景表必须设计快速反馈层级、预计时长、资源预算、超时和可复用证据；默认先跑受影响测试，再跑模块门禁，候选验证只复用身份一致的全量证据 |
| state snapshot | Plan 首次 capture 固定不可变 `changeBase`；后续刷新只更新 HEAD 和各段指纹，不得把当前 HEAD 写回为基线。读取 `state-snapshot.json` 了解 project/worktree root、HEAD/base、profile/rules/map/knowledge 指纹；失效由脚本刷新，**不得仅凭缓存跳过代码探索或验证门禁**（design §3.6） |
| 协议 | sensitive-info / evidence-based-reporting / state-layout |

产出物表、frontmatter、legacy 兼容、结束输出模板 → `reference.md`

## 渐进披露

- **Read `checklist.md`** — 阶段检查与覆盖表
- **Read `protocols.md`** — 阶段 4/6 原生协议
- **Read `reference.md`** — 模板与 worktree JSON

## 交互白名单

1. **设计审批包**（阶段 4）：设计 + **范围（做什么/不做什么）** + 场景表 + worktree + change-name
2. **decision-grilling**（阶段 2/3 澄清）：语义歧义或高风险业务裁决（一次一问）

## 执行日志

`events.ndjson` 为唯一事实源（schema_version 3，兼容读取 v1/v2；`note` 承载人类可读摘要）；`logs/execution-log.md` 由 `harness_events.py` 渲染，**禁止用 Write/Edit 直接维护**。直接修改的内容会在 `phase.end` 或 finalize 时被完整重建覆盖，属于数据丢失；需要保留的详情必须进入事件 `note`。结构 → [[../protocols/report-pipeline-protocol.md|report-pipeline-protocol]]

**`phase.start` 由 `harness_gate.py begin` 写，不要再手工追加一次。** 两条同 `run-id` 的
`phase.start` 会让 `plan finalize` 以 `PHASE_START_DUPLICATE` 卡死，而且手工那次会先触发
auto-seal、把正在开始的 attempt 封成 `RECOVERED`。要补触发指令说明就带 `--note` 跑 `gate begin`。
（重复追加现已按 `(phase, run-id)` 判为幂等 no-op，但依赖它不如不写。）

```powershell
# 阶段开始：gate begin 负责，note 在这里给
python <skills-root>/scripts/harness_gate.py begin --change-dir ".harness/changes/<change-name>" --phase <phase> --note "<触发指令>"
# 阶段中的其他事件才用 append
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type <command|issue|verification> --run-id <phase-run-id> --note "<摘要>"
```

> **脚本接线**：`harness_events.py append`；`harness_archive.py finalize`；`harness_preflight.py check`；`harness_ledger.py can-reuse`；`harness_service.py ensure/stop`（须 `--files`/`serviceStart.inputFiles`）。JSON 输出按 D13 护栏解读。

> **Task 4 §6.1 写入契约**：普通 `append` = 加锁 -> 追加一行 -> fsync -> 解锁，**不 load 历史、不渲染**（O(1)，跨进程锁 `events.ndjson.lock`，UUID 用完整 `uuid4().hex` 无需去重扫描）。仅 `--type phase.end` append 在追加成功后渲染一次 `execution-log.md`；显式 `harness_events.py render` 随时从完整 events 重建；`harness_archive.py finalize` 在 collect 前强制 render 一次。高频 command append 期间 log 可能滞后，phase 边界保持最新。

每个阶段的 `phase.start` 与对应 `phase.end` 必须复用同一 `--run-id` / `--attempt`；阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`。重试同一阶段时生成新的 run-id 并增加 `--attempt <n>`，不得覆盖或伪装成一次执行。**`attempt` 按 phase 全局递增，不是按 run-id**：一个 run-id 只绑定一个 attempt，重试必须「新 run-id ＋ 下一个 attempt」两者同时换，只换其一会撞 `EVENT_ATTEMPT_CONFLICT` 或 `PHASE_ALREADY_CLOSED`。已发布 plan 的修订通过重跑 `plan evidence-pack` + `plan finalize` 分配新 attempt（`harness_plan_finalize.py republish` 已于 0.3.0 移除）。

阶段跑得久（plan/run 常见）时用 `harness_context.py renew --project . --change <cn> --executor <tool>` 续租；租约到期本身不再阻断 `close`（同一 owner 的过期租约不构成冲突，收据里记 `leaseLapsed`），但续租能让 `view` 的状态如实反映在跑。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`，并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`；也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
> 片段：logging · phase=`plan`
