@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
|
@@ -1,171 +1,94 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// Keeps UTF-8, avoids PowerShell string interpolation issues.
|
|
6
|
-
// All numbers come from summary-data.json or manifest — never re-inferred here.
|
|
7
|
-
|
|
8
|
-
import fs from 'node:fs';
|
|
9
|
-
import path from 'node:path';
|
|
2
|
+
// Compact deterministic renderer for Harness archive summary-data.json.
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
10
5
|
|
|
11
6
|
const args = process.argv.slice(2);
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
return
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const outPath = getArg('--out', args[1] || 'final-summary.html');
|
|
7
|
+
const arg = (name, fallback) => {
|
|
8
|
+
const index = args.indexOf(name);
|
|
9
|
+
return index >= 0 && args[index + 1] ? args[index + 1] : fallback;
|
|
10
|
+
};
|
|
11
|
+
const summaryPath = arg("--summary", args[0]);
|
|
12
|
+
const outPath = arg("--out", args[1] || "final-summary.html");
|
|
19
13
|
if (!summaryPath) {
|
|
20
|
-
console.error(
|
|
14
|
+
console.error("Usage: node render-summary.mjs --summary summary-data.json --out final-summary.html");
|
|
21
15
|
process.exit(2);
|
|
22
16
|
}
|
|
23
17
|
|
|
24
|
-
const data = JSON.parse(fs.readFileSync(summaryPath,
|
|
25
|
-
const esc = (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return
|
|
18
|
+
const data = JSON.parse(fs.readFileSync(summaryPath, "utf8"));
|
|
19
|
+
const esc = (value) => String(value ?? "").replace(/[&<>"']/g, (char) => ({
|
|
20
|
+
"&": "&", "<": "<", ">": ">", '"': """, "'": "'"
|
|
21
|
+
})[char]);
|
|
22
|
+
const list = (value) => Array.isArray(value) ? value : [];
|
|
23
|
+
const record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
24
|
+
const number = (value) => Number(value) || 0;
|
|
25
|
+
const shortHash = (value) => String(value || "N/A").slice(0, 10);
|
|
26
|
+
const statusClass = (value) => {
|
|
27
|
+
const status = String(value || "UNKNOWN").toUpperCase();
|
|
28
|
+
if (/FAIL|ERROR/.test(status)) return "danger";
|
|
29
|
+
if (/WARN|BLOCK|SKIP|CONDITIONAL|PARTIAL|NOT_RUN|UNKNOWN/.test(status)) return "warning";
|
|
30
|
+
if (/ADVISORY|REUSED/.test(status)) return "neutral";
|
|
31
|
+
return "success";
|
|
35
32
|
};
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const m = String(rate).match(/(\d+)\s*\/\s*(\d+)/);
|
|
42
|
-
return m ? { passed: +m[1], total: +m[2] } : null;
|
|
33
|
+
const pill = (value) => `<span class="pill ${statusClass(value)}">${esc(value || "UNKNOWN")}</span>`;
|
|
34
|
+
const describe = (value) => {
|
|
35
|
+
if (typeof value === "string") return value;
|
|
36
|
+
if (!value || typeof value !== "object") return String(value ?? "");
|
|
37
|
+
return value.message || value.action || value.note || value.summary || JSON.stringify(value);
|
|
43
38
|
};
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return `<div class="rate"><div class="bar-track"><div class="bar-fill ${cls}" style="width:${pct}%"></div></div><span class="muted small">${esc(rateStr)} · ${pct}%</span></div>`;
|
|
39
|
+
const duration = (minutes) => {
|
|
40
|
+
const value = number(minutes);
|
|
41
|
+
if (value < 1) return `${Math.round(value * 60)} 秒`;
|
|
42
|
+
if (value < 60) return `${Math.round(value * 10) / 10} 分钟`;
|
|
43
|
+
return `${Math.floor(value / 60)} 小时 ${Math.round(value % 60)} 分钟`;
|
|
50
44
|
};
|
|
51
45
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const diffStat = obj(data.diffStat);
|
|
67
|
-
const changedRows = arr(data.changedFiles).map(f => `<tr><td><code>${esc(f.path || f.file || '')}</code></td><td>${esc(f.summary || f.change || '')}</td><td class="ins">+${esc(f.insertions ?? 0)}</td><td class="del">-${esc(f.deletions ?? 0)}</td></tr>`).join('\n') || `<tr><td colspan="4" class="muted">未记录变更文件</td></tr>`;
|
|
68
|
-
const artifactList = arr(data.artifacts);
|
|
69
|
-
const artifactRows = artifactList.map(a => `<tr><td>${esc(a.name || '')}</td><td><code>${esc(a.path || '')}</code></td><td>${esc(a.size || '')}</td><td><code>${esc(a.sha256 || '')}</code></td></tr>`).join('\n');
|
|
70
|
-
const artifactCard = artifactList.length > 0 ? `<div class="card"><h2>📦 产物清单</h2><table><tr><th>名称</th><th>路径</th><th>大小</th><th>SHA-256</th></tr>${artifactRows}</table></div>` : '';
|
|
71
|
-
const notes = arr(data.maintenanceNotes).map(n => `<li>${esc(n)}</li>`).join('\n') || `<li class="muted">无额外维护说明</li>`;
|
|
72
|
-
const risks = arr(data.knownRisks).map(n => `<li>${esc(n)}</li>`).join('\n') || `<li class="muted">无已知遗留风险</li>`;
|
|
73
|
-
const manual = arr(data.manualActions).map(n => `<li>${esc(n)}</li>`).join('\n') || `<li class="muted">无人工后续动作</li>`;
|
|
74
|
-
const uncommittedList = arr(data.uncommittedTestEvidence);
|
|
75
|
-
const uncommittedTests = uncommittedList.map(n => `<li>${esc(n)}</li>`).join('\n');
|
|
76
|
-
const uncommittedCard = uncommittedList.length > 0 ? `<div class="card"><h2>🧪 未提交测试证据</h2><ul>${uncommittedTests}</ul></div>` : '';
|
|
77
|
-
const review = obj(data.reviewSummary);
|
|
78
|
-
const manifest = obj(data.archiveManifest);
|
|
79
|
-
const timeline = arr(data.timeline).map(t => `<tr><td>${esc(t.stage || '')}</td><td>${badge(t.result || '')}</td><td>${esc(t.evidence || '')}</td></tr>`).join('\n');
|
|
80
|
-
|
|
81
|
-
// durations + skillCalls (schema 2.1); fall back to timeline when absent
|
|
82
|
-
const durations = obj(data.durations);
|
|
83
|
-
const durStages = arr(durations.stages);
|
|
84
|
-
const maxMin = durStages.reduce((m, s) => Math.max(m, num(s.minutes)), 0);
|
|
85
|
-
const durRows = durStages.map(s => {
|
|
86
|
-
const min = num(s.minutes);
|
|
87
|
-
const pct = maxMin > 0 ? Math.round(min / maxMin * 100) : 0;
|
|
88
|
-
return `<div class="dur-row"><div class="dur-label">${esc(s.skill || s.stage || '')} <span class="muted small">${esc(s.result || '')}</span></div><div class="dur-track"><div class="dur-bar" style="width:${pct}%"></div></div><div class="dur-min">${esc(min)}m</div></div>`;
|
|
89
|
-
}).join('\n');
|
|
90
|
-
const skillCallsHtml = arr(data.skillCalls).map(s => `${esc(s.skill)}×${esc(s.count)} ${badge(s.result)}`).join(' ') || '<span class="muted">未记录</span>';
|
|
91
|
-
|
|
92
|
-
const durCard = durStages.length > 0
|
|
93
|
-
? `<div class="card"><h2>⏱️ 阶段耗时与 Skill 调用</h2>${durRows}<div class="muted small" style="margin-top:10px">Skill 调用:${skillCallsHtml}</div></div>`
|
|
94
|
-
: (timeline ? `<div class="card"><h2>阶段时间线</h2><table><tr><th>阶段</th><th>结果</th><th>证据</th></tr>${timeline}</table></div>` : '');
|
|
46
|
+
const stages = Object.entries(record(data.stageStatus));
|
|
47
|
+
const verification = record(data.verification);
|
|
48
|
+
const unit = record(verification.unitTests);
|
|
49
|
+
const api = record(verification.apiTests);
|
|
50
|
+
const durations = record(data.durations);
|
|
51
|
+
const durationStages = list(durations.stages);
|
|
52
|
+
const maxMinutes = Math.max(1, ...durationStages.map((item) => number(item.minutes)));
|
|
53
|
+
const diff = record(data.diffStat);
|
|
54
|
+
const risks = list(data.knownRisks);
|
|
55
|
+
const actions = list(data.manualActions);
|
|
56
|
+
const files = list(data.changedFiles);
|
|
57
|
+
const commands = list(record(data.reportPipeline).commands);
|
|
58
|
+
const timeline = list(data.timeline);
|
|
95
59
|
|
|
96
|
-
const
|
|
60
|
+
const stageHtml = stages.map(([name, status]) => `<div class="row"><span>${esc(name)}</span>${pill(status)}</div>`).join("") || '<p class="empty">没有阶段状态记录</p>';
|
|
61
|
+
const verificationHtml = [
|
|
62
|
+
["单元测试", unit.status || ((number(unit.failures) + number(unit.errors)) > 0 ? "FAIL" : (number(unit.run) > 0 ? "OK" : "NOT_RUN")), `${number(unit.run)} 个 · ${number(unit.failures)} 失败 · ${number(unit.errors)} 错误`],
|
|
63
|
+
["API 测试", api.status || "NOT_RUN", `${number(api.passed)}/${number(api.total)} 通过 · ${number(api.blocked)} 阻塞`],
|
|
64
|
+
["数据库兼容", verification.dbCompatibility || "NOT_RUN", verification.coverageDisplay || "未记录覆盖率"]
|
|
65
|
+
].map(([name, status, note]) => `<div class="row"><div><strong>${esc(name)}</strong><small>${esc(note)}</small></div>${pill(status)}</div>`).join("");
|
|
66
|
+
const durationHtml = durationStages.map((item) => {
|
|
67
|
+
const width = Math.max(2, Math.round(number(item.minutes) / maxMinutes * 100));
|
|
68
|
+
const attempts = list(item.attempts).length;
|
|
69
|
+
return `<div class="duration"><div><span>${esc(item.skill || item.stage)}</span><span>${duration(item.minutes)}${attempts > 1 ? ` · ${attempts} 次尝试` : ""}</span></div><i><b style="width:${width}%"></b></i></div>`;
|
|
70
|
+
}).join("") || '<p class="empty">没有可计算的阶段耗时</p>';
|
|
71
|
+
const riskHtml = risks.map((item) => `<li>${esc(describe(item))}</li>`).join("") || "<li>未记录已知风险</li>";
|
|
72
|
+
const actionHtml = actions.map((item) => `<li>${esc(describe(item))}</li>`).join("") || "<li>无需人工后续动作</li>";
|
|
73
|
+
const fileRows = files.map((item) => `<tr><td><code>${esc(item.path || item.file)}</code></td><td class="plus">+${number(item.insertions)}</td><td class="minus">-${number(item.deletions)}</td></tr>`).join("") || '<tr><td colspan="3">没有变更文件证据</td></tr>';
|
|
74
|
+
const timelineRows = timeline.map((item) => `<tr><td>${esc(item.phase || item.stage || "-")}</td><td>${esc(item.attempt || "-")}</td><td>${pill(item.status || item.result || item.type)}</td><td>${esc(item.executorTool || item.executor_tool || item.summary || "-")}</td></tr>`).join("") || '<tr><td colspan="4">没有时间线记录</td></tr>';
|
|
75
|
+
const commandRows = commands.map((item) => `<tr><td>${esc(item.phase || "-")}</td><td><code>${esc(item.command)}</code></td><td>${pill(number(item.exit_code) === 0 ? "OK" : "FAIL")}</td></tr>`).join("") || '<tr><td colspan="3">没有命令证据</td></tr>';
|
|
97
76
|
|
|
98
|
-
const html = `<!doctype html>
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
h3 { margin: 18px 0 8px; }
|
|
113
|
-
.muted { color: #94a3b8; }
|
|
114
|
-
.small { font-size: 12px; }
|
|
115
|
-
table { width: 100%; border-collapse: collapse; table-layout: fixed; }
|
|
116
|
-
th, td { border-bottom: 1px solid rgba(255,255,255,.12); padding: 10px; text-align: left; vertical-align: top; overflow-wrap: anywhere; }
|
|
117
|
-
th { color: #cbd5e1; font-weight: 600; }
|
|
118
|
-
tbody tr:nth-child(even) { background: rgba(255,255,255,.03); }
|
|
119
|
-
tbody tr:hover { background: rgba(96,165,250,.08); }
|
|
120
|
-
code { color: #bfdbfe; word-break: break-all; }
|
|
121
|
-
.ins { color: #86efac; font-weight: 600; }
|
|
122
|
-
.del { color: #fca5a5; font-weight: 600; }
|
|
123
|
-
.badge { display: inline-block; padding: 4px 8px; border-radius: 999px; font-weight: 700; font-size: 12px; }
|
|
124
|
-
.badge.ok { background: rgba(34,197,94,.16); color: #86efac; }
|
|
125
|
-
.badge.warn { background: rgba(245,158,11,.18); color: #fcd34d; }
|
|
126
|
-
.badge.bad { background: rgba(239,68,68,.18); color: #fca5a5; }
|
|
127
|
-
.badge.info { background: rgba(59,130,246,.18); color: #93c5fd; }
|
|
128
|
-
.grid { display: grid; grid-template-columns: repeat(auto-fit,minmax(200px,1fr)); gap: 16px; }
|
|
129
|
-
.kpi { font-size: 18px; font-weight: 700; margin-top: 4px; word-break: break-all; }
|
|
130
|
-
.hero { border-color: rgba(96,165,250,.35); background: linear-gradient(135deg, rgba(59,130,246,.12), rgba(255,255,255,.04)); }
|
|
131
|
-
ul { margin-top: 8px; }
|
|
132
|
-
.bar-track { background: rgba(255,255,255,.08); border-radius: 999px; height: 8px; overflow: hidden; margin: 4px 0; }
|
|
133
|
-
.bar-fill { height: 100%; border-radius: 999px; }
|
|
134
|
-
.bar-fill.ok { background: #22c55e; }
|
|
135
|
-
.bar-fill.warn { background: #f59e0b; }
|
|
136
|
-
.bar-fill.bad { background: #ef4444; }
|
|
137
|
-
.bar-fill.info { background: #3b82f6; }
|
|
138
|
-
.rate { display: flex; flex-direction: column; gap: 2px; min-width: 160px; }
|
|
139
|
-
.dur-row { display: grid; grid-template-columns: 150px 1fr 50px; align-items: center; gap: 10px; padding: 6px 0; }
|
|
140
|
-
.dur-label { color: #93c5fd; font-size: 13px; }
|
|
141
|
-
.dur-track { background: rgba(255,255,255,.06); border-radius: 4px; height: 18px; overflow: hidden; }
|
|
142
|
-
.dur-bar { height: 100%; background: linear-gradient(90deg, #3b82f6, #60a5fa); border-radius: 4px; }
|
|
143
|
-
.dur-min { color: #94a3b8; font-size: 13px; text-align: right; }
|
|
144
|
-
@media (max-width: 640px) { main { padding: 16px; } .grid { grid-template-columns: 1fr; } .dur-row { grid-template-columns: 100px 1fr 40px; } }
|
|
145
|
-
</style>
|
|
146
|
-
</head>
|
|
147
|
-
<body><main>
|
|
148
|
-
<div class="card hero">
|
|
149
|
-
<h1>变更最终报告:${esc(data.changeName || '')}</h1>
|
|
150
|
-
<p class="muted">${esc(data.businessGoal || '未记录业务目标')}</p>
|
|
151
|
-
</div>
|
|
152
|
-
<div class="grid">
|
|
153
|
-
<div class="card"><div class="muted">🎯 最终状态</div><div class="kpi">${badge(data.finalStatus || data.overallStatus || 'N/A')}</div></div>
|
|
154
|
-
<div class="card"><div class="muted">🔒 Final Commit</div><div class="kpi"><code>${esc(data.finalCommit || 'N/A')}</code></div><div class="muted small">${esc(data.finalCommitBranch || '')}</div></div>
|
|
155
|
-
<div class="card"><div class="muted">📊 代码变更</div><div class="kpi">${esc(diffStat.filesChanged ?? 'N/A')} 文件</div><div class="muted small"><span class="ins">+${esc(diffStat.insertions ?? 0)}</span> <span class="del">-${esc(diffStat.deletions ?? 0)}</span></div></div>
|
|
156
|
-
<div class="card"><div class="muted">⏱️ 总耗时</div><div class="kpi">${esc(totalLabel)}</div><div class="muted small">${esc(durStages.length)} 个阶段</div></div>
|
|
157
|
-
<div class="card"><div class="muted">📝 Review</div><div class="kpi">${esc(review.status || 'N/A')}</div><div class="muted small">RED ${esc(review.red ?? 0)}(修${esc(review.redFixed ?? 0)}/确${esc(review.redConfirmed ?? 0)}) · YEL ${esc(review.yellow ?? 0)}(修${esc(review.yellowFixed ?? 0)}/留${esc(review.yellowDeferred ?? 0)})</div></div>
|
|
158
|
-
<div class="card"><div class="muted">📦 Archive</div><div class="kpi">${esc(manifest.totalArchiveFiles ?? 'N/A')} 文件</div><div class="muted small">${esc(manifest.checksumStatus || 'N/A')}</div></div>
|
|
159
|
-
</div>
|
|
160
|
-
<div class="card"><h2>📋 阶段状态</h2><table><tr><th>阶段</th><th>状态</th></tr>${stageRows}</table></div>
|
|
161
|
-
${durCard}
|
|
162
|
-
<div class="card"><h2>🛡️ 验证明细</h2><table><tr><th>类型</th><th>结果</th></tr>${verificationRows}</table></div>
|
|
163
|
-
<div class="card"><h2>📁 变更文件</h2><table><tr><th>文件</th><th>说明</th><th>+</th><th>-</th></tr>${changedRows}</table></div>
|
|
164
|
-
${artifactCard}
|
|
165
|
-
${uncommittedCard}
|
|
166
|
-
<div class="card"><h2>👨🔧 给后续维护者</h2><ul>${notes}</ul></div>
|
|
167
|
-
<div class="card"><h2>⚠️ 已知风险 / 人工确认项</h2><ul>${risks}</ul><h3>人工后续动作</h3><ul>${manual}</ul></div>
|
|
77
|
+
const html = `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
78
|
+
<title>Harness 最终报告 · ${esc(data.changeName)}</title><style>
|
|
79
|
+
:root{color-scheme:light dark;--bg:#f3f6fa;--card:#fff;--soft:#f7f9fc;--text:#182132;--muted:#667085;--line:#dce3ed;--blue:#2563eb;--good:#087443;--warn:#9b5a00;--bad:#b42318;--shadow:0 8px 24px rgba(18,35,70,.07)}
|
|
80
|
+
@media(prefers-color-scheme:dark){:root{--bg:#0b1018;--card:#121925;--soft:#182130;--text:#eef3fb;--muted:#9aa8bc;--line:#29364a;--blue:#7aa9ff;--good:#55d99d;--warn:#f0bc63;--bad:#ff8b83;--shadow:0 12px 30px rgba(0,0,0,.25)}}
|
|
81
|
+
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 Inter,"Segoe UI","Microsoft YaHei",sans-serif}main{width:min(1140px,calc(100% - 28px));margin:22px auto 44px}.hero,.card,.metric,details{background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow)}.hero{padding:23px 25px;border-top:3px solid var(--blue)}.eyebrow{color:var(--blue);font-weight:750;letter-spacing:.08em}h1{font-size:27px;line-height:1.2;margin:5px 0 7px;overflow-wrap:anywhere}.goal,.empty,small{color:var(--muted)}.status{display:flex;align-items:center;gap:10px;margin-top:16px}.metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:11px;margin:11px 0}.metric{padding:14px 15px}.metric strong{display:block;font-size:19px;margin:4px 0}.grid{display:grid;grid-template-columns:1.12fr .88fr;gap:11px}.card{padding:17px;margin-bottom:11px}h2{font-size:16px;margin:0 0 10px}.row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 0;border-bottom:1px solid var(--line)}.row:last-child{border:0}.row small{display:block;margin-top:2px}.pill{display:inline-flex;border-radius:999px;padding:3px 9px;font-size:11px;font-weight:750;white-space:nowrap}.pill.success{color:var(--good);background:color-mix(in srgb,var(--good) 13%,transparent)}.pill.warning{color:var(--warn);background:color-mix(in srgb,var(--warn) 14%,transparent)}.pill.danger{color:var(--bad);background:color-mix(in srgb,var(--bad) 13%,transparent)}.pill.neutral{color:var(--blue);background:color-mix(in srgb,var(--blue) 12%,transparent)}.duration{margin:11px 0}.duration>div{display:flex;justify-content:space-between;color:var(--muted);font-size:12px}.duration>div span:first-child{color:var(--text);font-weight:650}.duration i{display:block;height:7px;background:var(--soft);border-radius:99px;overflow:hidden;margin-top:6px}.duration b{display:block;height:100%;background:linear-gradient(90deg,var(--blue),#7c70ff)}.risk{display:grid;grid-template-columns:1fr 1fr;gap:10px}.risk>div{background:var(--soft);border-radius:10px;padding:11px}.risk h3{font-size:13px;margin:0 0 5px}.risk ul{padding-left:18px;margin:0;color:var(--muted)}details{margin:9px 0}summary{cursor:pointer;padding:13px 15px;font-weight:650}details>div{padding:0 15px 15px;overflow:auto}table{width:100%;border-collapse:collapse}th,td{text-align:left;padding:8px 9px;border-bottom:1px solid var(--line);vertical-align:top}th{color:var(--muted);font-size:11px;letter-spacing:.04em}code{font-family:"Cascadia Code",Consolas,monospace;color:var(--blue);overflow-wrap:anywhere}.plus{color:var(--good)}.minus{color:var(--bad)}dl{display:grid;grid-template-columns:145px 1fr;gap:7px 11px}dt{color:var(--muted)}dd{margin:0;overflow-wrap:anywhere}
|
|
82
|
+
@media(max-width:800px){.metrics{grid-template-columns:1fr 1fr}.grid,.risk{grid-template-columns:1fr}}@media(max-width:480px){main{width:calc(100% - 18px);margin-top:9px}.metrics{grid-template-columns:1fr}.hero{padding:19px}h1{font-size:23px}dl{grid-template-columns:1fr}}
|
|
83
|
+
</style></head><body><main>
|
|
84
|
+
<section class="hero"><div class="eyebrow">HARNESS EXECUTION REPORT</div><h1>${esc(data.changeName || "未命名变更")}</h1><p class="goal">${esc(data.businessGoal || "未记录业务目标")}</p><div class="status">${pill(data.finalStatus)}<span>基于事件、验证账本与 Git 证据生成</span></div></section>
|
|
85
|
+
<section class="metrics"><article class="metric"><small>任务提交</small><strong><code title="${esc(data.finalCommit)}">${esc(shortHash(data.finalCommit))}</code></strong><small>${esc(data.finalCommitBranch || "未记录分支")}</small></article><article class="metric"><small>代码范围</small><strong>${number(diff.filesChanged)} 个文件</strong><small><span class="plus">+${number(diff.insertions)}</span> · <span class="minus">-${number(diff.deletions)}</span></small></article><article class="metric"><small>总耗时</small><strong>${esc(durations.totalLabel || duration(durations.totalMinutes))}</strong><small>${durationStages.length} 个阶段</small></article><article class="metric"><small>归档完整性</small><strong>${esc(record(data.archiveManifest).checksumStatus || "UNKNOWN")}</strong><small>${number(record(data.archiveManifest).totalArchiveFiles)} 个归档文件</small></article></section>
|
|
86
|
+
<section class="grid"><div><article class="card"><h2>验证结论</h2>${verificationHtml}</article><article class="card"><h2>阶段耗时</h2>${durationHtml}</article></div><div><article class="card"><h2>阶段状态</h2>${stageHtml}</article><article class="card"><h2>风险与后续</h2><div class="risk"><div><h3>已知风险</h3><ul>${riskHtml}</ul></div><div><h3>人工动作</h3><ul>${actionHtml}</ul></div></div></article></div></section>
|
|
87
|
+
<details><summary>变更文件(${files.length})</summary><div><table><thead><tr><th>文件</th><th>新增</th><th>删除</th></tr></thead><tbody>${fileRows}</tbody></table></div></details>
|
|
88
|
+
<details><summary>执行时间线与工具交接(${timeline.length})</summary><div><table><thead><tr><th>阶段</th><th>尝试</th><th>状态</th><th>来源 / 摘要</th></tr></thead><tbody>${timelineRows}</tbody></table></div></details>
|
|
89
|
+
<details><summary>命令证据(${commands.length})</summary><div><table><thead><tr><th>阶段</th><th>命令</th><th>结果</th></tr></thead><tbody>${commandRows}</tbody></table></div></details>
|
|
90
|
+
<details><summary>技术元数据</summary><div><dl><dt>完整提交</dt><dd><code>${esc(data.finalCommit || "N/A")}</code></dd><dt>基线提交</dt><dd><code>${esc(data.baseCommit || "N/A")}</code></dd><dt>Git 范围</dt><dd><code>${esc(diff.range || "N/A")}</code></dd><dt>报告数据版本</dt><dd>${esc(data.schemaVersion || "N/A")}</dd><dt>事实来源</dt><dd>${esc(list(record(data.reportPipeline).sources).join(" · ") || "N/A")}</dd></dl></div></details>
|
|
168
91
|
</main></body></html>`;
|
|
169
92
|
|
|
170
93
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
171
|
-
fs.writeFileSync(outPath, html,
|
|
94
|
+
fs.writeFileSync(outPath, html, "utf8");
|
|
@@ -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=8337ddaf1dcd93a2; overlay=java; agent=cursor; do not edit -->
|
|
7
7
|
# harness-codebase-map — 代码库地图
|
|
8
8
|
|
|
9
9
|
## Purpose
|
|
@@ -85,7 +85,7 @@ description: 分析现有代码库并生成 .harness/codebase/map/
|
|
|
85
85
|
|
|
86
86
|
## 执行日志
|
|
87
87
|
|
|
88
|
-
`events.ndjson` 为唯一事实源(schema_version
|
|
88
|
+
`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]]
|
|
89
89
|
|
|
90
90
|
```powershell
|
|
91
91
|
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
|
|
@@ -94,3 +94,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
94
94
|
> **脚本接线**:`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 护栏解读。
|
|
95
95
|
|
|
96
96
|
> **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 边界保持最新。
|
|
97
|
+
|
|
98
|
+
每次阶段结束必须写 `--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` 环境变量统一注入。
|
|
@@ -3,7 +3,7 @@ name: harness-knowledge-ingest
|
|
|
3
3
|
description: 从 .harness/archive 归档整理、同步和维护项目知识索引。适用场景:ingest knowledge、sync
|
|
4
4
|
knowledge、rebuild knowledge index、promote knowledge、确认知识条目、检查知识库是否过期。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=8337ddaf1dcd93a2; overlay=java; agent=cursor; do not edit -->
|
|
7
7
|
# harness-knowledge-ingest
|
|
8
8
|
|
|
9
9
|
从 `.harness/archive/**/reports/final/summary-data.json` 抽取项目知识,生成并维护本地 `.harness/knowledge/` 索引。
|
|
@@ -219,7 +219,7 @@ powershell.exe -Command "python '<skill-dir>\scripts\harness_knowledge.py' sync
|
|
|
219
219
|
- 当前 `active` / `candidate` / `stale` 数量统计。
|
|
220
220
|
- 产物路径(`index.json`、`index.sqlite`、`views/`、`reports/`)。
|
|
221
221
|
- `lifecycle` 摘要:`validatorsApplied`、`candidateAutoPromoted`、`pendingAgentJudge` 等。
|
|
222
|
-
- **已处理报告**(auto + judge 后):promoted/dropped/superseded/kept/skipped 计数与 `reports/judge-decisions-*.json`、`judgements-*.json`
|
|
222
|
+
- **已处理报告**(auto + judge 后):promoted/dropped/superseded/kept/deferred/skipped 计数与 `reports/judge-decisions-*.json`、`judgements-*.json` 路径;人工判断还会追加到 `judgements/decisions.json`,后续 ingest 不得抹除。`defer` 必须带 `reviewAfter`,到期前不重复进入待判断清单。
|
|
223
223
|
- 仅当 `upToDate=false` 时建议 `sync --update`;查询历史 → `/harness-knowledge-query`。**禁止**默认列出「请人工确认 N 条 candidate」待办。
|
|
224
224
|
|
|
225
225
|
## 渐进披露
|
|
@@ -232,7 +232,7 @@ powershell.exe -Command "python '<skill-dir>\scripts\harness_knowledge.py' sync
|
|
|
232
232
|
|
|
233
233
|
## 执行日志
|
|
234
234
|
|
|
235
|
-
`events.ndjson` 为唯一事实源(schema_version
|
|
235
|
+
`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]]
|
|
236
236
|
|
|
237
237
|
```powershell
|
|
238
238
|
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
|
|
@@ -241,3 +241,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
241
241
|
> **脚本接线**:`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 护栏解读。
|
|
242
242
|
|
|
243
243
|
> **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 边界保持最新。
|
|
244
|
+
|
|
245
|
+
每次阶段结束必须写 `--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` 环境变量统一注入。
|
|
@@ -776,13 +776,14 @@ def write_cached_archive_entries(
|
|
|
776
776
|
|
|
777
777
|
def load_preserved_entries(knowledge: Path) -> list[dict[str, Any]]:
|
|
778
778
|
active_entries = load_entries_from_dir(knowledge / "entries" / "active")
|
|
779
|
-
|
|
779
|
+
manually_decided_entries = [
|
|
780
780
|
entry
|
|
781
|
-
for status in ["candidate", "stale"]
|
|
781
|
+
for status in ["candidate", "stale", "superseded", "conflicted"]
|
|
782
782
|
for entry in load_entries_from_dir(knowledge / "entries" / status)
|
|
783
783
|
if entry.get("lifecycle", {}).get("demotedAt")
|
|
784
|
+
or entry.get("lifecycle", {}).get("judgeAction")
|
|
784
785
|
]
|
|
785
|
-
return active_entries +
|
|
786
|
+
return active_entries + manually_decided_entries
|
|
786
787
|
|
|
787
788
|
|
|
788
789
|
def combine_generated_with_preserved(knowledge: Path, generated: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
@@ -2981,6 +2982,7 @@ def query_index(
|
|
|
2981
2982
|
file_filters: list[str] | None = None,
|
|
2982
2983
|
statuses: list[str] | None = None,
|
|
2983
2984
|
types: list[str] | None = None,
|
|
2985
|
+
change_id: str | None = None,
|
|
2984
2986
|
) -> dict[str, Any]:
|
|
2985
2987
|
project = project.resolve()
|
|
2986
2988
|
knowledge = project / ".harness" / "knowledge"
|
|
@@ -2993,6 +2995,26 @@ def query_index(
|
|
|
2993
2995
|
build_index(project, snapshot=snapshot)
|
|
2994
2996
|
entries = search_entries(sqlite_path, query, limit, file_filters, statuses, types)
|
|
2995
2997
|
context_path = write_context_pack(project, knowledge, query, entries)
|
|
2998
|
+
if change_id:
|
|
2999
|
+
changes_root = (project / ".harness" / "changes").resolve()
|
|
3000
|
+
change_dir = (changes_root / change_id).resolve()
|
|
3001
|
+
try:
|
|
3002
|
+
change_dir.relative_to(changes_root)
|
|
3003
|
+
except ValueError as exc:
|
|
3004
|
+
raise ValueError("change id escapes .harness/changes") from exc
|
|
3005
|
+
if not change_dir.is_dir():
|
|
3006
|
+
raise ValueError(f"change does not exist: {change_id}")
|
|
3007
|
+
write_json(
|
|
3008
|
+
change_dir / "meta" / "knowledge-context.json",
|
|
3009
|
+
{
|
|
3010
|
+
"schemaVersion": 1,
|
|
3011
|
+
"changeId": change_id,
|
|
3012
|
+
"generatedAt": now_iso(),
|
|
3013
|
+
"query": query,
|
|
3014
|
+
"contextPack": str(context_path),
|
|
3015
|
+
"matchIds": [entry["id"] for entry in entries],
|
|
3016
|
+
},
|
|
3017
|
+
)
|
|
2996
3018
|
filters = {
|
|
2997
3019
|
"files": file_filters or [],
|
|
2998
3020
|
"statuses": statuses or [],
|
|
@@ -3367,7 +3389,7 @@ def render_entry_for_context(entry: dict[str, Any], include_stale: bool = False)
|
|
|
3367
3389
|
NEAR_DUPLICATE_THRESHOLD = 0.88
|
|
3368
3390
|
AUTO_SUPERSEDE_SIMILARITY = 0.75
|
|
3369
3391
|
AUTO_SUPERSEDE_WEAK_SIMILARITY = 0.55
|
|
3370
|
-
JUDGE_ACTIONS = {"promote", "drop", "supersede", "keep-conflict"}
|
|
3392
|
+
JUDGE_ACTIONS = {"promote", "drop", "supersede", "keep-conflict", "defer"}
|
|
3371
3393
|
ENTRY_STATUSES = {"candidate", "active", "stale", "superseded", "conflicted"}
|
|
3372
3394
|
|
|
3373
3395
|
|
|
@@ -3829,6 +3851,8 @@ def judge_export(project: Path) -> dict[str, Any]:
|
|
|
3829
3851
|
for entry in entries:
|
|
3830
3852
|
if entry.get("status") != "conflicted":
|
|
3831
3853
|
continue
|
|
3854
|
+
if entry.get("lifecycle", {}).get("judgeAction") in {"keep-conflict", "defer"}:
|
|
3855
|
+
continue
|
|
3832
3856
|
for other_id in entry.get("lifecycle", {}).get("conflictsWith") or []:
|
|
3833
3857
|
pair = tuple(sorted([entry["id"], str(other_id)]))
|
|
3834
3858
|
if pair in seen_pairs:
|
|
@@ -3878,6 +3902,10 @@ def judge_export(project: Path) -> dict[str, Any]:
|
|
|
3878
3902
|
for entry in entries:
|
|
3879
3903
|
if entry.get("status") != "candidate":
|
|
3880
3904
|
continue
|
|
3905
|
+
if entry.get("lifecycle", {}).get("judgeAction") == "defer":
|
|
3906
|
+
review_after = str(entry.get("lifecycle", {}).get("reviewAfter") or "")
|
|
3907
|
+
if not review_after or review_after > dt.date.today().isoformat():
|
|
3908
|
+
continue
|
|
3881
3909
|
promote_candidates.append(
|
|
3882
3910
|
{
|
|
3883
3911
|
"kind": "promote-candidate",
|
|
@@ -3984,6 +4012,18 @@ def apply_judge_decision(
|
|
|
3984
4012
|
relocate_entry_file(knowledge, entry, source_path)
|
|
3985
4013
|
path_by_id[entry["id"]] = knowledge / "entries" / "conflicted" / entry_filename(entry)
|
|
3986
4014
|
after_status = "conflicted"
|
|
4015
|
+
elif action == "defer":
|
|
4016
|
+
lifecycle = entry.setdefault("lifecycle", {})
|
|
4017
|
+
lifecycle["judgeAction"] = action
|
|
4018
|
+
lifecycle["judgeReason"] = reason
|
|
4019
|
+
lifecycle["deferredAt"] = now_iso()
|
|
4020
|
+
lifecycle["reviewAfter"] = decision.get("reviewAfter")
|
|
4021
|
+
lifecycle["lastCheckedAt"] = now_iso()
|
|
4022
|
+
relocate_entry_file(knowledge, entry, source_path)
|
|
4023
|
+
path_by_id[entry["id"]] = (
|
|
4024
|
+
knowledge / "entries" / str(entry.get("status") or "candidate") / entry_filename(entry)
|
|
4025
|
+
)
|
|
4026
|
+
after_status = str(entry.get("status") or "candidate")
|
|
3987
4027
|
else:
|
|
3988
4028
|
raise ValueError(f"unsupported judge action: {action}")
|
|
3989
4029
|
|
|
@@ -3997,6 +4037,35 @@ def apply_judge_decision(
|
|
|
3997
4037
|
}
|
|
3998
4038
|
|
|
3999
4039
|
|
|
4040
|
+
def persist_judge_decisions(knowledge: Path, applied: list[dict[str, Any]]) -> Path:
|
|
4041
|
+
"""Keep one durable latest decision per entry independently of generated reports."""
|
|
4042
|
+
path = knowledge / "judgements" / "decisions.json"
|
|
4043
|
+
current: dict[str, Any] = {"schemaVersion": 1, "decisions": {}}
|
|
4044
|
+
if path.exists():
|
|
4045
|
+
try:
|
|
4046
|
+
loaded = read_json(path)
|
|
4047
|
+
if isinstance(loaded, dict) and isinstance(loaded.get("decisions"), dict):
|
|
4048
|
+
current = loaded
|
|
4049
|
+
except (OSError, json.JSONDecodeError):
|
|
4050
|
+
pass
|
|
4051
|
+
decisions = dict(current.get("decisions") or {})
|
|
4052
|
+
for record in applied:
|
|
4053
|
+
decisions[str(record["id"])] = {
|
|
4054
|
+
"id": record["id"],
|
|
4055
|
+
"action": record["action"],
|
|
4056
|
+
"reason": record.get("reason") or "",
|
|
4057
|
+
"supersededBy": record.get("supersededBy"),
|
|
4058
|
+
"status": record.get("after", {}).get("status"),
|
|
4059
|
+
"decidedAt": now_iso(),
|
|
4060
|
+
}
|
|
4061
|
+
write_json(path, {
|
|
4062
|
+
"schemaVersion": 1,
|
|
4063
|
+
"updatedAt": now_iso(),
|
|
4064
|
+
"decisions": dict(sorted(decisions.items())),
|
|
4065
|
+
})
|
|
4066
|
+
return path
|
|
4067
|
+
|
|
4068
|
+
|
|
4000
4069
|
def judge_apply(project: Path, decisions_path: Path, force: bool = False) -> dict[str, Any]:
|
|
4001
4070
|
project = project.resolve()
|
|
4002
4071
|
knowledge = project / ".harness" / "knowledge"
|
|
@@ -4051,11 +4120,13 @@ def judge_apply(project: Path, decisions_path: Path, force: bool = False) -> dic
|
|
|
4051
4120
|
}
|
|
4052
4121
|
judgement_path = knowledge / "reports" / f"judgements-{timestamp()}.json"
|
|
4053
4122
|
write_json(judgement_path, judgement)
|
|
4123
|
+
decisions_ledger = persist_judge_decisions(knowledge, applied)
|
|
4054
4124
|
index = refresh_outputs_from_entry_files(project, knowledge)
|
|
4055
4125
|
return {
|
|
4056
4126
|
"project": str(project),
|
|
4057
4127
|
"generatedAt": judgement["generatedAt"],
|
|
4058
4128
|
"judgement": str(judgement_path),
|
|
4129
|
+
"decisionsLedger": str(decisions_ledger),
|
|
4059
4130
|
"applied": len(applied),
|
|
4060
4131
|
"errors": errors,
|
|
4061
4132
|
"stats": index.get("stats", {}),
|
|
@@ -4262,6 +4333,26 @@ def maintain_knowledge(project: Path, archive_id: str) -> dict[str, Any]:
|
|
|
4262
4333
|
}
|
|
4263
4334
|
|
|
4264
4335
|
|
|
4336
|
+
def drain_maintenance_outbox(project: Path, limit: int = 50) -> dict[str, Any]:
|
|
4337
|
+
"""Advance all retryable maintenance items in one bounded invocation."""
|
|
4338
|
+
project = project.resolve()
|
|
4339
|
+
root = _outbox_root(project)
|
|
4340
|
+
all_archive_ids = sorted({
|
|
4341
|
+
path.stem
|
|
4342
|
+
for status in ("pending", "failed")
|
|
4343
|
+
for path in (root / status).glob("*.json")
|
|
4344
|
+
})
|
|
4345
|
+
archive_ids = all_archive_ids[:max(0, limit)]
|
|
4346
|
+
results = [maintain_knowledge(project, archive_id) for archive_id in archive_ids]
|
|
4347
|
+
return {
|
|
4348
|
+
"ok": all(result.get("ok") for result in results),
|
|
4349
|
+
"project": str(project),
|
|
4350
|
+
"processed": len(results),
|
|
4351
|
+
"remaining": max(0, len(all_archive_ids) - len(results)),
|
|
4352
|
+
"results": results,
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4355
|
+
|
|
4265
4356
|
def main(argv: list[str] | None = None) -> int:
|
|
4266
4357
|
parser = argparse.ArgumentParser(description="Build and query Harness knowledge indexes.")
|
|
4267
4358
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
@@ -4321,6 +4412,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
4321
4412
|
query.add_argument("--file", action="append", dest="files", default=[], help="Only return entries linked to this source file")
|
|
4322
4413
|
query.add_argument("--status", action="append", dest="statuses", default=[], help="Only return entries with this lifecycle status")
|
|
4323
4414
|
query.add_argument("--type", action="append", dest="types", default=[], help="Only return entries of this knowledge type")
|
|
4415
|
+
query.add_argument("--change", default=None, help="Write a change-scoped knowledge-context pointer")
|
|
4324
4416
|
|
|
4325
4417
|
promote = sub.add_parser("promote", help="Promote a candidate knowledge entry to active")
|
|
4326
4418
|
promote.add_argument("--project", default=".", help="Project root containing .harness/knowledge")
|
|
@@ -4378,7 +4470,10 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
4378
4470
|
help="Single-process maintenance: claim outbox, ingest+dedupe, supersede, reverify, judge-export",
|
|
4379
4471
|
)
|
|
4380
4472
|
maintain.add_argument("--project", default=".", help="Project root containing .harness/knowledge")
|
|
4381
|
-
maintain.
|
|
4473
|
+
maintain_target = maintain.add_mutually_exclusive_group(required=True)
|
|
4474
|
+
maintain_target.add_argument("--archive-id", help="Archive id of the outbox item to maintain")
|
|
4475
|
+
maintain_target.add_argument("--drain", action="store_true", help="Process all pending/failed items once")
|
|
4476
|
+
maintain.add_argument("--limit", type=int, default=50, help="Maximum items processed by --drain")
|
|
4382
4477
|
maintain.add_argument("--json", action="store_true", help="Emit machine-readable JSON (default behavior)")
|
|
4383
4478
|
|
|
4384
4479
|
args = parser.parse_args(argv)
|
|
@@ -4414,7 +4509,10 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
4414
4509
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
4415
4510
|
return 0
|
|
4416
4511
|
if args.command == "query":
|
|
4417
|
-
result = query_index(
|
|
4512
|
+
result = query_index(
|
|
4513
|
+
Path(args.project), args.query, args.limit, args.files, args.statuses,
|
|
4514
|
+
args.types, args.change,
|
|
4515
|
+
)
|
|
4418
4516
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
4419
4517
|
return 0
|
|
4420
4518
|
if args.command == "promote":
|
|
@@ -4468,7 +4566,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
4468
4566
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
4469
4567
|
return 0
|
|
4470
4568
|
if args.command == "maintain":
|
|
4471
|
-
result =
|
|
4569
|
+
result = (
|
|
4570
|
+
drain_maintenance_outbox(Path(args.project), args.limit)
|
|
4571
|
+
if args.drain
|
|
4572
|
+
else maintain_knowledge(Path(args.project), args.archive_id)
|
|
4573
|
+
)
|
|
4472
4574
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
4473
4575
|
return 0 if result.get("ok") else 1
|
|
4474
4576
|
return 2
|
package/harness/bundles/java/cursor/harness-knowledge-ingest/scripts/harness_knowledge_mcp.py
CHANGED
|
@@ -219,6 +219,7 @@ def create_server() -> Any:
|
|
|
219
219
|
files: list[str] | None = None,
|
|
220
220
|
statuses: list[str] | None = None,
|
|
221
221
|
types: list[str] | None = None,
|
|
222
|
+
change_id: str | None = None,
|
|
222
223
|
) -> dict[str, Any]:
|
|
223
224
|
return hk.query_index(
|
|
224
225
|
Path(project),
|
|
@@ -227,6 +228,7 @@ def create_server() -> Any:
|
|
|
227
228
|
file_filters=files or [],
|
|
228
229
|
statuses=statuses or [],
|
|
229
230
|
types=types or [],
|
|
231
|
+
change_id=change_id,
|
|
230
232
|
)
|
|
231
233
|
|
|
232
234
|
@server.tool(description=TOOL_DESCRIPTIONS[7]["description"])
|
|
@@ -3,9 +3,11 @@ name: harness-knowledge-query
|
|
|
3
3
|
description: 根据新需求、设计任务、代码修改请求或问题排查,在规划和编码前查询 .harness/knowledge
|
|
4
4
|
历史上下文。适用场景:query knowledge、查找历史需求、根据归档理解需求、继续之前类似开发、生成需求上下文包。
|
|
5
5
|
---
|
|
6
|
-
<!-- generated by harness_deploy.py; core=
|
|
6
|
+
<!-- generated by harness_deploy.py; core=8337ddaf1dcd93a2; overlay=java; agent=cursor; do not edit -->
|
|
7
7
|
# harness-knowledge-query
|
|
8
8
|
|
|
9
|
+
并行 change 中查询必须传 `--change <change-id>`;成功后以该 change 的 `meta/knowledge-context.json` 为稳定指针。全局 `context-packs/latest.json` 仅向后兼容,不得用于判断多个 active change 的上下文归属。
|
|
10
|
+
|
|
9
11
|
根据用户的新需求或排查问题,从项目本地 `.harness/knowledge` 中检索历史需求、决策、实现、风险和测试证据,并生成 AI 可读的 context pack。
|
|
10
12
|
|
|
11
13
|
此 skill 只负责查询和使用知识。整理、同步和 promote 条目由 `harness-knowledge-ingest` 负责。
|
|
@@ -130,7 +132,7 @@ powershell.exe -Command "python '<ingest-skill-dir>\scripts\harness_knowledge.py
|
|
|
130
132
|
|
|
131
133
|
## 执行日志
|
|
132
134
|
|
|
133
|
-
`events.ndjson` 为唯一事实源(schema_version
|
|
135
|
+
`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
136
|
|
|
135
137
|
```powershell
|
|
136
138
|
python <skills-root>/scripts/harness_events.py append --change-dir ".harness/changes/<change-name>" --phase <phase> --type phase.start --note "<触发指令>"
|
|
@@ -139,3 +141,5 @@ python <skills-root>/scripts/harness_events.py append --change-dir ".harness/cha
|
|
|
139
141
|
> **脚本接线**:`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
142
|
|
|
141
143
|
> **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 边界保持最新。
|
|
144
|
+
|
|
145
|
+
每次阶段结束必须写 `--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` 环境变量统一注入。
|