@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
|
@@ -29,6 +29,7 @@ if str(SCRIPT_DIR) not in sys.path:
|
|
|
29
29
|
sys.path.insert(0, str(SCRIPT_DIR))
|
|
30
30
|
|
|
31
31
|
import harness_environment as henv
|
|
32
|
+
import harness_process as hprocess
|
|
32
33
|
|
|
33
34
|
DEFAULT_MAX_WORKERS = 2
|
|
34
35
|
DEFAULT_TIMEOUT_SECONDS = 300.0
|
|
@@ -139,35 +140,30 @@ def build_unittest_plan(
|
|
|
139
140
|
|
|
140
141
|
|
|
141
142
|
def _pid_is_running(pid: int) -> bool:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
kernel32 = ctypes.windll.kernel32
|
|
149
|
-
kernel32.OpenProcess.restype = ctypes.c_void_p
|
|
150
|
-
handle = kernel32.OpenProcess(
|
|
151
|
-
process_query_limited_information,
|
|
152
|
-
False,
|
|
153
|
-
pid,
|
|
154
|
-
)
|
|
155
|
-
if not handle:
|
|
156
|
-
return False
|
|
157
|
-
try:
|
|
158
|
-
exit_code = ctypes.c_uint32()
|
|
159
|
-
if not kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code)):
|
|
160
|
-
return False
|
|
161
|
-
return int(exit_code.value) == 259
|
|
162
|
-
finally:
|
|
163
|
-
kernel32.CloseHandle(ctypes.c_void_p(handle))
|
|
143
|
+
return hprocess.is_pid_alive(pid)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _verify_recorded_owner(owner: Mapping[str, object]) -> bool | None:
|
|
147
|
+
"""Verify a legacy lock owner through the canonical process provider."""
|
|
148
|
+
|
|
164
149
|
try:
|
|
165
|
-
|
|
166
|
-
except
|
|
167
|
-
return
|
|
168
|
-
|
|
150
|
+
pid = int(owner.get("pid") or 0)
|
|
151
|
+
except (TypeError, ValueError):
|
|
152
|
+
return None
|
|
153
|
+
observed = hprocess.observe_process_identity(pid)
|
|
154
|
+
decision = hprocess.verify_process_identity(
|
|
155
|
+
{
|
|
156
|
+
"pid": pid,
|
|
157
|
+
"createdAt": owner.get("startedAt"),
|
|
158
|
+
"executable": owner.get("executable"),
|
|
159
|
+
},
|
|
160
|
+
observed,
|
|
161
|
+
)
|
|
162
|
+
if decision.get("ok") is True:
|
|
169
163
|
return True
|
|
170
|
-
|
|
164
|
+
if decision.get("reasonCode") == "IDENTITY_UNVERIFIABLE":
|
|
165
|
+
return None
|
|
166
|
+
return False
|
|
171
167
|
|
|
172
168
|
|
|
173
169
|
def classify_test_lock(path: Path) -> dict[str, object]:
|
|
@@ -237,13 +233,7 @@ def classify_test_lock(path: Path) -> dict[str, object]:
|
|
|
237
233
|
"token": payload.get("token"),
|
|
238
234
|
"owner": owner,
|
|
239
235
|
}
|
|
240
|
-
verified =
|
|
241
|
-
{
|
|
242
|
-
"pid": owner_pid,
|
|
243
|
-
"startedAt": owner["startedAt"],
|
|
244
|
-
"processIdentity": {"executable": owner["executable"]},
|
|
245
|
-
}
|
|
246
|
-
)
|
|
236
|
+
verified = _verify_recorded_owner(owner)
|
|
247
237
|
classification = (
|
|
248
238
|
"OWNER_ACTIVE"
|
|
249
239
|
if verified is True
|
|
@@ -348,9 +338,9 @@ class TestRunLock:
|
|
|
348
338
|
)
|
|
349
339
|
else:
|
|
350
340
|
now = time.time()
|
|
351
|
-
created =
|
|
341
|
+
created = hprocess.get_process_create_time(os.getpid())
|
|
352
342
|
executable = (
|
|
353
|
-
|
|
343
|
+
hprocess.get_process_executable(os.getpid())
|
|
354
344
|
or str(Path(sys.executable).resolve())
|
|
355
345
|
)
|
|
356
346
|
payload = {
|
|
@@ -794,35 +784,12 @@ def _terminate_process_tree(
|
|
|
794
784
|
*,
|
|
795
785
|
windows_job: _WindowsKillOnCloseJob | None,
|
|
796
786
|
) -> None:
|
|
787
|
+
"""Legacy seam retained for callers; ownership must be provider-backed."""
|
|
788
|
+
|
|
797
789
|
if windows_job is not None and windows_job.handle is not None:
|
|
798
790
|
windows_job.terminate_and_wait()
|
|
799
|
-
elif os.name == "nt":
|
|
800
|
-
subprocess.run(
|
|
801
|
-
["taskkill", "/PID", str(process.pid), "/T", "/F"],
|
|
802
|
-
stdout=subprocess.DEVNULL,
|
|
803
|
-
stderr=subprocess.DEVNULL,
|
|
804
|
-
check=False,
|
|
805
|
-
timeout=10,
|
|
806
|
-
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
|
|
807
|
-
)
|
|
808
791
|
else:
|
|
809
|
-
|
|
810
|
-
os.killpg(process.pid, signal.SIGTERM)
|
|
811
|
-
except ProcessLookupError:
|
|
812
|
-
pass
|
|
813
|
-
try:
|
|
814
|
-
process.wait(timeout=2)
|
|
815
|
-
except subprocess.TimeoutExpired:
|
|
816
|
-
try:
|
|
817
|
-
os.killpg(process.pid, signal.SIGKILL)
|
|
818
|
-
except ProcessLookupError:
|
|
819
|
-
pass
|
|
820
|
-
if process.poll() is None:
|
|
821
|
-
process.kill()
|
|
822
|
-
try:
|
|
823
|
-
process.wait(timeout=5)
|
|
824
|
-
except subprocess.TimeoutExpired:
|
|
825
|
-
pass
|
|
792
|
+
raise RuntimeError("PROCESS_OWNERSHIP_REQUIRED")
|
|
826
793
|
|
|
827
794
|
|
|
828
795
|
def _cleanup_descendants_after_success(
|
|
@@ -832,12 +799,7 @@ def _cleanup_descendants_after_success(
|
|
|
832
799
|
) -> bool:
|
|
833
800
|
if windows_job is not None and windows_job.handle is not None:
|
|
834
801
|
return windows_job.terminate_and_wait()
|
|
835
|
-
|
|
836
|
-
try:
|
|
837
|
-
os.killpg(process.pid, signal.SIGTERM)
|
|
838
|
-
except ProcessLookupError:
|
|
839
|
-
pass
|
|
840
|
-
return os.name != "nt"
|
|
802
|
+
return False
|
|
841
803
|
|
|
842
804
|
|
|
843
805
|
def run_managed_command(
|
|
@@ -859,74 +821,84 @@ def run_managed_command(
|
|
|
859
821
|
child_env.setdefault("PYTHONUNBUFFERED", "1")
|
|
860
822
|
lower_process_priority()
|
|
861
823
|
|
|
862
|
-
popen_kwargs: dict[str, object] = {
|
|
863
|
-
"cwd": str(Path(cwd).resolve()),
|
|
864
|
-
"env": child_env,
|
|
865
|
-
"shell": False,
|
|
866
|
-
}
|
|
867
824
|
output_stream = tempfile.TemporaryFile(mode="w+b") if capture_output else None
|
|
868
|
-
if output_stream is not None:
|
|
869
|
-
popen_kwargs["stdout"] = output_stream
|
|
870
|
-
popen_kwargs["stderr"] = subprocess.STDOUT
|
|
871
|
-
if os.name == "nt":
|
|
872
|
-
popen_kwargs["creationflags"] = getattr(
|
|
873
|
-
subprocess,
|
|
874
|
-
"CREATE_NEW_PROCESS_GROUP",
|
|
875
|
-
0,
|
|
876
|
-
)
|
|
877
|
-
else:
|
|
878
|
-
popen_kwargs["start_new_session"] = True
|
|
879
|
-
|
|
880
825
|
started = time.monotonic()
|
|
826
|
+
spawned = None
|
|
827
|
+
process: subprocess.Popen[object] | None = None
|
|
828
|
+
cleanup_complete = False
|
|
881
829
|
try:
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
830
|
+
spawned = hprocess.spawn_structured_argv(
|
|
831
|
+
list(argv),
|
|
832
|
+
cwd=Path(cwd).resolve(),
|
|
833
|
+
environment=child_env,
|
|
834
|
+
owner_token=f"test-runner:{uuid.uuid4().hex}",
|
|
835
|
+
stdout=output_stream,
|
|
836
|
+
stderr=(subprocess.STDOUT if output_stream is not None else None),
|
|
887
837
|
)
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
838
|
+
process = spawned.process
|
|
839
|
+
isolated = bool(
|
|
840
|
+
isinstance(spawned.ownershipProof, dict)
|
|
841
|
+
and spawned.ownershipProof.get("membersComplete") is True
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
def provider_cleanup() -> bool:
|
|
845
|
+
hprocess.capture_owned_members(spawned)
|
|
846
|
+
result = hprocess.terminate_owned_tree(
|
|
847
|
+
spawned.attestation,
|
|
848
|
+
spawned.ownershipProof,
|
|
849
|
+
{"graceSeconds": 3.0},
|
|
850
|
+
)
|
|
851
|
+
return bool(result.get("cleanupComplete"))
|
|
852
|
+
|
|
853
|
+
def wait_owned_members_gone() -> bool:
|
|
854
|
+
proof = spawned.ownershipProof
|
|
855
|
+
if not isinstance(proof, dict):
|
|
856
|
+
return False
|
|
857
|
+
members = proof.get("members")
|
|
858
|
+
if not isinstance(members, list):
|
|
859
|
+
return False
|
|
860
|
+
pids = {
|
|
861
|
+
int(item["pid"])
|
|
862
|
+
for item in members
|
|
863
|
+
if isinstance(item, dict) and isinstance(item.get("pid"), int)
|
|
864
|
+
}
|
|
865
|
+
deadline = time.monotonic() + 5.0
|
|
866
|
+
while pids and time.monotonic() < deadline:
|
|
867
|
+
pids = {pid for pid in pids if hprocess.is_pid_alive(pid)}
|
|
868
|
+
if pids:
|
|
869
|
+
time.sleep(0.05)
|
|
870
|
+
return not pids
|
|
871
|
+
|
|
893
872
|
timed_out = False
|
|
894
873
|
try:
|
|
895
874
|
deadline = time.monotonic() + max(0.01, float(timeout_seconds))
|
|
896
875
|
while True:
|
|
897
|
-
if os.name == "nt":
|
|
898
|
-
windows_snapshot_ok = (
|
|
899
|
-
_update_windows_descendants(
|
|
900
|
-
process.pid,
|
|
901
|
-
known_windows_descendants,
|
|
902
|
-
)
|
|
903
|
-
and windows_snapshot_ok
|
|
904
|
-
)
|
|
905
876
|
polled = process.poll()
|
|
906
877
|
if polled is not None:
|
|
907
878
|
returncode = polled
|
|
908
879
|
break
|
|
909
880
|
if time.monotonic() >= deadline:
|
|
910
881
|
timed_out = True
|
|
911
|
-
|
|
882
|
+
cleanup_complete = provider_cleanup()
|
|
912
883
|
returncode = 124
|
|
913
884
|
break
|
|
914
885
|
time.sleep(0.05)
|
|
915
886
|
except KeyboardInterrupt:
|
|
916
|
-
|
|
917
|
-
if os.name == "nt":
|
|
918
|
-
_terminate_tracked_windows_descendants(known_windows_descendants)
|
|
887
|
+
cleanup_complete = provider_cleanup()
|
|
919
888
|
raise
|
|
920
|
-
descendants_drained = True
|
|
921
889
|
if not timed_out:
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
890
|
+
cleanup_complete = provider_cleanup()
|
|
891
|
+
# A named Windows Job remains an owned capability after the leader
|
|
892
|
+
# exits. Closing that provider handle drains any attested children;
|
|
893
|
+
# PID-only or POSIX group fallbacks stay fail-closed.
|
|
894
|
+
if not cleanup_complete and os.name == "nt":
|
|
895
|
+
spawned.close()
|
|
896
|
+
cleanup_complete = wait_owned_members_gone()
|
|
897
|
+
if process.poll() is None:
|
|
898
|
+
try:
|
|
899
|
+
process.wait(timeout=5)
|
|
900
|
+
except subprocess.TimeoutExpired:
|
|
901
|
+
cleanup_complete = False
|
|
930
902
|
duration = time.monotonic() - started
|
|
931
903
|
output_tail = ""
|
|
932
904
|
if output_stream is not None:
|
|
@@ -939,10 +911,28 @@ def run_managed_command(
|
|
|
939
911
|
returncode=returncode,
|
|
940
912
|
timed_out=timed_out,
|
|
941
913
|
duration_seconds=duration,
|
|
942
|
-
process_tree_isolated=isolated,
|
|
914
|
+
process_tree_isolated=isolated and cleanup_complete,
|
|
943
915
|
output_tail=output_tail,
|
|
944
916
|
)
|
|
945
917
|
finally:
|
|
918
|
+
if (
|
|
919
|
+
not cleanup_complete
|
|
920
|
+
and process is not None
|
|
921
|
+
and process.poll() is None
|
|
922
|
+
and spawned is not None
|
|
923
|
+
):
|
|
924
|
+
try:
|
|
925
|
+
hprocess.capture_owned_members(spawned)
|
|
926
|
+
cleanup = hprocess.terminate_owned_tree(
|
|
927
|
+
spawned.attestation,
|
|
928
|
+
spawned.ownershipProof,
|
|
929
|
+
{"graceSeconds": 3.0},
|
|
930
|
+
)
|
|
931
|
+
cleanup_complete = bool(cleanup.get("cleanupComplete"))
|
|
932
|
+
except Exception:
|
|
933
|
+
cleanup_complete = False
|
|
934
|
+
if spawned is not None:
|
|
935
|
+
spawned.close()
|
|
946
936
|
if output_stream is not None:
|
|
947
937
|
output_stream.close()
|
|
948
938
|
|
|
@@ -7,12 +7,63 @@ import argparse
|
|
|
7
7
|
import fnmatch
|
|
8
8
|
import json
|
|
9
9
|
import time
|
|
10
|
+
from collections import Counter
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import Any
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
_COVERAGE_RANK = {"incremental": 0, "module": 1, "module-am": 2, "full": 3}
|
|
15
16
|
|
|
17
|
+
_REASON_EXPLANATIONS = {
|
|
18
|
+
"PRODUCT_IDENTITY_REQUIRED": "A product identity is required before a verification can run.",
|
|
19
|
+
"CAPABILITY_MISSING": "A required execution capability is not available.",
|
|
20
|
+
"REQUIRED_COVERAGE_INVALID": "The required coverage declaration is invalid.",
|
|
21
|
+
"COVERAGE_DECLARATION_REQUIRED": "The target does not declare the coverage level needed for this gate.",
|
|
22
|
+
"COVERAGE_INSUFFICIENT": "The target coverage is below the required gate level.",
|
|
23
|
+
"NOT_APPLICABLE": "The target is explicitly not applicable to this change.",
|
|
24
|
+
"FROZEN_IDENTITY_REQUIRED": "The target requires a frozen product identity.",
|
|
25
|
+
"FROZEN_IDENTITY_DRIFT": "The current product identity differs from the frozen identity.",
|
|
26
|
+
"REUSE_POLICY_NEVER": "The target policy explicitly requires a fresh execution.",
|
|
27
|
+
"REUSE_EVIDENCE_INVALID": "Ledger evidence does not match the target and product identity exactly.",
|
|
28
|
+
"REUSE_ELIGIBLE": "Identity-matched ledger evidence permits reuse.",
|
|
29
|
+
"SELECTED_FOR_EXECUTION": "The target was selected for execution and passed its gate checks.",
|
|
30
|
+
"COMMAND_NOT_DECLARED": "The target command key is absent from the authoritative command declaration map.",
|
|
31
|
+
"COMMAND_DECLARATION_INVALID": "The authoritative command declaration is empty or malformed.",
|
|
32
|
+
"DEPENDENCY_UNKNOWN": "The target depends on a node that is not in the verification graph.",
|
|
33
|
+
"DEPENDENCY_BLOCKED": "A dependency was skipped or blocked, so this target cannot run.",
|
|
34
|
+
"DEPENDENCY_CYCLE": "The verification graph contains a dependency cycle.",
|
|
35
|
+
"VERIFICATION_REUSED": "The target reused an identity-matched verification receipt.",
|
|
36
|
+
"VERIFICATION_ARGV_NOT_CONCRETE": "The target command arguments are not concrete and executable.",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _reason_explanation(reason_codes: list[str], decision: str) -> str:
|
|
41
|
+
explanations: list[str] = []
|
|
42
|
+
for raw in reason_codes:
|
|
43
|
+
code = str(raw).split(":", 1)[0]
|
|
44
|
+
explanation = _REASON_EXPLANATIONS.get(code)
|
|
45
|
+
if explanation is None:
|
|
46
|
+
if raw.startswith("depends-on:"):
|
|
47
|
+
explanation = f"The target waits for dependency {raw.split(':', 1)[1]} to complete."
|
|
48
|
+
elif raw.startswith("consumer-of:"):
|
|
49
|
+
explanation = f"The target is included because it consumes {raw.split(':', 1)[1]}."
|
|
50
|
+
elif raw.startswith("input-match:"):
|
|
51
|
+
explanation = f"A changed input matched {raw.split(':', 1)[1]}."
|
|
52
|
+
elif raw.startswith("CAPABILITY_MISSING:"):
|
|
53
|
+
explanation = f"Capability {raw.split(':', 1)[1]} is unavailable."
|
|
54
|
+
else:
|
|
55
|
+
explanation = f"Verification decision reason: {raw}."
|
|
56
|
+
if explanation not in explanations:
|
|
57
|
+
explanations.append(explanation)
|
|
58
|
+
if explanations:
|
|
59
|
+
return " ".join(explanations)
|
|
60
|
+
return {
|
|
61
|
+
"EXECUTE": "The target passed all declared gate checks and is ready to execute.",
|
|
62
|
+
"REUSE": "The target has valid identity-matched reusable evidence.",
|
|
63
|
+
"SKIP": "The target is not applicable to this change.",
|
|
64
|
+
"BLOCKED": "The target cannot execute until its blocking conditions are resolved.",
|
|
65
|
+
}.get(decision, "The target has no executable decision.")
|
|
66
|
+
|
|
16
67
|
|
|
17
68
|
def _normalize_targets(raw_targets: Any) -> list[dict[str, Any]]:
|
|
18
69
|
if isinstance(raw_targets, dict):
|
|
@@ -133,10 +184,26 @@ def _target_decision(
|
|
|
133
184
|
frozen_identity: str,
|
|
134
185
|
available_capabilities: set[str],
|
|
135
186
|
verification_ledger: list[dict[str, Any]],
|
|
187
|
+
command_declarations: dict[str, Any] | None = None,
|
|
136
188
|
) -> tuple[str, list[str]]:
|
|
137
189
|
blockers: list[str] = []
|
|
138
190
|
if not product_identity:
|
|
139
191
|
return "BLOCKED", ["PRODUCT_IDENTITY_REQUIRED"]
|
|
192
|
+
if command_declarations is not None:
|
|
193
|
+
command_key = str(target.get("commandKey") or "").strip()
|
|
194
|
+
declaration = command_declarations.get(command_key) if command_key else None
|
|
195
|
+
if declaration is None:
|
|
196
|
+
blockers.append("COMMAND_NOT_DECLARED")
|
|
197
|
+
elif isinstance(declaration, dict):
|
|
198
|
+
argv = declaration.get("argv") or declaration.get("argvTemplate")
|
|
199
|
+
if not isinstance(argv, list) or not argv or not all(
|
|
200
|
+
isinstance(item, str) and item for item in argv
|
|
201
|
+
):
|
|
202
|
+
blockers.append("COMMAND_DECLARATION_INVALID")
|
|
203
|
+
elif not isinstance(declaration, list) or not declaration or not all(
|
|
204
|
+
isinstance(item, str) and item for item in declaration
|
|
205
|
+
):
|
|
206
|
+
blockers.append("COMMAND_DECLARATION_INVALID")
|
|
140
207
|
required_capabilities = {
|
|
141
208
|
str(item).strip()
|
|
142
209
|
for item in target.get("requiredCapabilities", [])
|
|
@@ -238,6 +305,12 @@ def schedule_verifications(payload: dict[str, Any]) -> dict[str, Any]:
|
|
|
238
305
|
for item in payload.get("verificationLedger", [])
|
|
239
306
|
if isinstance(item, dict)
|
|
240
307
|
]
|
|
308
|
+
raw_commands = payload.get("commands")
|
|
309
|
+
command_declarations = (
|
|
310
|
+
{str(key): value for key, value in raw_commands.items()}
|
|
311
|
+
if isinstance(raw_commands, dict)
|
|
312
|
+
else None
|
|
313
|
+
)
|
|
241
314
|
decisions: dict[str, tuple[str, list[str]]] = {
|
|
242
315
|
target_id: _target_decision(
|
|
243
316
|
target,
|
|
@@ -245,6 +318,7 @@ def schedule_verifications(payload: dict[str, Any]) -> dict[str, Any]:
|
|
|
245
318
|
frozen_identity=frozen_identity,
|
|
246
319
|
available_capabilities=available_capabilities,
|
|
247
320
|
verification_ledger=verification_ledger,
|
|
321
|
+
command_declarations=command_declarations,
|
|
248
322
|
)
|
|
249
323
|
for target_id, target in by_id.items()
|
|
250
324
|
}
|
|
@@ -345,10 +419,14 @@ def schedule_verifications(payload: dict[str, Any]) -> dict[str, Any]:
|
|
|
345
419
|
"id": target_id,
|
|
346
420
|
"decision": decision,
|
|
347
421
|
"reasonCodes": reason_codes,
|
|
422
|
+
"explanation": _reason_explanation(reason_codes, decision),
|
|
348
423
|
"resourceLocks": _resource_locks(target),
|
|
349
424
|
"wave": placed[target_id],
|
|
350
425
|
}
|
|
351
426
|
)
|
|
427
|
+
decision_counts = dict(
|
|
428
|
+
sorted(Counter(item["decision"] for item in plan).items())
|
|
429
|
+
)
|
|
352
430
|
return {
|
|
353
431
|
"ok": all(item["decision"] != "BLOCKED" for item in plan),
|
|
354
432
|
"code": (
|
|
@@ -359,6 +437,7 @@ def schedule_verifications(payload: dict[str, Any]) -> dict[str, Any]:
|
|
|
359
437
|
"productIdentity": product_identity,
|
|
360
438
|
"frozenIdentity": frozen_identity,
|
|
361
439
|
"plan": plan,
|
|
440
|
+
"decisionCounts": decision_counts,
|
|
362
441
|
"waves": [
|
|
363
442
|
{"wave": index, "targets": target_ids}
|
|
364
443
|
for index, target_ids in enumerate(waves)
|