---
name: harness-execute
description: "执行阶段：按变更簇执行 TDD 编码循环（RED→GREEN→REFACTOR→编译验证），随后完成单元/接口/数据兼容验证并输出测试报告。仅当用户显式调用 /harness-execute 时使用；不得因用户提到编码/实现/测试就自动触发，也不得被其他阶段 skill 自动接续。"
---
<!-- generated by harness_deploy.py; core=453b5642e5b9134d; agent=codebuddy; do not edit -->
# harness-execute — 执行（编码 + 验证）

> 2026-08 阶段合并：原 `run`（编码）与 `test`（验证）合并为单一 `execute` 阶段；
> 0.4.9（workflow-harness）起 `/harness-run`、`/harness-test` 入口移除，统一由本 skill 承担。

## Purpose

基于 plan + test-scenarios，按**变更簇**执行 TDD（RED→GREEN→REFACTOR→构建验证），随后在同一阶段内完成单元测试、接口测试与数据兼容验证，写入 verification-ledger 并输出测试报告。负责 worktree 创建/切换。

## When to Use

仅当用户显式调用 `/harness-execute` 时执行；plan 完成后**不自动**进入本阶段。参数：`--subagent` 强制 Subagent-Driven；`--inline` 等同默认；`--fixback` 读最新 review fixback。**默认 Inline，不询问执行模式**。

**单阶段原则**：execute 关门后必须停止并交还用户，仅提示 `plannedPhases` 中的真实下一阶段；禁止自动开始其他阶段。

## 前置条件

- 设计文档（`plans/*-design.md` 优先，回退 `spec/*-design.md`）与 `plans/*-plan.md`（含 frontmatter）存在且已审批
- `plans/*-test-scenarios.md` 存在（测试真相源）
- 读 `meta/worktree.json`：`requested=true` 时 worktree 须存在或 execute 负责创建；worktree 已创建则在 worktree 中执行测试，不得静默回到主目录

## 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 · 创建命令 → `coding-reference.md`

## 统一读取协议

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

## Workflow 概要

0. 加载上下文：**推荐路径（批次 2 WI-2）**——一条命令 `harness_context.py bootstrap-execute --project . --change <id> --executor <tool> --json`（内部完成 prepare → context begin 交接校验 → gate begin 含测试基线 guard；幂等：重跑复用同一 runId；foundation-gate pending 时需带 `--task <n>`）。返回 `EXECUTE_BOOTSTRAPPED` 即进入步骤 0.5；返回 `BOOTSTRAP_EXECUTE_*_FAILED` 时按信封内 `recoveryAction` 排障（原三连命令路径保留为出口）：`harness_context.py prepare --project . --change <id> --phase execute --executor <tool> --json` → `harness_context.py begin --project . --change <id> --phase execute --executor <tool> --json` → `harness_gate.py begin --phase execute --change <id>`。旧名 `--phase run` / `--phase test` 仍被接受并归一为 execute。<br>Git Bash 注意：`--note` 等字符串参数**不要以 `/` 开头**（MSYS 会把它当路径改写成 `C:/Program Files/Git/...`）；以 `/` 开头时先 `MSYS_NO_PATHCONV=1`。<br>**交接凭证不必手工补**：v2 计划的 `plan finalize` 不写 context 事务，`prepare` 会在检测到 committed 的 `meta/publication-journals/*.json` 时自动补录 `plan → execute` 凭证（凭证带 `bootstrapSource=plan_publication_journal` 留痕）。仍报 `HANDOFF_REQUIRED`/`LEGACY_BOOTSTRAP_REQUIRED` 说明**没有**这份发布证据——回到 plan 阶段确认发布是否真的完成，**不得**自己拼 `classify + configure-plan + close` 造凭证。<br>**Fixback** 不得拼装底层步骤，必须只调用一次 `harness_fixback.py launch-review --project . --change <id> --change-dir <change-dir> --executor <tool> --skills-root <skills-root> --product-identity <当前产品身份> --json`；返回 `FIXBACK_NOTHING_TO_APPLY` 时直接报告并停止。禁止手写 `events.ndjson` / `phase.end`。

