@hunter-harness/workflow-harness 0.4.10 → 0.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/harness/bundles/general/claude-code/.harness-build.json +1 -1
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-execute/SKILL.md +4 -2
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +12 -1
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +18 -11
- package/harness/bundles/general/claude-code/scripts/harness_events.py +12 -8
- package/harness/bundles/general/claude-code/scripts/harness_fixback.py +37 -1
- package/harness/bundles/general/claude-code/scripts/harness_gate.py +99 -2
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +27 -3
- package/harness/bundles/general/claude-code/scripts/harness_process.py +18 -0
- package/harness/bundles/general/claude-code/scripts/harness_runtime.py +47 -1
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-execute/SKILL.md +4 -2
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +12 -1
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +18 -11
- package/harness/bundles/general/codebuddy/scripts/harness_events.py +12 -8
- package/harness/bundles/general/codebuddy/scripts/harness_fixback.py +37 -1
- package/harness/bundles/general/codebuddy/scripts/harness_gate.py +99 -2
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +27 -3
- package/harness/bundles/general/codebuddy/scripts/harness_process.py +18 -0
- package/harness/bundles/general/codebuddy/scripts/harness_runtime.py +47 -1
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-execute/SKILL.md +4 -2
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/scripts/harness_archive.py +18 -11
- package/harness/bundles/general/codex/scripts/harness_events.py +12 -8
- package/harness/bundles/general/codex/scripts/harness_fixback.py +37 -1
- package/harness/bundles/general/codex/scripts/harness_gate.py +99 -2
- package/harness/bundles/general/codex/scripts/harness_ledger.py +27 -3
- package/harness/bundles/general/codex/scripts/harness_process.py +18 -0
- package/harness/bundles/general/codex/scripts/harness_runtime.py +47 -1
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-execute/SKILL.md +4 -2
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/scripts/harness_archive.py +18 -11
- package/harness/bundles/general/cursor/scripts/harness_events.py +12 -8
- package/harness/bundles/general/cursor/scripts/harness_fixback.py +37 -1
- package/harness/bundles/general/cursor/scripts/harness_gate.py +99 -2
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +27 -3
- package/harness/bundles/general/cursor/scripts/harness_process.py +18 -0
- package/harness/bundles/general/cursor/scripts/harness_runtime.py +47 -1
- package/harness/bundles/general/pi/.harness-build.json +1 -1
- package/harness/bundles/general/pi/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/pi/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/pi/harness-execute/SKILL.md +4 -2
- package/harness/bundles/general/pi/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/pi/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/pi/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/pi/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/pi/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/pi/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/pi/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/pi/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/pi/scripts/harness_archive.py +18 -11
- package/harness/bundles/general/pi/scripts/harness_events.py +12 -8
- package/harness/bundles/general/pi/scripts/harness_fixback.py +37 -1
- package/harness/bundles/general/pi/scripts/harness_gate.py +99 -2
- package/harness/bundles/general/pi/scripts/harness_ledger.py +27 -3
- package/harness/bundles/general/pi/scripts/harness_process.py +18 -0
- package/harness/bundles/general/pi/scripts/harness_runtime.py +47 -1
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-execute/SKILL.md +4 -2
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +12 -1
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +18 -11
- package/harness/bundles/java/claude-code/scripts/harness_events.py +12 -8
- package/harness/bundles/java/claude-code/scripts/harness_fixback.py +37 -1
- package/harness/bundles/java/claude-code/scripts/harness_gate.py +99 -2
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +27 -3
- package/harness/bundles/java/claude-code/scripts/harness_process.py +18 -0
- package/harness/bundles/java/claude-code/scripts/harness_runtime.py +47 -1
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-execute/SKILL.md +4 -2
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +12 -1
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +18 -11
- package/harness/bundles/java/codebuddy/scripts/harness_events.py +12 -8
- package/harness/bundles/java/codebuddy/scripts/harness_fixback.py +37 -1
- package/harness/bundles/java/codebuddy/scripts/harness_gate.py +99 -2
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +27 -3
- package/harness/bundles/java/codebuddy/scripts/harness_process.py +18 -0
- package/harness/bundles/java/codebuddy/scripts/harness_runtime.py +47 -1
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-execute/SKILL.md +4 -2
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/scripts/harness_archive.py +18 -11
- package/harness/bundles/java/codex/scripts/harness_events.py +12 -8
- package/harness/bundles/java/codex/scripts/harness_fixback.py +37 -1
- package/harness/bundles/java/codex/scripts/harness_gate.py +99 -2
- package/harness/bundles/java/codex/scripts/harness_ledger.py +27 -3
- package/harness/bundles/java/codex/scripts/harness_process.py +18 -0
- package/harness/bundles/java/codex/scripts/harness_runtime.py +47 -1
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-execute/SKILL.md +4 -2
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/scripts/harness_archive.py +18 -11
- package/harness/bundles/java/cursor/scripts/harness_events.py +12 -8
- package/harness/bundles/java/cursor/scripts/harness_fixback.py +37 -1
- package/harness/bundles/java/cursor/scripts/harness_gate.py +99 -2
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +27 -3
- package/harness/bundles/java/cursor/scripts/harness_process.py +18 -0
- package/harness/bundles/java/cursor/scripts/harness_runtime.py +47 -1
- package/harness/bundles/java/pi/.harness-build.json +1 -1
- package/harness/bundles/java/pi/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/pi/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/pi/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/pi/harness-execute/SKILL.md +4 -2
- package/harness/bundles/java/pi/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/pi/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/pi/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/pi/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/pi/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/pi/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/pi/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/pi/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/pi/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/pi/scripts/harness_archive.py +18 -11
- package/harness/bundles/java/pi/scripts/harness_events.py +12 -8
- package/harness/bundles/java/pi/scripts/harness_fixback.py +37 -1
- package/harness/bundles/java/pi/scripts/harness_gate.py +99 -2
- package/harness/bundles/java/pi/scripts/harness_ledger.py +27 -3
- package/harness/bundles/java/pi/scripts/harness_process.py +18 -0
- package/harness/bundles/java/pi/scripts/harness_runtime.py +47 -1
- package/harness/manifests/general/claude-code.json +19 -19
- package/harness/manifests/general/codebuddy.json +19 -19
- package/harness/manifests/general/codex.json +19 -19
- package/harness/manifests/general/cursor.json +19 -19
- package/harness/manifests/general/pi.json +19 -19
- package/harness/manifests/java/claude-code.json +21 -21
- package/harness/manifests/java/codebuddy.json +21 -21
- package/harness/manifests/java/codex.json +21 -21
- package/harness/manifests/java/cursor.json +21 -21
- package/harness/manifests/java/pi.json +21 -21
- package/hunter-workflow-family.json +2 -2
- package/package.json +1 -1
|
@@ -21,7 +21,7 @@ disallowed-tools:
|
|
|
21
21
|
- Bash(awk *)
|
|
22
22
|
- Bash(curl *)
|
|
23
23
|
---
|
|
24
|
-
<!-- generated by harness_deploy.py; core=
|
|
24
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
25
25
|
# harness-archive — 变更归档
|
|
26
26
|
|
|
27
27
|
## Purpose
|
|
@@ -20,7 +20,7 @@ disallowed-tools:
|
|
|
20
20
|
- Bash(awk *)
|
|
21
21
|
- Bash(curl *)
|
|
22
22
|
---
|
|
23
|
-
<!-- generated by harness_deploy.py; core=
|
|
23
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
24
24
|
# harness-codebase-map — 代码库地图
|
|
25
25
|
|
|
26
26
|
## Purpose
|
|
@@ -23,7 +23,7 @@ disallowed-tools:
|
|
|
23
23
|
- Bash(node *)
|
|
24
24
|
- Bash(codegraph *)
|
|
25
25
|
---
|
|
26
|
-
<!-- generated by harness_deploy.py; core=
|
|
26
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
27
27
|
# harness-execute — 执行(编码 + 验证)
|
|
28
28
|
|
|
29
29
|
> 2026-08 阶段合并:原 `run`(编码)与 `test`(验证)合并为单一 `execute` 阶段;
|
|
@@ -81,12 +81,14 @@ disallowed-tools:
|
|
|
81
81
|
## Workflow 概要
|
|
82
82
|
|
|
83
83
|
0. 加载上下文:先 `harness_context.py prepare --project . --change <id> --phase execute --executor <tool> --json`(`--project` 必填),再 `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`。
|
|
84
|
+
|
|
85
|
+
**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,更早版本请写全路径。
|
|
84
86
|
0.5. **测试基础设施探测**与**命令执行模式 preflight** → `testing-reference.md`「命令执行模式 preflight」;测试基线已由 gate begin 内部建立,不得再次执行 guard begin
|
|
85
87
|
1. **变更簇 TDD** — `protocols.md` `run-tdd-protocol`;批量 RED/GREEN;按需 `change-cluster-review-protocol`
|
|
86
88
|
2. 构建验证 + **仅**通过 `harness_ledger.py record` 写 ledger(禁止 Write/Edit `verification-ledger.json`);profile 缺失或陈旧时先 `harness_preflight.py detect --project . --json`;`record --project . --profile-input <key>` 从同一 target 推导 scope、coverage、规范命令与输入闭包
|
|
87
89
|
3. **验证执行**:单元测试可复用则跳过(`harness_ledger.py can-reuse`);接口测试**强制批量执行器**一次跑完全部场景;数据兼容验证按场景表执行 → `testing-reference.md`
|
|
88
90
|
4. **场景覆盖检查**(场景表映射,禁止用用例数冒充场景数)
|
|
89
|
-
5. **关门检查**(10 项)→ 只执行一次 `harness_gate.py close`;`--to-phase` 可省略——计划后继唯一(排除 fixback 自环)时自动派生并交接(输出含 `derivedToPhase`)。仅 fixback 回环时显式传 `--to-phase execute
|
|
91
|
+
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` 原样重试,已完成步骤幂等复用。
|
|
90
92
|
|
|
91
93
|
**阶段归属规则**:只用 `ownerPhase=execute` 的任务和场景判定本阶段结果。`ownerPhase=review`/`submit` 的场景按计划留给后续阶段属于正常移交,出现在关门返回值的 `deferred` 里,不阻断 execute。合并前 `ownerPhase=run`/`test` 的旧清单经别名表归一为 execute,在 execute 关门时一并到期。
|
|
92
94
|
|
|
@@ -8,7 +8,7 @@ disallowed-tools:
|
|
|
8
8
|
- Bash(git *)
|
|
9
9
|
- Bash(curl *)
|
|
10
10
|
---
|
|
11
|
-
<!-- generated by harness_deploy.py; core=
|
|
11
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
12
12
|
# harness-knowledge-ingest
|
|
13
13
|
|
|
14
14
|
知识 ingest 完全由 Hunter Platform 负责。归档完成时,客户端生成一个确定性 ZIP;
|
|
@@ -8,7 +8,7 @@ disallowed-tools:
|
|
|
8
8
|
- Bash(git *)
|
|
9
9
|
- Bash(curl *)
|
|
10
10
|
---
|
|
11
|
-
<!-- generated by harness_deploy.py; core=
|
|
11
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
12
12
|
# harness-knowledge-query
|
|
13
13
|
|
|
14
14
|
项目知识以 Hunter Platform 的服务端索引为唯一真源。客户端只提交查询并消费结果:
|
|
@@ -21,7 +21,7 @@ disallowed-tools:
|
|
|
21
21
|
- Bash(awk *)
|
|
22
22
|
- Bash(curl *)
|
|
23
23
|
---
|
|
24
|
-
<!-- generated by harness_deploy.py; core=
|
|
24
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
25
25
|
# harness-plan — 需求规划
|
|
26
26
|
|
|
27
27
|
## Purpose
|
|
@@ -21,7 +21,7 @@ disallowed-tools:
|
|
|
21
21
|
- Bash(awk *)
|
|
22
22
|
- Bash(curl *)
|
|
23
23
|
---
|
|
24
|
-
<!-- generated by harness_deploy.py; core=
|
|
24
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
25
25
|
# harness-pull — 从 Hunter Platform 下拉与恢复
|
|
26
26
|
|
|
27
27
|
## Purpose
|
|
@@ -21,7 +21,7 @@ disallowed-tools:
|
|
|
21
21
|
- Bash(awk *)
|
|
22
22
|
- Bash(curl *)
|
|
23
23
|
---
|
|
24
|
-
<!-- generated by harness_deploy.py; core=
|
|
24
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
25
25
|
# harness-push — 上传到 Hunter Platform
|
|
26
26
|
|
|
27
27
|
## Purpose
|
|
@@ -22,7 +22,7 @@ disallowed-tools:
|
|
|
22
22
|
- Bash(curl *)
|
|
23
23
|
- Bash(codegraph *)
|
|
24
24
|
---
|
|
25
|
-
<!-- generated by harness_deploy.py; core=
|
|
25
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
26
26
|
# harness-review — 代码审查
|
|
27
27
|
|
|
28
28
|
## Purpose
|
|
@@ -75,6 +75,17 @@ review 默认优先在独立上下文执行,避免主会话已形成的实现
|
|
|
75
75
|
|
|
76
76
|
无隔离能力、定义损坏、spawn 失败、空返回或只有元数据时,不重试,立即由主会话按同一 6 维度检查清单完成审查。无论是否委派,都必须追加一条结构化 `decision` 事件:`--execution-mode delegated|inline` 标明执行方式;委派时写 `--executor-agent harness-reviewer --decision-reason-code REVIEW_DELEGATED`;回退时只写一个 `--fallback-reason-code REVIEW_INLINE_UNAVAILABLE|REVIEW_INLINE_SPAWN_FAILED|REVIEW_INLINE_INVALID_RESULT`。`decision` 和 `reason` 必须用中文完整说明,禁止把原因码拼进正文。正常主会话评审是可接受结果,不显示成流程故障;`fallbackPolicy=inline-no-retry`。
|
|
77
77
|
|
|
78
|
+
完整示例(委派路径,可直接复制改参):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<cn>" \
|
|
82
|
+
--phase review --type decision --execution-mode delegated \
|
|
83
|
+
--executor-agent harness-reviewer --decision-reason-code REVIEW_DELEGATED \
|
|
84
|
+
--decision "已使用独立评审完成 6 维度审查" --reason "固定 reviewer 可用,避免主会话实现结论影响审查" --json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
注意:`decision` 事件**不接受** `--name`/`--status`(那是 verification 的字段);`issue` 事件必须带 `--severity`。
|
|
88
|
+
|
|
78
89
|
项目规模小、风险低或没有 CodeGraph 都不是跳过独立评审的理由。原因码是稳定机器字段,仅用于协议与技术详情;面向用户的事件摘要必须写成「已使用独立评审」或「当前环境没有可用的隔离评审能力,已由主会话完成评审」等中文说明。
|
|
79
90
|
|
|
80
91
|
3. **持久化报告与事实 sidecar(强制,主会话)** — Agent 返回后主会话 Write 到 `reports/review/review-report-*.md`,并调用 `python <skills-root>/scripts/harness_review.py scaffold --change-dir <change-dir> [--run-id <id>]` 生成 findings 写入骨架(runId 缺省从 events.ndjson 最新 review phase.start 推断),补齐每一条发现后经 `harness_review.py write-findings --change-dir <change-dir> --stdin` 落地。每条发现必须填写 `fixbackAction=code|manual|workflow`:只有确实需要修改产品代码的项使用 `code`;人工验收用 `manual`;流程、暂存或工具使用建议用 `workflow`。权威计数来自 `reports/review/review-findings.json`,不是 Markdown。任一写入缺失 → 评审未完整结束,不得关门。
|
|
@@ -21,7 +21,7 @@ disallowed-tools:
|
|
|
21
21
|
- Bash(awk *)
|
|
22
22
|
- Bash(curl *)
|
|
23
23
|
---
|
|
24
|
-
<!-- generated by harness_deploy.py; core=
|
|
24
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
25
25
|
# harness-submit — 最终提交(含 worktree 合并)
|
|
26
26
|
|
|
27
27
|
## Purpose
|
|
@@ -83,7 +83,7 @@ worktree 合并前必须运行 `harness_change.py integration-lock acquire --run
|
|
|
83
83
|
4. **提交方式** — 主目录:blocking user confirmation 三选项(commit+push / 仅本地 / 取消);**worktree:固定仅本地 commit**
|
|
84
84
|
5. **commit-message.txt** ⚠️ — 展示 staged、diff stat、完整中文 message;用户确认
|
|
85
85
|
6. **commit / push** — `git commit -F`;主目录按选项 push(push 前 fetch 检查远端);无 upstream 时允许仅本地 commit。commit 后立即重新运行 `harness_archive.py certify-local`:若验证输入未变化,脚本把候选从提交前身份安全重绑定到最终 commit,不得重跑测试或手工复制账本;**worktree:只 commit,记录 local hash**
|
|
86
|
-
7. **收尾** — `harness_gate.py close --phase submit --status ... --to-phase <实际计划后继>`;通常后继为 Archive,但不得显示“等待 merge”或固定写死。主目录停止并提示真实下一阶段;worktree 才接续下方同一 Submit
|
|
86
|
+
7. **收尾** — `harness_gate.py close --phase submit --status ... --to-phase <实际计划后继>`;通常后继为 Archive,但不得显示“等待 merge”或固定写死。主目录停止并提示真实下一阶段;worktree 才接续下方同一 Submit 内的合并流程。`--json` 的 stdout 只含 JSON;关门摘要横幅在 stderr——管道给 `jq` 时**不要** `2>&1` 合并(stderr 不缓冲会先写,合并后横幅会混进 JSON 流)。
|
|
87
87
|
|
|
88
88
|
详细步骤见 `checklist.md`。
|
|
89
89
|
|
|
@@ -19,7 +19,7 @@ disallowed-tools:
|
|
|
19
19
|
- Bash(awk *)
|
|
20
20
|
- Bash(curl *)
|
|
21
21
|
---
|
|
22
|
-
<!-- generated by harness_deploy.py; core=
|
|
22
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=claude-code; do not edit -->
|
|
23
23
|
# harness-sync
|
|
24
24
|
|
|
25
25
|
## Purpose
|
|
@@ -3010,8 +3010,12 @@ def check_status(
|
|
|
3010
3010
|
if archive_intent == "record-only"
|
|
3011
3011
|
else str(ci_gate.get("code") or "PRODUCT_CANDIDATE_NOT_VERIFIED")
|
|
3012
3012
|
),
|
|
3013
|
-
"message":
|
|
3014
|
-
|
|
3013
|
+
"message": (
|
|
3014
|
+
# A-2:record-only 豁免阻断,但原文透传授权要求会误读成阻断指引
|
|
3015
|
+
"record-only 归档不要求发布授权,本项仅记录:"
|
|
3016
|
+
+ str(ci_gate.get("message") or "product candidate not verified")
|
|
3017
|
+
if archive_intent == "record-only"
|
|
3018
|
+
else str(ci_gate.get("message") or "product candidate not verified")
|
|
3015
3019
|
),
|
|
3016
3020
|
}
|
|
3017
3021
|
if archive_intent == "record-only":
|
|
@@ -3104,15 +3108,18 @@ def check_status(
|
|
|
3104
3108
|
code = str(issue.get("code") or "REPORT_ADEQUACY_FAILED")
|
|
3105
3109
|
if any(item.get("code") == code for item in blockers):
|
|
3106
3110
|
continue
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
)
|
|
3111
|
+
blocker: dict[str, Any] = {
|
|
3112
|
+
"code": code,
|
|
3113
|
+
"message": adequacy_messages_zh.get(
|
|
3114
|
+
code,
|
|
3115
|
+
str(issue.get("message") or "归档报告不完整"),
|
|
3116
|
+
),
|
|
3117
|
+
}
|
|
3118
|
+
# A-3:出路必须随阻断项下发(对齐 SKILL 二·A 表),不再让使用者翻文档
|
|
3119
|
+
next_action = issue.get("nextAction")
|
|
3120
|
+
if isinstance(next_action, str) and next_action.strip():
|
|
3121
|
+
blocker["recoveryAction"] = next_action.strip()
|
|
3122
|
+
blockers.append(blocker)
|
|
3116
3123
|
for issue in report_adequacy.get("issues") or []:
|
|
3117
3124
|
if not isinstance(issue, dict) or issue.get("severity") != "warning":
|
|
3118
3125
|
continue
|
|
@@ -898,14 +898,18 @@ def validate_append_event(args: argparse.Namespace) -> tuple[str, str] | None:
|
|
|
898
898
|
f"EVENT_REQUIRED_FIELD: {event_type} requires " + ", ".join(missing),
|
|
899
899
|
)
|
|
900
900
|
allowed = _EVENT_ALLOWED_FIELDS[event_type]
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
901
|
+
disallowed = [
|
|
902
|
+
f"--{field.replace('_', '-')}"
|
|
903
|
+
for field in OPTIONAL_FIELDS
|
|
904
|
+
if getattr(args, field, None) is not None and field not in allowed
|
|
905
|
+
]
|
|
906
|
+
if disallowed:
|
|
907
|
+
# 一次列出全部不被接受的字段——逐个报错会让调用方每字段往返一轮(R-4)
|
|
908
|
+
return (
|
|
909
|
+
"EVENT_FIELD_NOT_ALLOWED",
|
|
910
|
+
f"EVENT_FIELD_NOT_ALLOWED: {event_type} does not accept "
|
|
911
|
+
+ ", ".join(disallowed),
|
|
912
|
+
)
|
|
909
913
|
for field, length in (("trace_id", 32), ("span_id", 16), ("parent_span_id", 16)):
|
|
910
914
|
value = getattr(args, field, None)
|
|
911
915
|
if value is not None and not re.fullmatch(rf"[0-9a-f]{{{length}}}", str(value)):
|
|
@@ -75,6 +75,14 @@ def _write_json(path: Path, value: dict[str, Any]) -> None:
|
|
|
75
75
|
temporary.unlink(missing_ok=True)
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
def _read_json(path: Path) -> Any:
|
|
79
|
+
try:
|
|
80
|
+
value = json.loads(path.read_text(encoding="utf-8-sig"))
|
|
81
|
+
except (OSError, json.JSONDecodeError):
|
|
82
|
+
return None
|
|
83
|
+
return value
|
|
84
|
+
|
|
85
|
+
|
|
78
86
|
def _load(change_dir: Path, batch_id: str) -> dict[str, Any]:
|
|
79
87
|
path = _batch_path(change_dir, batch_id)
|
|
80
88
|
try:
|
|
@@ -1186,7 +1194,21 @@ def evidence_template(
|
|
|
1186
1194
|
"provenance": provenance,
|
|
1187
1195
|
}
|
|
1188
1196
|
target = Path(out) if out else Path("runtime") / f"fixback-{resolved_id}.json"
|
|
1189
|
-
|
|
1197
|
+
if target.is_absolute():
|
|
1198
|
+
path = target
|
|
1199
|
+
else:
|
|
1200
|
+
# F-3:与 ledger --out 同一类坑——相对路径已含 change-dir 前缀时直接拼
|
|
1201
|
+
# change_dir 会产生嵌套幽灵目录(2026-08-31 实测)。前缀命中按 cwd 解析。
|
|
1202
|
+
cwd = Path.cwd().resolve()
|
|
1203
|
+
change_resolved = change_dir.resolve()
|
|
1204
|
+
if _is_within(change_resolved, [cwd]):
|
|
1205
|
+
change_rel = change_resolved.relative_to(cwd)
|
|
1206
|
+
if target.parts[: len(change_rel.parts)] == change_rel.parts:
|
|
1207
|
+
path = cwd / target
|
|
1208
|
+
else:
|
|
1209
|
+
path = change_dir / target
|
|
1210
|
+
else:
|
|
1211
|
+
path = change_dir / target
|
|
1190
1212
|
path = path.resolve()
|
|
1191
1213
|
if not _is_within(path, [change_dir.resolve(), state_root, _project_root(change_dir)]):
|
|
1192
1214
|
return {
|
|
@@ -1708,6 +1730,20 @@ def close_batch(
|
|
|
1708
1730
|
}
|
|
1709
1731
|
)
|
|
1710
1732
|
_write_json(_batch_path(change_dir, batch_id), batch)
|
|
1733
|
+
# F-5:批次关闭会同步把托管会话置为 CLOSED——否则 session 永远 ACTIVE,
|
|
1734
|
+
# 后续 launch-review 被 CONTEXT_PREPARATION_ACTIVE 幂等保护误拦(2026-08-31
|
|
1735
|
+
# demo-datasource 实测)。
|
|
1736
|
+
session_file = _session_path(change_dir)
|
|
1737
|
+
session = _read_json(session_file)
|
|
1738
|
+
if (
|
|
1739
|
+
isinstance(session, dict)
|
|
1740
|
+
and session.get("batchId") == batch_id
|
|
1741
|
+
and session.get("status") == "ACTIVE"
|
|
1742
|
+
):
|
|
1743
|
+
session["status"] = "CLOSED"
|
|
1744
|
+
session["nextStep"] = "done"
|
|
1745
|
+
session["updatedAt"] = closed_at
|
|
1746
|
+
_write_json(session_file, session)
|
|
1711
1747
|
return batch
|
|
1712
1748
|
|
|
1713
1749
|
|
|
@@ -2085,6 +2085,52 @@ def _terminal_matches_context_session(
|
|
|
2085
2085
|
)
|
|
2086
2086
|
|
|
2087
2087
|
|
|
2088
|
+
def _repair_handoff_missing_lease(
|
|
2089
|
+
project: Path,
|
|
2090
|
+
change_id: str,
|
|
2091
|
+
args: argparse.Namespace,
|
|
2092
|
+
*,
|
|
2093
|
+
status: str,
|
|
2094
|
+
handoff: dict[str, Any],
|
|
2095
|
+
) -> dict[str, Any] | None:
|
|
2096
|
+
"""S-1:交接因 context 租约缺失失败时自愈一次。
|
|
2097
|
+
|
|
2098
|
+
阶段租约仍持有但 context 租约已被消费/缺失时,close_transition 只能报
|
|
2099
|
+
CONTEXT_LEASE_REQUIRED——原样重跑永远撞同一错误(2026-08-31 submit 实测,
|
|
2100
|
+
recoveryAction 声称“会重试”但重试无自愈能力)。这里补齐缺口:
|
|
2101
|
+
prepare_context 幂等重建本阶段 context 租约后重试一次交接,与
|
|
2102
|
+
`harness_context.py handoff` 命令同源(同 prepare→close 顺序)。
|
|
2103
|
+
"""
|
|
2104
|
+
if handoff.get("code") != "CONTEXT_LEASE_REQUIRED":
|
|
2105
|
+
return None
|
|
2106
|
+
executor = getattr(args, "executor", None)
|
|
2107
|
+
if not executor:
|
|
2108
|
+
view = hctx.context_view(project, change_id)
|
|
2109
|
+
current = view.get("current") if isinstance(view, dict) else None
|
|
2110
|
+
executor = (
|
|
2111
|
+
str(current.get("executor"))
|
|
2112
|
+
if isinstance(current, dict) and current.get("executor")
|
|
2113
|
+
else None
|
|
2114
|
+
)
|
|
2115
|
+
if not executor:
|
|
2116
|
+
return None
|
|
2117
|
+
try:
|
|
2118
|
+
prepared = hctx.prepare_context(
|
|
2119
|
+
project,
|
|
2120
|
+
change=change_id,
|
|
2121
|
+
phase=args.phase,
|
|
2122
|
+
executor=executor,
|
|
2123
|
+
)
|
|
2124
|
+
except Exception:
|
|
2125
|
+
return None
|
|
2126
|
+
if not prepared.get("ok"):
|
|
2127
|
+
return None
|
|
2128
|
+
repaired = _close_context_handoff(project, change_id, args, status=status)
|
|
2129
|
+
if isinstance(repaired, dict):
|
|
2130
|
+
repaired = {**repaired, "leaseRepaired": True}
|
|
2131
|
+
return repaired
|
|
2132
|
+
|
|
2133
|
+
|
|
2088
2134
|
def _close_context_handoff(
|
|
2089
2135
|
project: Path,
|
|
2090
2136
|
change_id: str,
|
|
@@ -3105,6 +3151,20 @@ def _resume_closed_phase(
|
|
|
3105
3151
|
for candidate in hctx.allowed_next_phases(project, change_id, args.phase)
|
|
3106
3152
|
if hp.resolve_phase_name(candidate) != args.phase
|
|
3107
3153
|
]
|
|
3154
|
+
# F-4:续跑路径同样——fixback 回环的 execute 关门应回 review 的后继
|
|
3155
|
+
if args.phase == "execute" and isinstance(transitions, list) and transitions:
|
|
3156
|
+
latest_t = transitions[-1]
|
|
3157
|
+
if (
|
|
3158
|
+
isinstance(latest_t, dict)
|
|
3159
|
+
and hp.resolve_phase_name(latest_t.get("fromPhase")) == "review"
|
|
3160
|
+
and hp.resolve_phase_name(latest_t.get("toPhase")) == "execute"
|
|
3161
|
+
and latest_t.get("trigger") == "review-fixback"
|
|
3162
|
+
):
|
|
3163
|
+
candidates = [
|
|
3164
|
+
candidate
|
|
3165
|
+
for candidate in hctx.allowed_next_phases(project, change_id, "review")
|
|
3166
|
+
if hp.resolve_phase_name(candidate) != args.phase
|
|
3167
|
+
]
|
|
3108
3168
|
if to_phase is None and len(candidates) == 1:
|
|
3109
3169
|
# 计划后继唯一(排除 fixback 自环):close 的意图没有歧义,直接补跑 handoff
|
|
3110
3170
|
to_phase = candidates[0]
|
|
@@ -3140,6 +3200,14 @@ def _resume_closed_phase(
|
|
|
3140
3200
|
status=close_status,
|
|
3141
3201
|
)
|
|
3142
3202
|
monitor = _sync_after_phase_close(project, change_dir)
|
|
3203
|
+
if isinstance(handoff, dict) and not handoff.get("ok"):
|
|
3204
|
+
# S-1:续跑路径同样的自愈
|
|
3205
|
+
repaired = _repair_handoff_missing_lease(
|
|
3206
|
+
project, change_id, args,
|
|
3207
|
+
status=close_status, handoff=handoff,
|
|
3208
|
+
)
|
|
3209
|
+
if repaired is not None:
|
|
3210
|
+
handoff = repaired
|
|
3143
3211
|
if isinstance(handoff, dict) and not handoff.get("ok"):
|
|
3144
3212
|
return emit_error(
|
|
3145
3213
|
"PHASE_HANDOFF_PENDING",
|
|
@@ -3691,10 +3759,25 @@ def cmd_close(args: argparse.Namespace) -> int:
|
|
|
3691
3759
|
and context_view.get("ok")
|
|
3692
3760
|
and isinstance(context_view.get("current"), dict)
|
|
3693
3761
|
):
|
|
3762
|
+
# F-4:fixback 回环(最新交接是 review→execute 且 trigger=
|
|
3763
|
+
# review-fixback)关门时,后继不是再来一轮 review,而是 review
|
|
3764
|
+
# 当时的计划后继(submit)。不按此处理会把 transitions 链写成
|
|
3765
|
+
# …→review→execute→review 的语义错乱(2026-08-31 实测)。
|
|
3766
|
+
source_phase = args.phase
|
|
3767
|
+
transitions = context_view.get("transitions") or []
|
|
3768
|
+
if args.phase == "execute" and transitions:
|
|
3769
|
+
latest = transitions[-1]
|
|
3770
|
+
if (
|
|
3771
|
+
isinstance(latest, dict)
|
|
3772
|
+
and hp.resolve_phase_name(latest.get("fromPhase")) == "review"
|
|
3773
|
+
and hp.resolve_phase_name(latest.get("toPhase")) == "execute"
|
|
3774
|
+
and latest.get("trigger") == "review-fixback"
|
|
3775
|
+
):
|
|
3776
|
+
source_phase = "review"
|
|
3694
3777
|
forward = [
|
|
3695
3778
|
candidate
|
|
3696
3779
|
for candidate in hctx.allowed_next_phases(
|
|
3697
|
-
project, str(resolved["changeId"]),
|
|
3780
|
+
project, str(resolved["changeId"]), source_phase
|
|
3698
3781
|
)
|
|
3699
3782
|
if hp.resolve_phase_name(candidate) != args.phase
|
|
3700
3783
|
]
|
|
@@ -3708,6 +3791,15 @@ def cmd_close(args: argparse.Namespace) -> int:
|
|
|
3708
3791
|
status=close_status,
|
|
3709
3792
|
)
|
|
3710
3793
|
platform_monitor = _sync_after_phase_close(project, change_dir)
|
|
3794
|
+
if isinstance(context_handoff, dict) and not context_handoff.get("ok"):
|
|
3795
|
+
# S-1:context 租约缺失时自愈一次(prepare 幂等重建 + 重试交接),
|
|
3796
|
+
# 修复前“原样重跑”永远撞同一个 CONTEXT_LEASE_REQUIRED
|
|
3797
|
+
repaired = _repair_handoff_missing_lease(
|
|
3798
|
+
project, str(resolved["changeId"]), args,
|
|
3799
|
+
status=close_status, handoff=context_handoff,
|
|
3800
|
+
)
|
|
3801
|
+
if repaired is not None:
|
|
3802
|
+
context_handoff = repaired
|
|
3711
3803
|
if isinstance(context_handoff, dict) and not context_handoff.get("ok"):
|
|
3712
3804
|
record_gate_blocked(
|
|
3713
3805
|
change_dir,
|
|
@@ -3730,7 +3822,12 @@ def cmd_close(args: argparse.Namespace) -> int:
|
|
|
3730
3822
|
"platformMonitor": platform_monitor,
|
|
3731
3823
|
"recoveryAction": (
|
|
3732
3824
|
"租约仍持有,无需重新 claim——原样重跑同一 close 命令即可幂等续跑"
|
|
3733
|
-
"(phase.end 已落会跳过,handoff
|
|
3825
|
+
"(phase.end 已落会跳过,handoff 会重试;context 租约缺失时会自动"
|
|
3826
|
+
"补建)。仍失败时用一条命令补齐交接:"
|
|
3827
|
+
"harness_context.py handoff --project . "
|
|
3828
|
+
f"--change {resolved['changeId']} --to-phase "
|
|
3829
|
+
f"{getattr(args, 'to_phase', None) or '<后继阶段>'} "
|
|
3830
|
+
"--executor <tool> --json"
|
|
3734
3831
|
),
|
|
3735
3832
|
},
|
|
3736
3833
|
)
|
|
@@ -2565,6 +2565,20 @@ def cmd_can_reuse(args: argparse.Namespace) -> int:
|
|
|
2565
2565
|
return 0
|
|
2566
2566
|
|
|
2567
2567
|
|
|
2568
|
+
def _profile_input_missing_warning(
|
|
2569
|
+
verification: str, target: Any, profile_input: str | None
|
|
2570
|
+
) -> str | None:
|
|
2571
|
+
"""S-4:profile 声明了 target 却没带 --profile-input 时提前预警。"""
|
|
2572
|
+
if profile_input or not isinstance(target, dict):
|
|
2573
|
+
return None
|
|
2574
|
+
return (
|
|
2575
|
+
"PROFILE_INPUT_MISSING: 未传 --profile-input,本条记录缺少 profile 推导的"
|
|
2576
|
+
" scope/coverage/inputsHash 身份字段,can-reuse 将无法复用。建议重跑:"
|
|
2577
|
+
f"record --verification {verification} --profile-input <key> --project . "
|
|
2578
|
+
"(key 见 build-profile.json verificationInputs)"
|
|
2579
|
+
)
|
|
2580
|
+
|
|
2581
|
+
|
|
2568
2582
|
def cmd_record(args: argparse.Namespace) -> int:
|
|
2569
2583
|
as_json = bool(args.json)
|
|
2570
2584
|
verbose = bool(getattr(args, "verbose", False))
|
|
@@ -2612,6 +2626,11 @@ def cmd_record(args: argparse.Namespace) -> int:
|
|
|
2612
2626
|
"verificationGraph.targets",
|
|
2613
2627
|
as_json=as_json,
|
|
2614
2628
|
)
|
|
2629
|
+
# S-4:profile 声明了 target 却没带 --profile-input,条目缺身份字段,
|
|
2630
|
+
# can-reuse 事后才报 MISSING_FIELDS——只能重跑一遍(2026-08-31 实测)。
|
|
2631
|
+
profile_input_missing_warn = _profile_input_missing_warning(
|
|
2632
|
+
verification, target, profile_input
|
|
2633
|
+
)
|
|
2615
2634
|
if profile_input:
|
|
2616
2635
|
resolved_files, err = expand_profile_input_files(project_root, profile_input)
|
|
2617
2636
|
if err:
|
|
@@ -3068,9 +3087,14 @@ def cmd_record(args: argparse.Namespace) -> int:
|
|
|
3068
3087
|
zero_warn = _zero_tests_with_selector_warning(
|
|
3069
3088
|
str(args.command), args.evidence, project_root
|
|
3070
3089
|
)
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3090
|
+
record_warnings = [
|
|
3091
|
+
warning for warning in (zero_warn, profile_input_missing_warn)
|
|
3092
|
+
if warning is not None
|
|
3093
|
+
]
|
|
3094
|
+
if record_warnings:
|
|
3095
|
+
payload["warnings"] = record_warnings
|
|
3096
|
+
for warning in record_warnings:
|
|
3097
|
+
print(f"[harness-ledger] WARNING {warning}", file=sys.stderr)
|
|
3074
3098
|
emit_compact_or_verbose(
|
|
3075
3099
|
payload, as_json=as_json, verbose=verbose,
|
|
3076
3100
|
compact_fn=_compact_record_payload,
|
|
@@ -15,6 +15,7 @@ import json
|
|
|
15
15
|
import os
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
import signal
|
|
18
|
+
import shutil
|
|
18
19
|
import subprocess
|
|
19
20
|
import sys
|
|
20
21
|
import time
|
|
@@ -946,6 +947,23 @@ def capture_owned_members(spawned: SpawnedProcess) -> dict[str, Any] | None:
|
|
|
946
947
|
return proof
|
|
947
948
|
|
|
948
949
|
|
|
950
|
+
def resolve_windows_executable(argv: Sequence[str]) -> list[str]:
|
|
951
|
+
"""Windows 无扩展名命令解析为实际可执行文件(F-1)。
|
|
952
|
+
|
|
953
|
+
CreateProcess 不走 shell,`mvn`/`npm` 这类 .cmd 包装会报 WinError 2;
|
|
954
|
+
shutil.which 遵循 PATHEXT 能找到 mvn.cmd。POSIX 或未命中时原样返回。
|
|
955
|
+
"""
|
|
956
|
+
if os.name != "nt" or not argv:
|
|
957
|
+
return list(argv)
|
|
958
|
+
head = argv[0]
|
|
959
|
+
if not isinstance(head, str) or not head or os.path.splitext(head)[1]:
|
|
960
|
+
return list(argv)
|
|
961
|
+
resolved = shutil.which(head)
|
|
962
|
+
if resolved is None:
|
|
963
|
+
return list(argv)
|
|
964
|
+
return [resolved, *argv[1:]]
|
|
965
|
+
|
|
966
|
+
|
|
949
967
|
def spawn_structured_argv(
|
|
950
968
|
argv: Sequence[str],
|
|
951
969
|
*,
|
|
@@ -1099,6 +1099,9 @@ def _spawn_detached_worker(
|
|
|
1099
1099
|
env: dict[str, str],
|
|
1100
1100
|
output: Any = subprocess.DEVNULL,
|
|
1101
1101
|
) -> subprocess.Popen[bytes]:
|
|
1102
|
+
from harness_process import resolve_windows_executable
|
|
1103
|
+
|
|
1104
|
+
argv = resolve_windows_executable(argv)
|
|
1102
1105
|
kwargs: dict[str, Any] = {
|
|
1103
1106
|
"cwd": str(cwd),
|
|
1104
1107
|
"env": env,
|
|
@@ -1171,6 +1174,40 @@ def start_run_session(
|
|
|
1171
1174
|
working_directory = working_directory.expanduser().resolve()
|
|
1172
1175
|
if not working_directory.is_dir():
|
|
1173
1176
|
raise ValueError(f"RUN_SESSION_WORKDIR_MISSING: {working_directory}")
|
|
1177
|
+
# F-2:fixback 会话缺 product-identity 时白跑一轮、register 才拒。批次开着
|
|
1178
|
+
# 就从 baseProductIdentity 自动注入;没有 OPEN 批次则当场拒绝(2026-08-31
|
|
1179
|
+
# demo-datasource 实测)。
|
|
1180
|
+
if verification.startswith("fixback") and not (
|
|
1181
|
+
product_identity and product_identity.strip()
|
|
1182
|
+
):
|
|
1183
|
+
batches_dir = state_root / "fixback" / "batches"
|
|
1184
|
+
open_identity: str | None = None
|
|
1185
|
+
open_batch_id: str | None = None
|
|
1186
|
+
if batches_dir.is_dir():
|
|
1187
|
+
for batch_file in sorted(batches_dir.glob("*.json")):
|
|
1188
|
+
try:
|
|
1189
|
+
batch = json.loads(batch_file.read_text(encoding="utf-8-sig"))
|
|
1190
|
+
except (OSError, json.JSONDecodeError):
|
|
1191
|
+
continue
|
|
1192
|
+
if (
|
|
1193
|
+
isinstance(batch, dict)
|
|
1194
|
+
and batch.get("status") == "OPEN"
|
|
1195
|
+
and isinstance(batch.get("baseProductIdentity"), str)
|
|
1196
|
+
and batch["baseProductIdentity"].strip()
|
|
1197
|
+
):
|
|
1198
|
+
open_identity = str(batch["baseProductIdentity"]).strip()
|
|
1199
|
+
open_batch_id = str(batch.get("batchId") or "")
|
|
1200
|
+
break
|
|
1201
|
+
if open_identity is None:
|
|
1202
|
+
raise ValueError(
|
|
1203
|
+
"FIXBACK_PRODUCT_IDENTITY_REQUIRED: fixback 会话必须有产品身份——"
|
|
1204
|
+
"传 --product-identity,或先启动 fixback 批次(其 baseProductIdentity "
|
|
1205
|
+
"会被自动注入)"
|
|
1206
|
+
)
|
|
1207
|
+
product_identity = open_identity
|
|
1208
|
+
product_identity_source = f"fixback-batch:{open_batch_id}"
|
|
1209
|
+
else:
|
|
1210
|
+
product_identity_source = None
|
|
1174
1211
|
session_id = session_id or f"run-{uuid.uuid4().hex}"
|
|
1175
1212
|
run_id = run_id or session_id
|
|
1176
1213
|
session_root = _run_session_root(state_root, session_id)
|
|
@@ -1215,6 +1252,11 @@ def start_run_session(
|
|
|
1215
1252
|
"heartbeatSeconds": heartbeat_seconds,
|
|
1216
1253
|
"expectedDurationSeconds": expected_duration_seconds,
|
|
1217
1254
|
"productIdentity": product_identity,
|
|
1255
|
+
**(
|
|
1256
|
+
{"productIdentitySource": product_identity_source}
|
|
1257
|
+
if product_identity_source is not None
|
|
1258
|
+
else {}
|
|
1259
|
+
),
|
|
1218
1260
|
"resourceLocks": normalized_resource_locks,
|
|
1219
1261
|
"resourceLockTokens": {},
|
|
1220
1262
|
"resourceWaitMs": 0,
|
|
@@ -1427,8 +1469,12 @@ def _run_session_worker(state_root: Path, session_id: str) -> int:
|
|
|
1427
1469
|
"wb", buffering=0
|
|
1428
1470
|
) as stderr:
|
|
1429
1471
|
try:
|
|
1430
|
-
from harness_process import
|
|
1472
|
+
from harness_process import (
|
|
1473
|
+
resolve_windows_executable,
|
|
1474
|
+
spawn_structured_argv,
|
|
1475
|
+
)
|
|
1431
1476
|
|
|
1477
|
+
argv = resolve_windows_executable(argv)
|
|
1432
1478
|
spawned = spawn_structured_argv(
|
|
1433
1479
|
argv,
|
|
1434
1480
|
cwd=Path(str(receipt["workingDirectory"])),
|
|
@@ -3,7 +3,7 @@ name: harness-archive
|
|
|
3
3
|
description: 归档所有变更产出(计划/测试报告/审查/SQL/API文档)到 .harness/archive/,含归档元数据。仅当用户显式调用
|
|
4
4
|
/harness-archive 时使用;不得被其他阶段 skill 自动接续触发。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-archive — 变更归档
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -3,7 +3,7 @@ name: harness-codebase-map
|
|
|
3
3
|
description: 分析现有代码库并生成 .harness/codebase/map/
|
|
4
4
|
下的结构化代码库地图。用于陌生项目理解、重大改造前梳理、harness-sync 提示 map 过期后的人工刷新。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=dc9fe149ebf1952b; overlay=none; agent=codebuddy; do not edit -->
|
|
7
7
|
# harness-codebase-map — 代码库地图
|
|
8
8
|
|
|
9
9
|
## Purpose
|