@hunter-harness/workflow-harness 0.2.43 → 0.2.45
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/build-profile-v3.schema.json +100 -0
- package/harness/bundles/general/claude-code/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/general/claude-code/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/general/claude-code/contracts/run-session.schema.json +142 -0
- package/harness/bundles/general/claude-code/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/general/claude-code/contracts/service-session.schema.json +147 -0
- package/harness/bundles/general/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-submit/checklist.md +12 -0
- package/harness/bundles/general/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/claude-code/scripts/harness_archive.py +324 -28
- package/harness/bundles/general/claude-code/scripts/harness_change.py +137 -2
- package/harness/bundles/general/claude-code/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/general/claude-code/scripts/harness_environment.py +55 -25
- package/harness/bundles/general/claude-code/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/general/claude-code/scripts/harness_integration.py +289 -52
- package/harness/bundles/general/claude-code/scripts/harness_ledger.py +5 -0
- package/harness/bundles/general/claude-code/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/general/claude-code/scripts/harness_process.py +1065 -0
- package/harness/bundles/general/claude-code/scripts/harness_profile.py +244 -4
- package/harness/bundles/general/claude-code/scripts/harness_runtime.py +646 -132
- package/harness/bundles/general/claude-code/scripts/harness_service.py +1469 -194
- package/harness/bundles/general/claude-code/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/general/claude-code/scripts/harness_verification.py +79 -0
- package/harness/bundles/general/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/general/codebuddy/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/general/codebuddy/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/general/codebuddy/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/general/codebuddy/contracts/run-session.schema.json +142 -0
- package/harness/bundles/general/codebuddy/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/general/codebuddy/contracts/service-session.schema.json +147 -0
- package/harness/bundles/general/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-submit/checklist.md +12 -0
- package/harness/bundles/general/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codebuddy/scripts/harness_archive.py +324 -28
- package/harness/bundles/general/codebuddy/scripts/harness_change.py +137 -2
- package/harness/bundles/general/codebuddy/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/general/codebuddy/scripts/harness_environment.py +55 -25
- package/harness/bundles/general/codebuddy/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/general/codebuddy/scripts/harness_integration.py +289 -52
- package/harness/bundles/general/codebuddy/scripts/harness_ledger.py +5 -0
- package/harness/bundles/general/codebuddy/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/general/codebuddy/scripts/harness_process.py +1065 -0
- package/harness/bundles/general/codebuddy/scripts/harness_profile.py +244 -4
- package/harness/bundles/general/codebuddy/scripts/harness_runtime.py +646 -132
- package/harness/bundles/general/codebuddy/scripts/harness_service.py +1469 -194
- package/harness/bundles/general/codebuddy/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/general/codebuddy/scripts/harness_verification.py +79 -0
- package/harness/bundles/general/codex/.harness-build.json +1 -1
- package/harness/bundles/general/codex/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/general/codex/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/general/codex/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/general/codex/contracts/run-session.schema.json +142 -0
- package/harness/bundles/general/codex/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/general/codex/contracts/service-session.schema.json +147 -0
- package/harness/bundles/general/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-submit/checklist.md +12 -0
- package/harness/bundles/general/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/codex/scripts/harness_archive.py +324 -28
- package/harness/bundles/general/codex/scripts/harness_change.py +137 -2
- package/harness/bundles/general/codex/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/general/codex/scripts/harness_environment.py +55 -25
- package/harness/bundles/general/codex/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/general/codex/scripts/harness_integration.py +289 -52
- package/harness/bundles/general/codex/scripts/harness_ledger.py +5 -0
- package/harness/bundles/general/codex/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/general/codex/scripts/harness_process.py +1065 -0
- package/harness/bundles/general/codex/scripts/harness_profile.py +244 -4
- package/harness/bundles/general/codex/scripts/harness_runtime.py +646 -132
- package/harness/bundles/general/codex/scripts/harness_service.py +1469 -194
- package/harness/bundles/general/codex/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/general/codex/scripts/harness_verification.py +79 -0
- package/harness/bundles/general/cursor/.harness-build.json +1 -1
- package/harness/bundles/general/cursor/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/general/cursor/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/general/cursor/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/general/cursor/contracts/run-session.schema.json +142 -0
- package/harness/bundles/general/cursor/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/general/cursor/contracts/service-session.schema.json +147 -0
- package/harness/bundles/general/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-submit/checklist.md +12 -0
- package/harness/bundles/general/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/general/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/general/cursor/scripts/harness_archive.py +324 -28
- package/harness/bundles/general/cursor/scripts/harness_change.py +137 -2
- package/harness/bundles/general/cursor/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/general/cursor/scripts/harness_environment.py +55 -25
- package/harness/bundles/general/cursor/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/general/cursor/scripts/harness_integration.py +289 -52
- package/harness/bundles/general/cursor/scripts/harness_ledger.py +5 -0
- package/harness/bundles/general/cursor/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/general/cursor/scripts/harness_process.py +1065 -0
- package/harness/bundles/general/cursor/scripts/harness_profile.py +244 -4
- package/harness/bundles/general/cursor/scripts/harness_runtime.py +646 -132
- package/harness/bundles/general/cursor/scripts/harness_service.py +1469 -194
- package/harness/bundles/general/cursor/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/general/cursor/scripts/harness_verification.py +79 -0
- package/harness/bundles/java/claude-code/.harness-build.json +1 -1
- package/harness/bundles/java/claude-code/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/java/claude-code/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/java/claude-code/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/java/claude-code/contracts/run-session.schema.json +142 -0
- package/harness/bundles/java/claude-code/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/java/claude-code/contracts/service-session.schema.json +147 -0
- package/harness/bundles/java/claude-code/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-submit/checklist.md +12 -0
- package/harness/bundles/java/claude-code/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/claude-code/scripts/harness_archive.py +324 -28
- package/harness/bundles/java/claude-code/scripts/harness_change.py +137 -2
- package/harness/bundles/java/claude-code/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/java/claude-code/scripts/harness_environment.py +55 -25
- package/harness/bundles/java/claude-code/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/java/claude-code/scripts/harness_integration.py +289 -52
- package/harness/bundles/java/claude-code/scripts/harness_ledger.py +5 -0
- package/harness/bundles/java/claude-code/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/java/claude-code/scripts/harness_process.py +1065 -0
- package/harness/bundles/java/claude-code/scripts/harness_profile.py +244 -4
- package/harness/bundles/java/claude-code/scripts/harness_runtime.py +646 -132
- package/harness/bundles/java/claude-code/scripts/harness_service.py +1469 -194
- package/harness/bundles/java/claude-code/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/java/claude-code/scripts/harness_verification.py +79 -0
- package/harness/bundles/java/codebuddy/.harness-build.json +1 -1
- package/harness/bundles/java/codebuddy/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/java/codebuddy/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/java/codebuddy/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/java/codebuddy/contracts/run-session.schema.json +142 -0
- package/harness/bundles/java/codebuddy/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/java/codebuddy/contracts/service-session.schema.json +147 -0
- package/harness/bundles/java/codebuddy/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-submit/checklist.md +12 -0
- package/harness/bundles/java/codebuddy/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codebuddy/scripts/harness_archive.py +324 -28
- package/harness/bundles/java/codebuddy/scripts/harness_change.py +137 -2
- package/harness/bundles/java/codebuddy/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/java/codebuddy/scripts/harness_environment.py +55 -25
- package/harness/bundles/java/codebuddy/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/java/codebuddy/scripts/harness_integration.py +289 -52
- package/harness/bundles/java/codebuddy/scripts/harness_ledger.py +5 -0
- package/harness/bundles/java/codebuddy/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/java/codebuddy/scripts/harness_process.py +1065 -0
- package/harness/bundles/java/codebuddy/scripts/harness_profile.py +244 -4
- package/harness/bundles/java/codebuddy/scripts/harness_runtime.py +646 -132
- package/harness/bundles/java/codebuddy/scripts/harness_service.py +1469 -194
- package/harness/bundles/java/codebuddy/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/java/codebuddy/scripts/harness_verification.py +79 -0
- package/harness/bundles/java/codex/.harness-build.json +1 -1
- package/harness/bundles/java/codex/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/java/codex/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/java/codex/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/java/codex/contracts/run-session.schema.json +142 -0
- package/harness/bundles/java/codex/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/java/codex/contracts/service-session.schema.json +147 -0
- package/harness/bundles/java/codex/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-submit/checklist.md +12 -0
- package/harness/bundles/java/codex/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/codex/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/codex/scripts/harness_archive.py +324 -28
- package/harness/bundles/java/codex/scripts/harness_change.py +137 -2
- package/harness/bundles/java/codex/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/java/codex/scripts/harness_environment.py +55 -25
- package/harness/bundles/java/codex/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/java/codex/scripts/harness_integration.py +289 -52
- package/harness/bundles/java/codex/scripts/harness_ledger.py +5 -0
- package/harness/bundles/java/codex/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/java/codex/scripts/harness_process.py +1065 -0
- package/harness/bundles/java/codex/scripts/harness_profile.py +244 -4
- package/harness/bundles/java/codex/scripts/harness_runtime.py +646 -132
- package/harness/bundles/java/codex/scripts/harness_service.py +1469 -194
- package/harness/bundles/java/codex/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/java/codex/scripts/harness_verification.py +79 -0
- package/harness/bundles/java/cursor/.harness-build.json +1 -1
- package/harness/bundles/java/cursor/contracts/build-profile-v3.schema.json +100 -0
- package/harness/bundles/java/cursor/contracts/fixtures/managed-execution.json +341 -0
- package/harness/bundles/java/cursor/contracts/process-identity.schema.json +132 -0
- package/harness/bundles/java/cursor/contracts/run-session.schema.json +142 -0
- package/harness/bundles/java/cursor/contracts/service-retirement-receipt.schema.json +85 -0
- package/harness/bundles/java/cursor/contracts/service-session.schema.json +147 -0
- package/harness/bundles/java/cursor/harness-apidoc/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-archive/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-codebase-map/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-ingest/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-knowledge-query/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-package/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-plan/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-review/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-run/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-submit/checklist.md +12 -0
- package/harness/bundles/java/cursor/harness-sync/SKILL.md +1 -1
- package/harness/bundles/java/cursor/harness-test/SKILL.md +1 -1
- package/harness/bundles/java/cursor/scripts/harness_archive.py +324 -28
- package/harness/bundles/java/cursor/scripts/harness_change.py +137 -2
- package/harness/bundles/java/cursor/scripts/harness_efficiency.py +153 -0
- package/harness/bundles/java/cursor/scripts/harness_environment.py +55 -25
- package/harness/bundles/java/cursor/scripts/harness_execution_contracts.py +596 -0
- package/harness/bundles/java/cursor/scripts/harness_integration.py +289 -52
- package/harness/bundles/java/cursor/scripts/harness_ledger.py +5 -0
- package/harness/bundles/java/cursor/scripts/harness_orchestration.py +344 -0
- package/harness/bundles/java/cursor/scripts/harness_process.py +1065 -0
- package/harness/bundles/java/cursor/scripts/harness_profile.py +244 -4
- package/harness/bundles/java/cursor/scripts/harness_runtime.py +646 -132
- package/harness/bundles/java/cursor/scripts/harness_service.py +1469 -194
- package/harness/bundles/java/cursor/scripts/harness_test_runner.py +107 -117
- package/harness/bundles/java/cursor/scripts/harness_verification.py +79 -0
- package/harness/manifests/general/claude-code.json +58 -26
- package/harness/manifests/general/codebuddy.json +58 -26
- package/harness/manifests/general/codex.json +58 -26
- package/harness/manifests/general/cursor.json +58 -26
- package/harness/manifests/java/claude-code.json +60 -28
- package/harness/manifests/java/codebuddy.json +60 -28
- package/harness/manifests/java/codex.json +60 -28
- package/harness/manifests/java/cursor.json +60 -28
- package/hunter-workflow-family.json +4 -4
- package/package.json +1 -1
|
@@ -36,6 +36,7 @@ if str(SCRIPTS_DIR) not in sys.path:
|
|
|
36
36
|
import harness_change # noqa: E402
|
|
37
37
|
import harness_ledger as hl # noqa: E402
|
|
38
38
|
import harness_paths # noqa: E402
|
|
39
|
+
import harness_process # noqa: E402
|
|
39
40
|
import harness_service # noqa: E402
|
|
40
41
|
|
|
41
42
|
if hasattr(sys.stdout, "reconfigure"):
|
|
@@ -415,6 +416,10 @@ class LedgerSyncError(IntegrationError):
|
|
|
415
416
|
code = "LEDGER_SYNC_PENDING"
|
|
416
417
|
|
|
417
418
|
|
|
419
|
+
class EvidenceRebindRefusedError(IntegrationError):
|
|
420
|
+
code = "EVIDENCE_REBIND_REFUSED"
|
|
421
|
+
|
|
422
|
+
|
|
418
423
|
class CleanupRefusedError(IntegrationError):
|
|
419
424
|
code = "CLEANUP_REFUSED"
|
|
420
425
|
|
|
@@ -599,22 +604,7 @@ def load_journal(project_root: Path, transaction_id: str) -> dict[str, Any]:
|
|
|
599
604
|
|
|
600
605
|
|
|
601
606
|
def _pid_alive(pid: int) -> bool:
|
|
602
|
-
|
|
603
|
-
return False
|
|
604
|
-
# On Windows, os.kill(pid, 0) can transiently report EINVAL for the
|
|
605
|
-
# current low-priority/background process. The current process is
|
|
606
|
-
# unambiguously alive, so avoid reclaiming its lock on that platform race.
|
|
607
|
-
if pid == os.getpid():
|
|
608
|
-
return True
|
|
609
|
-
try:
|
|
610
|
-
os.kill(pid, 0)
|
|
611
|
-
except ProcessLookupError:
|
|
612
|
-
return False
|
|
613
|
-
except PermissionError:
|
|
614
|
-
return True
|
|
615
|
-
except OSError:
|
|
616
|
-
return False
|
|
617
|
-
return True
|
|
607
|
+
return harness_process.is_pid_alive(pid)
|
|
618
608
|
|
|
619
609
|
|
|
620
610
|
def _acquire_journal_lock(lock_path: Path, journal_path: Path) -> int:
|
|
@@ -727,6 +717,7 @@ class IntegrationTransaction:
|
|
|
727
717
|
"schemaVersion": 1,
|
|
728
718
|
"transactionId": self.transaction_id,
|
|
729
719
|
"changeName": self.change_id,
|
|
720
|
+
"evidenceChangeName": self.change_id,
|
|
730
721
|
"runId": self.run_id,
|
|
731
722
|
"repositoryId": harness_paths.repository_identity(self.project_root),
|
|
732
723
|
"primaryRoot": str(self.project_root),
|
|
@@ -750,12 +741,66 @@ class IntegrationTransaction:
|
|
|
750
741
|
"createdAt": now_iso(),
|
|
751
742
|
}
|
|
752
743
|
|
|
753
|
-
def
|
|
754
|
-
|
|
755
|
-
|
|
744
|
+
def _change_roots(self, change_id: str) -> tuple[Path, Path]:
|
|
745
|
+
if re.fullmatch(r"[a-z0-9]+(?:-[a-z0-9]+)*", change_id) is None:
|
|
746
|
+
raise EvidenceRebindRefusedError(
|
|
747
|
+
"evidence change must be a non-empty kebab-case path segment"
|
|
748
|
+
)
|
|
749
|
+
contract_base = (self.project_root / ".harness" / "changes").resolve()
|
|
750
|
+
state_base = (
|
|
751
|
+
self.project_root / ".harness" / "state" / "changes"
|
|
756
752
|
).resolve()
|
|
753
|
+
raw_change_dir = contract_base / change_id
|
|
754
|
+
raw_state_dir = state_base / change_id
|
|
755
|
+
change_dir = raw_change_dir.resolve()
|
|
756
|
+
state_dir = raw_state_dir.resolve()
|
|
757
|
+
direct_contract_path = os.path.normcase(str(raw_change_dir.absolute()))
|
|
758
|
+
direct_state_path = os.path.normcase(str(raw_state_dir.absolute()))
|
|
759
|
+
if (
|
|
760
|
+
change_dir.parent != contract_base
|
|
761
|
+
or state_dir.parent != state_base
|
|
762
|
+
or os.path.normcase(str(change_dir)) != direct_contract_path
|
|
763
|
+
or os.path.normcase(str(state_dir)) != direct_state_path
|
|
764
|
+
):
|
|
765
|
+
raise EvidenceRebindRefusedError(
|
|
766
|
+
"evidence change must remain inside the direct Change roots"
|
|
767
|
+
)
|
|
768
|
+
return change_dir, state_dir
|
|
769
|
+
|
|
770
|
+
def _bound_evidence_change(self, journal: dict[str, Any]) -> str:
|
|
771
|
+
if journal.get("changeName") != self.change_id:
|
|
772
|
+
raise JournalConflictError(
|
|
773
|
+
"transaction Change does not match the journal"
|
|
774
|
+
)
|
|
775
|
+
if "evidenceChangeName" not in journal:
|
|
776
|
+
return self.change_id
|
|
777
|
+
raw_change_id = journal.get("evidenceChangeName")
|
|
778
|
+
if not isinstance(raw_change_id, str) or not raw_change_id:
|
|
779
|
+
raise EvidenceRebindRefusedError(
|
|
780
|
+
"journal evidence Change binding is invalid"
|
|
781
|
+
)
|
|
782
|
+
change_id = raw_change_id
|
|
783
|
+
self._change_roots(change_id)
|
|
784
|
+
if change_id == self.change_id:
|
|
785
|
+
return change_id
|
|
786
|
+
rebind = journal.get("evidenceRebind") or {}
|
|
787
|
+
if (
|
|
788
|
+
rebind.get("status") != "DONE"
|
|
789
|
+
or rebind.get("from") != self.change_id
|
|
790
|
+
or rebind.get("to") != change_id
|
|
791
|
+
or rebind.get("reason")
|
|
792
|
+
!= "REMOTE_CONFIRMED_DEFAULT_LEDGER_MISSING"
|
|
793
|
+
or rebind.get("remoteHead") != journal.get("mergeCommit")
|
|
794
|
+
):
|
|
795
|
+
raise EvidenceRebindRefusedError(
|
|
796
|
+
"journal evidence Change override has no valid recovery receipt"
|
|
797
|
+
)
|
|
798
|
+
return change_id
|
|
799
|
+
|
|
800
|
+
def _evidence_identity(self, change_id: str | None = None) -> dict[str, Any]:
|
|
801
|
+
change_dir, state_dir = self._change_roots(change_id or self.change_id)
|
|
757
802
|
state_candidates = [
|
|
758
|
-
|
|
803
|
+
state_dir,
|
|
759
804
|
change_dir,
|
|
760
805
|
]
|
|
761
806
|
events_file = next(
|
|
@@ -792,25 +837,23 @@ class IntegrationTransaction:
|
|
|
792
837
|
break
|
|
793
838
|
if artifact_hash:
|
|
794
839
|
break
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
if not ledger_path.is_file():
|
|
798
|
-
continue
|
|
840
|
+
ledger_path = self._ledger_path(change_id or self.change_id)
|
|
841
|
+
if ledger_path is not None:
|
|
799
842
|
try:
|
|
800
843
|
ledger = json.loads(ledger_path.read_text(encoding="utf-8-sig"))
|
|
801
844
|
except (OSError, json.JSONDecodeError):
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
845
|
+
ledger = {}
|
|
846
|
+
if isinstance(ledger, dict):
|
|
847
|
+
for key in (
|
|
848
|
+
"schemaVersion",
|
|
849
|
+
"repositoryId",
|
|
850
|
+
"changeName",
|
|
851
|
+
"baseCommit",
|
|
852
|
+
"currentHead",
|
|
853
|
+
"diffHash",
|
|
854
|
+
"ownershipHash",
|
|
855
|
+
):
|
|
856
|
+
ledger_identity[key] = ledger.get(key)
|
|
814
857
|
return {
|
|
815
858
|
"eventHighWater": {
|
|
816
859
|
"count": event_count,
|
|
@@ -821,16 +864,26 @@ class IntegrationTransaction:
|
|
|
821
864
|
"ledgerIdentity": ledger_identity,
|
|
822
865
|
}
|
|
823
866
|
|
|
824
|
-
def _ledger_path(self) -> Path | None:
|
|
867
|
+
def _ledger_path(self, change_id: str) -> Path | None:
|
|
868
|
+
change_dir, state_dir = self._change_roots(change_id)
|
|
825
869
|
candidates = (
|
|
826
|
-
|
|
827
|
-
/ "evidence" / "verification-ledger.json",
|
|
828
|
-
|
|
829
|
-
/ "evidence" / "verification-ledger.json",
|
|
830
|
-
self.project_root / ".harness" / "state" / "changes" / self.change_id
|
|
831
|
-
/ "verification-ledger.json",
|
|
870
|
+
state_dir / "evidence" / "verification-ledger.json",
|
|
871
|
+
change_dir / "evidence" / "verification-ledger.json",
|
|
872
|
+
state_dir / "verification-ledger.json",
|
|
832
873
|
)
|
|
833
|
-
|
|
874
|
+
for path in candidates:
|
|
875
|
+
if not path.is_file():
|
|
876
|
+
continue
|
|
877
|
+
resolved = path.resolve()
|
|
878
|
+
if path.is_symlink() or not any(
|
|
879
|
+
resolved.is_relative_to(root)
|
|
880
|
+
for root in (state_dir, change_dir)
|
|
881
|
+
):
|
|
882
|
+
raise EvidenceRebindRefusedError(
|
|
883
|
+
"verification ledger must remain inside its direct Change root"
|
|
884
|
+
)
|
|
885
|
+
return path
|
|
886
|
+
return None
|
|
834
887
|
|
|
835
888
|
def _finalize_remote_push(
|
|
836
889
|
self,
|
|
@@ -839,7 +892,9 @@ class IntegrationTransaction:
|
|
|
839
892
|
merge_commit: str,
|
|
840
893
|
remote_head: str,
|
|
841
894
|
) -> None:
|
|
895
|
+
evidence_change = self._bound_evidence_change(journal)
|
|
842
896
|
journal.update({
|
|
897
|
+
"evidenceChangeName": evidence_change,
|
|
843
898
|
"pushedHead": merge_commit,
|
|
844
899
|
"mergeFinalHash": merge_commit,
|
|
845
900
|
"ciExpectedHead": merge_commit,
|
|
@@ -854,14 +909,14 @@ class IntegrationTransaction:
|
|
|
854
909
|
"ledgerSync": {"status": "PENDING", "updatedAt": now_iso()},
|
|
855
910
|
})
|
|
856
911
|
self._save(journal)
|
|
857
|
-
ledger_path = self._ledger_path()
|
|
912
|
+
ledger_path = self._ledger_path(evidence_change)
|
|
858
913
|
if ledger_path is None:
|
|
859
914
|
result = {"ok": False, "code": "LEDGER_MISSING"}
|
|
860
915
|
else:
|
|
861
916
|
result = hl.record_integration_hashes(
|
|
862
917
|
ledger_path,
|
|
863
918
|
change_dir=(
|
|
864
|
-
self.project_root / ".harness" / "changes" /
|
|
919
|
+
self.project_root / ".harness" / "changes" / evidence_change
|
|
865
920
|
),
|
|
866
921
|
repository_id=str(journal["repositoryId"]),
|
|
867
922
|
merge_final_hash=merge_commit,
|
|
@@ -881,6 +936,7 @@ class IntegrationTransaction:
|
|
|
881
936
|
)
|
|
882
937
|
|
|
883
938
|
def _assert_cleanup_consistency(self, journal: dict[str, Any]) -> None:
|
|
939
|
+
evidence_change = self._bound_evidence_change(journal)
|
|
884
940
|
push_step = self._step(journal, "push")
|
|
885
941
|
merge_commit = str(journal.get("mergeCommit") or "")
|
|
886
942
|
remote_line = self.runner.text(
|
|
@@ -909,7 +965,7 @@ class IntegrationTransaction:
|
|
|
909
965
|
raise CleanupRefusedError(
|
|
910
966
|
"remote/journal final hashes are missing or inconsistent"
|
|
911
967
|
)
|
|
912
|
-
ledger_path = self._ledger_path()
|
|
968
|
+
ledger_path = self._ledger_path(evidence_change)
|
|
913
969
|
if ledger_path is None:
|
|
914
970
|
raise CleanupRefusedError("verification ledger missing before cleanup")
|
|
915
971
|
try:
|
|
@@ -1344,6 +1400,7 @@ class IntegrationTransaction:
|
|
|
1344
1400
|
raise VerifyPlanMissingError(
|
|
1345
1401
|
"integration verification requires at least one executable command"
|
|
1346
1402
|
)
|
|
1403
|
+
evidence_change = self._bound_evidence_change(journal)
|
|
1347
1404
|
intg_root = Path(journal["integrationRoot"]).resolve()
|
|
1348
1405
|
results: list[dict[str, Any]] = []
|
|
1349
1406
|
merge_tree = self.runner.text(intg_root, "rev-parse", "--verify", "HEAD^{tree}")
|
|
@@ -1355,7 +1412,7 @@ class IntegrationTransaction:
|
|
|
1355
1412
|
)
|
|
1356
1413
|
reused = (
|
|
1357
1414
|
_reusable_merge_evidence(
|
|
1358
|
-
self._ledger_path(),
|
|
1415
|
+
self._ledger_path(evidence_change),
|
|
1359
1416
|
verification=policy.get("verification"),
|
|
1360
1417
|
command=command,
|
|
1361
1418
|
product_tree_hash=merge_tree_hash,
|
|
@@ -1509,6 +1566,7 @@ class IntegrationTransaction:
|
|
|
1509
1566
|
|
|
1510
1567
|
def push(self) -> dict[str, Any]:
|
|
1511
1568
|
def action(journal: dict[str, Any]) -> None:
|
|
1569
|
+
self._bound_evidence_change(journal)
|
|
1512
1570
|
verify_step = self._step(journal, "verify")
|
|
1513
1571
|
verify_results = journal.get("verifyResults")
|
|
1514
1572
|
if (
|
|
@@ -2002,10 +2060,174 @@ class IntegrationTransaction:
|
|
|
2002
2060
|
|
|
2003
2061
|
# ------------------------------------------------------------ recover
|
|
2004
2062
|
|
|
2063
|
+
def _rebind_evidence_change_for_recovery(
|
|
2064
|
+
self,
|
|
2065
|
+
journal: dict[str, Any],
|
|
2066
|
+
evidence_change_id: str,
|
|
2067
|
+
) -> dict[str, Any]:
|
|
2068
|
+
self._change_roots(evidence_change_id)
|
|
2069
|
+
current_change = self._bound_evidence_change(journal)
|
|
2070
|
+
if current_change == evidence_change_id:
|
|
2071
|
+
return journal
|
|
2072
|
+
if current_change != self.change_id:
|
|
2073
|
+
raise EvidenceRebindRefusedError(
|
|
2074
|
+
"transaction evidence is already bound to another Change"
|
|
2075
|
+
)
|
|
2076
|
+
|
|
2077
|
+
push_step = self._step(journal, "push")
|
|
2078
|
+
ledger_sync = journal.get("ledgerSync") or {}
|
|
2079
|
+
ledger_result = ledger_sync.get("result") or {}
|
|
2080
|
+
merge_commit = str(journal.get("mergeCommit") or "")
|
|
2081
|
+
push_intent = journal.get("pushIntent") or {}
|
|
2082
|
+
if (
|
|
2083
|
+
push_step.get("status") != "FAILED"
|
|
2084
|
+
or ledger_sync.get("status") != "FAILED"
|
|
2085
|
+
or ledger_result.get("code") != "LEDGER_MISSING"
|
|
2086
|
+
or push_intent.get("status") != "REMOTE_CONFIRMED"
|
|
2087
|
+
or push_intent.get("mergeCommit") != merge_commit
|
|
2088
|
+
or push_intent.get("observedRemoteHead") != merge_commit
|
|
2089
|
+
):
|
|
2090
|
+
raise EvidenceRebindRefusedError(
|
|
2091
|
+
"evidence rebind requires a remote-confirmed push blocked only by "
|
|
2092
|
+
"the default LEDGER_MISSING result"
|
|
2093
|
+
)
|
|
2094
|
+
|
|
2095
|
+
probe = self.runner.remote_probe(
|
|
2096
|
+
self.project_root,
|
|
2097
|
+
"ls-remote",
|
|
2098
|
+
"origin",
|
|
2099
|
+
self.target_branch,
|
|
2100
|
+
)
|
|
2101
|
+
if probe["exitCode"] != 0:
|
|
2102
|
+
raise RemoteProbeFailedError(
|
|
2103
|
+
"cannot confirm the remote target before evidence rebind: "
|
|
2104
|
+
f"{probe['category']}: {probe['redactedStderr']}"
|
|
2105
|
+
)
|
|
2106
|
+
if not merge_commit or probe["stdoutHash"] != merge_commit:
|
|
2107
|
+
raise EvidenceRebindRefusedError(
|
|
2108
|
+
"remote target no longer equals the transaction merge commit"
|
|
2109
|
+
)
|
|
2110
|
+
|
|
2111
|
+
ledger_path = self._ledger_path(evidence_change_id)
|
|
2112
|
+
if ledger_path is None:
|
|
2113
|
+
raise EvidenceRebindRefusedError(
|
|
2114
|
+
"requested evidence Change has no verification ledger"
|
|
2115
|
+
)
|
|
2116
|
+
try:
|
|
2117
|
+
ledger = json.loads(ledger_path.read_text(encoding="utf-8-sig"))
|
|
2118
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
2119
|
+
raise EvidenceRebindRefusedError(
|
|
2120
|
+
f"requested evidence ledger is unreadable: {exc}"
|
|
2121
|
+
) from exc
|
|
2122
|
+
missing = hl.validate_ledger_identity(ledger)
|
|
2123
|
+
if missing:
|
|
2124
|
+
raise EvidenceRebindRefusedError(
|
|
2125
|
+
"requested evidence ledger identity is invalid: "
|
|
2126
|
+
+ ", ".join(missing)
|
|
2127
|
+
)
|
|
2128
|
+
if ledger.get("changeName") != evidence_change_id:
|
|
2129
|
+
raise EvidenceRebindRefusedError(
|
|
2130
|
+
"requested evidence ledger Change identity does not match"
|
|
2131
|
+
)
|
|
2132
|
+
if ledger.get("repositoryId") != journal.get("repositoryId"):
|
|
2133
|
+
raise EvidenceRebindRefusedError(
|
|
2134
|
+
"requested evidence ledger repository identity does not match"
|
|
2135
|
+
)
|
|
2136
|
+
|
|
2137
|
+
expected_handoff = str(
|
|
2138
|
+
journal.get("remoteTargetHead") or journal.get("base") or ""
|
|
2139
|
+
)
|
|
2140
|
+
handoff_values = [
|
|
2141
|
+
ledger.get("mergeFinalHash"),
|
|
2142
|
+
ledger.get("ciExpectedHead"),
|
|
2143
|
+
ledger.get("remoteHead"),
|
|
2144
|
+
]
|
|
2145
|
+
handoff_valid = all(
|
|
2146
|
+
isinstance(value, str)
|
|
2147
|
+
and re.fullmatch(r"[0-9a-f]{40}", value) is not None
|
|
2148
|
+
for value in handoff_values
|
|
2149
|
+
)
|
|
2150
|
+
handoff_hashes = set(handoff_values) if handoff_valid else set()
|
|
2151
|
+
if (
|
|
2152
|
+
not handoff_valid
|
|
2153
|
+
or len(handoff_hashes) != 1
|
|
2154
|
+
or next(iter(handoff_hashes), None)
|
|
2155
|
+
not in {expected_handoff, merge_commit}
|
|
2156
|
+
):
|
|
2157
|
+
raise EvidenceRebindRefusedError(
|
|
2158
|
+
"requested evidence ledger has no matching prior integration handoff"
|
|
2159
|
+
)
|
|
2160
|
+
|
|
2161
|
+
current_head = str(ledger.get("currentHead") or "")
|
|
2162
|
+
feature_head = str(journal.get("featureHead") or "")
|
|
2163
|
+
current_in_feature = self.runner.run(
|
|
2164
|
+
self.project_root,
|
|
2165
|
+
"merge-base",
|
|
2166
|
+
"--is-ancestor",
|
|
2167
|
+
current_head,
|
|
2168
|
+
feature_head,
|
|
2169
|
+
check=False,
|
|
2170
|
+
).returncode == 0
|
|
2171
|
+
feature_in_merge = self.runner.run(
|
|
2172
|
+
self.project_root,
|
|
2173
|
+
"merge-base",
|
|
2174
|
+
"--is-ancestor",
|
|
2175
|
+
feature_head,
|
|
2176
|
+
merge_commit,
|
|
2177
|
+
check=False,
|
|
2178
|
+
).returncode == 0
|
|
2179
|
+
if not current_in_feature or not feature_in_merge:
|
|
2180
|
+
raise EvidenceRebindRefusedError(
|
|
2181
|
+
"requested evidence ledger head is not in the integrated feature lineage"
|
|
2182
|
+
)
|
|
2183
|
+
|
|
2184
|
+
previous_identity = journal.get("evidenceIdentity")
|
|
2185
|
+
previous_verification_identity = journal.get("verificationIdentity")
|
|
2186
|
+
target_identity = self._evidence_identity(evidence_change_id)
|
|
2187
|
+
journal["evidenceChangeName"] = evidence_change_id
|
|
2188
|
+
journal["evidenceIdentity"] = target_identity
|
|
2189
|
+
verification_basis = json.dumps(
|
|
2190
|
+
{
|
|
2191
|
+
"mergeCommit": journal.get("mergeCommit"),
|
|
2192
|
+
"results": journal.get("verifyResults"),
|
|
2193
|
+
"evidenceIdentity": target_identity,
|
|
2194
|
+
},
|
|
2195
|
+
ensure_ascii=False,
|
|
2196
|
+
sort_keys=True,
|
|
2197
|
+
)
|
|
2198
|
+
journal["verificationIdentity"] = {
|
|
2199
|
+
"sha256": (
|
|
2200
|
+
"sha256:"
|
|
2201
|
+
+ hashlib.sha256(verification_basis.encode("utf-8")).hexdigest()
|
|
2202
|
+
),
|
|
2203
|
+
"ledgerIdentity": target_identity.get("ledgerIdentity") or {},
|
|
2204
|
+
}
|
|
2205
|
+
journal["evidenceRebind"] = {
|
|
2206
|
+
"status": "DONE",
|
|
2207
|
+
"from": current_change,
|
|
2208
|
+
"to": evidence_change_id,
|
|
2209
|
+
"reason": "REMOTE_CONFIRMED_DEFAULT_LEDGER_MISSING",
|
|
2210
|
+
"remoteHead": merge_commit,
|
|
2211
|
+
"handoffHash": next(iter(handoff_hashes)),
|
|
2212
|
+
"previousEvidenceIdentity": previous_identity,
|
|
2213
|
+
"previousVerificationIdentity": previous_verification_identity,
|
|
2214
|
+
"targetLedgerIdentity": target_identity.get("ledgerIdentity") or {},
|
|
2215
|
+
"updatedAt": now_iso(),
|
|
2216
|
+
}
|
|
2217
|
+
return self._save(journal)
|
|
2218
|
+
|
|
2005
2219
|
def recover(
|
|
2006
|
-
self,
|
|
2220
|
+
self,
|
|
2221
|
+
verify_commands: Sequence[Sequence[str]] | None = None,
|
|
2222
|
+
*,
|
|
2223
|
+
evidence_change_id: str | None = None,
|
|
2007
2224
|
) -> dict[str, Any]:
|
|
2008
2225
|
journal = self._load()
|
|
2226
|
+
if evidence_change_id is not None:
|
|
2227
|
+
journal = self._rebind_evidence_change_for_recovery(
|
|
2228
|
+
journal,
|
|
2229
|
+
evidence_change_id,
|
|
2230
|
+
)
|
|
2009
2231
|
actions: dict[str, Callable[[], dict[str, Any]]] = {
|
|
2010
2232
|
"preflight": self.preflight,
|
|
2011
2233
|
"prepare": self.prepare,
|
|
@@ -2102,7 +2324,11 @@ def cmd_abandon(args: argparse.Namespace) -> int:
|
|
|
2102
2324
|
|
|
2103
2325
|
|
|
2104
2326
|
def cmd_recover(args: argparse.Namespace) -> int:
|
|
2105
|
-
return _emit(
|
|
2327
|
+
return _emit(
|
|
2328
|
+
_txn_from_args(args).recover(
|
|
2329
|
+
evidence_change_id=getattr(args, "evidence_change", None)
|
|
2330
|
+
)
|
|
2331
|
+
)
|
|
2106
2332
|
|
|
2107
2333
|
|
|
2108
2334
|
def cmd_status(args: argparse.Namespace) -> int:
|
|
@@ -2138,7 +2364,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2138
2364
|
parser = argparse.ArgumentParser(prog="harness_integration.py")
|
|
2139
2365
|
sub = parser.add_subparsers(dest="command_name", required=True)
|
|
2140
2366
|
|
|
2141
|
-
def add(
|
|
2367
|
+
def add(
|
|
2368
|
+
name: str,
|
|
2369
|
+
func: Callable[[argparse.Namespace], int],
|
|
2370
|
+
) -> argparse.ArgumentParser:
|
|
2142
2371
|
p = sub.add_parser(name)
|
|
2143
2372
|
p.add_argument("--change", required=True)
|
|
2144
2373
|
p.add_argument("--run-id", required=True)
|
|
@@ -2146,6 +2375,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2146
2375
|
p.add_argument("--feature-branch", required=True)
|
|
2147
2376
|
p.add_argument("--temp-root")
|
|
2148
2377
|
p.set_defaults(func=func)
|
|
2378
|
+
return p
|
|
2149
2379
|
|
|
2150
2380
|
add("preflight", cmd_preflight)
|
|
2151
2381
|
add("prepare", cmd_prepare)
|
|
@@ -2153,7 +2383,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2153
2383
|
add("push", cmd_push)
|
|
2154
2384
|
add("cleanup", cmd_cleanup)
|
|
2155
2385
|
add("abandon", cmd_abandon)
|
|
2156
|
-
add("recover", cmd_recover)
|
|
2386
|
+
p_recover = add("recover", cmd_recover)
|
|
2387
|
+
p_recover.add_argument(
|
|
2388
|
+
"--evidence-change",
|
|
2389
|
+
help=(
|
|
2390
|
+
"one-time recovery binding after a remote-confirmed "
|
|
2391
|
+
"default LEDGER_MISSING failure"
|
|
2392
|
+
),
|
|
2393
|
+
)
|
|
2157
2394
|
add("status", cmd_status)
|
|
2158
2395
|
|
|
2159
2396
|
p_journal = sub.add_parser("journal")
|
|
@@ -2233,6 +2233,11 @@ def cmd_record(args: argparse.Namespace) -> int:
|
|
|
2233
2233
|
"finishedAt": now_iso(),
|
|
2234
2234
|
}
|
|
2235
2235
|
)
|
|
2236
|
+
# A fresh successful execution supersedes the prior target's fixback
|
|
2237
|
+
# invalidation. Carrying these projection-only fields forward would
|
|
2238
|
+
# make every rerecord permanently non-reusable.
|
|
2239
|
+
entry.pop("reusable", None)
|
|
2240
|
+
entry.pop("invalidation", None)
|
|
2236
2241
|
metrics_raw = getattr(args, "metrics_json", None)
|
|
2237
2242
|
metrics_file = getattr(args, "metrics_file", None)
|
|
2238
2243
|
if metrics_file and metrics_raw is not None and str(metrics_raw).strip():
|