**Fixback 步骤序列**（0.4.11 起文档化；每步失败都有 recoveryAction）：launch-review 启动批次 → 托管 RED 会话（`harness_runtime.py run-start --verification fixback-red --product-identity <批次 baseProductIdentity，缺省自动注入> -- ...`）→ `harness_fixback.py evidence-template` 生成证据 + `register-evidence` 注册 → 修复 → GREEN 会话同法 → 逐 issue `resolve-issue` → `harness_review.py write-dispositions` 处置 → affected 验证会话 + review 收据 → `harness_fixback.py close --change-dir <change-dir> --batch-id <批次> --final-product-identity <身份>`（批次关闭会同步把 fixback-session 置 CLOSED）→ `harness_gate.py close --phase execute`（fixback 回环自动派生回 submit）。Windows 上命令名写 `mvn` 会 WinError 2——0.4.11 起 launcher 自动解析 .cmd/.exe，更早版本请写全路径。
0.5. **测试基础设施探测**与**命令执行模式 preflight** → `testing-reference.md`「命令执行模式 preflight」；测试基线已由 gate begin 内部建立，不得再次执行 guard begin
1. **变更簇 TDD** — `protocols.md` `run-tdd-protocol`；批量 RED/GREEN；按需 `change-cluster-review-protocol`
2. 构建验证 + 写 ledger（禁止 Write/Edit `verification-ledger.json`）。**推荐路径（批次 2 WI-1）**：`exec` 带 `--result-receipt <change-dir>/evidence/receipts/<verification>.json` 落结果收据，再 `harness_ledger.py record-from-receipt --change-dir <dir> --receipt <收据> --verification <kind>` 消费——status/command/exitCode/durationMs/evidence 全部来自真实执行，无需手工转录；定向验证加 `--files "<变更源,测试文件>"`。手工 `record` 仅用于收据校验失败（`RECEIPT_INVALID`）的回退与无收据的受控例外，并在事件 note 说明原因。profile 缺失或陈旧时先 `harness_preflight.py detect --project . --json`；`--profile-input <key>` 从同一 target 推导 scope、coverage、规范命令与输入闭包
3. **验证执行**：单元测试可复用则跳过（`harness_ledger.py can-reuse`）；接口测试**强制批量执行器**一次跑完全部场景；数据兼容验证按场景表执行 → `testing-reference.md`
4. **场景覆盖检查**（场景表映射，禁止用用例数冒充场景数）
5. **关门检查**（10 项）→ 只执行一次 `harness_gate.py close`；`--to-phase` 可省略——计划后继唯一（排除 fixback 自环）时自动派生并交接（输出含 `derivedToPhase`）。仅 fixback 回环时显式传 `--to-phase execute`；fixback 批次的 execute 关门自动派生 review 的后继（submit），不会错误地再回一轮 review（0.4.11 起）。该命令内部关闭 test guard、写 `phase.end`、释放租约、写 handoff 并补传事件；不得再单独调用 test-guard/context close。失败时按结构化 `recoveryAction` 原样重试，已完成步骤幂等复用。

**阶段归属规则**：只用 `ownerPhase=execute` 的任务和场景判定本阶段结果。`ownerPhase=review`/`submit` 的场景按计划留给后续阶段属于正常移交，出现在关门返回值的 `deferred` 里，不阻断 execute。合并前 `ownerPhase=run`/`test` 的旧清单经别名表归一为 execute，在 execute 关门时一并到期。

> 关门脚本与本规则一致：`harness_gate.py close --phase execute` 的 C9 场景覆盖要求全部 `ownerPhase=execute` 的必需场景有通过 receipt——包括本属原 run 半段的编码验证与原 test 半段的接口/兼容验证，这正是两阶段合并的语义。

