@hunter-harness/workflow-harness 0.2.85 → 0.3.0
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 +46 -17
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-execute/SKILL.md +189 -0
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +10 -9
- package/harness/bundles/general/claude-code/harness-plan/checklist.md +19 -91
- package/harness/bundles/general/claude-code/harness-plan/protocols.md +3 -1
- package/harness/bundles/general/claude-code/harness-plan/reference.md +74 -83
- package/harness/bundles/general/claude-code/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +3 -3
- package/harness/bundles/general/claude-code/harness-review/protocols.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +13 -5
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/claude-code/harness-submit/checklist.md +1 -1
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +11 -2
- package/harness/bundles/general/claude-code/scripts/harness_adoption_metrics.py +391 -0
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +23 -14
- package/harness/bundles/general/claude-code/scripts/harness_change.py +25 -7
- package/harness/bundles/general/claude-code/scripts/harness_context.py +154 -60
- package/harness/bundles/general/claude-code/scripts/harness_events.py +1 -1
- package/harness/bundles/general/claude-code/scripts/harness_events_sync.py +1 -0
- package/harness/bundles/general/claude-code/scripts/harness_fixback.py +14 -10
- package/harness/bundles/general/claude-code/scripts/harness_gate.py +293 -90
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +47 -2
- package/harness/bundles/general/claude-code/scripts/harness_paths.py +175 -0
- package/harness/bundles/general/claude-code/scripts/harness_phase.py +65 -23
- package/harness/bundles/general/claude-code/scripts/harness_plan_finalize.py +140 -874
- package/harness/bundles/general/claude-code/scripts/harness_review.py +25 -4
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/contracts/workflow-policy.json +46 -17
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-execute/SKILL.md +170 -0
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +10 -9
- package/harness/bundles/general/codebuddy/harness-plan/checklist.md +19 -91
- package/harness/bundles/general/codebuddy/harness-plan/protocols.md +3 -1
- package/harness/bundles/general/codebuddy/harness-plan/reference.md +74 -83
- package/harness/bundles/general/codebuddy/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +3 -3
- package/harness/bundles/general/codebuddy/harness-review/protocols.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +13 -5
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/codebuddy/harness-submit/checklist.md +1 -1
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +11 -2
- package/harness/bundles/general/codebuddy/scripts/harness_adoption_metrics.py +391 -0
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +23 -14
- package/harness/bundles/general/codebuddy/scripts/harness_change.py +25 -7
- package/harness/bundles/general/codebuddy/scripts/harness_context.py +154 -60
- package/harness/bundles/general/codebuddy/scripts/harness_events.py +1 -1
- package/harness/bundles/general/codebuddy/scripts/harness_events_sync.py +1 -0
- package/harness/bundles/general/codebuddy/scripts/harness_fixback.py +14 -10
- package/harness/bundles/general/codebuddy/scripts/harness_gate.py +293 -90
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +47 -2
- package/harness/bundles/general/codebuddy/scripts/harness_paths.py +175 -0
- package/harness/bundles/general/codebuddy/scripts/harness_phase.py +65 -23
- package/harness/bundles/general/codebuddy/scripts/harness_plan_finalize.py +140 -874
- package/harness/bundles/general/codebuddy/scripts/harness_review.py +25 -4
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/contracts/workflow-policy.json +46 -17
- package/harness/bundles/general/codex/harness-archive/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-execute/SKILL.md +170 -0
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-plan/SKILL.md +10 -9
- package/harness/bundles/general/codex/harness-plan/checklist.md +19 -91
- package/harness/bundles/general/codex/harness-plan/protocols.md +3 -1
- package/harness/bundles/general/codex/harness-plan/reference.md +74 -83
- package/harness/bundles/general/codex/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-review/SKILL.md +3 -3
- package/harness/bundles/general/codex/harness-review/protocols.md +1 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +13 -5
- package/harness/bundles/general/codex/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/codex/harness-submit/checklist.md +1 -1
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +11 -2
- package/harness/bundles/general/codex/scripts/harness_adoption_metrics.py +391 -0
- package/harness/bundles/general/codex/scripts/harness_archive.py +23 -14
- package/harness/bundles/general/codex/scripts/harness_change.py +25 -7
- package/harness/bundles/general/codex/scripts/harness_context.py +154 -60
- package/harness/bundles/general/codex/scripts/harness_events.py +1 -1
- package/harness/bundles/general/codex/scripts/harness_events_sync.py +1 -0
- package/harness/bundles/general/codex/scripts/harness_fixback.py +14 -10
- package/harness/bundles/general/codex/scripts/harness_gate.py +293 -90
- package/harness/bundles/general/codex/scripts/harness_ledger.py +47 -2
- package/harness/bundles/general/codex/scripts/harness_paths.py +175 -0
- package/harness/bundles/general/codex/scripts/harness_phase.py +65 -23
- package/harness/bundles/general/codex/scripts/harness_plan_finalize.py +140 -874
- package/harness/bundles/general/codex/scripts/harness_review.py +25 -4
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/contracts/workflow-policy.json +46 -17
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-execute/SKILL.md +170 -0
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +10 -9
- package/harness/bundles/general/cursor/harness-plan/checklist.md +19 -91
- package/harness/bundles/general/cursor/harness-plan/protocols.md +3 -1
- package/harness/bundles/general/cursor/harness-plan/reference.md +74 -83
- package/harness/bundles/general/cursor/harness-pull/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-push/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-review/SKILL.md +3 -3
- package/harness/bundles/general/cursor/harness-review/protocols.md +1 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +13 -5
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +2 -2
- package/harness/bundles/general/cursor/harness-submit/checklist.md +1 -1
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +11 -2
- package/harness/bundles/general/cursor/scripts/harness_adoption_metrics.py +391 -0
- package/harness/bundles/general/cursor/scripts/harness_archive.py +23 -14
- package/harness/bundles/general/cursor/scripts/harness_change.py +25 -7
- package/harness/bundles/general/cursor/scripts/harness_context.py +154 -60
- package/harness/bundles/general/cursor/scripts/harness_events.py +1 -1
- package/harness/bundles/general/cursor/scripts/harness_events_sync.py +1 -0
- package/harness/bundles/general/cursor/scripts/harness_fixback.py +14 -10
- package/harness/bundles/general/cursor/scripts/harness_gate.py +293 -90
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +47 -2
- package/harness/bundles/general/cursor/scripts/harness_paths.py +175 -0
- package/harness/bundles/general/cursor/scripts/harness_phase.py +65 -23
- package/harness/bundles/general/cursor/scripts/harness_plan_finalize.py +140 -874
- package/harness/bundles/general/cursor/scripts/harness_review.py +25 -4
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/contracts/workflow-policy.json +46 -17
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-execute/SKILL.md +189 -0
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +10 -9
- package/harness/bundles/java/claude-code/harness-plan/checklist.md +19 -91
- package/harness/bundles/java/claude-code/harness-plan/protocols.md +3 -1
- package/harness/bundles/java/claude-code/harness-plan/reference.md +74 -83
- package/harness/bundles/java/claude-code/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +3 -3
- package/harness/bundles/java/claude-code/harness-review/protocols.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +13 -5
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/claude-code/harness-submit/checklist.md +1 -1
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +11 -2
- package/harness/bundles/java/claude-code/scripts/harness_adoption_metrics.py +391 -0
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +23 -14
- package/harness/bundles/java/claude-code/scripts/harness_change.py +25 -7
- package/harness/bundles/java/claude-code/scripts/harness_context.py +154 -60
- package/harness/bundles/java/claude-code/scripts/harness_events.py +1 -1
- package/harness/bundles/java/claude-code/scripts/harness_events_sync.py +1 -0
- package/harness/bundles/java/claude-code/scripts/harness_fixback.py +14 -10
- package/harness/bundles/java/claude-code/scripts/harness_gate.py +293 -90
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +47 -2
- package/harness/bundles/java/claude-code/scripts/harness_paths.py +175 -0
- package/harness/bundles/java/claude-code/scripts/harness_phase.py +65 -23
- package/harness/bundles/java/claude-code/scripts/harness_plan_finalize.py +140 -874
- package/harness/bundles/java/claude-code/scripts/harness_review.py +25 -4
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/contracts/workflow-policy.json +46 -17
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-execute/SKILL.md +170 -0
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +10 -9
- package/harness/bundles/java/codebuddy/harness-plan/checklist.md +19 -91
- package/harness/bundles/java/codebuddy/harness-plan/protocols.md +3 -1
- package/harness/bundles/java/codebuddy/harness-plan/reference.md +74 -83
- package/harness/bundles/java/codebuddy/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +3 -3
- package/harness/bundles/java/codebuddy/harness-review/protocols.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +13 -5
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/codebuddy/harness-submit/checklist.md +1 -1
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +11 -2
- package/harness/bundles/java/codebuddy/scripts/harness_adoption_metrics.py +391 -0
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +23 -14
- package/harness/bundles/java/codebuddy/scripts/harness_change.py +25 -7
- package/harness/bundles/java/codebuddy/scripts/harness_context.py +154 -60
- package/harness/bundles/java/codebuddy/scripts/harness_events.py +1 -1
- package/harness/bundles/java/codebuddy/scripts/harness_events_sync.py +1 -0
- package/harness/bundles/java/codebuddy/scripts/harness_fixback.py +14 -10
- package/harness/bundles/java/codebuddy/scripts/harness_gate.py +293 -90
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +47 -2
- package/harness/bundles/java/codebuddy/scripts/harness_paths.py +175 -0
- package/harness/bundles/java/codebuddy/scripts/harness_phase.py +65 -23
- package/harness/bundles/java/codebuddy/scripts/harness_plan_finalize.py +140 -874
- package/harness/bundles/java/codebuddy/scripts/harness_review.py +25 -4
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/contracts/workflow-policy.json +46 -17
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-archive/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-execute/SKILL.md +170 -0
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-package/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-plan/SKILL.md +10 -9
- package/harness/bundles/java/codex/harness-plan/checklist.md +19 -91
- package/harness/bundles/java/codex/harness-plan/protocols.md +3 -1
- package/harness/bundles/java/codex/harness-plan/reference.md +74 -83
- package/harness/bundles/java/codex/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-review/SKILL.md +3 -3
- package/harness/bundles/java/codex/harness-review/protocols.md +1 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +13 -5
- package/harness/bundles/java/codex/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/codex/harness-submit/checklist.md +1 -1
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +11 -2
- package/harness/bundles/java/codex/scripts/harness_adoption_metrics.py +391 -0
- package/harness/bundles/java/codex/scripts/harness_archive.py +23 -14
- package/harness/bundles/java/codex/scripts/harness_change.py +25 -7
- package/harness/bundles/java/codex/scripts/harness_context.py +154 -60
- package/harness/bundles/java/codex/scripts/harness_events.py +1 -1
- package/harness/bundles/java/codex/scripts/harness_events_sync.py +1 -0
- package/harness/bundles/java/codex/scripts/harness_fixback.py +14 -10
- package/harness/bundles/java/codex/scripts/harness_gate.py +293 -90
- package/harness/bundles/java/codex/scripts/harness_ledger.py +47 -2
- package/harness/bundles/java/codex/scripts/harness_paths.py +175 -0
- package/harness/bundles/java/codex/scripts/harness_phase.py +65 -23
- package/harness/bundles/java/codex/scripts/harness_plan_finalize.py +140 -874
- package/harness/bundles/java/codex/scripts/harness_review.py +25 -4
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/contracts/workflow-policy.json +46 -17
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-execute/SKILL.md +170 -0
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-package/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +10 -9
- package/harness/bundles/java/cursor/harness-plan/checklist.md +19 -91
- package/harness/bundles/java/cursor/harness-plan/protocols.md +3 -1
- package/harness/bundles/java/cursor/harness-plan/reference.md +74 -83
- package/harness/bundles/java/cursor/harness-pull/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-push/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-review/SKILL.md +3 -3
- package/harness/bundles/java/cursor/harness-review/protocols.md +1 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +13 -5
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +2 -2
- package/harness/bundles/java/cursor/harness-submit/checklist.md +1 -1
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +11 -2
- package/harness/bundles/java/cursor/scripts/harness_adoption_metrics.py +391 -0
- package/harness/bundles/java/cursor/scripts/harness_archive.py +23 -14
- package/harness/bundles/java/cursor/scripts/harness_change.py +25 -7
- package/harness/bundles/java/cursor/scripts/harness_context.py +154 -60
- package/harness/bundles/java/cursor/scripts/harness_events.py +1 -1
- package/harness/bundles/java/cursor/scripts/harness_events_sync.py +1 -0
- package/harness/bundles/java/cursor/scripts/harness_fixback.py +14 -10
- package/harness/bundles/java/cursor/scripts/harness_gate.py +293 -90
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +47 -2
- package/harness/bundles/java/cursor/scripts/harness_paths.py +175 -0
- package/harness/bundles/java/cursor/scripts/harness_phase.py +65 -23
- package/harness/bundles/java/cursor/scripts/harness_plan_finalize.py +140 -874
- package/harness/bundles/java/cursor/scripts/harness_review.py +25 -4
- package/harness/manifests/general/claude-code.json +40 -32
- package/harness/manifests/general/codebuddy.json +40 -32
- package/harness/manifests/general/codex.json +40 -32
- package/harness/manifests/general/cursor.json +40 -32
- package/harness/manifests/java/claude-code.json +42 -34
- package/harness/manifests/java/codebuddy.json +42 -34
- package/harness/manifests/java/codex.json +42 -34
- package/harness/manifests/java/cursor.json +42 -34
- package/hunter-workflow-family.json +4 -4
- package/package.json +1 -1
|
@@ -28,6 +28,7 @@ if str(SCRIPTS_DIR) not in sys.path:
|
|
|
28
28
|
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
29
29
|
|
|
30
30
|
import harness_paths # noqa: E402
|
|
31
|
+
import harness_plan_finalize as hpf # noqa: E402
|
|
31
32
|
import harness_profile # noqa: E402
|
|
32
33
|
|
|
33
34
|
|
|
@@ -201,14 +202,16 @@ def frozen_ownership_check(change_dir: Path) -> dict[str, Any]:
|
|
|
201
202
|
if not capsule_root.is_dir():
|
|
202
203
|
return {"ok": True, "code": "OWNERSHIP_FREEZE_NOT_APPLICABLE"}
|
|
203
204
|
capsules: list[dict[str, Any]] = []
|
|
204
|
-
|
|
205
|
+
# capsule 文件名是 <phase>-<hash>.json;execute 合并(run/test→execute)后
|
|
206
|
+
# 新旧两批名字都可能躺在在途 change 里,按内容归一判定而不是按文件名。
|
|
207
|
+
for path in capsule_root.glob("*.json"):
|
|
205
208
|
try:
|
|
206
209
|
item = json.loads(path.read_text(encoding="utf-8-sig"))
|
|
207
210
|
except (OSError, json.JSONDecodeError):
|
|
208
211
|
continue
|
|
209
212
|
if (
|
|
210
213
|
isinstance(item, dict)
|
|
211
|
-
and item.get("phase") == "
|
|
214
|
+
and harness_paths.resolve_phase_name(item.get("phase")) == "execute"
|
|
212
215
|
and not item.get("closedAt")
|
|
213
216
|
and _nonempty_str(item.get("ownershipHash"))
|
|
214
217
|
):
|
|
@@ -1629,6 +1632,24 @@ def _scenario_receipt_error(code: str, message: str) -> dict[str, Any]:
|
|
|
1629
1632
|
return {"ok": False, "code": code, "error": message}
|
|
1630
1633
|
|
|
1631
1634
|
|
|
1635
|
+
def resolve_scenario_manifest(manifest: Any) -> dict[str, Any]:
|
|
1636
|
+
"""把 v2 artifact 包装体解成 legacy 形状;legacy 输入原样返回。
|
|
1637
|
+
|
|
1638
|
+
解包规则住在 harness_plan_finalize(它定义了 legacy manifest 的 schema),
|
|
1639
|
+
门禁与本模块共用同一份,两边不会各推一套。
|
|
1640
|
+
|
|
1641
|
+
此前这里只判包装体、一律 fail-closed;更早的时候连判都不判——cmd_record
|
|
1642
|
+
的版本探测初值是 0,包装体探测不出 schemaVersion 就停在 0,
|
|
1643
|
+
`manifest_schema >= 2` 判假,--scenario-receipt-file 的强制要求被静默跳过。
|
|
1644
|
+
|
|
1645
|
+
返回 ``{"ok": True, "manifest": ...}`` 或 ``{"ok": False, "code": ...}``。
|
|
1646
|
+
"""
|
|
1647
|
+
unpacked = hpf.unpack_v2_scenario_manifest(manifest)
|
|
1648
|
+
if unpacked is None:
|
|
1649
|
+
return {"ok": True, "manifest": manifest}
|
|
1650
|
+
return unpacked
|
|
1651
|
+
|
|
1652
|
+
|
|
1632
1653
|
def _resolve_receipt_path(
|
|
1633
1654
|
raw: str, change_dir: Path
|
|
1634
1655
|
) -> tuple[Path | None, list[Path]]:
|
|
@@ -1694,6 +1715,12 @@ def validate_scenario_execution_receipt(
|
|
|
1694
1715
|
)
|
|
1695
1716
|
except (OSError, json.JSONDecodeError) as exc:
|
|
1696
1717
|
return _scenario_receipt_error("SCENARIO_MANIFEST_INVALID", str(exc))
|
|
1718
|
+
resolved = resolve_scenario_manifest(manifest)
|
|
1719
|
+
if not resolved["ok"]:
|
|
1720
|
+
return _scenario_receipt_error(
|
|
1721
|
+
str(resolved["code"]), str(resolved.get("message") or "")
|
|
1722
|
+
)
|
|
1723
|
+
manifest = resolved["manifest"]
|
|
1697
1724
|
if not isinstance(manifest, dict) or not isinstance(manifest.get("scenarios"), list):
|
|
1698
1725
|
return _scenario_receipt_error(
|
|
1699
1726
|
"SCENARIO_MANIFEST_INVALID",
|
|
@@ -2827,6 +2854,15 @@ def cmd_record(args: argparse.Namespace) -> int:
|
|
|
2827
2854
|
as_json=as_json,
|
|
2828
2855
|
error_code="SCENARIO_MANIFEST_INVALID",
|
|
2829
2856
|
)
|
|
2857
|
+
resolved_probe = resolve_scenario_manifest(manifest_probe)
|
|
2858
|
+
if not resolved_probe["ok"]:
|
|
2859
|
+
return emit_error(
|
|
2860
|
+
str(resolved_probe.get("message") or ""),
|
|
2861
|
+
as_json=as_json,
|
|
2862
|
+
error_code=str(resolved_probe["code"]),
|
|
2863
|
+
extra={"artifactType": "scenario_manifest"},
|
|
2864
|
+
)
|
|
2865
|
+
manifest_probe = resolved_probe["manifest"]
|
|
2830
2866
|
raw_schema = (
|
|
2831
2867
|
manifest_probe.get("schemaVersion")
|
|
2832
2868
|
if isinstance(manifest_probe, dict)
|
|
@@ -3064,6 +3100,15 @@ def cmd_scenario_receipt_template(args: argparse.Namespace) -> int:
|
|
|
3064
3100
|
as_json=as_json,
|
|
3065
3101
|
error_code="SCENARIO_MANIFEST_INVALID",
|
|
3066
3102
|
)
|
|
3103
|
+
resolved = resolve_scenario_manifest(manifest)
|
|
3104
|
+
if not resolved["ok"]:
|
|
3105
|
+
return emit_error(
|
|
3106
|
+
str(resolved.get("message") or ""),
|
|
3107
|
+
as_json=as_json,
|
|
3108
|
+
error_code=str(resolved["code"]),
|
|
3109
|
+
extra={"artifactType": "scenario_manifest"},
|
|
3110
|
+
)
|
|
3111
|
+
manifest = resolved["manifest"]
|
|
3067
3112
|
scenarios = manifest.get("scenarios") if isinstance(manifest, dict) else None
|
|
3068
3113
|
if not isinstance(scenarios, list):
|
|
3069
3114
|
return emit_error(
|
|
@@ -34,6 +34,67 @@ if hasattr(sys.stderr, "reconfigure"):
|
|
|
34
34
|
LIFECYCLE_STATUSES = {"draft", "active", "superseded", "archived", "cancelled"}
|
|
35
35
|
CHANGE_CONTEXT_REL = Path("meta") / "change-context.json"
|
|
36
36
|
|
|
37
|
+
# 工作流阶段的唯一权威清单。这里是叶子模块(不 import 任何 harness_*),所以
|
|
38
|
+
# harness_context 与 harness_phase 都能引它而不成环。
|
|
39
|
+
#
|
|
40
|
+
# 此前这份清单在两个模块各写一遍,靠约定保持同步——直到 harness_phase 少了一个
|
|
41
|
+
# merge,worktree 变更走到 merge 阶段就在 target_required_dag 里直接 raise
|
|
42
|
+
# "unsupported reconcile target phase"。同一份事实不该有两个副本。
|
|
43
|
+
WORKFLOW_PHASES = (
|
|
44
|
+
"plan",
|
|
45
|
+
"execute",
|
|
46
|
+
"review",
|
|
47
|
+
"package",
|
|
48
|
+
"apidoc",
|
|
49
|
+
"submit",
|
|
50
|
+
"merge",
|
|
51
|
+
"archive",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# 出现在阶段位置、但不是工作流阶段的名字。它们是真实存在的:
|
|
55
|
+
# workflow-policy.json 的 skills.*.phase 用 sync/codebase-map/knowledge-query 标注
|
|
56
|
+
# 技能归属,harness_gate.GATE_RELEASE_PHASES 含 release/deploy。把它们与"拼错的
|
|
57
|
+
# 阶段名"区分开,调用方才能既不误判也不硬崩。
|
|
58
|
+
KNOWN_NON_WORKFLOW_PHASES = frozenset({
|
|
59
|
+
"release",
|
|
60
|
+
"deploy",
|
|
61
|
+
"sync",
|
|
62
|
+
"codebase-map",
|
|
63
|
+
"knowledge-query",
|
|
64
|
+
"knowledge-ingest",
|
|
65
|
+
"push",
|
|
66
|
+
"pull",
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
# 旧阶段名 → 现阶段名。阶段合并/改名时在这里登记,读时映射即可让历史
|
|
70
|
+
# gate-policy.json、plannedPhases 与已落盘 transitions 继续可读,不必迁移
|
|
71
|
+
# 已落盘的 change。注意:映射只能用在哈希校验之后的业务比较层——对已哈希
|
|
72
|
+
# 的 receipt 先映射再重算会断 _payload_hash 链。
|
|
73
|
+
# 2026-08 run+test 合并为 execute(方案 c,review 保留独立阶段)。
|
|
74
|
+
LEGACY_PHASE_ALIASES: dict[str, str] = {"run": "execute", "test": "execute"}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def resolve_phase_name(phase: Any) -> str | None:
|
|
78
|
+
"""把可能是旧名的阶段解析成当前的工作流阶段名;不是工作流阶段则返回 None。"""
|
|
79
|
+
name = str(phase or "").strip()
|
|
80
|
+
if name in WORKFLOW_PHASES:
|
|
81
|
+
return name
|
|
82
|
+
mapped = LEGACY_PHASE_ALIASES.get(name)
|
|
83
|
+
return mapped if mapped in WORKFLOW_PHASES else None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def classify_phase_name(phase: Any) -> str:
|
|
87
|
+
"""三态判定:``workflow`` | ``non_workflow`` | ``unknown``。
|
|
88
|
+
|
|
89
|
+
调用方需要区分这三者:``workflow`` 照常处理;``non_workflow`` 是别的子系统
|
|
90
|
+
的标注,跳过而不是报错;只有 ``unknown`` 才是真的有问题,值得让人看见。
|
|
91
|
+
"""
|
|
92
|
+
if resolve_phase_name(phase) is not None:
|
|
93
|
+
return "workflow"
|
|
94
|
+
if str(phase or "").strip() in KNOWN_NON_WORKFLOW_PHASES:
|
|
95
|
+
return "non_workflow"
|
|
96
|
+
return "unknown"
|
|
97
|
+
|
|
37
98
|
|
|
38
99
|
def _git_text(cwd: Path, *args: str) -> str | None:
|
|
39
100
|
proc = subprocess.run(
|
|
@@ -170,6 +231,120 @@ def load_change_contract(contract_dir: Path) -> dict[str, Any]:
|
|
|
170
231
|
return data
|
|
171
232
|
|
|
172
233
|
|
|
234
|
+
def _unpack_v2_gate_policy(wrapper: Any) -> dict[str, Any] | None:
|
|
235
|
+
"""meta/plan-profile.json(v2 包装体)→ Python schemaVersion:1 形状。
|
|
236
|
+
|
|
237
|
+
门禁权威所需的四个字段(mode/planned_phases/required_gate_dag/
|
|
238
|
+
required_validations_by_phase)缺一即视为不完整快照(0.2.92-era 的发布
|
|
239
|
+
没有门禁 overlay),调用方回退工作副本。返回 None 表示不可用作权威。
|
|
240
|
+
"""
|
|
241
|
+
if not isinstance(wrapper, dict):
|
|
242
|
+
return None
|
|
243
|
+
if wrapper.get("artifact_type") != "gate_policy":
|
|
244
|
+
return None
|
|
245
|
+
content = wrapper.get("content")
|
|
246
|
+
if not isinstance(content, dict):
|
|
247
|
+
return None
|
|
248
|
+
planned = content.get("planned_phases")
|
|
249
|
+
dag = content.get("required_gate_dag")
|
|
250
|
+
by_phase = content.get("required_validations_by_phase")
|
|
251
|
+
if (
|
|
252
|
+
not isinstance(content.get("mode"), str)
|
|
253
|
+
or not (isinstance(planned, list) and all(isinstance(p, str) for p in planned))
|
|
254
|
+
or not isinstance(dag, dict)
|
|
255
|
+
or not isinstance(by_phase, dict)
|
|
256
|
+
):
|
|
257
|
+
return None
|
|
258
|
+
policy: dict[str, Any] = {
|
|
259
|
+
"schemaVersion": 1,
|
|
260
|
+
"tier": content.get("tier"),
|
|
261
|
+
"source": content.get("source"),
|
|
262
|
+
"plannedPhases": list(planned),
|
|
263
|
+
"requiredValidations": list(content.get("required_validations") or []),
|
|
264
|
+
"requiredValidationsByPhase": dict(by_phase),
|
|
265
|
+
"requiredGateDag": dict(dag),
|
|
266
|
+
}
|
|
267
|
+
return policy
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def load_change_gate_policy(change_dir: Path) -> dict[str, Any]:
|
|
271
|
+
"""v2-first 的每 change 门禁策略加载(权威切换 2026-08)。
|
|
272
|
+
|
|
273
|
+
meta/plan-profile.json(v2 发布快照,门禁字段已哈希绑定)优先于
|
|
274
|
+
meta/gate-policy.json(classify 在 0.5 写的工作副本)。v2 快照不完整
|
|
275
|
+
或缺失 → 回退工作副本。两者并存且 plannedPhases(canonical 归一去重
|
|
276
|
+
后)不一致 → drift 报告,策略以 v2 为准(已审批定稿 > 工作副本;
|
|
277
|
+
发布后改写工作副本本身就是异常)。
|
|
278
|
+
|
|
279
|
+
返回 ``{"policy": <schemaVersion:1 dict 或 None>, "source":
|
|
280
|
+
"v2-plan-profile" | "gate-policy-json" | None, "drift": dict | None,
|
|
281
|
+
"working_error": bool}``——``working_error`` 区分"工作副本不存在"与
|
|
282
|
+
"存在但不可读"(`_phase_plan` 的 legacy:policy-unreadable 语义靠它)。
|
|
283
|
+
"""
|
|
284
|
+
change_dir = Path(change_dir)
|
|
285
|
+
v2_policy: dict[str, Any] | None = None
|
|
286
|
+
v2_path = change_dir / "meta" / "plan-profile.json"
|
|
287
|
+
if v2_path.is_file():
|
|
288
|
+
try:
|
|
289
|
+
v2_policy = _unpack_v2_gate_policy(
|
|
290
|
+
json.loads(v2_path.read_text(encoding="utf-8-sig"))
|
|
291
|
+
)
|
|
292
|
+
except (OSError, ValueError, json.JSONDecodeError):
|
|
293
|
+
v2_policy = None
|
|
294
|
+
|
|
295
|
+
working: dict[str, Any] | None = None
|
|
296
|
+
working_error = False
|
|
297
|
+
working_path = change_dir / "meta" / "gate-policy.json"
|
|
298
|
+
if working_path.is_file():
|
|
299
|
+
try:
|
|
300
|
+
candidate = json.loads(working_path.read_text(encoding="utf-8-sig"))
|
|
301
|
+
# 工作副本保持与历史一致的宽松接受:任何 dict 都交给下游各自的
|
|
302
|
+
# 形状校验(effective_workflow_policy 查 schemaVersion,reconcile
|
|
303
|
+
# 查 requiredGateDag),加载器不提前收紧。
|
|
304
|
+
if isinstance(candidate, dict):
|
|
305
|
+
working = candidate
|
|
306
|
+
else:
|
|
307
|
+
working_error = True
|
|
308
|
+
except (OSError, ValueError, json.JSONDecodeError):
|
|
309
|
+
working_error = True
|
|
310
|
+
|
|
311
|
+
drift: dict[str, Any] | None = None
|
|
312
|
+
if v2_policy is not None and working is not None:
|
|
313
|
+
canonical = lambda names: sorted(
|
|
314
|
+
{name for raw in (names or []) if (name := resolve_phase_name(raw))}
|
|
315
|
+
)
|
|
316
|
+
v2_phases = canonical(v2_policy.get("plannedPhases"))
|
|
317
|
+
working_phases = canonical(working.get("plannedPhases"))
|
|
318
|
+
if v2_phases != working_phases:
|
|
319
|
+
drift = {
|
|
320
|
+
"kind": "plannedPhases",
|
|
321
|
+
"v2": v2_phases,
|
|
322
|
+
"gate_policy_json": working_phases,
|
|
323
|
+
"resolution": "v2-plan-profile wins (approved publication snapshot)",
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if v2_policy is not None:
|
|
327
|
+
return {
|
|
328
|
+
"policy": v2_policy,
|
|
329
|
+
"source": "v2-plan-profile",
|
|
330
|
+
"drift": drift,
|
|
331
|
+
"working_error": working_error,
|
|
332
|
+
}
|
|
333
|
+
if working is not None:
|
|
334
|
+
return {
|
|
335
|
+
"policy": working,
|
|
336
|
+
"source": "gate-policy-json",
|
|
337
|
+
"drift": None,
|
|
338
|
+
"working_error": False,
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
"policy": None,
|
|
342
|
+
"source": None,
|
|
343
|
+
"drift": None,
|
|
344
|
+
"working_error": working_error,
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
173
348
|
def contract_layout_kind(contract: dict[str, Any]) -> str:
|
|
174
349
|
"""``split-v1`` when the contract declares a separate runtime root."""
|
|
175
350
|
ownership = contract.get("stateOwnership")
|
|
@@ -33,14 +33,16 @@ import harness_paths as hpaths # noqa: E402
|
|
|
33
33
|
|
|
34
34
|
SCHEMA_VERSION = 1
|
|
35
35
|
CI_METRICS_SCHEMA_VERSION = 1
|
|
36
|
-
|
|
36
|
+
# 唯一权威清单在 harness_paths,harness_context 引的是同一个对象。此前这里另有
|
|
37
|
+
# 一份拷贝,少了 merge,worktree 变更走到 merge 就在 target_required_dag 里硬 raise。
|
|
38
|
+
PHASE_ORDER = hpaths.WORKFLOW_PHASES
|
|
37
39
|
VALIDATION_PHASES = {
|
|
38
|
-
"compile": "
|
|
39
|
-
"unitTest": "
|
|
40
|
-
"unitTestFull": "
|
|
41
|
-
"apiTest": "
|
|
42
|
-
"browserTest": "
|
|
43
|
-
"dbCompatibility": "
|
|
40
|
+
"compile": "execute",
|
|
41
|
+
"unitTest": "execute",
|
|
42
|
+
"unitTestFull": "execute",
|
|
43
|
+
"apiTest": "execute",
|
|
44
|
+
"browserTest": "execute",
|
|
45
|
+
"dbCompatibility": "execute",
|
|
44
46
|
"package": "package",
|
|
45
47
|
}
|
|
46
48
|
FINAL_SEQUENCE_RECEIPTS_REL = (
|
|
@@ -74,10 +76,11 @@ def _write_json(path: Path, value: Any) -> None:
|
|
|
74
76
|
|
|
75
77
|
|
|
76
78
|
def _final_sequence_contract(change_dir: Path) -> dict[str, Any] | None:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
# 权威切换(2026-08):v2 发布快照优先,classify 工作副本回退。
|
|
80
|
+
loaded = hpaths.load_change_gate_policy(change_dir)
|
|
81
|
+
policy = loaded.get("policy")
|
|
82
|
+
if policy is None:
|
|
79
83
|
return None
|
|
80
|
-
policy = _read_json(policy_path)
|
|
81
84
|
declared = policy.get("finalSequence")
|
|
82
85
|
if isinstance(declared, dict) and isinstance(declared.get("nodeIds"), list):
|
|
83
86
|
return declared
|
|
@@ -700,8 +703,18 @@ def target_required_dag(policy: dict[str, Any], target_phase: str) -> dict[str,
|
|
|
700
703
|
stage nodes for the target itself are deliberately excluded because their
|
|
701
704
|
``phase.end`` is the result of close, not evidence required before close.
|
|
702
705
|
"""
|
|
703
|
-
|
|
704
|
-
|
|
706
|
+
# 未知阶段名先过读时映射:阶段改名后,历史 change 的 gate-policy.json 与
|
|
707
|
+
# plannedPhases 里仍然是旧名,靠 LEGACY_PHASE_ALIASES 继续可读,不必迁移已落盘
|
|
708
|
+
# 的 change(本仓库没有 change 级 schema 迁移机制,硬 raise 等于让在途变更报废)。
|
|
709
|
+
resolved_target = hpaths.resolve_phase_name(target_phase)
|
|
710
|
+
if resolved_target is None:
|
|
711
|
+
raise ValueError(
|
|
712
|
+
f"unsupported reconcile target phase: {target_phase}; "
|
|
713
|
+
f"已知阶段 {', '.join(PHASE_ORDER)}。"
|
|
714
|
+
"若这是改名前的旧阶段,在 harness_paths.LEGACY_PHASE_ALIASES 登记映射;"
|
|
715
|
+
"若这份 gate-policy.json 本身过期,重跑 harness_gate.py classify 重建"
|
|
716
|
+
)
|
|
717
|
+
target_phase = resolved_target
|
|
705
718
|
dag = policy.get("requiredGateDag")
|
|
706
719
|
if not isinstance(dag, dict) or not isinstance(dag.get("nodes"), list):
|
|
707
720
|
raise ValueError("gate policy missing requiredGateDag; run harness_gate.py classify")
|
|
@@ -718,9 +731,15 @@ def target_required_dag(policy: dict[str, Any], target_phase: str) -> dict[str,
|
|
|
718
731
|
if node_kind == "validation"
|
|
719
732
|
else node_name
|
|
720
733
|
)
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
734
|
+
raw_phase = str(node.get("phase") or inferred_phase or "")
|
|
735
|
+
node_phase = hpaths.resolve_phase_name(raw_phase)
|
|
736
|
+
if node_phase is None:
|
|
737
|
+
raise ValueError(
|
|
738
|
+
f"requiredGateDag node has unsupported phase: {node_id} (phase={raw_phase!r});"
|
|
739
|
+
"旧阶段名在 harness_paths.LEGACY_PHASE_ALIASES 登记映射,"
|
|
740
|
+
"或重跑 harness_gate.py classify 重建这份 gate-policy.json"
|
|
741
|
+
)
|
|
742
|
+
node["phase"] = node_phase
|
|
724
743
|
node_rank = PHASE_ORDER.index(node_phase)
|
|
725
744
|
is_validation = node.get("kind") == "validation" or node_id.startswith(
|
|
726
745
|
"validation:"
|
|
@@ -768,6 +787,13 @@ def select_phase_capsule(
|
|
|
768
787
|
"message": f"invalid phase selector: {phase}",
|
|
769
788
|
"remediation": "pass the phase name emitted by harness_gate.py begin",
|
|
770
789
|
}
|
|
790
|
+
# 在途 change 的 capsule 可能按旧名(run/test)写入:归一后按 canonical
|
|
791
|
+
# 优先、旧名兜底查找。新写一律 canonical。
|
|
792
|
+
phase = hpaths.resolve_phase_name(phase) or phase
|
|
793
|
+
phase_names = [phase] + [
|
|
794
|
+
legacy for legacy, canonical in hpaths.LEGACY_PHASE_ALIASES.items()
|
|
795
|
+
if canonical == phase
|
|
796
|
+
]
|
|
771
797
|
state_dir = hpaths.resolve_state_dir_for_contract(change_dir, project_root)
|
|
772
798
|
capsule_dir = state_dir / "runtime" / "phase-context"
|
|
773
799
|
candidates = (
|
|
@@ -775,7 +801,7 @@ def select_phase_capsule(
|
|
|
775
801
|
path
|
|
776
802
|
for path in capsule_dir.iterdir()
|
|
777
803
|
if path.is_file()
|
|
778
|
-
and path.name.startswith(f"{
|
|
804
|
+
and any(path.name.startswith(f"{name}-") for name in phase_names)
|
|
779
805
|
and path.suffix == ".json"
|
|
780
806
|
]
|
|
781
807
|
if capsule_dir.is_dir()
|
|
@@ -795,14 +821,20 @@ def select_phase_capsule(
|
|
|
795
821
|
"message": f"{len(candidates)} capsule(s) exist for phase {phase}",
|
|
796
822
|
"remediation": f"rerun reconcile with --phase {phase} --run-id <run-id>",
|
|
797
823
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
824
|
+
path: Path | None = None
|
|
825
|
+
for name in phase_names:
|
|
826
|
+
key = hashlib.sha256(f"{name}\0{run_id}".encode("utf-8")).hexdigest()[:20]
|
|
827
|
+
candidate = capsule_dir / f"{name}-{key}.json"
|
|
828
|
+
if candidate.is_file():
|
|
829
|
+
path = candidate
|
|
830
|
+
break
|
|
831
|
+
if path is None:
|
|
832
|
+
key = hashlib.sha256(f"{phase}\0{run_id}".encode("utf-8")).hexdigest()[:20]
|
|
801
833
|
return {
|
|
802
834
|
"ok": False,
|
|
803
835
|
"code": "PHASE_CAPSULE_NOT_FOUND",
|
|
804
836
|
"message": f"no capsule for phase {phase} and run-id {run_id}",
|
|
805
|
-
"expectedPath": str(
|
|
837
|
+
"expectedPath": str(capsule_dir / f"{phase}-{key}.json"),
|
|
806
838
|
"remediation": f"begin/resume the phase or verify --phase {phase} --run-id {run_id}",
|
|
807
839
|
}
|
|
808
840
|
try:
|
|
@@ -815,7 +847,10 @@ def select_phase_capsule(
|
|
|
815
847
|
"path": str(path),
|
|
816
848
|
"remediation": f"repair or resume the capsule for --phase {phase} --run-id {run_id}",
|
|
817
849
|
}
|
|
818
|
-
if
|
|
850
|
+
if (
|
|
851
|
+
hpaths.resolve_phase_name(capsule.get("phase")) != phase
|
|
852
|
+
or capsule.get("runId") != run_id
|
|
853
|
+
):
|
|
819
854
|
return {
|
|
820
855
|
"ok": False,
|
|
821
856
|
"code": "PHASE_CAPSULE_IDENTITY_MISMATCH",
|
|
@@ -1350,7 +1385,14 @@ def reconcile(
|
|
|
1350
1385
|
) -> dict[str, Any]:
|
|
1351
1386
|
change_dir = change_dir.resolve()
|
|
1352
1387
|
project_root = project_root.resolve()
|
|
1353
|
-
|
|
1388
|
+
# 权威切换(2026-08):v2 发布快照优先,classify 工作副本回退。
|
|
1389
|
+
loaded = hpaths.load_change_gate_policy(change_dir)
|
|
1390
|
+
policy = loaded.get("policy")
|
|
1391
|
+
if policy is None:
|
|
1392
|
+
raise ValueError(
|
|
1393
|
+
"gate policy missing; run harness_gate.py classify "
|
|
1394
|
+
"or publish via plan finalize"
|
|
1395
|
+
)
|
|
1354
1396
|
dag = (
|
|
1355
1397
|
target_required_dag(policy, target_phase)
|
|
1356
1398
|
if target_phase is not None
|
|
@@ -1615,7 +1657,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1615
1657
|
reconcile_parser.add_argument("--json", action="store_true")
|
|
1616
1658
|
reconcile_parser.add_argument("--output")
|
|
1617
1659
|
reconcile_parser.add_argument("--close", action="store_true")
|
|
1618
|
-
reconcile_parser.add_argument("--phase", default="
|
|
1660
|
+
reconcile_parser.add_argument("--phase", default="execute")
|
|
1619
1661
|
reconcile_parser.add_argument("--run-id")
|
|
1620
1662
|
reconcile_parser.set_defaults(func=_cmd_reconcile)
|
|
1621
1663
|
|