@hunter-harness/workflow-harness 0.2.5 → 0.2.6
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 +5 -2
- package/harness/bundles/general/claude-code/harness-archive/reference.md +1 -1
- package/harness/bundles/general/claude-code/harness-archive/templates/render-summary.mjs +78 -155
- package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +4 -2
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +5 -3
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +4 -2
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +6 -3
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +6 -3
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +5 -2
- package/harness/bundles/general/claude-code/harness-run/reference.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +5 -2
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +5 -3
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +5 -2
- package/harness/bundles/general/claude-code/harness-test/reference.md +1 -1
- package/harness/bundles/general/claude-code/protocols/ledger-protocol.md +1 -1
- package/harness/bundles/general/claude-code/protocols/report-pipeline-protocol.md +11 -4
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +190 -32
- package/harness/bundles/general/claude-code/scripts/harness_events.py +134 -40
- package/harness/bundles/general/claude-code/scripts/harness_preflight.py +46 -3
- package/harness/bundles/general/claude-code/scripts/harness_profile.py +2 -2
- package/harness/bundles/general/claude-code/scripts/harness_state.py +148 -0
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +5 -2
- package/harness/bundles/general/codebuddy/harness-archive/reference.md +1 -1
- package/harness/bundles/general/codebuddy/harness-archive/templates/render-summary.mjs +78 -155
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +4 -2
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +5 -3
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +4 -2
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +6 -3
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +6 -3
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +5 -2
- package/harness/bundles/general/codebuddy/harness-run/reference.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +5 -2
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +5 -3
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +5 -2
- package/harness/bundles/general/codebuddy/harness-test/reference.md +1 -1
- package/harness/bundles/general/codebuddy/protocols/ledger-protocol.md +1 -1
- package/harness/bundles/general/codebuddy/protocols/report-pipeline-protocol.md +11 -4
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +190 -32
- package/harness/bundles/general/codebuddy/scripts/harness_events.py +134 -40
- package/harness/bundles/general/codebuddy/scripts/harness_preflight.py +46 -3
- package/harness/bundles/general/codebuddy/scripts/harness_profile.py +2 -2
- package/harness/bundles/general/codebuddy/scripts/harness_state.py +148 -0
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/harness-archive/SKILL.md +5 -2
- package/harness/bundles/general/codex/harness-archive/reference.md +1 -1
- package/harness/bundles/general/codex/harness-archive/templates/render-summary.mjs +78 -155
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +4 -2
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +5 -3
- package/harness/bundles/general/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +4 -2
- package/harness/bundles/general/codex/harness-plan/SKILL.md +6 -3
- package/harness/bundles/general/codex/harness-review/SKILL.md +5 -2
- package/harness/bundles/general/codex/harness-run/SKILL.md +5 -2
- package/harness/bundles/general/codex/harness-run/reference.md +1 -1
- package/harness/bundles/general/codex/harness-submit/SKILL.md +5 -2
- package/harness/bundles/general/codex/harness-sync/SKILL.md +5 -3
- package/harness/bundles/general/codex/harness-test/SKILL.md +5 -2
- package/harness/bundles/general/codex/harness-test/reference.md +1 -1
- package/harness/bundles/general/codex/protocols/ledger-protocol.md +1 -1
- package/harness/bundles/general/codex/protocols/report-pipeline-protocol.md +11 -4
- package/harness/bundles/general/codex/scripts/harness_archive.py +190 -32
- package/harness/bundles/general/codex/scripts/harness_events.py +134 -40
- package/harness/bundles/general/codex/scripts/harness_preflight.py +46 -3
- package/harness/bundles/general/codex/scripts/harness_profile.py +2 -2
- package/harness/bundles/general/codex/scripts/harness_state.py +148 -0
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +5 -2
- package/harness/bundles/general/cursor/harness-archive/reference.md +1 -1
- package/harness/bundles/general/cursor/harness-archive/templates/render-summary.mjs +78 -155
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +4 -2
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +5 -3
- package/harness/bundles/general/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +4 -2
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +6 -3
- package/harness/bundles/general/cursor/harness-review/SKILL.md +5 -2
- package/harness/bundles/general/cursor/harness-run/SKILL.md +5 -2
- package/harness/bundles/general/cursor/harness-run/reference.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +5 -2
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +5 -3
- package/harness/bundles/general/cursor/harness-test/SKILL.md +5 -2
- package/harness/bundles/general/cursor/harness-test/reference.md +1 -1
- package/harness/bundles/general/cursor/protocols/ledger-protocol.md +1 -1
- package/harness/bundles/general/cursor/protocols/report-pipeline-protocol.md +11 -4
- package/harness/bundles/general/cursor/scripts/harness_archive.py +190 -32
- package/harness/bundles/general/cursor/scripts/harness_events.py +134 -40
- package/harness/bundles/general/cursor/scripts/harness_preflight.py +46 -3
- package/harness/bundles/general/cursor/scripts/harness_profile.py +2 -2
- package/harness/bundles/general/cursor/scripts/harness_state.py +148 -0
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +5 -2
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +5 -2
- package/harness/bundles/java/claude-code/harness-archive/reference.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/templates/render-summary.mjs +78 -155
- package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +4 -2
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +5 -3
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +4 -2
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +5 -2
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +6 -3
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +6 -3
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +5 -2
- package/harness/bundles/java/claude-code/harness-run/reference.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +5 -2
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +5 -3
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +5 -2
- package/harness/bundles/java/claude-code/harness-test/reference.md +1 -1
- package/harness/bundles/java/claude-code/protocols/ledger-protocol.md +1 -1
- package/harness/bundles/java/claude-code/protocols/report-pipeline-protocol.md +11 -4
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +190 -32
- package/harness/bundles/java/claude-code/scripts/harness_events.py +134 -40
- package/harness/bundles/java/claude-code/scripts/harness_preflight.py +46 -3
- package/harness/bundles/java/claude-code/scripts/harness_profile.py +2 -2
- package/harness/bundles/java/claude-code/scripts/harness_state.py +148 -0
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +5 -2
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +5 -2
- package/harness/bundles/java/codebuddy/harness-archive/reference.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/templates/render-summary.mjs +78 -155
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +4 -2
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +5 -3
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +4 -2
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +5 -2
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +6 -3
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +6 -3
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +5 -2
- package/harness/bundles/java/codebuddy/harness-run/reference.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +5 -2
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +5 -3
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +5 -2
- package/harness/bundles/java/codebuddy/harness-test/reference.md +1 -1
- package/harness/bundles/java/codebuddy/protocols/ledger-protocol.md +1 -1
- package/harness/bundles/java/codebuddy/protocols/report-pipeline-protocol.md +11 -4
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +190 -32
- package/harness/bundles/java/codebuddy/scripts/harness_events.py +134 -40
- package/harness/bundles/java/codebuddy/scripts/harness_preflight.py +46 -3
- package/harness/bundles/java/codebuddy/scripts/harness_profile.py +2 -2
- package/harness/bundles/java/codebuddy/scripts/harness_state.py +148 -0
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +5 -2
- package/harness/bundles/java/codex/harness-archive/SKILL.md +5 -2
- package/harness/bundles/java/codex/harness-archive/reference.md +1 -1
- package/harness/bundles/java/codex/harness-archive/templates/render-summary.mjs +78 -155
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +4 -2
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +5 -3
- package/harness/bundles/java/codex/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +4 -2
- package/harness/bundles/java/codex/harness-package/SKILL.md +5 -2
- package/harness/bundles/java/codex/harness-plan/SKILL.md +6 -3
- package/harness/bundles/java/codex/harness-review/SKILL.md +5 -2
- package/harness/bundles/java/codex/harness-run/SKILL.md +5 -2
- package/harness/bundles/java/codex/harness-run/reference.md +1 -1
- package/harness/bundles/java/codex/harness-submit/SKILL.md +5 -2
- package/harness/bundles/java/codex/harness-sync/SKILL.md +5 -3
- package/harness/bundles/java/codex/harness-test/SKILL.md +5 -2
- package/harness/bundles/java/codex/harness-test/reference.md +1 -1
- package/harness/bundles/java/codex/protocols/ledger-protocol.md +1 -1
- package/harness/bundles/java/codex/protocols/report-pipeline-protocol.md +11 -4
- package/harness/bundles/java/codex/scripts/harness_archive.py +190 -32
- package/harness/bundles/java/codex/scripts/harness_events.py +134 -40
- package/harness/bundles/java/codex/scripts/harness_preflight.py +46 -3
- package/harness/bundles/java/codex/scripts/harness_profile.py +2 -2
- package/harness/bundles/java/codex/scripts/harness_state.py +148 -0
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +5 -2
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +5 -2
- package/harness/bundles/java/cursor/harness-archive/reference.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/templates/render-summary.mjs +78 -155
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +4 -2
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +5 -3
- package/harness/bundles/java/cursor/harness-knowledge-ingest/scripts/harness_knowledge.py +83 -6
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +4 -2
- package/harness/bundles/java/cursor/harness-package/SKILL.md +5 -2
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +6 -3
- package/harness/bundles/java/cursor/harness-review/SKILL.md +5 -2
- package/harness/bundles/java/cursor/harness-run/SKILL.md +5 -2
- package/harness/bundles/java/cursor/harness-run/reference.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +5 -2
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +5 -3
- package/harness/bundles/java/cursor/harness-test/SKILL.md +5 -2
- package/harness/bundles/java/cursor/harness-test/reference.md +1 -1
- package/harness/bundles/java/cursor/protocols/ledger-protocol.md +1 -1
- package/harness/bundles/java/cursor/protocols/report-pipeline-protocol.md +11 -4
- package/harness/bundles/java/cursor/scripts/harness_archive.py +190 -32
- package/harness/bundles/java/cursor/scripts/harness_events.py +134 -40
- package/harness/bundles/java/cursor/scripts/harness_preflight.py +46 -3
- package/harness/bundles/java/cursor/scripts/harness_profile.py +2 -2
- package/harness/bundles/java/cursor/scripts/harness_state.py +148 -0
- package/harness/manifests/general/claude-code.json +24 -24
- package/harness/manifests/general/codebuddy.json +24 -24
- package/harness/manifests/general/codex.json +24 -24
- package/harness/manifests/general/cursor.json +24 -24
- package/harness/manifests/java/claude-code.json +26 -26
- package/harness/manifests/java/codebuddy.json +26 -26
- package/harness/manifests/java/codex.json +26 -26
- package/harness/manifests/java/cursor.json +26 -26
- package/hunter-workflow-family.json +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ name: harness-review
|
|
|
3
3
|
description: 6维度代码审查(架构/安全/规范/兼容/测试/性能),对照项目规则(见
|
|
4
4
|
.harness/context-index.json)和测试场景表,在隔离上下文运行。使用场景:代码审查、提交前检查、合并评审
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=5be110134c0a5e82; overlay=java; agent=codex; do not edit -->
|
|
7
7
|
# harness-review — 代码审查
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -39,6 +39,7 @@ description: 6维度代码审查(架构/安全/规范/兼容/测试/性能)
|
|
|
39
39
|
6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
|
|
40
40
|
7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
|
|
41
41
|
8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
|
|
42
|
+
9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/knowledge/change/code 指纹,不再重复跑全量 `harness-sync`、知识重建或代码库扫描。只有对应 segment 变化或缺失时才定向刷新;快照不能替代源码读取和验证门禁。
|
|
42
43
|
|
|
43
44
|
状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
|
|
44
45
|
|
|
@@ -130,7 +131,7 @@ review 读取 `state-snapshot.json`(`harness_state.py` / state-layout-protocol
|
|
|
130
131
|
|
|
131
132
|
## 执行日志
|
|
132
133
|
|
|
133
|
-
`events.ndjson` 为唯一事实源(schema_version
|
|
134
|
+
`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]]
|
|
134
135
|
|
|
135
136
|
```powershell
|
|
136
137
|
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
|
|
@@ -139,3 +140,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
139
140
|
> **脚本接线**:`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 护栏解读。
|
|
140
141
|
|
|
141
142
|
> **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 边界保持最新。
|
|
143
|
+
|
|
144
|
+
每次阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`;重试同一阶段时增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-run
|
|
3
3
|
description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编译验证),逐变更簇实现计划中的任务。使用场景:开始编码、实现功能、写代码、TDD 编码
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=5be110134c0a5e82; overlay=java; agent=codex; do not edit -->
|
|
6
6
|
# harness-run — 需求编码
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -44,6 +44,7 @@ description: 按变更簇执行 TDD 编码循环(RED→GREEN→REFACTOR→编
|
|
|
44
44
|
6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
|
|
45
45
|
7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
|
|
46
46
|
8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
|
|
47
|
+
9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/knowledge/change/code 指纹,不再重复跑全量 `harness-sync`、知识重建或代码库扫描。只有对应 segment 变化或缺失时才定向刷新;快照不能替代源码读取和验证门禁。
|
|
47
48
|
|
|
48
49
|
状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
|
|
49
50
|
|
|
@@ -132,7 +133,7 @@ python <skills-root>/scripts/harness_test_guard.py record --project . --change-d
|
|
|
132
133
|
|
|
133
134
|
## 执行日志
|
|
134
135
|
|
|
135
|
-
`events.ndjson` 为唯一事实源(schema_version
|
|
136
|
+
`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]]
|
|
136
137
|
|
|
137
138
|
```powershell
|
|
138
139
|
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
|
|
@@ -141,3 +142,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
141
142
|
> **脚本接线**:`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 护栏解读。
|
|
142
143
|
|
|
143
144
|
> **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 边界保持最新。
|
|
145
|
+
|
|
146
|
+
每次阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`;重试同一阶段时增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
|
|
@@ -792,7 +792,7 @@ powershell.exe -Command "git -C '<project-path>' diff --check"
|
|
|
792
792
|
|
|
793
793
|
## 执行日志记录
|
|
794
794
|
|
|
795
|
-
`/harness-run` 只向 `events.ndjson` 追加事件(schema_version
|
|
795
|
+
`/harness-run` 只向 `events.ndjson` 追加事件(schema_version 3,兼容读取 v1/v2);`logs/execution-log.md` 由 `harness_events.py append` 自动渲染。步骤 0 之前 append `phase.start`;各阶段写入 `command` / `verification` / `decision` / `issue`,人类可读摘要放 `note`。详见 [[../../protocols/report-pipeline-protocol.md|report-pipeline-protocol]] 与 core `harness-run/SKILL.md`。
|
|
796
796
|
|
|
797
797
|
## verification-ledger 可复用判定
|
|
798
798
|
|
|
@@ -3,7 +3,7 @@ name: harness-submit
|
|
|
3
3
|
description: 最终提交封装:验证→中文 commit→提交/推送;worktree 模式含 --no-ff
|
|
4
4
|
合并回主分支。使用场景:提交代码、commit、push、合并分支、merge to main、完成开发
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=5be110134c0a5e82; overlay=java; agent=codex; do not edit -->
|
|
7
7
|
# harness-submit — 最终提交(含 worktree 合并)
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -40,6 +40,7 @@ description: 最终提交封装:验证→中文 commit→提交/推送;workt
|
|
|
40
40
|
6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
|
|
41
41
|
7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
|
|
42
42
|
8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
|
|
43
|
+
9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/knowledge/change/code 指纹,不再重复跑全量 `harness-sync`、知识重建或代码库扫描。只有对应 segment 变化或缺失时才定向刷新;快照不能替代源码读取和验证门禁。
|
|
43
44
|
|
|
44
45
|
状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
|
|
45
46
|
|
|
@@ -140,7 +141,7 @@ git 经 PowerShell;commit/报告不得含明文密钥。遵循 `../protocols/s
|
|
|
140
141
|
|
|
141
142
|
## 执行日志
|
|
142
143
|
|
|
143
|
-
`events.ndjson` 为唯一事实源(schema_version
|
|
144
|
+
`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]]
|
|
144
145
|
|
|
145
146
|
```powershell
|
|
146
147
|
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
|
|
@@ -149,3 +150,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
149
150
|
> **脚本接线**:`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 护栏解读。
|
|
150
151
|
|
|
151
152
|
> **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 边界保持最新。
|
|
153
|
+
|
|
154
|
+
每次阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`;重试同一阶段时增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-sync
|
|
3
3
|
description: 检查并更新项目AI元数据(CodeGraph索引、harness-codebase-map分析、CLAUDE.md、AGENTS.md、.harness/完整性),确保Claude对代码库的理解与最新代码一致。当用户说'同步/更新索引/刷新元数据/检查一致性'时使用
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=5be110134c0a5e82; overlay=java; agent=codex; do not edit -->
|
|
6
6
|
# harness-sync — 元数据同步
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -22,7 +22,7 @@ description: 检查并更新项目AI元数据(CodeGraph索引、harness-codeba
|
|
|
22
22
|
| 2 | CodeGraph 索引是否需重建 |
|
|
23
23
|
| 3 | `.harness/codebase/map/` 是否过期 → 报告建议 `/harness-codebase-map` |
|
|
24
24
|
| 3.5 | `harness_knowledge.py sync`;可 `sync --update`;失败不得假装可用。**知识闭环主入口**是 `/harness-knowledge-ingest auto`(含 Agent judge),sync 不重复列人工知识待办 |
|
|
25
|
-
| 3.6 |
|
|
25
|
+
| 3.6 | 单次运行 `harness_knowledge.py maintain --project . --drain --json`,有界推进全部 `.harness/knowledge/maintenance-outbox/{pending,failed}`;不得为每个条目重复启动 Python/重建索引(§8.2:archive close 只 enqueue,sync 异步推进 outbox 到 completed/pending-judge) |
|
|
26
26
|
| 4 | CLAUDE.md 完整性/行数 → 超限 AskUserQuestion 瘦身 |
|
|
27
27
|
| 5 | AGENTS.md 与 CLAUDE.md 一致 |
|
|
28
28
|
| 6 | `.harness/` 结构(init 规程 → `reference.md` 第 6 步);可选 `harness_deploy.py diff` 检查已装 skill 是否过期 |
|
|
@@ -54,7 +54,7 @@ description: 检查并更新项目AI元数据(CodeGraph索引、harness-codeba
|
|
|
54
54
|
|
|
55
55
|
## 执行日志
|
|
56
56
|
|
|
57
|
-
`events.ndjson` 为唯一事实源(schema_version
|
|
57
|
+
`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]]
|
|
58
58
|
|
|
59
59
|
```powershell
|
|
60
60
|
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
|
|
@@ -63,3 +63,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
63
63
|
> **脚本接线**:`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 护栏解读。
|
|
64
64
|
|
|
65
65
|
> **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 边界保持最新。
|
|
66
|
+
|
|
67
|
+
每次阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`;重试同一阶段时增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: harness-test
|
|
3
3
|
description: 测试执行:读取场景表,执行单元测试+API接口测试+数据兼容验证,输出测试报告。当用户说'跑测试/验证/跑用例/接口测试/单元测试'时使用
|
|
4
4
|
---
|
|
5
|
-
<!-- generated by harness_deploy.py; core=
|
|
5
|
+
<!-- generated by harness_deploy.py; core=5be110134c0a5e82; overlay=java; agent=codex; do not edit -->
|
|
6
6
|
# harness-test — 测试执行
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
@@ -36,6 +36,7 @@ description: 测试执行:读取场景表,执行单元测试+API接口测试
|
|
|
36
36
|
6. **implementation-detail** — 自适应执行参考;legacy 缺失 🟡WARN,不阻断
|
|
37
37
|
7. **test-scenarios** — 测试真相源:`plans/<change>-test-scenarios.md`
|
|
38
38
|
8. **禁止读取 `docs/superpowers/` 作为正式输入** — 旧草稿仅人工线索
|
|
39
|
+
9. **每个 skill 边界只刷新一次状态快照** — 先运行 `python <skills-root>/scripts/harness_state.py capture --project . --change-dir ".harness/changes/<change-name>" --base <baseCommit> --json`;`changedSegments=[]` 时复用已有 profile/rules/map/knowledge/change/code 指纹,不再重复跑全量 `harness-sync`、知识重建或代码库扫描。只有对应 segment 变化或缺失时才定向刷新;快照不能替代源码读取和验证门禁。
|
|
39
40
|
|
|
40
41
|
状态目录分层:新路径优先,旧路径兼容 → [[../protocols/state-layout-protocol.md|state-layout-protocol]]
|
|
41
42
|
|
|
@@ -175,7 +176,7 @@ python <skills-root>/scripts/harness_test_guard.py record --project . --change-d
|
|
|
175
176
|
|
|
176
177
|
## 执行日志
|
|
177
178
|
|
|
178
|
-
`events.ndjson` 为唯一事实源(schema_version
|
|
179
|
+
`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]]
|
|
179
180
|
|
|
180
181
|
```powershell
|
|
181
182
|
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
|
|
@@ -184,3 +185,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
184
185
|
> **脚本接线**:`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 护栏解读。
|
|
185
186
|
|
|
186
187
|
> **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 边界保持最新。
|
|
188
|
+
|
|
189
|
+
每次阶段结束必须写 `--status OK|WARN|FAIL|BLOCKED`;重试同一阶段时增加 `--attempt <n>`,不得覆盖或伪装成一次执行。跨工具继续执行时写 `--executor-tool <codex|claude-code|codebuddy|cursor>`,并在接棒事件写 `--handoff-from-tool` / `--handoff-reason`;也可由 `HUNTER_HARNESS_TOOL/AGENT/MODEL/RUN_ID` 环境变量统一注入。
|
|
@@ -787,4 +787,4 @@ powershell.exe -NoProfile -Command "$base = '<baseCommit>'; $patch = '.harness/c
|
|
|
787
787
|
|
|
788
788
|
## 执行日志记录
|
|
789
789
|
|
|
790
|
-
`/harness-test` 只向 `events.ndjson` 追加事件(schema_version
|
|
790
|
+
`/harness-test` 只向 `events.ndjson` 追加事件(schema_version 3,兼容读取 v1/v2);`logs/execution-log.md` 由 `harness_events.py append` 自动渲染。Phase 0 之前 append `phase.start`;写入 `command` / `verification` / `decision` / `issue` / `artifact`,摘要放 `note`。详见 [[../../protocols/report-pipeline-protocol.md|report-pipeline-protocol]] 与 core `harness-test/SKILL.md`。
|
|
@@ -86,7 +86,7 @@ verification-ledger 把每次验证(compile / unit test / api test / package
|
|
|
86
86
|
},
|
|
87
87
|
"package": {
|
|
88
88
|
"status": "OK",
|
|
89
|
-
"command": "<打包命令(按技术栈:Java=mvn package -pl <module> -am -DskipTests;前端=npm run build;Python=python -m build)>",
|
|
89
|
+
"command": "<打包命令(按技术栈:Java=mvn clean package -pl <module> -am -DskipTests;前端=npm run build;Python=python -m build)>",
|
|
90
90
|
"baseCommit": "<final pushed hash>",
|
|
91
91
|
"deployArtifact": "<构建产物路径(Java=<module>/target/<artifact>.jar;前端=<module>/dist/;Python=<module>/dist/)>",
|
|
92
92
|
"sha256": "<artifact sha256>",
|
|
@@ -43,11 +43,11 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
43
43
|
|
|
44
44
|
`append` 写入契约(Task 4 §6.1):普通 append = 加锁 -> 追加一行 -> fsync -> 解锁,**不 load 历史、不渲染**(O(1),跨进程锁 `events.ndjson.lock`,UUID 用完整 `uuid4().hex`);仅 `--type phase.end` append 成功后渲染一次 `logs/execution-log.md`;显式 `harness_events.py render` 随时从完整 events 重建;`harness_archive.py finalize` 在 collect 前强制 render 一次。人类可读摘要(触发指令、降级原因、阶段结论)写入事件的 `note` 字段;阶段开始/结束、命令、验证、artifact、问题、决策各写对应 `type` 事件。旧 archive 缺少 events 可回放兼容;新变更不得以 execution-log 已存在为理由跳过 events。
|
|
45
45
|
|
|
46
|
-
基础事件结构(schema_version
|
|
46
|
+
基础事件结构(schema_version 3;兼容读取 v1/v2):
|
|
47
47
|
|
|
48
48
|
```json
|
|
49
49
|
{
|
|
50
|
-
"schema_version":
|
|
50
|
+
"schema_version": 3,
|
|
51
51
|
"id": "evt-...",
|
|
52
52
|
"timestamp": "2026-07-02T00:00:00.000Z",
|
|
53
53
|
"phase": "run",
|
|
@@ -55,6 +55,13 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
55
55
|
"command": "npm test",
|
|
56
56
|
"exit_code": 0,
|
|
57
57
|
"duration_ms": 42100,
|
|
58
|
+
"run_id": "run-...",
|
|
59
|
+
"attempt": 1,
|
|
60
|
+
"executor_tool": "codex",
|
|
61
|
+
"executor_agent": "main",
|
|
62
|
+
"executor_model": "gpt-5",
|
|
63
|
+
"handoff_from_tool": "claude-code",
|
|
64
|
+
"handoff_reason": "continue implementation",
|
|
58
65
|
"note": "可选:触发指令、降级原因、阶段摘要"
|
|
59
66
|
}
|
|
60
67
|
```
|
|
@@ -64,7 +71,7 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
64
71
|
| type | 必填/常用字段 | 用途 |
|
|
65
72
|
|---|---|---|
|
|
66
73
|
| `phase.start` | phase, timestamp | 阶段开始 |
|
|
67
|
-
| `phase.end` | phase, timestamp |
|
|
74
|
+
| `phase.end` | phase, timestamp, status | 阶段结束并记录 `OK/WARN/FAIL/BLOCKED`;计算当前 attempt 耗时 |
|
|
68
75
|
| `command` | command, exit_code, duration_ms | 命令事实 |
|
|
69
76
|
| `verification` | name, status, command | 验证事实 |
|
|
70
77
|
| `artifact` | path, kind | 报告、包、manifest 等产物 |
|
|
@@ -79,7 +86,7 @@ summary-data 必须保留原 archive final report 维度,并增加事件层摘
|
|
|
79
86
|
{
|
|
80
87
|
"schemaVersion": "2.2",
|
|
81
88
|
"reportPipeline": {
|
|
82
|
-
"schema_version":
|
|
89
|
+
"schema_version": 1,
|
|
83
90
|
"generated_at": "2026-07-02T00:00:00.000Z",
|
|
84
91
|
"event_count": 0,
|
|
85
92
|
"sources": ["events.ndjson", "evidence/verification-ledger.json"],
|
|
@@ -622,13 +622,16 @@ def _ledger_unit_tests(ledger: dict[str, Any] | None) -> dict[str, Any]:
|
|
|
622
622
|
status = str(unit.get("status") or "").upper()
|
|
623
623
|
evidence = unit.get("evidence") or {}
|
|
624
624
|
if isinstance(evidence, dict):
|
|
625
|
-
run = evidence.get(
|
|
625
|
+
run = evidence.get(
|
|
626
|
+
"run",
|
|
627
|
+
evidence.get("testsRun", unit.get("run", unit.get("testsRun", 0))),
|
|
628
|
+
)
|
|
626
629
|
failures = evidence.get("failures", unit.get("failures", 0))
|
|
627
630
|
errors = evidence.get("errors", unit.get("errors", 0))
|
|
628
631
|
skipped = evidence.get("skipped", unit.get("skipped", 0))
|
|
629
632
|
pass_rate = evidence.get("passRate") or unit.get("passRate")
|
|
630
633
|
else:
|
|
631
|
-
run = unit.get("run", 0)
|
|
634
|
+
run = unit.get("run", unit.get("testsRun", 0))
|
|
632
635
|
failures = unit.get("failures", 0)
|
|
633
636
|
errors = unit.get("errors", 0)
|
|
634
637
|
skipped = unit.get("skipped", 0)
|
|
@@ -886,16 +889,21 @@ def _durations_from_event_phases(event_summary: dict[str, Any]) -> dict[str, Any
|
|
|
886
889
|
minutes = round((dur or 0) / 60000, 2) if dur is not None else 0
|
|
887
890
|
if dur:
|
|
888
891
|
total_ms += int(dur)
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
}
|
|
898
|
-
|
|
892
|
+
attempts = info.get("attempts") if isinstance(info.get("attempts"), list) else []
|
|
893
|
+
result = str(info.get("status") or "UNKNOWN").upper()
|
|
894
|
+
if result in {"PASS", "PASSED", "SUCCESS"}:
|
|
895
|
+
result = "OK"
|
|
896
|
+
elif result in {"FAILED", "ERROR"}:
|
|
897
|
+
result = "FAIL"
|
|
898
|
+
stages.append({
|
|
899
|
+
"stage": str(name),
|
|
900
|
+
"skill": f"harness-{name}",
|
|
901
|
+
"startedAt": info.get("started_at") or NOT_AVAILABLE,
|
|
902
|
+
"endedAt": info.get("ended_at") or NOT_AVAILABLE,
|
|
903
|
+
"minutes": minutes,
|
|
904
|
+
"result": result,
|
|
905
|
+
"attempts": attempts,
|
|
906
|
+
})
|
|
899
907
|
total_min = round(total_ms / 60000, 2)
|
|
900
908
|
return {
|
|
901
909
|
"totalLabel": f"约 {int(round(total_min))} 分",
|
|
@@ -917,11 +925,31 @@ def _stage_status_from_sources(
|
|
|
917
925
|
"submit": "OK",
|
|
918
926
|
"archive": "OK",
|
|
919
927
|
}
|
|
928
|
+
for event in events:
|
|
929
|
+
if event.get("type") != "phase.end":
|
|
930
|
+
continue
|
|
931
|
+
phase = str(event.get("phase") or "").lower()
|
|
932
|
+
raw = str(event.get("status") or "").upper()
|
|
933
|
+
if phase not in status or not raw:
|
|
934
|
+
continue
|
|
935
|
+
if raw in {"PASS", "PASSED", "SUCCESS"}:
|
|
936
|
+
raw = "OK"
|
|
937
|
+
elif raw in {"FAILED", "ERROR"}:
|
|
938
|
+
raw = "FAIL"
|
|
939
|
+
elif raw in {"SKIP", "SKIPPED"}:
|
|
940
|
+
raw = "USER_SKIPPED"
|
|
941
|
+
status[phase] = raw
|
|
920
942
|
# Issues in events can downgrade
|
|
921
943
|
for e in events:
|
|
922
944
|
if e.get("type") != "issue":
|
|
923
945
|
continue
|
|
924
946
|
sev = str(e.get("severity") or "").lower()
|
|
947
|
+
issue_text = " ".join(str(e.get(key) or "") for key in ("message", "note", "code")).lower()
|
|
948
|
+
if not sev:
|
|
949
|
+
if any(token in issue_text for token in ("fail", "error", "blocked", "失败", "阻塞")):
|
|
950
|
+
sev = "error"
|
|
951
|
+
elif any(token in issue_text for token in ("warn", "skip", "风险", "警告")):
|
|
952
|
+
sev = "warn"
|
|
925
953
|
phase = str(e.get("phase") or "").lower()
|
|
926
954
|
if phase in status and sev in {"error", "fail", "failed", "critical"}:
|
|
927
955
|
status[phase] = "FAIL"
|
|
@@ -930,14 +958,19 @@ def _stage_status_from_sources(
|
|
|
930
958
|
|
|
931
959
|
api = _ledger_api_tests(ledger)
|
|
932
960
|
db = _ledger_db_compat(ledger)
|
|
933
|
-
|
|
961
|
+
api_status = str(api.get("status") or "").upper()
|
|
962
|
+
if api_status in {"FAIL", "FAILED", "ERROR"} or int(api.get("failed") or 0) > 0:
|
|
963
|
+
status["test"] = "FAIL"
|
|
964
|
+
elif api_status in {"BLOCKED", "BLOCKED_BY_ENV", "BLOCKED_BY_DBA"}:
|
|
965
|
+
status["test"] = api_status
|
|
966
|
+
elif api_status == "USER_SKIPPED":
|
|
934
967
|
status["test"] = "USER_SKIPPED"
|
|
935
968
|
elif db == "BLOCKED_BY_DBA":
|
|
936
969
|
status["test"] = "BLOCKED_BY_DBA"
|
|
937
970
|
elif api.get("status") == "NOT_RUN" and not find_test_reports(change_dir):
|
|
938
971
|
unit = _ledger_unit_tests(ledger)
|
|
939
972
|
if unit.get("source") == "not-run" and unit.get("run", 0) == 0:
|
|
940
|
-
status["test"] = "
|
|
973
|
+
status["test"] = "NOT_RUN"
|
|
941
974
|
|
|
942
975
|
if not find_review_reports(change_dir):
|
|
943
976
|
log_text = load_execution_log(change_dir)
|
|
@@ -954,8 +987,6 @@ def _compute_final_status(
|
|
|
954
987
|
api = verification.get("apiTests") or {}
|
|
955
988
|
db = str(verification.get("dbCompatibility") or "")
|
|
956
989
|
api_status = str(api.get("status") or "")
|
|
957
|
-
if api_status == "USER_SKIPPED" or db == "BLOCKED_BY_DBA":
|
|
958
|
-
return "CONDITIONAL_OK"
|
|
959
990
|
for v in stage_status.values():
|
|
960
991
|
if v == "FAIL":
|
|
961
992
|
return "FAIL"
|
|
@@ -964,8 +995,14 @@ def _compute_final_status(
|
|
|
964
995
|
return "FAIL"
|
|
965
996
|
if int(api.get("failed") or 0) > 0:
|
|
966
997
|
return "FAIL"
|
|
998
|
+
conditional = {
|
|
999
|
+
"USER_SKIPPED", "BLOCKED", "BLOCKED_BY_ENV", "BLOCKED_BY_DBA",
|
|
1000
|
+
"NOT_RUN", "PARTIAL",
|
|
1001
|
+
}
|
|
1002
|
+
if api_status in conditional or db in conditional:
|
|
1003
|
+
return "CONDITIONAL_OK"
|
|
967
1004
|
for v in stage_status.values():
|
|
968
|
-
if v in {"WARN",
|
|
1005
|
+
if v in {"WARN", *conditional}:
|
|
969
1006
|
return "WARN" if v == "WARN" else "CONDITIONAL_OK"
|
|
970
1007
|
return "OK"
|
|
971
1008
|
|
|
@@ -984,6 +1021,8 @@ def _changed_files_from_git(
|
|
|
984
1021
|
changed: list[dict[str, Any]] = []
|
|
985
1022
|
if not base or not head:
|
|
986
1023
|
return diff_stat, changed
|
|
1024
|
+
# The ledger bounds the complete task. Using only ``head^..head`` would
|
|
1025
|
+
# silently omit earlier checkpoint commits from the same change.
|
|
987
1026
|
rng = f"{base}..{head}"
|
|
988
1027
|
code, out, _ = git_run(project, "diff", "--numstat", rng)
|
|
989
1028
|
if code != 0 or not out:
|
|
@@ -1040,6 +1079,97 @@ def _review_summary(change_dir: Path, existing: dict[str, Any] | None) -> dict[s
|
|
|
1040
1079
|
return base
|
|
1041
1080
|
|
|
1042
1081
|
|
|
1082
|
+
def _final_commit_from_sources(
|
|
1083
|
+
ledger: dict[str, Any] | None,
|
|
1084
|
+
events: list[dict[str, Any]],
|
|
1085
|
+
existing: dict[str, Any] | None,
|
|
1086
|
+
project: Path,
|
|
1087
|
+
) -> str:
|
|
1088
|
+
if ledger:
|
|
1089
|
+
for key in (
|
|
1090
|
+
"mergeFinalHash", "finalCommit", "finalHash", "changeCommit", "headCommit",
|
|
1091
|
+
):
|
|
1092
|
+
value = str(ledger.get(key) or "").strip()
|
|
1093
|
+
if value:
|
|
1094
|
+
return value
|
|
1095
|
+
commit_pattern = re.compile(r"\b[0-9a-f]{7,40}\b", re.IGNORECASE)
|
|
1096
|
+
for event in reversed(events):
|
|
1097
|
+
if str(event.get("phase") or "").lower() not in {"submit", "merge", "archive"}:
|
|
1098
|
+
continue
|
|
1099
|
+
text = " ".join(str(event.get(key) or "") for key in ("note", "message", "command"))
|
|
1100
|
+
match = commit_pattern.search(text)
|
|
1101
|
+
if match:
|
|
1102
|
+
return match.group(0)
|
|
1103
|
+
if existing and existing.get("finalCommit"):
|
|
1104
|
+
return str(existing["finalCommit"])
|
|
1105
|
+
code, head, _ = git_run(project, "rev-parse", "HEAD")
|
|
1106
|
+
return head if code == 0 and head else ""
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
def _business_goal_from_sources(change_dir: Path, events: list[dict[str, Any]]) -> str:
|
|
1110
|
+
plans_root = change_dir / "plans"
|
|
1111
|
+
primary = sorted(plans_root.glob("*-plan.md"))
|
|
1112
|
+
secondary = [
|
|
1113
|
+
path for path in sorted(plans_root.glob("*.md"))
|
|
1114
|
+
if path not in primary
|
|
1115
|
+
and "implementation-detail" not in path.name
|
|
1116
|
+
and "test-scenarios" not in path.name
|
|
1117
|
+
]
|
|
1118
|
+
for plan in [*primary, *secondary]:
|
|
1119
|
+
try:
|
|
1120
|
+
text = plan.read_text(encoding="utf-8-sig")
|
|
1121
|
+
except OSError:
|
|
1122
|
+
continue
|
|
1123
|
+
body = re.sub(r"\A---\s*\n.*?\n---\s*\n", "", text, count=1, flags=re.DOTALL)
|
|
1124
|
+
goal = re.search(r"(?im)^\s*(?:goal|目标|业务目标|需求)\s*[::]\s*(.+)$", body)
|
|
1125
|
+
if goal:
|
|
1126
|
+
return goal.group(1).strip()
|
|
1127
|
+
scope = re.search(r"(?im)^\s*>?\s*(?:变更范围|目标)\s*[::]\s*(.+)$", body)
|
|
1128
|
+
if scope:
|
|
1129
|
+
return scope.group(1).strip()
|
|
1130
|
+
first_task = re.search(r"(?m)^\s*\|\s*1\s*\|\s*([^|]+?)\s*\|", body)
|
|
1131
|
+
if first_task:
|
|
1132
|
+
return first_task.group(1).strip()
|
|
1133
|
+
for line in body.splitlines():
|
|
1134
|
+
clean = line.strip().lstrip("#").strip()
|
|
1135
|
+
if clean and not clean.startswith(("---", ">", "|")) and len(clean) > 8:
|
|
1136
|
+
return clean
|
|
1137
|
+
for event in events:
|
|
1138
|
+
if event.get("type") == "decision":
|
|
1139
|
+
value = str(event.get("decision") or event.get("note") or "").strip()
|
|
1140
|
+
if value:
|
|
1141
|
+
return re.sub(r"^(?:需求收敛|目标)\s*[::]\s*", "", value)
|
|
1142
|
+
return ""
|
|
1143
|
+
|
|
1144
|
+
|
|
1145
|
+
def _timeline_from_events(event_summary: dict[str, Any], events: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
1146
|
+
timeline: list[dict[str, Any]] = []
|
|
1147
|
+
for phase, info in (event_summary.get("phases") or {}).items():
|
|
1148
|
+
attempts = info.get("attempts") if isinstance(info.get("attempts"), list) else []
|
|
1149
|
+
for attempt in attempts:
|
|
1150
|
+
timeline.append({
|
|
1151
|
+
"phase": phase,
|
|
1152
|
+
"attempt": attempt.get("attempt"),
|
|
1153
|
+
"startedAt": attempt.get("started_at"),
|
|
1154
|
+
"endedAt": attempt.get("ended_at"),
|
|
1155
|
+
"durationMs": attempt.get("duration_ms"),
|
|
1156
|
+
"status": attempt.get("status") or "UNKNOWN",
|
|
1157
|
+
"executorTool": attempt.get("executor_tool"),
|
|
1158
|
+
"executorAgent": attempt.get("executor_agent"),
|
|
1159
|
+
"handoffFromTool": attempt.get("handoff_from_tool"),
|
|
1160
|
+
})
|
|
1161
|
+
for event in events:
|
|
1162
|
+
if event.get("type") not in {"decision", "issue"}:
|
|
1163
|
+
continue
|
|
1164
|
+
timeline.append({
|
|
1165
|
+
"phase": event.get("phase"),
|
|
1166
|
+
"timestamp": event.get("timestamp"),
|
|
1167
|
+
"type": event.get("type"),
|
|
1168
|
+
"summary": event.get("decision") or event.get("message") or event.get("note") or "",
|
|
1169
|
+
})
|
|
1170
|
+
return timeline
|
|
1171
|
+
|
|
1172
|
+
|
|
1043
1173
|
def collect_summary_data(
|
|
1044
1174
|
change_dir: Path,
|
|
1045
1175
|
*,
|
|
@@ -1118,18 +1248,14 @@ def collect_summary_data(
|
|
|
1118
1248
|
if existing and existing.get("businessGoal"):
|
|
1119
1249
|
data["businessGoal"] = existing["businessGoal"]
|
|
1120
1250
|
else:
|
|
1121
|
-
|
|
1251
|
+
inferred_goal = _business_goal_from_sources(change_dir, events)
|
|
1252
|
+
data["businessGoal"] = inferred_goal or (NOT_AVAILABLE if for_replay else "")
|
|
1122
1253
|
|
|
1123
1254
|
# commits
|
|
1124
1255
|
project = find_project_root(change_dir)
|
|
1125
1256
|
if not data.get("finalCommit") or str(data.get("finalCommit")).startswith("<"):
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
data["finalCommit"] = head
|
|
1129
|
-
elif existing and existing.get("finalCommit"):
|
|
1130
|
-
data["finalCommit"] = existing["finalCommit"]
|
|
1131
|
-
else:
|
|
1132
|
-
data["finalCommit"] = NOT_AVAILABLE if for_replay else ""
|
|
1257
|
+
final_commit = _final_commit_from_sources(ledger, events, existing, project)
|
|
1258
|
+
data["finalCommit"] = final_commit or (NOT_AVAILABLE if for_replay else "")
|
|
1133
1259
|
|
|
1134
1260
|
if not data.get("baseCommit") or str(data.get("baseCommit")).startswith("<"):
|
|
1135
1261
|
if ledger and ledger.get("baseCommit"):
|
|
@@ -1235,18 +1361,43 @@ def collect_summary_data(
|
|
|
1235
1361
|
if not isinstance(data.get("artifacts"), list):
|
|
1236
1362
|
data["artifacts"] = []
|
|
1237
1363
|
if not for_replay:
|
|
1238
|
-
|
|
1239
|
-
pass
|
|
1364
|
+
data["artifacts"] = _artifacts_from_events(events)
|
|
1240
1365
|
|
|
1241
1366
|
data["reviewSummary"] = _review_summary(change_dir, existing if for_replay else None)
|
|
1242
|
-
|
|
1367
|
+
if not for_replay:
|
|
1368
|
+
data["timeline"] = _timeline_from_events(event_summary, events)
|
|
1369
|
+
else:
|
|
1370
|
+
data.setdefault("timeline", [])
|
|
1243
1371
|
data.setdefault("uncommittedTestEvidence", [])
|
|
1244
1372
|
|
|
1245
|
-
#
|
|
1373
|
+
# Derive risks/actions from evidence. These fields are facts, not model prose.
|
|
1246
1374
|
if not for_replay:
|
|
1247
|
-
data["maintenanceNotes"] = [
|
|
1248
|
-
|
|
1375
|
+
data["maintenanceNotes"] = [
|
|
1376
|
+
str(event.get("note") or event.get("message") or "")
|
|
1377
|
+
for event in events
|
|
1378
|
+
if event.get("type") == "decision" and (event.get("note") or event.get("message"))
|
|
1379
|
+
]
|
|
1380
|
+
data["knownRisks"] = [
|
|
1381
|
+
{
|
|
1382
|
+
"phase": event.get("phase"),
|
|
1383
|
+
"severity": event.get("severity") or "unknown",
|
|
1384
|
+
"message": event.get("message") or event.get("note") or event.get("code") or "",
|
|
1385
|
+
}
|
|
1386
|
+
for event in events
|
|
1387
|
+
if event.get("type") == "issue"
|
|
1388
|
+
and not (
|
|
1389
|
+
event.get("phase") == "archive"
|
|
1390
|
+
and event.get("code") == "missing-command"
|
|
1391
|
+
)
|
|
1392
|
+
]
|
|
1249
1393
|
data["manualActions"] = []
|
|
1394
|
+
for name, value in (data.get("stageStatus") or {}).items():
|
|
1395
|
+
if value in {"BLOCKED", "BLOCKED_BY_ENV", "BLOCKED_BY_DBA", "NOT_RUN", "USER_SKIPPED"}:
|
|
1396
|
+
data["manualActions"].append({
|
|
1397
|
+
"stage": name,
|
|
1398
|
+
"status": value,
|
|
1399
|
+
"action": "补充或确认该阶段的真实验证证据",
|
|
1400
|
+
})
|
|
1250
1401
|
else:
|
|
1251
1402
|
data.setdefault("maintenanceNotes", [])
|
|
1252
1403
|
data.setdefault("knownRisks", [])
|
|
@@ -1978,6 +2129,7 @@ def cmd_finalize(
|
|
|
1978
2129
|
_restore_target,
|
|
1979
2130
|
phase="archive",
|
|
1980
2131
|
type_="phase.end",
|
|
2132
|
+
status="FAIL",
|
|
1981
2133
|
note="finalize aborted; render failed; original preserved",
|
|
1982
2134
|
)
|
|
1983
2135
|
except OSError:
|
|
@@ -2117,6 +2269,7 @@ def cmd_finalize(
|
|
|
2117
2269
|
_safe_append_restored,
|
|
2118
2270
|
phase="archive",
|
|
2119
2271
|
type_="phase.end",
|
|
2272
|
+
status="FAIL",
|
|
2120
2273
|
note="finalize aborted; original preserved",
|
|
2121
2274
|
)
|
|
2122
2275
|
except OSError:
|
|
@@ -2168,7 +2321,12 @@ def cmd_finalize(
|
|
|
2168
2321
|
if service_result.get("warning"):
|
|
2169
2322
|
warnings.append(str(service_result["warning"]))
|
|
2170
2323
|
|
|
2171
|
-
_safe_append(
|
|
2324
|
+
_safe_append(
|
|
2325
|
+
phase="archive",
|
|
2326
|
+
type_="phase.end",
|
|
2327
|
+
status="WARN" if warnings else "OK",
|
|
2328
|
+
note="finalize complete",
|
|
2329
|
+
)
|
|
2172
2330
|
|
|
2173
2331
|
payload["ok"] = True
|
|
2174
2332
|
payload["warnings"] = warnings
|