**构建/测试执行入口**：所有构建与测试命令（mvn / gradle / npm test / pytest 等）必须经 `harness_test_runner.py exec` 发起，**禁止**用 `powershell.exe -Command` 直接裸跑。要写 ledger 的验证加 `--result-receipt`（收据落 change 目录 `evidence/receipts/`，随归档 manifest 覆盖；timeout 也写——失败是验证证据）：

```text
python <skills-root>/scripts/harness_test_runner.py exec --project . --timeout-seconds <预估上限> --result-receipt "<change-dir>/evidence/receipts/<verification>.json" -- <构建命令及参数>
```

资源档位是硬合同（`safe` 默认；`system`/`full` 需用户明确授权或 `--confirm-resource-intensive`）；Python `unittest` 必须逐模块隔离模式（`harness_test_runner.py unittest --profile safe --tests-dir <目录>`），并发上限由 runner 注入的 `HARNESS_TEST_MAX_WORKERS` 决定，不得自行提升。返回 `TEST_RUN_ALREADY_ACTIVE` 说明已有构建在跑：等待或查明持有者，不得绕开锁另起并行构建。完整约束 → `testing-checklist.md`「0.0-A 资源安全档位」。

**长阶段租约**：`gate begin` 的租约默认 TTL 3600 秒，execute 阶段常常跑得更久。租约过期不会中断执行，**也不再阻断收尾**：`gate close` 发现租约过期而 run-id 仍是本阶段的，会自动用原 run-id 重取并照常关门，只在返回体的 `leaseLapsed` 里记录。不需要定期续租；报 `LEASE_ABSENT`/`LEASE_INVALID` 时先确认 begin 真的跑过，**不要**重跑 `gate begin`。

**中断恢复（批次 2 WI-3）**：会话中断后重进 execute，先跑一条只读命令看「现在在哪、下一步做什么」——`harness_change.py status --change <id> --json`（返回 `CHANGE_RECOVERY_VIEW`：当前阶段/runId/attempt、plannedPhases 进度、ledger 验证状态、租约、脏树、`nextAction`）。`handoffPending=true` 表示 phase.end 已写但交接未落盘——按 `nextAction` 用原 close 命令补 `--to-phase` 幂等续跑。**不要**法证式逐个读 gate-policy/events/ledger/state-snapshot 原文来拼状态；视图只读派生同一批权威文件，不产生新状态。

**关门重试是幂等续跑**（0.4.5 workflow 起）：租约释放是 close 的最后一个可失败步骤，handoff/monitor/清草稿任一失败时租约仍持有，原样重跑同一命令即可（已完成步骤由 closeTransaction journal 幂等跳过）。万一处于「phase.end 已写 + 租约已放 + 交接未完成」的历史中间态，重跑 close 会自动识别并补跑剩余步骤（`PHASE_CLOSE_RESUMED`）：未带 `--to-phase` 时从 plannedPhases 派生唯一后继，后继不唯一才报 `PHASE_HANDOFF_PENDING` 并列出候选。**都不需要手工 `harness_change.py claim`**——LEASE_ABSENT 只在 begin 从未跑过时才该出现。

**接口测试执行器优先级**：接口测试执行器（默认首选）> PowerShell batch `.ps1` > Playwright MCP `browser_evaluate`（仅前两者不可用或用户明确选择）> curl + UTF-8 JSON body file（兜底）。执行器在 PowerShell 可用时**禁止**用 Playwright MCP 逐条执行。→ `testing-reference.md`「接口测试工具优先级」

**陈旧测试安全修复**：只有当前生产代码、已批准计划或可验证历史能唯一确定新契约时，才允许仅修改测试并立即重跑，然后记录：

```text
python <skills-root>/scripts/harness_test_guard.py record --project . --change-dir ".harness/changes/<change-name>" --files "<精确测试文件路径，逗号分隔>" --reason stale-test-repair --json
```

新建或正常更新测试分别使用 `tdd-created` / `test-updated`；存在业务歧义时记录 `BLOCKED_PREEXISTING` 并停止。**禁止临时排除测试**（`.bak`/改名/移出目录/禁用注解/exclude/`skipTests` 充当通过证据）。

