@hunter-harness/workflow-harness 0.2.5 → 0.2.7
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/contracts/workflow-policy.json +169 -0
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +7 -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 +109 -7
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +2 -0
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +6 -2
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +8 -3
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +8 -3
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +13 -7
- package/harness/bundles/general/claude-code/harness-run/checklist.md +2 -2
- package/harness/bundles/general/claude-code/harness-run/reference.md +6 -3
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +11 -6
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +5 -3
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +12 -3
- 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_apidoc.py +53 -0
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +231 -48
- package/harness/bundles/general/claude-code/scripts/harness_change.py +854 -0
- package/harness/bundles/general/claude-code/scripts/harness_events.py +134 -40
- package/harness/bundles/general/claude-code/scripts/harness_gate.py +1038 -0
- 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_service.py +34 -1
- package/harness/bundles/general/claude-code/scripts/harness_state.py +148 -0
- package/harness/bundles/general/claude-code/scripts/harness_test_guard.py +210 -14
- package/harness/bundles/general/claude-code/scripts/harness_workflow_policy.py +160 -0
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/contracts/workflow-policy.json +169 -0
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +7 -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 +109 -7
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +2 -0
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +6 -2
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +8 -3
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +8 -3
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +13 -7
- package/harness/bundles/general/codebuddy/harness-run/checklist.md +2 -2
- package/harness/bundles/general/codebuddy/harness-run/reference.md +6 -3
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +11 -6
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +5 -3
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +12 -3
- 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_apidoc.py +53 -0
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +231 -48
- package/harness/bundles/general/codebuddy/scripts/harness_change.py +854 -0
- package/harness/bundles/general/codebuddy/scripts/harness_events.py +134 -40
- package/harness/bundles/general/codebuddy/scripts/harness_gate.py +1038 -0
- 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_service.py +34 -1
- package/harness/bundles/general/codebuddy/scripts/harness_state.py +148 -0
- package/harness/bundles/general/codebuddy/scripts/harness_test_guard.py +210 -14
- package/harness/bundles/general/codebuddy/scripts/harness_workflow_policy.py +160 -0
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/contracts/workflow-policy.json +169 -0
- package/harness/bundles/general/codex/harness-archive/SKILL.md +7 -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 +109 -7
- package/harness/bundles/general/codex/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +2 -0
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +6 -2
- package/harness/bundles/general/codex/harness-plan/SKILL.md +8 -3
- package/harness/bundles/general/codex/harness-review/SKILL.md +7 -2
- package/harness/bundles/general/codex/harness-run/SKILL.md +13 -7
- package/harness/bundles/general/codex/harness-run/checklist.md +2 -2
- package/harness/bundles/general/codex/harness-run/reference.md +6 -3
- package/harness/bundles/general/codex/harness-submit/SKILL.md +11 -6
- package/harness/bundles/general/codex/harness-sync/SKILL.md +5 -3
- package/harness/bundles/general/codex/harness-test/SKILL.md +12 -3
- 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_apidoc.py +53 -0
- package/harness/bundles/general/codex/scripts/harness_archive.py +231 -48
- package/harness/bundles/general/codex/scripts/harness_change.py +854 -0
- package/harness/bundles/general/codex/scripts/harness_events.py +134 -40
- package/harness/bundles/general/codex/scripts/harness_gate.py +1038 -0
- 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_service.py +34 -1
- package/harness/bundles/general/codex/scripts/harness_state.py +148 -0
- package/harness/bundles/general/codex/scripts/harness_test_guard.py +210 -14
- package/harness/bundles/general/codex/scripts/harness_workflow_policy.py +160 -0
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/contracts/workflow-policy.json +169 -0
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +7 -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 +109 -7
- package/harness/bundles/general/cursor/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +2 -0
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +6 -2
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +8 -3
- package/harness/bundles/general/cursor/harness-review/SKILL.md +7 -2
- package/harness/bundles/general/cursor/harness-run/SKILL.md +13 -7
- package/harness/bundles/general/cursor/harness-run/checklist.md +2 -2
- package/harness/bundles/general/cursor/harness-run/reference.md +6 -3
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +11 -6
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +5 -3
- package/harness/bundles/general/cursor/harness-test/SKILL.md +12 -3
- 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_apidoc.py +53 -0
- package/harness/bundles/general/cursor/scripts/harness_archive.py +231 -48
- package/harness/bundles/general/cursor/scripts/harness_change.py +854 -0
- package/harness/bundles/general/cursor/scripts/harness_events.py +134 -40
- package/harness/bundles/general/cursor/scripts/harness_gate.py +1038 -0
- 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_service.py +34 -1
- package/harness/bundles/general/cursor/scripts/harness_state.py +148 -0
- package/harness/bundles/general/cursor/scripts/harness_test_guard.py +210 -14
- package/harness/bundles/general/cursor/scripts/harness_workflow_policy.py +160 -0
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/contracts/workflow-policy.json +169 -0
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +11 -2
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +7 -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 +109 -7
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +2 -0
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +6 -2
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +10 -4
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +8 -3
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +8 -3
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +13 -7
- package/harness/bundles/java/claude-code/harness-run/reference.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +11 -6
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +5 -3
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +12 -3
- 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_apidoc.py +53 -0
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +231 -48
- package/harness/bundles/java/claude-code/scripts/harness_change.py +854 -0
- package/harness/bundles/java/claude-code/scripts/harness_events.py +134 -40
- package/harness/bundles/java/claude-code/scripts/harness_gate.py +1038 -0
- 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_service.py +34 -1
- package/harness/bundles/java/claude-code/scripts/harness_state.py +148 -0
- package/harness/bundles/java/claude-code/scripts/harness_test_guard.py +210 -14
- package/harness/bundles/java/claude-code/scripts/harness_workflow_policy.py +160 -0
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/contracts/workflow-policy.json +169 -0
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +11 -2
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +7 -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 +109 -7
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +2 -0
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +6 -2
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +10 -4
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +8 -3
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +8 -3
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +13 -7
- package/harness/bundles/java/codebuddy/harness-run/reference.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +11 -6
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +5 -3
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +12 -3
- 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_apidoc.py +53 -0
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +231 -48
- package/harness/bundles/java/codebuddy/scripts/harness_change.py +854 -0
- package/harness/bundles/java/codebuddy/scripts/harness_events.py +134 -40
- package/harness/bundles/java/codebuddy/scripts/harness_gate.py +1038 -0
- 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_service.py +34 -1
- package/harness/bundles/java/codebuddy/scripts/harness_state.py +148 -0
- package/harness/bundles/java/codebuddy/scripts/harness_test_guard.py +210 -14
- package/harness/bundles/java/codebuddy/scripts/harness_workflow_policy.py +160 -0
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/contracts/workflow-policy.json +169 -0
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +11 -2
- package/harness/bundles/java/codex/harness-archive/SKILL.md +7 -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 +109 -7
- package/harness/bundles/java/codex/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +2 -0
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +6 -2
- package/harness/bundles/java/codex/harness-package/SKILL.md +10 -4
- package/harness/bundles/java/codex/harness-plan/SKILL.md +8 -3
- package/harness/bundles/java/codex/harness-review/SKILL.md +7 -2
- package/harness/bundles/java/codex/harness-run/SKILL.md +13 -7
- package/harness/bundles/java/codex/harness-run/reference.md +1 -1
- package/harness/bundles/java/codex/harness-submit/SKILL.md +11 -6
- package/harness/bundles/java/codex/harness-sync/SKILL.md +5 -3
- package/harness/bundles/java/codex/harness-test/SKILL.md +12 -3
- 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_apidoc.py +53 -0
- package/harness/bundles/java/codex/scripts/harness_archive.py +231 -48
- package/harness/bundles/java/codex/scripts/harness_change.py +854 -0
- package/harness/bundles/java/codex/scripts/harness_events.py +134 -40
- package/harness/bundles/java/codex/scripts/harness_gate.py +1038 -0
- 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_service.py +34 -1
- package/harness/bundles/java/codex/scripts/harness_state.py +148 -0
- package/harness/bundles/java/codex/scripts/harness_test_guard.py +210 -14
- package/harness/bundles/java/codex/scripts/harness_workflow_policy.py +160 -0
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/contracts/workflow-policy.json +169 -0
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +11 -2
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +7 -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 +109 -7
- package/harness/bundles/java/cursor/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py +2 -0
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +6 -2
- package/harness/bundles/java/cursor/harness-package/SKILL.md +10 -4
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +8 -3
- package/harness/bundles/java/cursor/harness-review/SKILL.md +7 -2
- package/harness/bundles/java/cursor/harness-run/SKILL.md +13 -7
- package/harness/bundles/java/cursor/harness-run/reference.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +11 -6
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +5 -3
- package/harness/bundles/java/cursor/harness-test/SKILL.md +12 -3
- 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_apidoc.py +53 -0
- package/harness/bundles/java/cursor/scripts/harness_archive.py +231 -48
- package/harness/bundles/java/cursor/scripts/harness_change.py +854 -0
- package/harness/bundles/java/cursor/scripts/harness_events.py +134 -40
- package/harness/bundles/java/cursor/scripts/harness_gate.py +1038 -0
- 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_service.py +34 -1
- package/harness/bundles/java/cursor/scripts/harness_state.py +148 -0
- package/harness/bundles/java/cursor/scripts/harness_test_guard.py +210 -14
- package/harness/bundles/java/cursor/scripts/harness_workflow_policy.py +160 -0
- package/harness/manifests/general/claude-code.json +48 -28
- package/harness/manifests/general/codebuddy.json +48 -28
- package/harness/manifests/general/codex.json +48 -28
- package/harness/manifests/general/cursor.json +48 -28
- package/harness/manifests/java/claude-code.json +49 -29
- package/harness/manifests/java/codebuddy.json +49 -29
- package/harness/manifests/java/codex.json +49 -29
- package/harness/manifests/java/cursor.json +49 -29
- package/hunter-workflow-family.json +2 -2
- package/package.json +1 -1
|
@@ -359,6 +359,28 @@ def find_review_reports(change_dir: Path) -> list[Path]:
|
|
|
359
359
|
return found
|
|
360
360
|
|
|
361
361
|
|
|
362
|
+
def review_phase_completed(events: list[dict[str, Any]]) -> bool:
|
|
363
|
+
"""True only when structured events record a review phase.end (UT-042)."""
|
|
364
|
+
for event in events:
|
|
365
|
+
if event.get("type") != "phase.end":
|
|
366
|
+
continue
|
|
367
|
+
if str(event.get("phase") or "").lower() == "review":
|
|
368
|
+
return True
|
|
369
|
+
return False
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def review_evidence_present(
|
|
373
|
+
change_dir: Path,
|
|
374
|
+
events: list[dict[str, Any]] | None = None,
|
|
375
|
+
) -> bool:
|
|
376
|
+
"""Review ran only when report files or review phase.end events exist."""
|
|
377
|
+
if find_review_reports(change_dir):
|
|
378
|
+
return True
|
|
379
|
+
if events is None:
|
|
380
|
+
events = he.load_events(change_dir / "events.ndjson")
|
|
381
|
+
return review_phase_completed(events)
|
|
382
|
+
|
|
383
|
+
|
|
362
384
|
def git_run(project: Path, *args: str) -> tuple[int, str, str]:
|
|
363
385
|
try:
|
|
364
386
|
proc = subprocess.run(
|
|
@@ -531,10 +553,9 @@ def check_status(change_dir: Path) -> dict[str, Any]:
|
|
|
531
553
|
# --- test / review reports ---
|
|
532
554
|
test_reports = find_test_reports(change_dir)
|
|
533
555
|
review_reports = find_review_reports(change_dir)
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
)
|
|
556
|
+
events_path = change_dir / "events.ndjson"
|
|
557
|
+
events = he.load_events(events_path) if events_path.is_file() else []
|
|
558
|
+
review_ran = review_evidence_present(change_dir, events)
|
|
538
559
|
|
|
539
560
|
checks["test_reports"] = [str(p.relative_to(change_dir)) for p in test_reports]
|
|
540
561
|
checks["review_reports"] = [str(p.relative_to(change_dir)) for p in review_reports]
|
|
@@ -555,13 +576,13 @@ def check_status(change_dir: Path) -> dict[str, Any]:
|
|
|
555
576
|
|
|
556
577
|
if review_reports:
|
|
557
578
|
checks["review_report_status"] = "present"
|
|
558
|
-
elif
|
|
579
|
+
elif review_phase_completed(events):
|
|
559
580
|
checks["review_report_status"] = "ran-but-not-persisted"
|
|
560
581
|
warnings.append(
|
|
561
582
|
{
|
|
562
583
|
"code": "review-not-persisted",
|
|
563
584
|
"message": (
|
|
564
|
-
"
|
|
585
|
+
"review phase.end recorded but no review-report file; "
|
|
565
586
|
"prefer persisting report before archive"
|
|
566
587
|
),
|
|
567
588
|
}
|
|
@@ -622,13 +643,16 @@ def _ledger_unit_tests(ledger: dict[str, Any] | None) -> dict[str, Any]:
|
|
|
622
643
|
status = str(unit.get("status") or "").upper()
|
|
623
644
|
evidence = unit.get("evidence") or {}
|
|
624
645
|
if isinstance(evidence, dict):
|
|
625
|
-
run = evidence.get(
|
|
646
|
+
run = evidence.get(
|
|
647
|
+
"run",
|
|
648
|
+
evidence.get("testsRun", unit.get("run", unit.get("testsRun", 0))),
|
|
649
|
+
)
|
|
626
650
|
failures = evidence.get("failures", unit.get("failures", 0))
|
|
627
651
|
errors = evidence.get("errors", unit.get("errors", 0))
|
|
628
652
|
skipped = evidence.get("skipped", unit.get("skipped", 0))
|
|
629
653
|
pass_rate = evidence.get("passRate") or unit.get("passRate")
|
|
630
654
|
else:
|
|
631
|
-
run = unit.get("run", 0)
|
|
655
|
+
run = unit.get("run", unit.get("testsRun", 0))
|
|
632
656
|
failures = unit.get("failures", 0)
|
|
633
657
|
errors = unit.get("errors", 0)
|
|
634
658
|
skipped = unit.get("skipped", 0)
|
|
@@ -886,16 +910,21 @@ def _durations_from_event_phases(event_summary: dict[str, Any]) -> dict[str, Any
|
|
|
886
910
|
minutes = round((dur or 0) / 60000, 2) if dur is not None else 0
|
|
887
911
|
if dur:
|
|
888
912
|
total_ms += int(dur)
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
}
|
|
898
|
-
|
|
913
|
+
attempts = info.get("attempts") if isinstance(info.get("attempts"), list) else []
|
|
914
|
+
result = str(info.get("status") or "UNKNOWN").upper()
|
|
915
|
+
if result in {"PASS", "PASSED", "SUCCESS"}:
|
|
916
|
+
result = "OK"
|
|
917
|
+
elif result in {"FAILED", "ERROR"}:
|
|
918
|
+
result = "FAIL"
|
|
919
|
+
stages.append({
|
|
920
|
+
"stage": str(name),
|
|
921
|
+
"skill": f"harness-{name}",
|
|
922
|
+
"startedAt": info.get("started_at") or NOT_AVAILABLE,
|
|
923
|
+
"endedAt": info.get("ended_at") or NOT_AVAILABLE,
|
|
924
|
+
"minutes": minutes,
|
|
925
|
+
"result": result,
|
|
926
|
+
"attempts": attempts,
|
|
927
|
+
})
|
|
899
928
|
total_min = round(total_ms / 60000, 2)
|
|
900
929
|
return {
|
|
901
930
|
"totalLabel": f"约 {int(round(total_min))} 分",
|
|
@@ -917,11 +946,31 @@ def _stage_status_from_sources(
|
|
|
917
946
|
"submit": "OK",
|
|
918
947
|
"archive": "OK",
|
|
919
948
|
}
|
|
949
|
+
for event in events:
|
|
950
|
+
if event.get("type") != "phase.end":
|
|
951
|
+
continue
|
|
952
|
+
phase = str(event.get("phase") or "").lower()
|
|
953
|
+
raw = str(event.get("status") or "").upper()
|
|
954
|
+
if phase not in status or not raw:
|
|
955
|
+
continue
|
|
956
|
+
if raw in {"PASS", "PASSED", "SUCCESS"}:
|
|
957
|
+
raw = "OK"
|
|
958
|
+
elif raw in {"FAILED", "ERROR"}:
|
|
959
|
+
raw = "FAIL"
|
|
960
|
+
elif raw in {"SKIP", "SKIPPED"}:
|
|
961
|
+
raw = "USER_SKIPPED"
|
|
962
|
+
status[phase] = raw
|
|
920
963
|
# Issues in events can downgrade
|
|
921
964
|
for e in events:
|
|
922
965
|
if e.get("type") != "issue":
|
|
923
966
|
continue
|
|
924
967
|
sev = str(e.get("severity") or "").lower()
|
|
968
|
+
issue_text = " ".join(str(e.get(key) or "") for key in ("message", "note", "code")).lower()
|
|
969
|
+
if not sev:
|
|
970
|
+
if any(token in issue_text for token in ("fail", "error", "blocked", "失败", "阻塞")):
|
|
971
|
+
sev = "error"
|
|
972
|
+
elif any(token in issue_text for token in ("warn", "skip", "风险", "警告")):
|
|
973
|
+
sev = "warn"
|
|
925
974
|
phase = str(e.get("phase") or "").lower()
|
|
926
975
|
if phase in status and sev in {"error", "fail", "failed", "critical"}:
|
|
927
976
|
status[phase] = "FAIL"
|
|
@@ -930,19 +979,22 @@ def _stage_status_from_sources(
|
|
|
930
979
|
|
|
931
980
|
api = _ledger_api_tests(ledger)
|
|
932
981
|
db = _ledger_db_compat(ledger)
|
|
933
|
-
|
|
982
|
+
api_status = str(api.get("status") or "").upper()
|
|
983
|
+
if api_status in {"FAIL", "FAILED", "ERROR"} or int(api.get("failed") or 0) > 0:
|
|
984
|
+
status["test"] = "FAIL"
|
|
985
|
+
elif api_status in {"BLOCKED", "BLOCKED_BY_ENV", "BLOCKED_BY_DBA"}:
|
|
986
|
+
status["test"] = api_status
|
|
987
|
+
elif api_status == "USER_SKIPPED":
|
|
934
988
|
status["test"] = "USER_SKIPPED"
|
|
935
989
|
elif db == "BLOCKED_BY_DBA":
|
|
936
990
|
status["test"] = "BLOCKED_BY_DBA"
|
|
937
991
|
elif api.get("status") == "NOT_RUN" and not find_test_reports(change_dir):
|
|
938
992
|
unit = _ledger_unit_tests(ledger)
|
|
939
993
|
if unit.get("source") == "not-run" and unit.get("run", 0) == 0:
|
|
940
|
-
status["test"] = "
|
|
994
|
+
status["test"] = "NOT_RUN"
|
|
941
995
|
|
|
942
|
-
if not
|
|
943
|
-
|
|
944
|
-
if not re.search(r"harness-review|##\s*review", log_text, re.IGNORECASE):
|
|
945
|
-
status["review"] = "ADVISORY"
|
|
996
|
+
if not review_evidence_present(change_dir, events):
|
|
997
|
+
status["review"] = "ADVISORY"
|
|
946
998
|
|
|
947
999
|
return status
|
|
948
1000
|
|
|
@@ -954,8 +1006,6 @@ def _compute_final_status(
|
|
|
954
1006
|
api = verification.get("apiTests") or {}
|
|
955
1007
|
db = str(verification.get("dbCompatibility") or "")
|
|
956
1008
|
api_status = str(api.get("status") or "")
|
|
957
|
-
if api_status == "USER_SKIPPED" or db == "BLOCKED_BY_DBA":
|
|
958
|
-
return "CONDITIONAL_OK"
|
|
959
1009
|
for v in stage_status.values():
|
|
960
1010
|
if v == "FAIL":
|
|
961
1011
|
return "FAIL"
|
|
@@ -964,8 +1014,14 @@ def _compute_final_status(
|
|
|
964
1014
|
return "FAIL"
|
|
965
1015
|
if int(api.get("failed") or 0) > 0:
|
|
966
1016
|
return "FAIL"
|
|
1017
|
+
conditional = {
|
|
1018
|
+
"USER_SKIPPED", "BLOCKED", "BLOCKED_BY_ENV", "BLOCKED_BY_DBA",
|
|
1019
|
+
"NOT_RUN", "PARTIAL",
|
|
1020
|
+
}
|
|
1021
|
+
if api_status in conditional or db in conditional:
|
|
1022
|
+
return "CONDITIONAL_OK"
|
|
967
1023
|
for v in stage_status.values():
|
|
968
|
-
if v in {"WARN",
|
|
1024
|
+
if v in {"WARN", *conditional}:
|
|
969
1025
|
return "WARN" if v == "WARN" else "CONDITIONAL_OK"
|
|
970
1026
|
return "OK"
|
|
971
1027
|
|
|
@@ -984,6 +1040,8 @@ def _changed_files_from_git(
|
|
|
984
1040
|
changed: list[dict[str, Any]] = []
|
|
985
1041
|
if not base or not head:
|
|
986
1042
|
return diff_stat, changed
|
|
1043
|
+
# The ledger bounds the complete task. Using only ``head^..head`` would
|
|
1044
|
+
# silently omit earlier checkpoint commits from the same change.
|
|
987
1045
|
rng = f"{base}..{head}"
|
|
988
1046
|
code, out, _ = git_run(project, "diff", "--numstat", rng)
|
|
989
1047
|
if code != 0 or not out:
|
|
@@ -1018,7 +1076,11 @@ def _changed_files_from_git(
|
|
|
1018
1076
|
return diff_stat, changed
|
|
1019
1077
|
|
|
1020
1078
|
|
|
1021
|
-
def _review_summary(
|
|
1079
|
+
def _review_summary(
|
|
1080
|
+
change_dir: Path,
|
|
1081
|
+
existing: dict[str, Any] | None,
|
|
1082
|
+
events: list[dict[str, Any]] | None = None,
|
|
1083
|
+
) -> dict[str, Any]:
|
|
1022
1084
|
base = {
|
|
1023
1085
|
"status": "ADVISORY",
|
|
1024
1086
|
"red": 0,
|
|
@@ -1033,13 +1095,102 @@ def _review_summary(change_dir: Path, existing: dict[str, Any] | None) -> dict[s
|
|
|
1033
1095
|
merged = dict(base)
|
|
1034
1096
|
merged.update(existing["reviewSummary"])
|
|
1035
1097
|
return merged
|
|
1036
|
-
if not
|
|
1037
|
-
|
|
1038
|
-
if not re.search(r"harness-review|##\s*review", log_text, re.IGNORECASE):
|
|
1039
|
-
base["status"] = "ADVISORY_NOT_RUN"
|
|
1098
|
+
if not review_evidence_present(change_dir, events):
|
|
1099
|
+
base["status"] = "ADVISORY_NOT_RUN"
|
|
1040
1100
|
return base
|
|
1041
1101
|
|
|
1042
1102
|
|
|
1103
|
+
def _final_commit_from_sources(
|
|
1104
|
+
ledger: dict[str, Any] | None,
|
|
1105
|
+
events: list[dict[str, Any]],
|
|
1106
|
+
existing: dict[str, Any] | None,
|
|
1107
|
+
project: Path,
|
|
1108
|
+
) -> str:
|
|
1109
|
+
if ledger:
|
|
1110
|
+
for key in (
|
|
1111
|
+
"mergeFinalHash", "finalCommit", "finalHash", "changeCommit", "headCommit",
|
|
1112
|
+
):
|
|
1113
|
+
value = str(ledger.get(key) or "").strip()
|
|
1114
|
+
if value:
|
|
1115
|
+
return value
|
|
1116
|
+
commit_pattern = re.compile(r"\b[0-9a-f]{7,40}\b", re.IGNORECASE)
|
|
1117
|
+
for event in reversed(events):
|
|
1118
|
+
if str(event.get("phase") or "").lower() not in {"submit", "merge", "archive"}:
|
|
1119
|
+
continue
|
|
1120
|
+
text = " ".join(str(event.get(key) or "") for key in ("note", "message", "command"))
|
|
1121
|
+
match = commit_pattern.search(text)
|
|
1122
|
+
if match:
|
|
1123
|
+
return match.group(0)
|
|
1124
|
+
if existing and existing.get("finalCommit"):
|
|
1125
|
+
return str(existing["finalCommit"])
|
|
1126
|
+
code, head, _ = git_run(project, "rev-parse", "HEAD")
|
|
1127
|
+
return head if code == 0 and head else ""
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
def _business_goal_from_sources(change_dir: Path, events: list[dict[str, Any]]) -> str:
|
|
1131
|
+
plans_root = change_dir / "plans"
|
|
1132
|
+
primary = sorted(plans_root.glob("*-plan.md"))
|
|
1133
|
+
secondary = [
|
|
1134
|
+
path for path in sorted(plans_root.glob("*.md"))
|
|
1135
|
+
if path not in primary
|
|
1136
|
+
and "implementation-detail" not in path.name
|
|
1137
|
+
and "test-scenarios" not in path.name
|
|
1138
|
+
]
|
|
1139
|
+
for plan in [*primary, *secondary]:
|
|
1140
|
+
try:
|
|
1141
|
+
text = plan.read_text(encoding="utf-8-sig")
|
|
1142
|
+
except OSError:
|
|
1143
|
+
continue
|
|
1144
|
+
body = re.sub(r"\A---\s*\n.*?\n---\s*\n", "", text, count=1, flags=re.DOTALL)
|
|
1145
|
+
goal = re.search(r"(?im)^\s*(?:goal|目标|业务目标|需求)\s*[::]\s*(.+)$", body)
|
|
1146
|
+
if goal:
|
|
1147
|
+
return goal.group(1).strip()
|
|
1148
|
+
scope = re.search(r"(?im)^\s*>?\s*(?:变更范围|目标)\s*[::]\s*(.+)$", body)
|
|
1149
|
+
if scope:
|
|
1150
|
+
return scope.group(1).strip()
|
|
1151
|
+
first_task = re.search(r"(?m)^\s*\|\s*1\s*\|\s*([^|]+?)\s*\|", body)
|
|
1152
|
+
if first_task:
|
|
1153
|
+
return first_task.group(1).strip()
|
|
1154
|
+
for line in body.splitlines():
|
|
1155
|
+
clean = line.strip().lstrip("#").strip()
|
|
1156
|
+
if clean and not clean.startswith(("---", ">", "|")) and len(clean) > 8:
|
|
1157
|
+
return clean
|
|
1158
|
+
for event in events:
|
|
1159
|
+
if event.get("type") == "decision":
|
|
1160
|
+
value = str(event.get("decision") or event.get("note") or "").strip()
|
|
1161
|
+
if value:
|
|
1162
|
+
return re.sub(r"^(?:需求收敛|目标)\s*[::]\s*", "", value)
|
|
1163
|
+
return ""
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
def _timeline_from_events(event_summary: dict[str, Any], events: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
1167
|
+
timeline: list[dict[str, Any]] = []
|
|
1168
|
+
for phase, info in (event_summary.get("phases") or {}).items():
|
|
1169
|
+
attempts = info.get("attempts") if isinstance(info.get("attempts"), list) else []
|
|
1170
|
+
for attempt in attempts:
|
|
1171
|
+
timeline.append({
|
|
1172
|
+
"phase": phase,
|
|
1173
|
+
"attempt": attempt.get("attempt"),
|
|
1174
|
+
"startedAt": attempt.get("started_at"),
|
|
1175
|
+
"endedAt": attempt.get("ended_at"),
|
|
1176
|
+
"durationMs": attempt.get("duration_ms"),
|
|
1177
|
+
"status": attempt.get("status") or "UNKNOWN",
|
|
1178
|
+
"executorTool": attempt.get("executor_tool"),
|
|
1179
|
+
"executorAgent": attempt.get("executor_agent"),
|
|
1180
|
+
"handoffFromTool": attempt.get("handoff_from_tool"),
|
|
1181
|
+
})
|
|
1182
|
+
for event in events:
|
|
1183
|
+
if event.get("type") not in {"decision", "issue"}:
|
|
1184
|
+
continue
|
|
1185
|
+
timeline.append({
|
|
1186
|
+
"phase": event.get("phase"),
|
|
1187
|
+
"timestamp": event.get("timestamp"),
|
|
1188
|
+
"type": event.get("type"),
|
|
1189
|
+
"summary": event.get("decision") or event.get("message") or event.get("note") or "",
|
|
1190
|
+
})
|
|
1191
|
+
return timeline
|
|
1192
|
+
|
|
1193
|
+
|
|
1043
1194
|
def collect_summary_data(
|
|
1044
1195
|
change_dir: Path,
|
|
1045
1196
|
*,
|
|
@@ -1118,18 +1269,14 @@ def collect_summary_data(
|
|
|
1118
1269
|
if existing and existing.get("businessGoal"):
|
|
1119
1270
|
data["businessGoal"] = existing["businessGoal"]
|
|
1120
1271
|
else:
|
|
1121
|
-
|
|
1272
|
+
inferred_goal = _business_goal_from_sources(change_dir, events)
|
|
1273
|
+
data["businessGoal"] = inferred_goal or (NOT_AVAILABLE if for_replay else "")
|
|
1122
1274
|
|
|
1123
1275
|
# commits
|
|
1124
1276
|
project = find_project_root(change_dir)
|
|
1125
1277
|
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 ""
|
|
1278
|
+
final_commit = _final_commit_from_sources(ledger, events, existing, project)
|
|
1279
|
+
data["finalCommit"] = final_commit or (NOT_AVAILABLE if for_replay else "")
|
|
1133
1280
|
|
|
1134
1281
|
if not data.get("baseCommit") or str(data.get("baseCommit")).startswith("<"):
|
|
1135
1282
|
if ledger and ledger.get("baseCommit"):
|
|
@@ -1235,18 +1382,47 @@ def collect_summary_data(
|
|
|
1235
1382
|
if not isinstance(data.get("artifacts"), list):
|
|
1236
1383
|
data["artifacts"] = []
|
|
1237
1384
|
if not for_replay:
|
|
1238
|
-
|
|
1239
|
-
pass
|
|
1385
|
+
data["artifacts"] = _artifacts_from_events(events)
|
|
1240
1386
|
|
|
1241
|
-
data["reviewSummary"] = _review_summary(
|
|
1242
|
-
|
|
1387
|
+
data["reviewSummary"] = _review_summary(
|
|
1388
|
+
change_dir,
|
|
1389
|
+
existing if for_replay else None,
|
|
1390
|
+
events,
|
|
1391
|
+
)
|
|
1392
|
+
if not for_replay:
|
|
1393
|
+
data["timeline"] = _timeline_from_events(event_summary, events)
|
|
1394
|
+
else:
|
|
1395
|
+
data.setdefault("timeline", [])
|
|
1243
1396
|
data.setdefault("uncommittedTestEvidence", [])
|
|
1244
1397
|
|
|
1245
|
-
#
|
|
1398
|
+
# Derive risks/actions from evidence. These fields are facts, not model prose.
|
|
1246
1399
|
if not for_replay:
|
|
1247
|
-
data["maintenanceNotes"] = [
|
|
1248
|
-
|
|
1400
|
+
data["maintenanceNotes"] = [
|
|
1401
|
+
str(event.get("note") or event.get("message") or "")
|
|
1402
|
+
for event in events
|
|
1403
|
+
if event.get("type") == "decision" and (event.get("note") or event.get("message"))
|
|
1404
|
+
]
|
|
1405
|
+
data["knownRisks"] = [
|
|
1406
|
+
{
|
|
1407
|
+
"phase": event.get("phase"),
|
|
1408
|
+
"severity": event.get("severity") or "unknown",
|
|
1409
|
+
"message": event.get("message") or event.get("note") or event.get("code") or "",
|
|
1410
|
+
}
|
|
1411
|
+
for event in events
|
|
1412
|
+
if event.get("type") == "issue"
|
|
1413
|
+
and not (
|
|
1414
|
+
event.get("phase") == "archive"
|
|
1415
|
+
and event.get("code") == "missing-command"
|
|
1416
|
+
)
|
|
1417
|
+
]
|
|
1249
1418
|
data["manualActions"] = []
|
|
1419
|
+
for name, value in (data.get("stageStatus") or {}).items():
|
|
1420
|
+
if value in {"BLOCKED", "BLOCKED_BY_ENV", "BLOCKED_BY_DBA", "NOT_RUN", "USER_SKIPPED"}:
|
|
1421
|
+
data["manualActions"].append({
|
|
1422
|
+
"stage": name,
|
|
1423
|
+
"status": value,
|
|
1424
|
+
"action": "补充或确认该阶段的真实验证证据",
|
|
1425
|
+
})
|
|
1250
1426
|
else:
|
|
1251
1427
|
data.setdefault("maintenanceNotes", [])
|
|
1252
1428
|
data.setdefault("knownRisks", [])
|
|
@@ -1978,6 +2154,7 @@ def cmd_finalize(
|
|
|
1978
2154
|
_restore_target,
|
|
1979
2155
|
phase="archive",
|
|
1980
2156
|
type_="phase.end",
|
|
2157
|
+
status="FAIL",
|
|
1981
2158
|
note="finalize aborted; render failed; original preserved",
|
|
1982
2159
|
)
|
|
1983
2160
|
except OSError:
|
|
@@ -2117,6 +2294,7 @@ def cmd_finalize(
|
|
|
2117
2294
|
_safe_append_restored,
|
|
2118
2295
|
phase="archive",
|
|
2119
2296
|
type_="phase.end",
|
|
2297
|
+
status="FAIL",
|
|
2120
2298
|
note="finalize aborted; original preserved",
|
|
2121
2299
|
)
|
|
2122
2300
|
except OSError:
|
|
@@ -2168,7 +2346,12 @@ def cmd_finalize(
|
|
|
2168
2346
|
if service_result.get("warning"):
|
|
2169
2347
|
warnings.append(str(service_result["warning"]))
|
|
2170
2348
|
|
|
2171
|
-
_safe_append(
|
|
2349
|
+
_safe_append(
|
|
2350
|
+
phase="archive",
|
|
2351
|
+
type_="phase.end",
|
|
2352
|
+
status="WARN" if warnings else "OK",
|
|
2353
|
+
note="finalize complete",
|
|
2354
|
+
)
|
|
2172
2355
|
|
|
2173
2356
|
payload["ok"] = True
|
|
2174
2357
|
payload["warnings"] = warnings
|