**Foundation Gate**：若 `meta/implementation-checkpoints.json` 中 `foundation-gate` 为 pending，不得开始 plan 中任务 6+；由 `harness_gate.py` 硬阻断。

## 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

## 关键规则（硬门禁速查）

> 编码侧细则 → `coding-reference.md`、`protocols.md`；验证侧细则 → `testing-reference.md`、`testing-checklist.md`、`testing-pitfalls.md`

| 域 | 要点 |
|----|------|
| **文档输入** | 只读 `.harness/changes/<cn>/`；禁止 `docs/superpowers/` |
| **变更簇 TDD** | 一簇一次 RED/GREEN；低价值项豁免；新分支必须 RED |
| **RED/GREEN** | RED 须有效；静态验证 ≠ 测试通过 |
| **探测/ledger** | 基础设施先探测；构建/测试经 `exec --result-receipt` + `record-from-receipt`（手工 `record` 仅回退/受控例外）；禁止手写 ledger JSON |
| **Gate/Guard** | execute 入口一条命令 `harness_context.py bootstrap-execute`（三连命令保留为排障出口）；跨 Agent/阶段先用 `harness_context.py prepare/begin`；阶段门禁统一用 `harness_gate.py begin/close`；gate 内部负责 guard begin/close（`harness_test_guard.py begin` / `harness_test_guard.py close` 仅由 gate 调用，不构成模型执行步骤），模型只在需要记录测试来源或修复时调用 guard 的 `record/stage/mark` |
| **关门/状态** | 10 项关门检查；仅 execute-owned P0 静态-only 导致 WARN；review/submit-owned 待办正常移交 |
| **Worktree** | `requested=true` 时代码只写 worktree |
| **构建/测试** | 一律经 `harness_test_runner.py exec`；禁止裸跑；`TEST_RUN_ALREADY_ACTIVE` 表示已有构建在跑，等待而非另起 |
| **租约** | 阶段超 TTL 由 close 自动用原 run-id 重取，无需续租；`LEASE_ABSENT`/`LEASE_INVALID` 才需人工 `harness_change.py claim`，一律不重跑 begin |
| **报告** | 区分"产品测试"与"工具维护"；API 维度 `OK`/`PARTIAL`/`BLOCKED`/`NOT_RUN`/`FAIL` 五态；报告用 `render-report` 派生（模型只追加解读段落） |

## Output Format

测试报告由脚本派生（批次 2 WI-4a）：`harness_ledger.py render-report --change-dir <dir> [--out <path>]` 从 ledger+events 渲染变更文件表 + 验证证据 + 场景覆盖摘要 + 五态状态，默认写 `.harness/changes/<change-name>/reports/test/test-report-YYYYMMDD-HHmm.md`（frontmatter 带 `generated: true`）。模型只在报告文末「解读（模型追加）」段落补充残余风险与下一步，不改写派生内容；报告可随时重渲染重建，不作为第二份可写状态。渲染报告与手写报告不并存于同一 change——旧 change 已有手写报告时沿用旧路径完成，新 change 不要手写。

## 渐进披露

- **Read `protocols.md`** — run-tdd + change-cluster-review
- **Read `coding-reference.md`** — Step 0–5 细节、TDD/RED/ledger/迁移/安全矩阵
- **Read `testing-checklist.md`** — 验证前各项强制检查、preflight、服务生命周期清单
- **Read `testing-reference.md`** — API 测试执行方法、执行器模板、运行时配置叠加
- **Read `testing-pitfalls.md`** — 测试踩坑规则

## 交互白名单

1. **预存变更**：保留 / 暂存 / 终止
2. **数据库迁移**：展示审查清单并确认（**永不自动执行**）
3. **worktree 创建失败**：是否改主目录
4. **Service Gate**：仅当 `harness_service.py ensure` 返回 `needs-user-decision` 时询问处理方式
5. **资源密集型测试确认**：仅当发布/验收确实需要 `system` 或 `full` 档位，且用户尚未明确授权时询问；获得授权后传入 `--confirm-resource-intensive`

## 执行日志

`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=`execute`
