diff --git a/evals/wor105/components/native-transition-record.yaml b/evals/wor105/components/native-transition-record.yaml index a70d666..18c1e41 100644 --- a/evals/wor105/components/native-transition-record.yaml +++ b/evals/wor105/components/native-transition-record.yaml @@ -2,11 +2,11 @@ schema: wor105-native-transition-v1 issue: WOR-105 transition_task: task-b06r enforcement_transition: bootstrap_policy_to_native -source_identity: 10aa3aceeea4c440feaccb484b897af67cd0df0b+repository-evidence-sha256:279f142fad6ea3005370b9eaa82d7c88444032e668309173e9230754b28c40ae +source_identity: 9dce5df221485174d6179f713e8b179bbc20567a+repository-evidence-sha256:279f142fad6ea3005370b9eaa82d7c88444032e668309173e9230754b28c40ae review_path: .work-bundle/orchestration/reviews/WOR-105-task-b06r-kernel-review-accepted.yaml review_sha256: d638b8959b4db57dd33e072b01428d6ce89f65a9771c05eff26d8e40d4293ebd -accepted_commit: 10aa3aceeea4c440feaccb484b897af67cd0df0b -accepted_tree: 3f70163e3aef240b0f1207f43433874c9139a7fb +accepted_commit: 9dce5df221485174d6179f713e8b179bbc20567a +accepted_tree: 5a1f38355eae8068bab528923e807ce54e6f6fe5 integrated_validation: {id: VAL-B06R-TEST, result: passed, tests: 300} handoff_validation: {id: VAL-B06R-IDENTITY, result: passed, adversarial_cases: 5} participant_handoffs: diff --git a/evals/wor105/freeze-manifest.json b/evals/wor105/freeze-manifest.json index fc80a83..6cb30cb 100644 --- a/evals/wor105/freeze-manifest.json +++ b/evals/wor105/freeze-manifest.json @@ -72,7 +72,7 @@ "components": { "profile": { "path": "evals/wor105/components/native-transition-record.yaml", - "sha256": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56" + "sha256": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a" }, "fixtures": { "path": "evals/wor105/fixtures", diff --git a/evals/wor105/results.jsonl b/evals/wor105/results.jsonl index 7de1172..1c4c932 100644 --- a/evals/wor105/results.jsonl +++ b/evals/wor105/results.jsonl @@ -1,12 +1,12 @@ -{"actual_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", "adjudication_sha256": "f256137ea263da8806b4b1a33a3a7510a48e3c6672c8262dc75aa4a26ffc71e4", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-01:mutation-denial:1", "event:ADV-01:mutation-denial:2"], "expected_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", "fixture_id": "ADV-01", "fixture_sha256": "52e6a3f83058f820fe01343e5b009940842874f05bdf47aa2e99ea60a27127bb", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"allowed_read_output_sha256": "1dc07dc04e672df4c66bca312cc8aa0fd3845e9117eb9c3722c805ba49cad7c4", "control_sentinel_after_sha256": "1e9c7b842690803f3f25a996afcd18348e571ef93083e2094f7c533dbdef0397", "control_sentinel_before_sha256": "1e9c7b842690803f3f25a996afcd18348e571ef93083e2094f7c533dbdef0397", "denial_classes": ["permission_denied", "permission_denied", "permission_denied", "permission_denied", "permission_denied"], "event_ids": ["event:ADV-01:mutation-denial:1", "event:ADV-01:mutation-denial:2"], "source_sentinel_after_sha256": "b2271b177e020061b04fc194bf5ec85a9cdbfc9e74074b73b52d76a5fc2af85a", "source_sentinel_before_sha256": "b2271b177e020061b04fc194bf5ec85a9cdbfc9e74074b73b52d76a5fc2af85a", "validator_output_sha256": "4caeb0c30f3bd412655341504b3b564ce92c160c17e419c5251d8b61a9c8f259"}, "raw_evidence_sha256": "4f2253688acd2c03346731731e6b10a9b3f95c991f91125ad00acf7e01e01d7d", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "pause_and_reslice_after_second_expansion", "adjudication_sha256": "0c312e858a1dabac1efc47526ba05dd56a25c4351574d543104213fe4fef87b6", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-02:1"], "expected_decision": "pause_and_reslice_after_second_expansion", "fixture_id": "ADV-02", "fixture_sha256": "78fe6de27c7833336f6883a73dc61aaa0b15b37735ae639e9061e071e0dfcd40", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"binding_state": "repair_owned", "expansion_event_ids": ["event:ADV-02:expand:1", "event:ADV-02:expand:2"], "original_evidence_sha256": "dbe1e53e72d77259941220006b4dabe76e616d22ae79027474eacbc4312e67a0", "reslice_artifact_sha256": "cf7240e355ba73ddef7d7376e813c8fb4d4d462b40409bdf9a933fc241dd3d6e", "return_owner": "plan_owner"}, "raw_evidence_sha256": "29d9edb9048ae76009eb3a23e5c83665d2d3dd8335ca9e24f5c8399a42a47662", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_and_route_allocation_gap_to_plan_reslice", "adjudication_sha256": "51468752b9157242d57bb4bb7c23937578afbd08933b94f7fca98b86ab8fabae", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-03:1"], "expected_decision": "reject_and_route_allocation_gap_to_plan_reslice", "fixture_id": "ADV-03", "fixture_sha256": "490ebdd8890adb88bd52a6cd24a54d02bdc6750d0a3f9cc63e78cc6a2d68d260", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"canonical_finding_sha256": "b18f342fdb3df678ce2df6d512fcdb70739adce75c9cd0cb38d713a8435d20f5", "rejected_record_sha256": "c3800c532270ebf1766019851e6edc82a13b9ce0901668bb0f4b7fa99bfd6df0", "validation_error_code": "finding_route_mismatch"}, "raw_evidence_sha256": "5ce46fcc3d48798e1aeceb6b01e206612cf0e45066968d898e8d8d601fc16c6c", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_blocking_and_record_nonblocking_advisory", "adjudication_sha256": "8f68aa67e626e52e4d9e901dda96c65a95db4f72ced495e1d6a76ea587c442b3", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-04:1"], "expected_decision": "reject_blocking_and_record_nonblocking_advisory", "fixture_id": "ADV-04", "fixture_sha256": "19db36abd1deb09f95179b572e2c559509aea0a2607aa1a04cd4b5af7db291ef", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"advisory_id": "advisory:ADV-04", "stage_state_after_sha256": "7f11ee6a1f8fc67e343ff9ede27759613559df69e153fefa0540dc55281a2df6", "stage_state_before_sha256": "7f11ee6a1f8fc67e343ff9ede27759613559df69e153fefa0540dc55281a2df6", "validation_error_code": "blocking_basis_required"}, "raw_evidence_sha256": "741437d7d693ed2aaeba457eab8f7dfa216cb52e26a1e43245882052439b2213", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "stale_run_append_invalidation_preserve_raw_evidence", "adjudication_sha256": "a4fd0509b02c28e16a858b9c277227da3585eafe562992594f5c6f8683c9dc44", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-05:1"], "expected_decision": "stale_run_append_invalidation_preserve_raw_evidence", "fixture_id": "ADV-05", "fixture_sha256": "42251cea32fd52341f87067be6bbc4194da7139b65b823dcf230b11f50baa3f7", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"invalidation_id": "invalidation:ADV-05", "new_digest": "da9456aece01c51674e58791a55e81a9357e4c6dd14de537038a668b1c26c2ec", "old_digest": "de0043aa39f8969804ba2e21f6abbc4a5eb50bf22177c9ce60e8807ec1fe671b", "raw_response_after_sha256": "1a16053dccca94ba3e07bf3f1119c0a3387364cecc59752c2ab4a7d734f9de4f", "raw_response_before_sha256": "1a16053dccca94ba3e07bf3f1119c0a3387364cecc59752c2ab4a7d734f9de4f", "raw_trace_after_sha256": "68245e9a19559d24fa246ec87f100351c7e2f97b88374254956d08611ef3a84d", "raw_trace_before_sha256": "68245e9a19559d24fa246ec87f100351c7e2f97b88374254956d08611ef3a84d", "stale_run_id": "run:ADV-05"}, "raw_evidence_sha256": "0821a85ca0f258bd769bc848136420c9c46035322e9e1cc85726da32c8648ef7", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "preserve_product_observation_update_packaging_only", "adjudication_sha256": "b784bb58f6b27be4f92782776647fb3985d99a5cc07b0f9f4c3e276e8c5bde20", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-06:1"], "expected_decision": "preserve_product_observation_update_packaging_only", "fixture_id": "ADV-06", "fixture_sha256": "f5241e874cd284c3e41ecab7e2790d265a8ca7f0e53785d9bb151ebeefb16d6e", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"observation_after_sha256": "b5729bf53e81f9a32161b5e412cdc1ef24259d833f1cbc448caed5f0733dc8ea", "observation_before_sha256": "b5729bf53e81f9a32161b5e412cdc1ef24259d833f1cbc448caed5f0733dc8ea", "packaging_after": "3333333333333333333333333333333333333333", "packaging_before": "2222222222222222222222222222222222222222", "product_tree_after": "1111111111111111111111111111111111111111", "product_tree_before": "1111111111111111111111111111111111111111", "valid": true}, "raw_evidence_sha256": "4ee5d2cf39b9f696bf9ca32a6c246de0c0488656d3dc6c4b65a08fbba28a2676", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "mark_review_stale_and_remove_stage_credit", "adjudication_sha256": "6d06a92a211f065ba1216c44062d06fa78b49cb914d2d81cfa5a2a656344e64c", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-07:1"], "expected_decision": "mark_review_stale_and_remove_stage_credit", "fixture_id": "ADV-07", "fixture_sha256": "f9446f50289c62fd1115d89d2121576950174cbad68bb4e76cdd8cef1a962d47", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"countable_stage_reviews": 0, "review_id": "review:ADV-07", "staleness_reason": "target_identity_changed", "target_after_sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "target_before_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, "raw_evidence_sha256": "fc968bdfbc46c514d3c0965e0b19d7601af03bbf35c40c9ac67fb2e2e76238e4", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "execute_once_and_reuse_observation", "adjudication_sha256": "4216f0afd1e4634a240f548cc0ecc0872aeb8363c53d43e7f518211d6e84e94c", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-08:1"], "expected_decision": "execute_once_and_reuse_observation", "fixture_id": "ADV-08", "fixture_sha256": "b8b07a687c7d492ad983b88a72b837f18ae50b6c384aa394a732c60e940bd01e", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"observation_id": "observation:430c6f98184ff990ddfd", "request_ids": ["request:ADV-08:1", "request:ADV-08:2"], "reuse_of": "observation:430c6f98184ff990ddfd", "subprocess_invocation_count": 1}, "raw_evidence_sha256": "6ba81e81dfb301c7c481b9987e7ea1c5fbd55c362e2fa63b79aa83dbc4e0ad86", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "deny_release_preserve_owner_reason_history", "adjudication_sha256": "3abf36eb3dc3ccc6f1ff4616598825e2174ab8774b2b0a5931508c347a58462d", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-09:1"], "expected_decision": "deny_release_preserve_owner_reason_history", "fixture_id": "ADV-09", "fixture_sha256": "5a347c746583f3bc1175b50aa5b2fdf8250e07a929206d777a48bdb7e50ca70f", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"after_snapshot_sha256": "a34c0d2062af8cb91d18210f9008b41fbc70bf54d0149bea6f6c2d22eb116179", "before_snapshot_sha256": "a34c0d2062af8cb91d18210f9008b41fbc70bf54d0149bea6f6c2d22eb116179", "denial_event_ids": ["event:ADV-09:repair", "event:ADV-09:rereview"], "original_owner": "repair_owned", "original_reason": "binding retained by active repair owner"}, "raw_evidence_sha256": "79fe7d310cfde0ffccfdf4004232ff2d47be0f14ecac5031f69fff34077e1d33", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_each_and_require_fresh_reviewer", "adjudication_sha256": "510e35f8ad9d7560c08c15820c68e04a65f6bad545c201b2d23856a418ba6008", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-10:1"], "expected_decision": "reject_each_and_require_fresh_reviewer", "fixture_id": "ADV-10", "fixture_sha256": "7ea520dcefa78d9348f354b1a9dcc9726798a0759b0b768d1ae476770731c673", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"countable_stage_reviews": 0, "rejected_review_ids": ["review:ADV-10:authorship", "review:ADV-10:repair_participation", "review:ADV-10:decision_participation", "review:ADV-10:deliberation_participation"], "validation_error_codes": ["reviewer_not_independent", "reviewer_not_independent", "reviewer_not_independent", "reviewer_not_independent"]}, "raw_evidence_sha256": "8b72d990c3945f0cccb06a95663374d559c4711da54a895dca554d179912da4c", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "route_validation_oracle_defect_without_product_rollback", "adjudication_sha256": "921bb49dae8fcf29f6bbe956ca146f8d91b6c16f6c7a93f4ba5ee9687ee2609d", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-11:1"], "expected_decision": "route_validation_oracle_defect_without_product_rollback", "fixture_id": "ADV-11", "fixture_sha256": "4520983615ce735ab5cd6ba1729e464085e0af0ec2f152fcf32872e201041ef7", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"byte_oracle_failure_sha256": "3513611273bb3e74c5c8a7224ddae25322904f08dd42e595a47ed8c9120aed73", "product_revision_after": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "product_revision_before": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "public_contract_test_output_sha256": "ec996444a9a62cde22b0ee5e1713a679f65f9aac74f285bd0ecdb9c04ff01e00", "routed_finding_sha256": "11d26026ee1ecc06e4418dc9e850d5e09d185ab8638bdd6edbfa41a60d14336b"}, "raw_evidence_sha256": "02671b359b2c0d23649cfd9e99874413da954bd8b4ff3bb08d9b65e3b844bd24", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", "adjudication_sha256": "dd2390df12ef6f7363a688d9e96424889975274a8be62c9cf99cab34136500fb", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "aa01163346022ddc9b55481cf1698614da08a1c0f75a98d38b5ce3008b57cd56", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-12:1"], "expected_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", "fixture_id": "ADV-12", "fixture_sha256": "7f29996db2939960f308f7bfe72e139e3eded355804a72860b98add5d0ce0e18", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"checkout_absent": true, "first_apply_state_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "member_snapshot_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "origin_absent": true, "replay_state_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "validation_error_code": "placeholder_remote_forbidden"}, "raw_evidence_sha256": "83ea6e80a7289ec65e87c0c694c11b8903bff0a8339085ff31a8391b2790be50", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", "adjudication_sha256": "f256137ea263da8806b4b1a33a3a7510a48e3c6672c8262dc75aa4a26ffc71e4", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-01:mutation-denial:1", "event:ADV-01:mutation-denial:2"], "expected_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", "fixture_id": "ADV-01", "fixture_sha256": "52e6a3f83058f820fe01343e5b009940842874f05bdf47aa2e99ea60a27127bb", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"allowed_read_output_sha256": "1dc07dc04e672df4c66bca312cc8aa0fd3845e9117eb9c3722c805ba49cad7c4", "control_sentinel_after_sha256": "1e9c7b842690803f3f25a996afcd18348e571ef93083e2094f7c533dbdef0397", "control_sentinel_before_sha256": "1e9c7b842690803f3f25a996afcd18348e571ef93083e2094f7c533dbdef0397", "denial_classes": ["permission_denied", "permission_denied", "permission_denied", "permission_denied", "permission_denied"], "event_ids": ["event:ADV-01:mutation-denial:1", "event:ADV-01:mutation-denial:2"], "source_sentinel_after_sha256": "b2271b177e020061b04fc194bf5ec85a9cdbfc9e74074b73b52d76a5fc2af85a", "source_sentinel_before_sha256": "b2271b177e020061b04fc194bf5ec85a9cdbfc9e74074b73b52d76a5fc2af85a", "validator_output_sha256": "4caeb0c30f3bd412655341504b3b564ce92c160c17e419c5251d8b61a9c8f259"}, "raw_evidence_sha256": "4f2253688acd2c03346731731e6b10a9b3f95c991f91125ad00acf7e01e01d7d", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "pause_and_reslice_after_second_expansion", "adjudication_sha256": "0c312e858a1dabac1efc47526ba05dd56a25c4351574d543104213fe4fef87b6", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-02:1"], "expected_decision": "pause_and_reslice_after_second_expansion", "fixture_id": "ADV-02", "fixture_sha256": "78fe6de27c7833336f6883a73dc61aaa0b15b37735ae639e9061e071e0dfcd40", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"binding_state": "repair_owned", "expansion_event_ids": ["event:ADV-02:expand:1", "event:ADV-02:expand:2"], "original_evidence_sha256": "dbe1e53e72d77259941220006b4dabe76e616d22ae79027474eacbc4312e67a0", "reslice_artifact_sha256": "cf7240e355ba73ddef7d7376e813c8fb4d4d462b40409bdf9a933fc241dd3d6e", "return_owner": "plan_owner"}, "raw_evidence_sha256": "29d9edb9048ae76009eb3a23e5c83665d2d3dd8335ca9e24f5c8399a42a47662", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "reject_and_route_allocation_gap_to_plan_reslice", "adjudication_sha256": "51468752b9157242d57bb4bb7c23937578afbd08933b94f7fca98b86ab8fabae", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-03:1"], "expected_decision": "reject_and_route_allocation_gap_to_plan_reslice", "fixture_id": "ADV-03", "fixture_sha256": "490ebdd8890adb88bd52a6cd24a54d02bdc6750d0a3f9cc63e78cc6a2d68d260", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"canonical_finding_sha256": "b18f342fdb3df678ce2df6d512fcdb70739adce75c9cd0cb38d713a8435d20f5", "rejected_record_sha256": "c3800c532270ebf1766019851e6edc82a13b9ce0901668bb0f4b7fa99bfd6df0", "validation_error_code": "finding_route_mismatch"}, "raw_evidence_sha256": "5ce46fcc3d48798e1aeceb6b01e206612cf0e45066968d898e8d8d601fc16c6c", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "reject_blocking_and_record_nonblocking_advisory", "adjudication_sha256": "8f68aa67e626e52e4d9e901dda96c65a95db4f72ced495e1d6a76ea587c442b3", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-04:1"], "expected_decision": "reject_blocking_and_record_nonblocking_advisory", "fixture_id": "ADV-04", "fixture_sha256": "19db36abd1deb09f95179b572e2c559509aea0a2607aa1a04cd4b5af7db291ef", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"advisory_id": "advisory:ADV-04", "stage_state_after_sha256": "7f11ee6a1f8fc67e343ff9ede27759613559df69e153fefa0540dc55281a2df6", "stage_state_before_sha256": "7f11ee6a1f8fc67e343ff9ede27759613559df69e153fefa0540dc55281a2df6", "validation_error_code": "blocking_basis_required"}, "raw_evidence_sha256": "741437d7d693ed2aaeba457eab8f7dfa216cb52e26a1e43245882052439b2213", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "stale_run_append_invalidation_preserve_raw_evidence", "adjudication_sha256": "a4fd0509b02c28e16a858b9c277227da3585eafe562992594f5c6f8683c9dc44", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-05:1"], "expected_decision": "stale_run_append_invalidation_preserve_raw_evidence", "fixture_id": "ADV-05", "fixture_sha256": "42251cea32fd52341f87067be6bbc4194da7139b65b823dcf230b11f50baa3f7", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"invalidation_id": "invalidation:ADV-05", "new_digest": "da9456aece01c51674e58791a55e81a9357e4c6dd14de537038a668b1c26c2ec", "old_digest": "de0043aa39f8969804ba2e21f6abbc4a5eb50bf22177c9ce60e8807ec1fe671b", "raw_response_after_sha256": "1a16053dccca94ba3e07bf3f1119c0a3387364cecc59752c2ab4a7d734f9de4f", "raw_response_before_sha256": "1a16053dccca94ba3e07bf3f1119c0a3387364cecc59752c2ab4a7d734f9de4f", "raw_trace_after_sha256": "68245e9a19559d24fa246ec87f100351c7e2f97b88374254956d08611ef3a84d", "raw_trace_before_sha256": "68245e9a19559d24fa246ec87f100351c7e2f97b88374254956d08611ef3a84d", "stale_run_id": "run:ADV-05"}, "raw_evidence_sha256": "0821a85ca0f258bd769bc848136420c9c46035322e9e1cc85726da32c8648ef7", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "preserve_product_observation_update_packaging_only", "adjudication_sha256": "b784bb58f6b27be4f92782776647fb3985d99a5cc07b0f9f4c3e276e8c5bde20", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-06:1"], "expected_decision": "preserve_product_observation_update_packaging_only", "fixture_id": "ADV-06", "fixture_sha256": "f5241e874cd284c3e41ecab7e2790d265a8ca7f0e53785d9bb151ebeefb16d6e", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"observation_after_sha256": "b5729bf53e81f9a32161b5e412cdc1ef24259d833f1cbc448caed5f0733dc8ea", "observation_before_sha256": "b5729bf53e81f9a32161b5e412cdc1ef24259d833f1cbc448caed5f0733dc8ea", "packaging_after": "3333333333333333333333333333333333333333", "packaging_before": "2222222222222222222222222222222222222222", "product_tree_after": "1111111111111111111111111111111111111111", "product_tree_before": "1111111111111111111111111111111111111111", "valid": true}, "raw_evidence_sha256": "4ee5d2cf39b9f696bf9ca32a6c246de0c0488656d3dc6c4b65a08fbba28a2676", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "mark_review_stale_and_remove_stage_credit", "adjudication_sha256": "6d06a92a211f065ba1216c44062d06fa78b49cb914d2d81cfa5a2a656344e64c", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-07:1"], "expected_decision": "mark_review_stale_and_remove_stage_credit", "fixture_id": "ADV-07", "fixture_sha256": "f9446f50289c62fd1115d89d2121576950174cbad68bb4e76cdd8cef1a962d47", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"countable_stage_reviews": 0, "review_id": "review:ADV-07", "staleness_reason": "target_identity_changed", "target_after_sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "target_before_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, "raw_evidence_sha256": "fc968bdfbc46c514d3c0965e0b19d7601af03bbf35c40c9ac67fb2e2e76238e4", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "execute_once_and_reuse_observation", "adjudication_sha256": "4216f0afd1e4634a240f548cc0ecc0872aeb8363c53d43e7f518211d6e84e94c", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-08:1"], "expected_decision": "execute_once_and_reuse_observation", "fixture_id": "ADV-08", "fixture_sha256": "b8b07a687c7d492ad983b88a72b837f18ae50b6c384aa394a732c60e940bd01e", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"observation_id": "observation:430c6f98184ff990ddfd", "request_ids": ["request:ADV-08:1", "request:ADV-08:2"], "reuse_of": "observation:430c6f98184ff990ddfd", "subprocess_invocation_count": 1}, "raw_evidence_sha256": "6ba81e81dfb301c7c481b9987e7ea1c5fbd55c362e2fa63b79aa83dbc4e0ad86", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "deny_release_preserve_owner_reason_history", "adjudication_sha256": "3abf36eb3dc3ccc6f1ff4616598825e2174ab8774b2b0a5931508c347a58462d", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-09:1"], "expected_decision": "deny_release_preserve_owner_reason_history", "fixture_id": "ADV-09", "fixture_sha256": "5a347c746583f3bc1175b50aa5b2fdf8250e07a929206d777a48bdb7e50ca70f", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"after_snapshot_sha256": "a34c0d2062af8cb91d18210f9008b41fbc70bf54d0149bea6f6c2d22eb116179", "before_snapshot_sha256": "a34c0d2062af8cb91d18210f9008b41fbc70bf54d0149bea6f6c2d22eb116179", "denial_event_ids": ["event:ADV-09:repair", "event:ADV-09:rereview"], "original_owner": "repair_owned", "original_reason": "binding retained by active repair owner"}, "raw_evidence_sha256": "79fe7d310cfde0ffccfdf4004232ff2d47be0f14ecac5031f69fff34077e1d33", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "reject_each_and_require_fresh_reviewer", "adjudication_sha256": "510e35f8ad9d7560c08c15820c68e04a65f6bad545c201b2d23856a418ba6008", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-10:1"], "expected_decision": "reject_each_and_require_fresh_reviewer", "fixture_id": "ADV-10", "fixture_sha256": "7ea520dcefa78d9348f354b1a9dcc9726798a0759b0b768d1ae476770731c673", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"countable_stage_reviews": 0, "rejected_review_ids": ["review:ADV-10:authorship", "review:ADV-10:repair_participation", "review:ADV-10:decision_participation", "review:ADV-10:deliberation_participation"], "validation_error_codes": ["reviewer_not_independent", "reviewer_not_independent", "reviewer_not_independent", "reviewer_not_independent"]}, "raw_evidence_sha256": "8b72d990c3945f0cccb06a95663374d559c4711da54a895dca554d179912da4c", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "route_validation_oracle_defect_without_product_rollback", "adjudication_sha256": "921bb49dae8fcf29f6bbe956ca146f8d91b6c16f6c7a93f4ba5ee9687ee2609d", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-11:1"], "expected_decision": "route_validation_oracle_defect_without_product_rollback", "fixture_id": "ADV-11", "fixture_sha256": "4520983615ce735ab5cd6ba1729e464085e0af0ec2f152fcf32872e201041ef7", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"byte_oracle_failure_sha256": "3513611273bb3e74c5c8a7224ddae25322904f08dd42e595a47ed8c9120aed73", "product_revision_after": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "product_revision_before": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "public_contract_test_output_sha256": "ec996444a9a62cde22b0ee5e1713a679f65f9aac74f285bd0ecdb9c04ff01e00", "routed_finding_sha256": "11d26026ee1ecc06e4418dc9e850d5e09d185ab8638bdd6edbfa41a60d14336b"}, "raw_evidence_sha256": "02671b359b2c0d23649cfd9e99874413da954bd8b4ff3bb08d9b65e3b844bd24", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", "adjudication_sha256": "dd2390df12ef6f7363a688d9e96424889975274a8be62c9cf99cab34136500fb", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-12:1"], "expected_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", "fixture_id": "ADV-12", "fixture_sha256": "7f29996db2939960f308f7bfe72e139e3eded355804a72860b98add5d0ce0e18", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"checkout_absent": true, "first_apply_state_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "member_snapshot_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "origin_absent": true, "replay_state_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "validation_error_code": "placeholder_remote_forbidden"}, "raw_evidence_sha256": "83ea6e80a7289ec65e87c0c694c11b8903bff0a8339085ff31a8391b2790be50", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} diff --git a/evals/wor108/contracts-v1.schema.json b/evals/wor108/contracts-v1.schema.json new file mode 100644 index 0000000..7c9ed80 --- /dev/null +++ b/evals/wor108/contracts-v1.schema.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:work-bundle:wor108:closure-contracts:v1", + "title": "WOR-108 legacy closure fixture registry", + "type": "object", + "additionalProperties": false, + "required": ["contract", "evaluation_id", "fixtures"], + "properties": { + "contract": {"const": "wor108-closure-fixtures-v1"}, + "evaluation_id": {"const": "wor108-legacy-closure-v1"}, + "fixtures": { + "type": "array", + "minItems": 22, + "maxItems": 22, + "items": {"$ref": "#/$defs/fixture"} + } + }, + "$defs": { + "fixture": { + "type": "object", + "additionalProperties": false, + "required": ["fixture_id", "area", "title", "oracle", "pytest_nodes", "expected"], + "properties": { + "fixture_id": {"type": "string", "pattern": "^(RF|SG|CTX)-[0-9]{2}$"}, + "area": {"enum": ["repair_frontier", "subagent_ownership", "context_projection"]}, + "title": {"type": "string", "minLength": 1}, + "oracle": {"const": "pytest"}, + "pytest_nodes": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": {"type": "string", "pattern": "^tests/test_wor108_[a-z_]+\\.py::test_[a-z0-9_]+$"} + }, + "expected": {"const": "passed"} + } + } + } +} diff --git a/evals/wor108/fixtures.json b/evals/wor108/fixtures.json new file mode 100644 index 0000000..d199448 --- /dev/null +++ b/evals/wor108/fixtures.json @@ -0,0 +1,198 @@ +{ + "contract": "wor108-closure-fixtures-v1", + "evaluation_id": "wor108-legacy-closure-v1", + "fixtures": [ + { + "fixture_id": "RF-01", + "area": "repair_frontier", + "title": "Initial and repair review modes are native closed contract values", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_01_h1_repairs_recorded_a_without_reacquiring_unrecorded_latent_b"], + "expected": "passed" + }, + { + "fixture_id": "RF-02", + "area": "repair_frontier", + "title": "Repair frontier binds prior blocking findings, frozen evidence, and exact identities", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_02_repair_frontier_binds_prior_findings_evidence_and_exact_identities"], + "expected": "passed" + }, + { + "fixture_id": "RF-03", + "area": "repair_frontier", + "title": "Repair reuses frozen evidence by reference without copying whole review history", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_03_capable_untouched_invariant_stays_blocking_during_narrow_repair"], + "expected": "passed" + }, + { + "fixture_id": "RF-04", + "area": "repair_frontier", + "title": "Material redesign or authority boundary change forces a fresh initial review", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_04_material_change_requires_fresh_initial_review"], + "expected": "passed" + }, + { + "fixture_id": "RF-05", + "area": "repair_frontier", + "title": "Fresh initial review requires a fresh capable independent reviewer identity", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_05_reset_rejects_reused_repair_reviewer_identity"], + "expected": "passed" + }, + { + "fixture_id": "RF-06", + "area": "repair_frontier", + "title": "Repair rejects stale or relabelled repaired identities", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_06_final_broad_integrated_review_rediscovers_and_classifies_latent_b"], + "expected": "passed" + }, + { + "fixture_id": "RF-07", + "area": "repair_frontier", + "title": "Repair catches affected-boundary regressions and retains the original execution binding baseline", + "oracle": "pytest", + "pytest_nodes": [ + "tests/test_wor108_review_frontier.py::test_rf_07_repair_still_detects_regression_in_affected_boundary", + "tests/test_wor108_context_projection.py::test_rf_07_brief_rebuild_retains_original_execution_binding_and_baseline" + ], + "expected": "passed" + }, + { + "fixture_id": "RF-08", + "area": "repair_frontier", + "title": "Repair packages stay bounded for task and stage targets", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_08_repair_packet_is_bounded_for_task_and_stage_targets"], + "expected": "passed" + }, + { + "fixture_id": "SG-01", + "area": "subagent_ownership", + "title": "Execute-plan selection implicitly requires subagent task ownership", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_subagent_ownership.py::test_sg01_execute_plan_requires_implicit_subagent_ownership"], + "expected": "passed" + }, + { + "fixture_id": "SG-02", + "area": "subagent_ownership", + "title": "No-subagent execution fails closed before task mutation", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_subagent_ownership.py::test_sg02_no_subagent_fails_closed_before_mutation"], + "expected": "passed" + }, + { + "fixture_id": "SG-03", + "area": "subagent_ownership", + "title": "Legacy prefer_subagent metadata has no behavioral effect", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_subagent_ownership.py::test_sg03_legacy_preference_has_no_behavioral_effect"], + "expected": "passed" + }, + { + "fixture_id": "SG-04", + "area": "subagent_ownership", + "title": "Controller mutation of task write scope is rejected despite green validation", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_context_projection.py::test_completed_task_acceptance_rejects_controller_task_scope_mutation"], + "expected": "passed" + }, + { + "fixture_id": "SG-05", + "area": "subagent_ownership", + "title": "Independent disjoint tasks fan out before any wait", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_subagent_ownership.py::test_sg05_independent_disjoint_tasks_dispatch_before_wait_and_convergence"], + "expected": "passed" + }, + { + "fixture_id": "SG-06", + "area": "subagent_ownership", + "title": "Dependent, overlapping, and same-workspace tasks remain serialized and make progress", + "oracle": "pytest", + "pytest_nodes": [ + "tests/test_wor108_subagent_ownership.py::test_sg06_dependent_or_overlapping_tasks_are_serialized", + "tests/test_wor108_subagent_ownership.py::test_sg06_same_execution_workspace_is_never_fanned_out", + "tests/test_wor108_subagent_ownership.py::test_sg06_serialized_tasks_progress_across_successive_waves" + ], + "expected": "passed" + }, + { + "fixture_id": "SG-07", + "area": "subagent_ownership", + "title": "Repair mutation remains subagent-owned", + "oracle": "pytest", + "pytest_nodes": [ + "tests/test_wor108_subagent_ownership.py::test_sg07_repair_retains_owner_binding_baseline_evidence_and_frontier", + "tests/test_wor108_subagent_ownership.py::test_sg07_repair_allows_only_an_authorized_replacement_owner" + ], + "expected": "passed" + }, + { + "fixture_id": "SG-08", + "area": "subagent_ownership", + "title": "Ownership provenance is provider-neutral and rejects visibility-specific fields", + "oracle": "pytest", + "pytest_nodes": [ + "tests/test_wor108_subagent_ownership.py::test_sg08_native_mechanisms_preserve_task_binding_handoff_and_validation_semantics", + "tests/test_wor108_subagent_ownership.py::test_visibility_specific_provenance_is_rejected" + ], + "expected": "passed" + }, + { + "fixture_id": "CTX-01", + "area": "context_projection", + "title": "Unrelated provenance growth does not inflate briefs or repair packages", + "oracle": "pytest", + "pytest_nodes": [ + "tests/test_wor108_context_projection.py::test_ctx_01_unrelated_runtime_history_does_not_inflate_unchanged_task_brief", + "tests/test_wor108_context_projection.py::test_ctx_01_repair_package_uses_frontier_without_reacquiring_review_history" + ], + "expected": "passed" + }, + { + "fixture_id": "CTX-02", + "area": "context_projection", + "title": "Semantic authority is retained once and referenced by stable ID elsewhere", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_context_projection.py::test_ctx_02_semantic_authority_is_retained_once_and_referenced_by_id"], + "expected": "passed" + }, + { + "fixture_id": "CTX-03", + "area": "context_projection", + "title": "Executor capability projection contains only allocated capabilities and reasons", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_context_projection.py::test_ctx_03_executor_capability_projection_contains_only_allocated_capabilities"], + "expected": "passed" + }, + { + "fixture_id": "CTX-04", + "area": "context_projection", + "title": "Successful evidence projects compact digest and freshness receipts without history or stdout", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_context_projection.py::test_ctx_04_success_evidence_projects_compact_receipt_not_history_or_stdout"], + "expected": "passed" + }, + { + "fixture_id": "CTX-05", + "area": "context_projection", + "title": "Failed evidence expands only for an allowed explicit reason", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_context_projection.py::test_ctx_05_failed_evidence_expands_only_with_allowed_reason"], + "expected": "passed" + }, + { + "fixture_id": "CTX-06", + "area": "context_projection", + "title": "Compiled context has telemetry, lazy histories, no retrieval escape hatch, and no arbitrary hard limit", + "oracle": "pytest", + "pytest_nodes": ["tests/test_wor108_context_projection.py::test_ctx_06_no_retrieval_escape_hatch_and_context_metrics_use_existing_telemetry"], + "expected": "passed" + } + ] +} diff --git a/evals/wor108/migration-impact.json b/evals/wor108/migration-impact.json new file mode 100644 index 0000000..e12b3a8 --- /dev/null +++ b/evals/wor108/migration-impact.json @@ -0,0 +1,73 @@ +{ + "contract": "wor108-migration-impact-v1", + "issue": "WOR-108", + "evaluation_id": "wor108-legacy-closure-v1", + "accepted_legacy_baseline": { + "commit": "9dce5df221485174d6179f713e8b179bbc20567a", + "tree": "5a1f38355eae8068bab528923e807ce54e6f6fe5" + }, + "changed_surfaces": { + "public_contracts": [ + "references/assets/orchestration/contract/handoff-executor-result-v1.md", + "references/assets/orchestration/contract/stage-review-v1.schema.json", + "references/assets/orchestration/contract/task-v1.md", + "references/assets/orchestration/workflow.md" + ], + "instructions": [ + "references/assets/template/AGENTS.md", + "references/assets/template/bootstrap.yaml", + "references/assets/template/project.yaml", + "rules/orchestration/orch-handoff-required.md", + "rules/work-bundle/wb-project-context-preflight.md", + "skills/orch-create-handoff/SKILL.md", + "skills/orch-doctor/SKILL.md", + "skills/orch-execute-plan/SKILL.md", + "skills/wb-initialize-project/SKILL.md" + ], + "operations": [ + "scripts/orchestration/dispatcher.py", + "scripts/orchestration/execution_context.py", + "scripts/orchestration/plans.py", + "scripts/orchestration/review_runtime.py", + "scripts/orchestration/task_ownership.py", + "scripts/work-bundle/control_plane.py", + "scripts/work-bundle/core.py", + "scripts/work-bundle/dispatcher.py", + "scripts/work-bundle/migration.py", + "scripts/work-bundle/project.py", + "scripts/work-bundle/reviewer_workspace.py", + "scripts/work-bundle/stage_events.py" + ], + "fixtures_and_evaluations": [ + "evals/wor105/components/native-transition-record.yaml", + "evals/wor105/freeze-manifest.json", + "evals/wor105/results.jsonl", + "evals/wor108/contracts-v1.schema.json", + "evals/wor108/fixtures.json", + "evals/wor108/migration-impact.json", + "evals/wor108/verify.py", + "references/evals/orchestration/evals.json", + "tests/test_control_plane_v4.py", + "tests/test_multi_repository_member.py", + "tests/test_orchestration_execution_context.py", + "tests/test_orchestration_skill_rule_boundary.py", + "tests/test_orchestration_workflow_contracts.py", + "tests/test_project_initialization.py", + "tests/test_registry_layout_migration.py", + "tests/test_wor108_closure.py", + "tests/test_wor108_context_projection.py", + "tests/test_wor108_review_frontier.py", + "tests/test_wor108_subagent_ownership.py" + ] + }, + "evaluation_identities": [ + "RF-01", "RF-02", "RF-03", "RF-04", "RF-05", "RF-06", "RF-07", "RF-08", + "SG-01", "SG-02", "SG-03", "SG-04", "SG-05", "SG-06", "SG-07", "SG-08", + "CTX-01", "CTX-02", "CTX-03", "CTX-04", "CTX-05", "CTX-06" + ], + "handoff_constraints": { + "final_git_identity": "post_acceptance_handoff_only", + "advance_wor107": false, + "touch_work_bundle_mcp": false + } +} diff --git a/evals/wor108/verify.py b/evals/wor108/verify.py new file mode 100644 index 0000000..38f98cb --- /dev/null +++ b/evals/wor108/verify.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +"""Verify the deterministic WOR-108 closure and migration-impact package.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import re +import subprocess +from typing import Any + + +EVAL_ROOT = Path(__file__).resolve().parent +REPO_ROOT = EVAL_ROOT.parents[1] +EXPECTED_IDS = tuple( + [f"RF-{number:02d}" for number in range(1, 9)] + + [f"SG-{number:02d}" for number in range(1, 9)] + + [f"CTX-{number:02d}" for number in range(1, 7)] +) +EXPECTED_AREAS = { + "RF": "repair_frontier", + "SG": "subagent_ownership", + "CTX": "context_projection", +} +FIXTURE_KEYS = {"fixture_id", "area", "title", "oracle", "pytest_nodes", "expected"} +NODE = re.compile(r"^(tests/test_wor108_[a-z_]+\.py)::(test_[a-z0-9_]+)$") +MIGRATION_KEYS = { + "contract", "issue", "evaluation_id", "accepted_legacy_baseline", + "changed_surfaces", "evaluation_identities", "handoff_constraints", +} +SURFACE_KEYS = {"public_contracts", "instructions", "operations", "fixtures_and_evaluations"} + + +class VerificationError(RuntimeError): + pass + + +def _load(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise VerificationError(f"object required: {path}") + return value + + +def _verify_schema(path: Path) -> None: + schema = _load(path) + if schema.get("$id") != "urn:work-bundle:wor108:closure-contracts:v1": + raise VerificationError("closure schema identity mismatch") + if schema.get("additionalProperties") is not False: + raise VerificationError("closure schema must be closed") + fixture = schema.get("$defs", {}).get("fixture", {}) + fixture_id = fixture.get("properties", {}).get("fixture_id", {}) + if ( + fixture.get("type") != "object" + or fixture.get("additionalProperties") is not False + or set(fixture.get("required", [])) != FIXTURE_KEYS + or fixture_id.get("type") != "string" + ): + raise VerificationError("fixture schema is not a closed required shape") + + +def _verify_fixtures(path: Path) -> tuple[str, ...]: + package = _load(path) + if set(package) != {"contract", "evaluation_id", "fixtures"}: + raise VerificationError("fixture package closed shape mismatch") + if package["contract"] != "wor108-closure-fixtures-v1" or package["evaluation_id"] != "wor108-legacy-closure-v1": + raise VerificationError("fixture package identity mismatch") + fixtures = package["fixtures"] + if not isinstance(fixtures, list): + raise VerificationError("fixtures must be an array") + fixture_ids = tuple(item.get("fixture_id") for item in fixtures if isinstance(item, dict)) + if fixture_ids != EXPECTED_IDS: + raise VerificationError("fixture IDs must be exactly RF-01..08, SG-01..08, CTX-01..06 in order") + seen_nodes: set[str] = set() + for item in fixtures: + if set(item) != FIXTURE_KEYS: + raise VerificationError(f"fixture closed shape mismatch: {item.get('fixture_id')}") + fixture_id = item["fixture_id"] + if item["area"] != EXPECTED_AREAS[fixture_id.split("-", 1)[0]]: + raise VerificationError(f"fixture area mismatch: {fixture_id}") + if item["oracle"] != "pytest" or item["expected"] != "passed" or not item["title"]: + raise VerificationError(f"fixture oracle mismatch: {fixture_id}") + nodes = item["pytest_nodes"] + if not isinstance(nodes, list) or not nodes or len(nodes) != len(set(nodes)): + raise VerificationError(f"fixture pytest node set mismatch: {fixture_id}") + for node in nodes: + match = NODE.fullmatch(node) + if match is None: + raise VerificationError(f"invalid pytest node: {node}") + test_path = REPO_ROOT / match.group(1) + if not test_path.is_file() or f"def {match.group(2)}(" not in test_path.read_text(encoding="utf-8"): + raise VerificationError(f"pytest oracle does not resolve: {node}") + if node in seen_nodes: + raise VerificationError(f"pytest oracle assigned to multiple identities: {node}") + seen_nodes.add(node) + return fixture_ids + + +def _verify_migration(path: Path, fixture_ids: tuple[str, ...]) -> int: + manifest = _load(path) + if set(manifest) != MIGRATION_KEYS: + raise VerificationError("migration-impact closed shape mismatch") + if (manifest["contract"], manifest["issue"], manifest["evaluation_id"]) != ( + "wor108-migration-impact-v1", "WOR-108", "wor108-legacy-closure-v1" + ): + raise VerificationError("migration-impact identity mismatch") + if manifest["accepted_legacy_baseline"] != { + "commit": "9dce5df221485174d6179f713e8b179bbc20567a", + "tree": "5a1f38355eae8068bab528923e807ce54e6f6fe5", + }: + raise VerificationError("accepted WOR-105 baseline mismatch") + surfaces = manifest["changed_surfaces"] + if not isinstance(surfaces, dict) or set(surfaces) != SURFACE_KEYS: + raise VerificationError("changed surface classes mismatch") + flattened = [surface for group in surfaces.values() for surface in group] + if len(flattened) != len(set(flattened)) or any(not (REPO_ROOT / surface).is_file() for surface in flattened): + raise VerificationError("changed surfaces must be unique existing files") + baseline = manifest["accepted_legacy_baseline"]["commit"] + changed = subprocess.run( + ["git", "-C", str(REPO_ROOT), "diff", "--name-only", baseline, "HEAD", "--"], + check=False, + capture_output=True, + text=True, + ) + if changed.returncode != 0: + raise VerificationError("accepted baseline delta is unavailable") + if set(flattened) != set(changed.stdout.splitlines()): + raise VerificationError("changed surface completeness mismatch") + if tuple(manifest["evaluation_identities"]) != fixture_ids: + raise VerificationError("migration evaluation identities mismatch") + if manifest["handoff_constraints"] != { + "final_git_identity": "post_acceptance_handoff_only", + "advance_wor107": False, + "touch_work_bundle_mcp": False, + }: + raise VerificationError("migration handoff constraints mismatch") + forbidden_identity_fields = {"accepted_head", "accepted_tree", "final_commit", "final_tree"} + if forbidden_identity_fields.intersection(manifest): + raise VerificationError("self-referential final Git identity is forbidden") + return len(flattened) + + +def verify( + fixtures_path: Path = EVAL_ROOT / "fixtures.json", + migration_path: Path = EVAL_ROOT / "migration-impact.json", + schema_path: Path = EVAL_ROOT / "contracts-v1.schema.json", +) -> dict[str, Any]: + _verify_schema(schema_path) + fixture_ids = _verify_fixtures(fixtures_path) + surfaces = _verify_migration(migration_path, fixture_ids) + return { + "evaluation_id": "wor108-legacy-closure-v1", + "fixtures": len(fixture_ids), + "changed_surfaces": surfaces, + "verdict": "accepted", + } + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--fixtures", type=Path, default=EVAL_ROOT / "fixtures.json") + parser.add_argument("--migration-impact", type=Path, default=EVAL_ROOT / "migration-impact.json") + parser.add_argument("--schema", type=Path, default=EVAL_ROOT / "contracts-v1.schema.json") + args = parser.parse_args() + print(json.dumps(verify(args.fixtures, args.migration_impact, args.schema), sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/references/assets/orchestration/contract/handoff-executor-result-v1.md b/references/assets/orchestration/contract/handoff-executor-result-v1.md index 772e959..1d00189 100644 --- a/references/assets/orchestration/contract/handoff-executor-result-v1.md +++ b/references/assets/orchestration/contract/handoff-executor-result-v1.md @@ -142,12 +142,11 @@ codegraph: reason: null | no-index | sync-failed | not-source-code | blocked delegation_evidence: - delegated: true | false - surface: visible-thread | visible-worktree | visible-thread-and-worktree | single-agent-fallback | blocked - visible_reference: null - internal_spawn_used_for_task_delegation: false - internal_workers_used_for_support: false - fallback_reason: null + delegated: true + owner_kind: subagent + agent_id: agent-or-provider-identity + run_id: task-run-identity + mechanism: host-native | execution-flow allocation_evidence: allocated_rules: @@ -178,7 +177,7 @@ allocation_evidence: - `repository` is required when repository preflight, accepted baseline, changed paths, or blocker state matters for continuation. - `repository[].metadata` is required when project metadata baseline was used for target resolution, branch checks, commit checks, or CodeGraph policy decisions. - `codegraph` is required when source-code inspection or edits were in scope. Keep it compact: `root`, `applicable`, `up_to_date`, and required fallback or blocker facts are enough unless a failure needs detail. -- `delegation_evidence` is required when task, phase, or plan ownership was delegated or when the execution path needs proof that invisible internal spawn did not own delegation. +- `delegation_evidence` is required for every task executor-result and is optional for non-task scopes. Its five-field closed shape proves mandatory subagent ownership without UI-specific semantics. - `allocation_evidence` is required when allocated_rules or allocated_skills materially shaped execution or when an allocated rule/skill was unavailable, skipped, stale, or inapplicable. ## Forbidden Executor-Result Fields @@ -206,7 +205,7 @@ Compact handoffs must not weaken safety gates: - Repository evidence must preserve root, target kind, preflight kind, baseline, and clean or blocked result when applicable. - Metadata evidence must preserve repository id, expected and actual branch, expected and actual commit, branch status, commit status, and baseline status when project metadata preflight applies. - CodeGraph evidence must preserve no-index fallback, sync-failed, stale, or blocker facts when applicable. -- Delegation evidence must preserve visible surface, visible reference when available, and `internal_spawn_used_for_task_delegation: false`. +- Delegation evidence must preserve delegated state, `owner_kind: subagent`, minimum agent/run identity, and `host-native|execution-flow` mechanism. UI, visibility, fallback, controller-owner, and internal-worker fields are invalid. - Validation evidence must list exact commands or inspections and their result. Executor-authored `result`, `exit_code`, or an equivalently named receipt block is corroboration, not independent proof and not authority for `Completed`. Direct helper observation in the bound worktree is the terminal evidence. - Task-fit evidence must prove the executor followed the compiled brief and assigned task. Full specification, root-plan, and phase inspection is an escalation path when compiled context is inconsistent. - Acceptance-review evidence must identify review independence, the reviewed tree, verdict, and blocking or advisory findings. diff --git a/references/assets/orchestration/contract/stage-review-v1.schema.json b/references/assets/orchestration/contract/stage-review-v1.schema.json index d7d8155..d7139c2 100644 --- a/references/assets/orchestration/contract/stage-review-v1.schema.json +++ b/references/assets/orchestration/contract/stage-review-v1.schema.json @@ -7,6 +7,8 @@ "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, "gitOid": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, "time": {"type": "string", "format": "date-time", "pattern": "Z$"}, + "reviewMode": {"enum": ["initial", "repair"]}, + "reviewTargetKind": {"enum": ["task", "stage"]}, "targetIdentity": { "type": "object", "additionalProperties": false, @@ -47,8 +49,40 @@ "disposition": {"enum": ["reopen_specification", "repair_plan", "reslice_plan", "repair_task", "repair_oracle", "recover_environment", "record_advisory", "accepted", "rejected"]} } }, + "repairFrontier": { + "type": "object", + "additionalProperties": false, + "required": ["prior_review_id", "blocking_finding_ids", "previous_reviewed_identity", "repaired_identity", "affected_boundaries", "frozen_evidence_reference"], + "properties": { + "prior_review_id": {"$ref": "#/$defs/id"}, + "blocking_finding_ids": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"$ref": "#/$defs/id"}}, + "previous_reviewed_identity": {"$ref": "#/$defs/targetIdentity"}, + "repaired_identity": {"$ref": "#/$defs/targetIdentity"}, + "affected_boundaries": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "minLength": 1}}, + "frozen_evidence_reference": {"$ref": "#/$defs/sha256"} + } + }, + "reviewReset": { + "type": "object", + "additionalProperties": false, + "required": ["prior_review_id", "reason_class", "reason"], + "properties": { + "prior_review_id": {"$ref": "#/$defs/id"}, + "reason_class": {"enum": ["material_redesign", "authority", "scope", "acceptance", "decomposition", "validation_allocation"]}, + "reason": {"type": "string", "minLength": 1} + } + }, "stageReview": { "allOf": [ + { + "if": {"required": ["review_mode"], "properties": {"review_mode": {"const": "repair"}}}, + "then": {"required": ["repair_frontier"], "properties": {"repair_frontier": {"$ref": "#/$defs/repairFrontier"}, "review_reset": {"type": "null"}}}, + "else": {"properties": {"repair_frontier": {"type": "null"}}} + }, + { + "if": {"required": ["review_target_kind"]}, + "then": {"properties": {"review_target_kind": {"const": "stage"}}} + }, { "if": {"properties": {"verdict": {"const": "accepted"}}}, "then": { @@ -81,6 +115,10 @@ "required": ["review_id", "stage", "target_identity", "reviewer", "evidence", "verdict", "findings", "started_at", "completed_at", "staleness"], "properties": { "review_id": {"$ref": "#/$defs/id"}, + "review_mode": {"$ref": "#/$defs/reviewMode"}, + "review_target_kind": {"$ref": "#/$defs/reviewTargetKind"}, + "repair_frontier": {"anyOf": [{"$ref": "#/$defs/repairFrontier"}, {"type": "null"}]}, + "review_reset": {"anyOf": [{"$ref": "#/$defs/reviewReset"}, {"type": "null"}]}, "reviewer_run": { "type": "object", "additionalProperties": false, "required": ["run_id", "sha256"], "properties": {"run_id": {"$ref": "#/$defs/id"}, "sha256": {"$ref": "#/$defs/sha256"}} diff --git a/references/assets/orchestration/contract/task-v1.md b/references/assets/orchestration/contract/task-v1.md index 06e17ba..19e5d37 100644 --- a/references/assets/orchestration/contract/task-v1.md +++ b/references/assets/orchestration/contract/task-v1.md @@ -146,6 +146,10 @@ A legacy 3-column `Command or inspection | Proves | Expected` row without YAML ` - Shared completion validation has passed: task/plan identity, executor-result shape, fresh required validation, `knowledge_disposition`, and unresolved/blocker state. - When `acceptance_review.required` is false or omitted, `Completed` does not require an independent reviewer or `accept`. - When `acceptance_review.required` is true, `acceptance_review.verdict` is `accept`. +- A newly authored task acceptance record exposes `review_mode: initial|repair` and `review_target_kind: task`. Legacy records without these fields are tolerated only as initial-review migration input. +- When `task_fit_check.result` is `repaired`, completion requires `review_mode: repair`, the native review envelope fields, and exactly one `previous_review`. The closed `repair_frontier` binds that predecessor's review ID, blocking finding IDs, previous and repaired target identities, affected boundaries, and frozen evidence identity. Whole review history is not embedded or reacquired. +- Both the current and previous task-review records retain `required: true`, `reviewer_independent: true`, and `review_target_kind: task`; the adapter does not infer or overwrite those ownership facts. The accepted repaired target names the completed task and its `source_tree` plus `reviewed_head` must equal the helper-observed Git tree and head. +- Material redesign or changed authority, scope, acceptance, decomposition, or validation allocation uses a fresh `initial` review with `review_reset`; it may not reuse the repair reviewer identity. ## Planning verification diff --git a/references/assets/orchestration/workflow.md b/references/assets/orchestration/workflow.md index 5cee2d0..82df93d 100644 --- a/references/assets/orchestration/workflow.md +++ b/references/assets/orchestration/workflow.md @@ -178,7 +178,9 @@ their ordering priority. This does not introduce an initial-versus-repair fronti scheduler selects executable task -> compile task brief -> choose provider-neutral capability - -> implement with declared methodology + -> bind mandatory subagent owner or fail closed before mutation + -> dispatch every planner-approved disjoint ready task before waiting + -> subagent implements with declared methodology -> run fresh task-local validation -> write executor-result handoff -> validate-executor-result @@ -189,11 +191,11 @@ scheduler selects executable task -> Completed ``` -Executors own implementation, task-local verification, and executor-result evidence, including a task-local knowledge disposition of `none`, `update`, `supersede`, or `reclassify`. They never invoke persistence or read knowledge. Reviewers own acceptance judgment for the accepted Truth Basis, requirement fit, correctness, edge cases, test oracle, disposition, unnecessary complexity, allocated obligations, and validation sufficiency. Schedulers own dependencies, barriers, context compilation, delegation, and evidence shape; they do not perform code-quality review. +Subagent executors own every implementation and repair mutation, task-local verification, and executor-result evidence, including a task-local knowledge disposition of `none`, `update`, `supersede`, or `reclassify`. They never invoke persistence or read knowledge. Reviewers own acceptance judgment for the accepted Truth Basis, requirement fit, correctness, edge cases, test oracle, disposition, unnecessary complexity, allocated obligations, and validation sufficiency. Schedulers own dependencies, barriers, context compilation, neutral subagent binding, validation routing, and evidence shape; they do not perform code-quality review or mutate task write scope. -Visible multi-agent subagents are preferred only when user/environment policy allows and write scopes are disjoint. Invisible helper workers may support bounded analysis but never own delegated task implementation. When independent subagents are unavailable, record `reviewer_independent: false` and perform an explicit reduced-independence second pass. +Selecting `orch-execute-plan` requires a subagent owner for every task without a separate user opt-in. The production `TaskOwnershipScheduler` admission entry consumes either a host-native or Execution-Flow adapter; evidence records only the minimum agent/run identity and mechanism. If none is available, execution fails closed before task mutation. Independent disjoint tasks in distinct execution workspaces dispatch before any wait; dependent, overlapping, or same-workspace tasks serialize. Acceptance uses the same scheduler entry to reject controller mutation, and repair dispatch uses `operation: repair` through the same adapter path. -On `repair`, return blocking findings with the same brief and current diff, make the smallest repair, rerun claim-relevant validation, regenerate the package from the original base, and review again. After two failed low-cost repair rounds, escalate the capability tier; if evidence indicates a plan or specification defect, stop the retry loop and route the typed blocker. +On `repair`, return blocking findings with the same brief and current diff to the task-owning subagent. It makes the smallest repair, reruns claim-relevant validation, regenerates the package from the original base, and reviews again. If no subagent is available, fail closed before repair mutation. After two failed low-cost repair rounds, escalate the capability tier; if evidence indicates a plan or specification defect, stop the retry loop and route the typed blocker. A task becomes `Completed` only when implementation criteria, fresh validation, a valid executor-result handoff, and a passing `validate-executor-result` check all exist. `Completed` does not require `verdict: accept` unless review was required. Phase and plan status derive from accepted children plus declared dependency and barrier gates. diff --git a/references/assets/template/AGENTS.md b/references/assets/template/AGENTS.md index 395e2d0..c893f1d 100644 --- a/references/assets/template/AGENTS.md +++ b/references/assets/template/AGENTS.md @@ -23,10 +23,8 @@ must: - resolve `work_bundle_root` from `$work_bundle_config_root/bootstrap.yaml` -> `work_bundle_root` - resolve project registry from `$work_bundle_config_root/bootstrap.yaml` -> `project_registry` - resolve skill registry from `$work_bundle_config_root/bootstrap.yaml` -> `skill_registry` -- resolve effective `prefer_subagent` as `.work-bundle/project.yaml` -> `prefer_subagent`, then `$work_bundle_config_root/bootstrap.yaml` -> `prefer_subagent`, then `false` - before material implementation, establish or consume one Truth Basis containing purpose, as-is evidence, accepted decision authority, expected delta, and conflict status; after preflight and source grounding, lightweight planning runs one bounded `ks-what-is-helpful` gateway and records accepted authority or evidence-backed `none relevant`, while heavy execution compiles carried authority without executor retrieval - after each meaningful validated move, record a knowledge disposition of `none`, `update`, `supersede`, or `reclassify`; the lightweight completion owner resolves its approved `ks-*` follow-up, while heavy executors return task-local evidence only and final orchestration review owns heavy-path persistence follow-up -- treat `prefer_subagent` as permission to prefer sub-agent scheduling only when normal execution safety, write-scope, dependency, and fallback checks pass - use `work_bundle_root` only for toolkit assets, builtin skills, builtin rules, and references - use `work_bundle_config_root` only for non-project runtime state produced by tool use - resolve workspace-owned metadata, rules, knowledge, orchestration, `AGENTS.md`, `script/index.yaml`, and `credentials/credentials.yaml` from `workspace_root` in both workspace modes @@ -47,7 +45,6 @@ must_not: - treat utility discovery as permission to execute a script - inspect or transfer credential values through chat, prompts, subagent messages, tool arguments/results, terminal output, logs, handoffs, knowledge, or orchestration artifacts - infer registry paths without reading `bootstrap.yaml` when registry access is required -- let `prefer_subagent` bypass repository preflight, sub-agent capability checks, disjoint write-scope checks, dependency checks, or single-agent fallback - treat rule-store scope (`toolkit`, `global`, `project`) as separate from rule area directories such as `work-bundle`, `keep-summarizing`, and `orchestration` ## Rule Loading diff --git a/references/assets/template/bootstrap.yaml b/references/assets/template/bootstrap.yaml index 608d70d..0899d2b 100644 --- a/references/assets/template/bootstrap.yaml +++ b/references/assets/template/bootstrap.yaml @@ -3,4 +3,3 @@ authority: canonical work_bundle_root: __WORK_BUNDLE_ROOT__ project_registry: "$work_bundle_config_root/registry/projects.yaml" skill_registry: "$work_bundle_config_root/registry/skill-registry.yaml" -prefer_subagent: false diff --git a/references/assets/template/project.yaml b/references/assets/template/project.yaml index 21a6cf3..9127b2c 100644 --- a/references/assets/template/project.yaml +++ b/references/assets/template/project.yaml @@ -4,7 +4,6 @@ workspace_root: workspace_mode: project_root: industry: -prefer_subagent: false metadata_compatibility: readable_versions: [2, 3] diff --git a/references/evals/orchestration/evals.json b/references/evals/orchestration/evals.json index 883662e..bd1d30f 100644 --- a/references/evals/orchestration/evals.json +++ b/references/evals/orchestration/evals.json @@ -22,7 +22,7 @@ { "id": 4, "prompt": "Execute the next task in an existing plan.", - "expected_output": "Selects execute-plan, checks sub-agent support, uses the single-agent fallback when sub-agents are unavailable, executes one task, updates task and phase task status, reports concise execution result, and requires an executor handoff.", + "expected_output": "Selects execute-plan and invokes the production TaskOwnershipScheduler without requiring a separate user delegation request; it binds a subagent owner before task mutation and otherwise fails closed with workspace-blocked without changing task scope.", "files": [] }, { @@ -34,7 +34,7 @@ { "id": 6, "prompt": "Execute a phase whose next two tasks are independent and have disjoint write scopes, in an environment with sub-agents.", - "expected_output": "Selects execute-plan, uses the sub-agent scheduler path, delegates the independent tasks to multiple sub-agents, waits for completion, validates executor handoffs against task/phase/plan/spec, updates task statuses, and continues scheduling.", + "expected_output": "Selects execute-plan, passes planner-approved candidates to the production TaskOwnershipScheduler, binds independent tasks to separate subagents and isolated execution workspaces, dispatches both before awaiting either, validates executor handoffs against task/phase/plan/spec, updates task statuses, and continues scheduling.", "files": [] }, { @@ -64,7 +64,7 @@ { "id": 11, "prompt": "Doctor the orchestrator skill for installation health, malformed orch skills, and workflow bias.", - "expected_output": "Selects orch-doctor, uses dev-rules-doctor first, runs the develop-rules doctor command, audits orch skill files, workflow, evals, helper commands, and cross-skill v3 gateway wiring read-only, checks that orch-execute-plan preserves both sub-agent scheduler and single-agent fallback paths, verifies archival belongs to orch-review-plan, and reports concrete repairs without changing files.", + "expected_output": "Selects orch-doctor, uses dev-rules-doctor first, runs the develop-rules doctor command, audits orch skill files, workflow, evals, helper commands, and cross-skill v3 gateway wiring read-only, checks mandatory subagent ownership and fail-closed behavior, verifies archival belongs to orch-review-plan, and reports concrete repairs without changing files.", "files": [] }, { @@ -130,13 +130,13 @@ { "id": 22, "prompt": "Execute a plan task with a delegated sub-agent, then prepare its executor-result handoff after implementation reveals a task-scoped gap.", - "expected_output": "Preserves preflight, accepted baseline, dependencies, and write scopes; compiles a self-contained task brief; repairs the task-local gap, reruns fresh validation, writes a sparse handoff, compiles a bounded review package, and requires an accept review before completion.", + "expected_output": "Preserves preflight, accepted baseline, dependencies, and write scopes; routes operation repair through the production TaskOwnershipScheduler to a subagent, reruns fresh validation, writes a sparse handoff with neutral ownership evidence, compiles a bounded review package, and requires an accept review before completion.", "files": [] }, { "id": 23, - "prompt": "Execute a source-inspection task when the target repository has no .codegraph directory and effective prefer_subagent resolves to false.", - "expected_output": "Selects execute-plan, records that CodeGraph is skipped because the repository is not indexed, preserves text-search fallback, uses the single-agent fallback for exactly one task, keeps execution no-retrieval, does not require any additional runtime preference file, and does not bypass repository preflight, dependency checks, write-scope checks, or executor-result handoff requirements.", + "prompt": "Execute a source-inspection task when the target repository has no .codegraph directory and legacy metadata contains prefer_subagent: false.", + "expected_output": "Selects execute-plan, records that CodeGraph is skipped because the repository is not indexed, preserves text-search fallback, ignores the legacy preference field, requires subagent ownership, keeps execution no-retrieval, and does not bypass repository preflight, dependency checks, write-scope checks, or executor-result handoff requirements.", "files": [] }, { @@ -171,20 +171,20 @@ }, { "id": 29, - "prompt": "Execute a scheduler wave with visible multi-agent subagents available and safe disjoint task scopes.", - "expected_output": "Delegates task ownership only to visible multi-agent subagents, records compact delegation_evidence with each visible_reference when provided, sets internal_spawn_used_for_task_delegation to false, validates sparse executor-result handoffs, rejects cross-conversation delegation as a task ownership vehicle, and preserves preflight and scope gates.", + "prompt": "Execute a scheduler wave with host-native and Execution-Flow-routed subagents available and safe disjoint task scopes.", + "expected_output": "Treats both subagent mechanisms equivalently, records only delegated state, subagent owner kind, agent/run identity, and mechanism in compact delegation_evidence, dispatches disjoint tasks before waiting, and preserves preflight and scope gates.", "files": [] }, { "id": 30, - "prompt": "Execute a plan task when only invisible internal spawn workers are available for delegation.", - "expected_output": "Rejects invisible internal spawn workers as the task delegation vehicle, uses single-agent fallback for one task when valid or reports a delegation-visibility blocker, and does not mark delegated execution complete without compact visible delegation_evidence.", + "prompt": "Execute a plan task when no supported subagent execution mechanism is available.", + "expected_output": "The production TaskOwnershipScheduler checks adapter availability and fails closed with workspace-blocked before dispatch or task mutation; it does not substitute controller or single-agent implementation.", "files": [] }, { "id": 31, - "prompt": "Execute a delegated task while an internal helper worker is used only for bounded snippet comparison.", - "expected_output": "Allows the helper-worker support because visible multi-agent subagent ownership remains intact, records internal helper usage in compact delegation_evidence, keeps internal_spawn_used_for_task_delegation false, and requires the visible executor handoff to own validation and task_fit_check evidence.", + "prompt": "The orchestration controller directly patches a task-owned file and all task validations pass.", + "expected_output": "TaskOwnershipScheduler.validate_acceptance rejects task acceptance with review-blocked because controller mutation violates mandatory subagent ownership; green validation cannot override ownership provenance.", "files": [] }, { diff --git a/rules/orchestration/orch-handoff-required.md b/rules/orchestration/orch-handoff-required.md index 4e723b6..2330a08 100644 --- a/rules/orchestration/orch-handoff-required.md +++ b/rules/orchestration/orch-handoff-required.md @@ -28,7 +28,7 @@ Require compact executor-result handoffs before reporting execution complete or - For executor-result handoffs, preserve execution safety evidence where applicable: repository preflight or accepted-baseline evidence, validation evidence, drift/gap verification, unresolved blockers, and changed-path evidence. - For executor-result handoffs, include compact CodeGraph evidence when source-code inspection or edits were in scope. The evidence must be no larger than `root`, `applicable`, `up_to_date`, and required fallback or blocker facts unless a failure needs more detail. - For executor-result handoffs, explicitly record no-index fallback when a target repository lacks `.codegraph/`; do not omit CodeGraph evidence silently when source-code work was in scope. -- Use `delegation_evidence` only as proof of task ownership delegation when applicable. It records delegated flag, visible surface, `visible_reference` when available, `internal_spawn_used_for_task_delegation: false`, internal helper-worker usage if any, and fallback or blocker reason when visible delegation was unavailable or unsafe. +- Use `delegation_evidence` as neutral proof of mandatory task ownership. It records only delegated state, `owner_kind: subagent`, minimum agent/run identity, and provider-neutral `host-native|execution-flow` mechanism. - For contract-decoupled task handoffs, include compact `contract_decoupling` evidence: common contract group, common contracts checked, validation scope, `peer_implementation_validation_used: false`, and forbidden peer validation result. - For barrier participants, include compact `barrier` evidence with barrier id, participant role, readiness `reached|blocked`, and whether convergence remains pending. - For convergence owners, include compact `barrier` and `convergence` evidence showing every participant completed or blocked with executor-result handoffs before joint validation began. @@ -49,10 +49,10 @@ Require compact executor-result handoffs before reporting execution complete or - Omit changed files, validation evidence, unresolved blockers, or `task_fit_check` when they are applicable to the completed or partial result. - Claim a clean result without recording the compiled brief and assigned task checked, repairs made, and recheck outcome. - Omit applicable compact CodeGraph fallback, up-to-date, or blocker evidence from executor-result handoffs for source-code work. -- Omit visible `delegation_evidence` from executor-result handoffs when plan, phase, or task ownership was delegated, or record contradictory delegation evidence such as `internal_spawn_used_for_task_delegation: true`. +- Omit neutral `delegation_evidence` from a task executor-result handoff, record a non-subagent owner, or add UI, visibility, fallback, or internal-worker fields to ownership provenance. - Omit contract-only validation evidence from a contract-decoupled task handoff, or report peer implementation validation as used before barrier release. - Omit barrier readiness evidence from a barrier participant handoff, or schedule convergence without participant completed/blocked handoffs. -- Skip handoff creation because sub-agents, fallback mode, or partial completion made the outcome informal. +- Skip handoff creation because subagent execution blocked or partial completion made the outcome informal. - Create new active `handoff-orch-*` artifacts as continuation output. ## Validation @@ -63,7 +63,7 @@ Require compact executor-result handoffs before reporting execution complete or - Confirm executor-result handoffs created during execution did not invoke knowledge retrieval. - Confirm executor-result handoffs identify the compiled brief and assigned task checked; include findings, repairs, and final recheck evidence; and escalate to full source artifacts when compiled context is inconsistent. - Confirm executor-result handoffs include applicable compact CodeGraph evidence for every source-code target: root, applicability, `up_to_date`, and no-index, sync-failed, stale, or blocker facts when used. -- Confirm executor-result handoffs include `delegation_evidence` when delegation was used, including `visible_reference` when available and `internal_spawn_used_for_task_delegation: false`. +- Confirm task executor-result handoffs include closed neutral `delegation_evidence` with delegated state, subagent owner kind, agent/run identity, and provider-neutral mechanism. - Confirm contract-decoupled task handoffs include common-contract validation scope and `peer_implementation_validation_used: false`. - Confirm barrier participant and convergence-owner handoffs include readiness or release evidence by applicability. - Confirm the handoff index entry reflects the new or updated handoff. diff --git a/rules/work-bundle/wb-project-context-preflight.md b/rules/work-bundle/wb-project-context-preflight.md index 5c9db3a..8c7e214 100644 --- a/rules/work-bundle/wb-project-context-preflight.md +++ b/rules/work-bundle/wb-project-context-preflight.md @@ -50,7 +50,7 @@ Require agents to resolve the containing `workspace_root`, portable topology, an - Do not apply the metadata-v3 project-local authority model to metadata v4. - Do not write project registry state under `work_bundle_root` or `project_root`. - Do not load durable knowledge files solely to satisfy project-structure awareness. -- Do not let `prefer_subagent` bypass project context preflight, branch baseline checks, dependency checks, write-scope checks, validation, handoff, or single-agent fallback rules. +- Do not let legacy scheduling-preference metadata affect mandatory subagent ownership, project context preflight, branch baseline checks, dependency checks, write-scope checks, validation, or handoff rules. - Do not run destructive Git operations such as cleanup, reset, stash, or force push to satisfy preflight. - Do not initialize CodeGraph for a repository root that lacks `.codegraph/`. - Do not infer lifecycle Git stage or commit authority from initialization, doctor, repair, migration, or validation authority. diff --git a/scripts/orchestration/dispatcher.py b/scripts/orchestration/dispatcher.py index 7742edf..14c200c 100644 --- a/scripts/orchestration/dispatcher.py +++ b/scripts/orchestration/dispatcher.py @@ -2,11 +2,19 @@ from __future__ import annotations import argparse +import json from core import HANDOFF_TYPES from doctor import cmd_doctor from documents import cmd_git_status, cmd_next_action_candidates, cmd_related, cmd_state, cmd_write_doc -from execution_context import cmd_build_review_package, cmd_build_task_brief, cmd_validate_executor_result, cmd_observe_task_validation +from execution_context import ( + cmd_build_review_package, + cmd_build_task_brief, + cmd_adopt_existing_recovered_result, + cmd_create_accepted_base_absence_receipt, + cmd_observe_task_validation, + cmd_validate_executor_result, +) from handoffs import cmd_index_handoffs, cmd_list_handoffs, cmd_set_handoff_status, cmd_write_handoff from init import cmd_init from plans import cmd_archive_plan, cmd_index_plans, cmd_list_plans, cmd_set_plan_status, cmd_write_phase, cmd_write_plan, cmd_write_task @@ -16,13 +24,32 @@ RECOGNIZED_COMMANDS = frozenset({ "init", "doctor", "state", "next-action-candidates", "git-status", "repository-preflight", "build-task-brief", "build-review-package", - "validate-executor-result", "observe-task-validation", "related", "write-doc", "write-spec", + "validate-executor-result", "observe-task-validation", "create-accepted-base-absence-receipt", + "adopt-existing-recovered-result", + "related", "write-doc", "write-spec", "list-specs", "set-spec-status", "index-specs", "write-plan", "list-plans", "set-plan-status", "archive-plan", "index-plans", "write-phase", "write-task", "write-handoff", "list-handoffs", "set-handoff-status", "index-handoffs", }) +def _runtime_json(value: str) -> object: + try: + return json.loads(value) + except json.JSONDecodeError as error: + raise argparse.ArgumentTypeError(f"invalid controller runtime JSON: {error.msg}") from error + + +def _add_acceptance_runtime_inputs(parser: argparse.ArgumentParser) -> None: + """Expose harness observations without adding them to durable executor results.""" + + parser.add_argument("--mutation-events", type=_runtime_json) + parser.add_argument("--accepted-dependency-deltas", type=_runtime_json) + parser.add_argument("--prior-ownership", type=_runtime_json) + parser.add_argument("--repair-continuity", type=_runtime_json) + parser.add_argument("--authorized-replacements", type=_runtime_json) + + def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser() parser.add_argument("--project-root") @@ -55,14 +82,43 @@ def build_parser() -> argparse.ArgumentParser: review_package.add_argument("--handoff", required=True) review_package.add_argument("--base", required=True) review_package.add_argument("--head", required=True) + _add_acceptance_runtime_inputs(review_package) review_package.set_defaults(func=cmd_build_review_package) validate_result = sub.add_parser("validate-executor-result", parents=[parent]) validate_result.add_argument("--task", required=True) validate_result.add_argument("--handoff", required=True) + _add_acceptance_runtime_inputs(validate_result) validate_result.set_defaults(func=cmd_validate_executor_result) observe_validation = sub.add_parser("observe-task-validation", parents=[parent]) observe_validation.add_argument("--task", required=True) observe_validation.set_defaults(func=cmd_observe_task_validation) + create_recovery_receipt = sub.add_parser( + "create-accepted-base-absence-receipt", parents=[parent] + ) + create_recovery_receipt.add_argument("--plan-id", required=True) + create_recovery_receipt.add_argument("--task-id", required=True) + create_recovery_receipt.add_argument("--expected-head", required=True) + create_recovery_receipt.add_argument("--expected-tree", required=True) + create_recovery_receipt.add_argument("--proposed-handoff-id", required=True) + create_recovery_receipt.add_argument("--proposed-review-id", required=True) + create_recovery_receipt.add_argument("--final-head", required=True) + create_recovery_receipt.add_argument("--final-tree", required=True) + create_recovery_receipt.set_defaults(func=cmd_create_accepted_base_absence_receipt) + adopt_recovered_result = sub.add_parser( + "adopt-existing-recovered-result", parents=[parent] + ) + adopt_recovered_result.add_argument("--plan-id", required=True) + adopt_recovered_result.add_argument("--task-id", required=True) + adopt_recovered_result.add_argument("--expected-head", required=True) + adopt_recovered_result.add_argument("--expected-tree", required=True) + adopt_recovered_result.add_argument("--handoff-id", required=True) + adopt_recovered_result.add_argument("--handoff-sha256", required=True) + adopt_recovered_result.add_argument("--review-id", required=True) + adopt_recovered_result.add_argument("--final-head", required=True) + adopt_recovered_result.add_argument("--final-tree", required=True) + adopt_recovered_result.add_argument("--prior-receipt-id", required=True) + adopt_recovered_result.add_argument("--prior-receipt-sha256", required=True) + adopt_recovered_result.set_defaults(func=cmd_adopt_existing_recovered_result) related = sub.add_parser("related", parents=[parent]) related.add_argument("--id", required=True) related.set_defaults(func=cmd_related) @@ -108,9 +164,11 @@ def build_parser() -> argparse.ArgumentParser: set_plan.add_argument("--kind", choices=["plan", "phase", "task"]) set_plan.add_argument("--plan-id") set_plan.add_argument("--handoff") + _add_acceptance_runtime_inputs(set_plan) set_plan.set_defaults(func=cmd_set_plan_status) archive_plan = sub.add_parser("archive-plan", parents=[parent]) archive_plan.add_argument("--id", required=True) + _add_acceptance_runtime_inputs(archive_plan) archive_plan.set_defaults(func=cmd_archive_plan) sub.add_parser("index-plans", parents=[parent]).set_defaults(func=cmd_index_plans) write_phase = sub.add_parser("write-phase", parents=[parent]) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index beca22a..17b1065 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -11,7 +11,7 @@ import subprocess import sys from pathlib import Path -from typing import Any +from typing import Any, Iterable, Mapping from datetime import datetime, timezone @@ -19,6 +19,12 @@ from artifact_inputs import (_split_top_level, _split_key_value, _parse_scalar, parse_yaml_subset, _read_structured, _as_list, _input_path, _resolve_spec_paths) from repository_preflight import capture_repository_evidence, task_caused_paths +from task_ownership import ( + OwnershipBlocker, + RepairContinuity, + normalize_subagent_provenance, + validate_task_acceptance_ownership, +) SOURCE_ID_TOKEN = r"[A-Z][A-Z0-9_-]*-\d+[A-Z]?" @@ -58,6 +64,31 @@ "missing": "plan", "unexecuted": "task", } +CONTEXT_EXPANSION_REASONS = { + None, + "failed_validation", + "ambiguity", + "reviewer_request", + "authority_gap", +} + + +class AcceptanceOwnershipError(SystemExit): + """A completed result failed mandatory harness-owned ownership evidence.""" + + +class _SemanticReference(str): + """Legacy in-memory semantic view that serializes as its stable ID.""" + + def __new__(cls, semantic: str, reference_id: str): + value = super().__new__(cls, semantic) + value.reference_id = reference_id + return value + + def __reduce__(self): + return self.__class__, (str(self), self.reference_id) + + KNOWLEDGE_PERSISTENCE_INSTRUCTION_RE = re.compile( r"(?:\.work-bundle/knowledge(?:/|\b)|\bks-[a-z0-9-]+\b)", re.IGNORECASE, @@ -72,6 +103,8 @@ "strategy_advice", "knowledge_persistence", "baseline", + "mutation_events", + "accepted_dependency_deltas", } VALID_RESULT_STATES = {"completed", "blocked", "partial", "failed"} TASK_FIT_RESULTS = {"clean", "repaired", "unresolved", "skipped"} @@ -723,11 +756,188 @@ def _compile_truth_basis( "purpose": purpose.strip(), "as_is_evidence": _resolve_reference(list_fields["as_is_evidence"], records, source_paths), "decision_authority": compiled_authority, - "expected_delta": _resolve_reference(list_fields["expected_delta"], records, source_paths), + # Source meanings are projected once elsewhere; the Truth Basis carries + # their stable IDs rather than duplicating accepted prose. + "expected_delta": _retain_source_references(list_fields["expected_delta"], records), "conflict_status": conflict_status, } +def _retain_source_references(value: Any, records: dict[str, str]) -> Any: + if isinstance(value, str) and value in records: + return value + if isinstance(value, list): + return [_retain_source_references(item, records) for item in value] + if isinstance(value, dict): + return {key: _retain_source_references(item, records) for key, item in value.items()} + return value + + +def _compile_validation_references(value: Any, records: dict[str, str]) -> Any: + if isinstance(value, str) and value in records: + return _SemanticReference(f"{value}: {records[value]}", value) + if isinstance(value, list): + return [_compile_validation_references(item, records) for item in value] + if isinstance(value, dict): + return {key: _compile_validation_references(item, records) for key, item in value.items()} + return value + + +def semantic_digest(value: Any) -> str: + """Return a stable digest for projected semantic or evidence identity.""" + + payload = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + return hashlib.sha256(payload.encode("utf-8")).hexdigest() + + +def _compile_semantic_authority( + source_ids: list[str], + records: dict[str, str], + truth_basis: dict[str, Any], +) -> dict[str, Any]: + """Index each accepted meaning at its single canonical packet location.""" + + index: dict[str, dict[str, str]] = {} + interface_semantics: dict[str, str] = {} + validation_semantics: dict[str, str] = {} + for source_id in source_ids: + if source_id.startswith("CON-"): + field = "constraints" + elif source_id.startswith(("API-", "IFACE-")): + field = "interface_semantics" + interface_semantics[source_id] = f"{source_id}: {records[source_id]}" + elif source_id.startswith("TEST-"): + field = "validation_semantics" + validation_semantics[source_id] = f"{source_id}: {records[source_id]}" + else: + field = "requirements" + index[source_id] = { + "canonical_field": field, + "digest": semantic_digest(records[source_id]), + } + for authority in _as_list(truth_basis.get("decision_authority")): + alias = str(authority).split(":", 1)[0].strip() + if AUTH_ALIAS_RE.fullmatch(alias): + index[alias] = { + "canonical_field": "truth_basis.decision_authority", + "digest": semantic_digest(authority), + } + return { + "records": index, + "interface_semantics": interface_semantics, + "validation_semantics": validation_semantics, + } + + +def _compile_capability_projection( + executor_profile: dict[str, Any], rules: list[dict[str, Any]], skill_names: list[Any] +) -> dict[str, Any]: + return { + "executor": { + "capability": executor_profile["capability"], + "reason": "task executor_profile allocation", + }, + "rules": [ + {"id": str(item["id"]), "reason": str(item["requirement"])} + for item in rules + ], + "skills": [ + {"id": str(name), "reason": "task methodology allocation"} + for name in skill_names + ], + "traversal": "runtime_only", + } + + +def _encoded_bytes(value: Any) -> int: + return len(json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")) + + +def compiled_context_metrics( + task_brief: dict[str, Any], + *, + review_package: str | None = None, + evidence_projection: list[dict[str, Any]] | None = None, + omitted_by_reference_bytes: int = 0, + expansion_reason: str | None = None, +) -> dict[str, Any]: + """Measure a compiled packet without imposing a global size policy.""" + + if expansion_reason not in CONTEXT_EXPANSION_REASONS: + raise SystemExit("compiled context expansion_reason is invalid") + semantic_projection = { + "semantic_authority": task_brief.get("semantic_authority", {}), + "requirements": task_brief.get("requirements", []), + "constraints": task_brief.get("constraints", []), + "decision_authority": (task_brief.get("truth_basis") or {}).get("decision_authority", []), + } + return { + "task_brief_bytes": len(("\n".join(_dump_yaml({"task_brief": task_brief})) + "\n").encode("utf-8")), + "semantic_authority_bytes": _encoded_bytes(semantic_projection), + "allocated_rule_bytes": _encoded_bytes(task_brief.get("allocated_rules", [])), + "allocated_skill_bytes": _encoded_bytes((task_brief.get("methodology") or {}).get("skills", [])), + "capability_projection_bytes": _encoded_bytes(task_brief.get("capability_projection", {})), + "evidence_projection_bytes": _encoded_bytes( + evidence_projection if evidence_projection is not None else task_brief.get("evidence_capability", {}) + ), + "review_package_bytes": len(review_package.encode("utf-8")) if review_package is not None else 0, + "omitted_by_reference_bytes": max(0, int(omitted_by_reference_bytes)), + "expansion_reason": expansion_reason, + } + + +def project_validation_evidence( + items: list[dict[str, Any]], + *, + evidence_capability: dict[str, Any], + observed: list[dict[str, Any]] | None = None, + expansion_reason: str | None = None, +) -> list[dict[str, Any]]: + """Project successes as compact receipts and expand only explicit failures.""" + + if expansion_reason not in CONTEXT_EXPANSION_REASONS: + raise SystemExit("evidence projection expansion_reason is invalid") + observed_by_id = { + str(item.get("id")): item for item in (observed or []) if isinstance(item, dict) and item.get("id") + } + invariant_by_evidence: dict[str, dict[str, Any]] = {} + for invariant in _as_list(evidence_capability.get("invariants")): + if not isinstance(invariant, dict): + continue + for evidence_id in _as_list(invariant.get("evidence_ids")): + invariant_by_evidence.setdefault(str(evidence_id), invariant) + projected: list[dict[str, Any]] = [] + for position, item in enumerate(items, start=1): + evidence_id = str(item.get("id") or f"validation-{position:03d}") + result = str(item.get("result") or "ambiguous") + if result not in {"passed", "skipped"}: + reason = expansion_reason or ("failed_validation" if result == "failed" else "ambiguity") + if reason not in CONTEXT_EXPANSION_REASONS - {None}: + raise SystemExit("evidence projection expansion_reason is invalid") + projected.append({ + "id": evidence_id, + "digest": semantic_digest({"command": item.get("command"), "result": result}), + "result": result, + "expansion_reason": reason, + "details": dict(item), + }) + continue + invariant = invariant_by_evidence.get(evidence_id, {}) + observation = observed_by_id.get(evidence_id, {}) + projected.append({ + "id": evidence_id, + "digest": semantic_digest({"command": item.get("command"), "result": result}), + "result": result, + "boundary": invariant.get("boundary", "component"), + "freshness": invariant.get("freshness", "current_task_batch"), + "invalidation_receipt": observation.get("observation_id") or semantic_digest( + {"evidence_id": evidence_id, "result": result} + ), + "expansion_reason": None, + }) + return projected + + def _compile_evidence_capability( task: dict[str, Any], task_id: str, source_ids: list[str], validation: list[dict[str, Any]] ) -> dict[str, Any] | None: @@ -1279,14 +1489,1325 @@ def _path_is_forbidden(relative: str, forbidden: list[str]) -> bool: def _assert_task_caused_delta_in_write_scope( caused: list[str], task_files: dict[str, Any], + *, + accepted_dependency_paths: set[str] | None = None, ) -> None: write_paths = [str(path) for path in _as_list(task_files.get("write"))] forbidden = [str(path) for path in _as_list(task_files.get("forbidden"))] for relative in caused: + if relative in (accepted_dependency_paths or set()): + continue if _path_is_forbidden(relative, forbidden) or not _write_scope_match(relative, write_paths): raise SystemExit(f"Unauthorized task-caused delta outside write scope: {relative}") +def _exact_handoff_reference( + handoff_root: Path, + reference: object, +) -> tuple[Path, dict[str, Any]]: + if not isinstance(reference, Mapping) or set(reference) != {"handoff_id", "handoff_sha256"}: + raise SystemExit("accepted dependency handoff reference must use the closed identity shape") + handoff_id = str(reference["handoff_id"]) + matches: list[tuple[Path, dict[str, Any]]] = [] + for path in sorted(handoff_root.glob("executor/*/*")): + if not path.is_file() or path.is_symlink(): + continue + try: + document, _ = _read_structured(path) + except (OSError, SystemExit, ValueError): + continue + if document.get("id") == handoff_id: + matches.append((path, document)) + if len(matches) != 1: + raise SystemExit(f"accepted dependency handoff identity is missing or ambiguous: {handoff_id}") + path, handoff = matches[0] + if hashlib.sha256(path.read_bytes()).hexdigest() != str(reference["handoff_sha256"]): + raise SystemExit(f"accepted dependency handoff identity is stale: {handoff_id}") + return path, handoff + + +def _review_without_history(review: Mapping[str, Any]) -> dict[str, Any]: + return {key: value for key, value in review.items() if key != "previous_review"} + + +RECOVERY_RECEIPT_SCHEMA = "accepted-result-recovery-receipt-v1" +RECOVERY_RECEIPT_KEYS = { + "receipt_id", + "schema", + "plan_id", + "task_id", + "binding_id", + "binding_sha256", + "baseline_head", + "baseline_tree", + "expected_base_head", + "expected_base_tree", + "expected_base_query", + "proposed_recovered_result", + "observed_at", + "freshness", +} +LEGACY_RECOVERY_RECEIPT_KEYS = { + "receipt_id", + "schema", + "plan_id", + "task_id", + "binding_id", + "binding_sha256", + "baseline_head", + "baseline_tree", + "expected_base_head", + "expected_base_tree", + "queried_historical_revision", + "handoff_stores", + "handoff_index", + "absence_result", + "observed_at", + "freshness", +} + + +def _recovery_receipt_path( + control_root: Path, plan_id: str, task_id: str, receipt_id: str +) -> Path: + if not all(SAFE_ID_RE.fullmatch(value) for value in (plan_id, task_id, receipt_id)): + raise SystemExit("accepted-result recovery receipt identity is unsafe") + return ( + control_root.expanduser().resolve() + / ".work-bundle/runtime/execution" + / plan_id + / task_id + / "accepted-result-recovery" + / f"{receipt_id}.json" + ) + + +def _is_recoverable_accepted_base( + handoff: Mapping[str, Any], + plan_id: str, + task_id: str, + expected_base_head: str, + expected_base_tree: str, +) -> bool: + related = handoff.get("related") if isinstance(handoff.get("related"), Mapping) else {} + result = handoff.get("result") if isinstance(handoff.get("result"), Mapping) else {} + review = ( + handoff.get("acceptance_review") + if isinstance(handoff.get("acceptance_review"), Mapping) + else {} + ) + reset = review.get("review_reset") if isinstance(review.get("review_reset"), Mapping) else {} + if reset.get("reason_class") == "authority": + # This is the newly reconstructed whole-task result, never the missing + # historical accepted base whose absence authorizes reconstruction. + return False + if ( + handoff.get("type") != "executor-result" + or related.get("plan") != plan_id + or related.get("task") != task_id + or result.get("state") != "completed" + or review.get("verdict") != "accept" + ): + return False + identity = review.get("target_identity") if isinstance(review.get("target_identity"), Mapping) else {} + if ( + identity.get("artifact_id") != task_id + or identity.get("revision") != expected_base_head + or identity.get("source_tree") != expected_base_tree + or review.get("reviewed_head") != expected_base_head + ): + return False + try: + from review_runtime import ReviewContractError, validate_task_acceptance_review + + validate_task_acceptance_review(review) + except (ReviewContractError, KeyError, TypeError, ValueError): + return False + return True + + +def _accepted_base_query_snapshot( + control_root: Path, + plan_id: str, + task_id: str, + expected_base_head: str, + expected_base_tree: str, + proposed_handoff_id: str, + proposed_review_id: str, + final_head: str, + final_tree: str, +) -> dict[str, Any]: + handoff_root = control_root / ".work-bundle/orchestration/handoff" + recoverable_ids: list[str] = [] + proposal_records: list[dict[str, Any]] = [] + for status in ("active", "archived"): + for path in sorted((handoff_root / "executor" / status).glob("*")): + if not path.is_file() or path.is_symlink(): + continue + try: + handoff, _ = _read_structured(path) + except (OSError, SystemExit, ValueError): + continue + handoff_id = str(handoff.get("id") or "") + if _is_recoverable_accepted_base( + handoff, + plan_id, + task_id, + expected_base_head, + expected_base_tree, + ): + recoverable_ids.append(handoff_id) + related = handoff.get("related") if isinstance(handoff.get("related"), Mapping) else {} + review = ( + handoff.get("acceptance_review") + if isinstance(handoff.get("acceptance_review"), Mapping) + else {} + ) + identity = ( + review.get("target_identity") + if isinstance(review.get("target_identity"), Mapping) + else {} + ) + if handoff_id != proposed_handoff_id and review.get("review_id") != proposed_review_id: + continue + result = handoff.get("result") if isinstance(handoff.get("result"), Mapping) else {} + proposal_records.append( + { + "handoff_id": handoff_id, + "review_id": str(review.get("review_id") or ""), + "path": path.relative_to(control_root).as_posix(), + "sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + "exact": ( + handoff_id == proposed_handoff_id + and handoff.get("type") == "executor-result" + and related.get("plan") == plan_id + and related.get("task") == task_id + and result.get("state") == "completed" + and review.get("review_id") == proposed_review_id + and review.get("reviewed_head") == final_head + and identity.get("artifact_id") == task_id + and identity.get("revision") == final_head + and identity.get("source_tree") == final_tree + ), + } + ) + + index_path = handoff_root / "index.jsonl" + if not index_path.is_file() or index_path.is_symlink(): + raise SystemExit("accepted-result recovery requires the native handoff index") + proposal_index_entries: list[dict[str, Any]] = [] + for number, line in enumerate(index_path.read_text(encoding="utf-8").splitlines(), 1): + if not line.strip(): + continue + try: + entry = json.loads(line) + except json.JSONDecodeError as error: + raise SystemExit(f"accepted-result recovery handoff index is invalid at line {number}") from error + if not isinstance(entry, dict): + raise SystemExit(f"accepted-result recovery handoff index is invalid at line {number}") + if entry.get("id") == proposed_handoff_id: + proposal_index_entries.append(entry) + + exact_records = [record for record in proposal_records if record["exact"]] + proposal_state = "absent" + if proposal_records or proposal_index_entries: + if len(proposal_records) != 1 or len(proposal_index_entries) != 1: + proposal_state = "ambiguous" + elif not exact_records: + proposal_state = "mismatch" + else: + proposal_path = str(exact_records[0]["path"]) + index_entry = proposal_index_entries[0] + if ( + index_entry.get("related_plan") != plan_id + or index_entry.get("related_task") != task_id + or index_entry.get("path") != proposal_path + ): + proposal_state = "mismatch" + else: + proposal_state = "published" + + query_identity = { + "plan_id": plan_id, + "task_id": task_id, + "expected_base_head": expected_base_head, + "expected_base_tree": expected_base_tree, + } + return { + "expected_base_query": { + "sha256": semantic_digest(query_identity), + "result": "present" if recoverable_ids else "absent", + }, + "recoverable_base_handoff_ids": sorted(recoverable_ids), + "proposal_state": proposal_state, + } + + +def _legacy_recovery_global_snapshot( + control_root: Path, + plan_id: str, + task_id: str, + expected_base_head: str, + expected_base_tree: str, +) -> dict[str, Any]: + """Reconstruct the obsolete revision-11 global-digest snapshot.""" + + handoff_root = control_root / ".work-bundle/orchestration/handoff" + stores: dict[str, dict[str, Any]] = {} + for status in ("active", "archived"): + records: list[dict[str, str]] = [] + for path in sorted((handoff_root / "executor" / status).glob("*")): + if not path.is_file() or path.is_symlink(): + continue + try: + handoff, _ = _read_structured(path) + except (OSError, SystemExit, ValueError): + continue + related = handoff.get("related") if isinstance(handoff.get("related"), Mapping) else {} + if related.get("plan") != plan_id or related.get("task") != task_id: + continue + records.append( + { + "handoff_id": str(handoff.get("id") or ""), + "path": path.relative_to(control_root).as_posix(), + "sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + } + ) + stores[status] = { + "store_id": f"executor/{status}", + "records": records, + "sha256": semantic_digest(records), + } + + index_path = handoff_root / "index.jsonl" + if not index_path.is_file() or index_path.is_symlink(): + raise SystemExit("accepted-result recovery requires the native handoff index") + index_entries: list[dict[str, Any]] = [] + for number, line in enumerate(index_path.read_text(encoding="utf-8").splitlines(), 1): + if not line.strip(): + continue + try: + entry = json.loads(line) + except json.JSONDecodeError as error: + raise SystemExit( + f"accepted-result recovery handoff index is invalid at line {number}" + ) from error + if not isinstance(entry, dict): + raise SystemExit(f"accepted-result recovery handoff index is invalid at line {number}") + if entry.get("related_plan") == plan_id and entry.get("related_task") == task_id: + index_entries.append(entry) + index_identity = { + "index_id": "handoff/index.jsonl", + "path": index_path.relative_to(control_root).as_posix(), + "sha256": hashlib.sha256(index_path.read_bytes()).hexdigest(), + "projected_entries_sha256": semantic_digest(index_entries), + } + historical_revision = semantic_digest( + { + "expected_base_head": expected_base_head, + "expected_base_tree": expected_base_tree, + "handoff_stores": stores, + "handoff_index": index_identity, + } + ) + return { + "queried_historical_revision": historical_revision, + "handoff_stores": stores, + "handoff_index": index_identity, + } + + +def _valid_recovered_result( + handoff: Mapping[str, Any], plan_id: str, task_id: str +) -> bool: + related = handoff.get("related") if isinstance(handoff.get("related"), Mapping) else {} + result = handoff.get("result") if isinstance(handoff.get("result"), Mapping) else {} + review = ( + handoff.get("acceptance_review") + if isinstance(handoff.get("acceptance_review"), Mapping) + else {} + ) + reset = review.get("review_reset") if isinstance(review.get("review_reset"), Mapping) else {} + evidence = review.get("evidence") if isinstance(review.get("evidence"), Mapping) else {} + reviewer = review.get("reviewer") if isinstance(review.get("reviewer"), Mapping) else {} + identity = review.get("target_identity") if isinstance(review.get("target_identity"), Mapping) else {} + if ( + handoff.get("type") != "executor-result" + or related.get("plan") != plan_id + or related.get("task") != task_id + or result.get("state") != "completed" + or review.get("verdict") != "accept" + or review.get("review_mode") != "initial" + or review.get("review_target_kind") != "task" + or review.get("reviewer_independent") is not True + or review.get("repair_frontier") is not None + or reset.get("reason_class") != "authority" + or evidence.get("unavailable_evidence") != [] + or reviewer.get("capability") != "judgment" + or identity.get("artifact_id") != task_id + or review.get("reviewed_head") != identity.get("revision") + ): + return False + try: + from review_runtime import ReviewContractError, validate_task_acceptance_review + + validate_task_acceptance_review(review) + owner = normalize_subagent_provenance( + handoff.get("delegation_evidence") + if isinstance(handoff.get("delegation_evidence"), Mapping) + else None + ) + except (ReviewContractError, OwnershipBlocker, KeyError, TypeError, ValueError): + return False + return reviewer.get("agent_id") != owner.get("agent_id") + + +def _write_query_scoped_recovery_receipt( + control_root: Path, + plan_id: str, + task_id: str, + binding: Mapping[str, Any], + binding_path: Path, + expected_base_head: str, + expected_base_tree: str, + proposed_handoff_id: str, + proposed_review_id: str, + final_head: str, + final_tree: str, + expected_base_query: Mapping[str, Any], +) -> dict[str, str]: + baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} + observed_at = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") + receipt_id = ( + f"accepted-result-recovery-{task_id}-" + f"{str(expected_base_query['sha256'])[:12]}-" + f"{hashlib.sha256(observed_at.encode()).hexdigest()[:12]}" + ) + receipt = { + "receipt_id": receipt_id, + "schema": RECOVERY_RECEIPT_SCHEMA, + "plan_id": plan_id, + "task_id": task_id, + "binding_id": str((binding.get("ownership") or {}).get("binding_id") or ""), + "binding_sha256": hashlib.sha256(binding_path.read_bytes()).hexdigest(), + "baseline_head": str(baseline.get("head") or ""), + "baseline_tree": str(baseline.get("tree") or ""), + "expected_base_head": expected_base_head, + "expected_base_tree": expected_base_tree, + "expected_base_query": dict(expected_base_query), + "proposed_recovered_result": { + "handoff_id": proposed_handoff_id, + "review_id": proposed_review_id, + "final_head": final_head, + "final_tree": final_tree, + }, + "observed_at": observed_at, + "freshness": "current_validation_attempt", + } + path = _recovery_receipt_path(control_root, plan_id, task_id, receipt_id) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n", encoding="utf-8") + return { + "receipt_id": receipt_id, + "receipt_sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + } + + +def create_accepted_base_absence_receipt( + control_root: Path, + plan_id: str, + task_id: str, + expected_base_head: str, + expected_base_tree: str, + proposed_handoff_id: str, + proposed_review_id: str, + final_head: str, + final_tree: str, +) -> dict[str, str]: + """Persist helper-observed proof that no native accepted-result base survives.""" + + control_root = control_root.expanduser().resolve() + binding_path = _binding_path(control_root, plan_id, task_id) + binding = load_task_execution_binding(control_root, plan_id, task_id) + baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} + baseline_head = str(baseline.get("head") or "") + baseline_tree = str(baseline.get("tree") or "") + if not baseline_head or not baseline_tree: + raise SystemExit("accepted-result recovery requires the original one-time task baseline") + if not SAFE_ID_RE.fullmatch(proposed_handoff_id) or not SAFE_ID_RE.fullmatch(proposed_review_id): + raise SystemExit("accepted-result recovery proposed identity is unsafe") + execution_root = Path(str(binding.get("execution_path") or "")).resolve() + resolved_expected_head = _resolve_commit(execution_root, expected_base_head) + resolved_expected_tree = _git( + execution_root, "rev-parse", f"{resolved_expected_head}^{{tree}}" + ).strip() + if resolved_expected_tree != expected_base_tree: + raise SystemExit("accepted-result recovery expected base Git identity is mismatched") + resolved_final_head = _resolve_commit(execution_root, final_head) + resolved_final_tree = _git(execution_root, "rev-parse", f"{resolved_final_head}^{{tree}}").strip() + if resolved_final_tree != final_tree: + raise SystemExit("accepted-result recovery proposed final Git identity is mismatched") + snapshot = _accepted_base_query_snapshot( + control_root, + plan_id, + task_id, + resolved_expected_head, + resolved_expected_tree, + proposed_handoff_id, + proposed_review_id, + resolved_final_head, + resolved_final_tree, + ) + if snapshot["recoverable_base_handoff_ids"]: + raise SystemExit("accepted-result recovery rejected: recoverable accepted base handoff exists") + if snapshot["proposal_state"] != "absent": + raise SystemExit("accepted-result recovery rejected: proposed result already exists or is ambiguous") + return _write_query_scoped_recovery_receipt( + control_root, + plan_id, + task_id, + binding, + binding_path, + resolved_expected_head, + resolved_expected_tree, + proposed_handoff_id, + proposed_review_id, + resolved_final_head, + resolved_final_tree, + snapshot["expected_base_query"], + ) + + +def adopt_existing_recovered_result( + control_root: Path, + plan_id: str, + task_id: str, + expected_base_head: str, + expected_base_tree: str, + handoff_id: str, + handoff_sha256: str, + review_id: str, + final_head: str, + final_tree: str, + prior_receipt_reference: object, +) -> dict[str, str]: + """Adopt one already-published result whose rev11 receipt only globally staled.""" + + control_root = control_root.expanduser().resolve() + if not isinstance(prior_receipt_reference, Mapping) or set(prior_receipt_reference) != { + "receipt_id", "receipt_sha256" + }: + raise SystemExit("post-publication adoption prior receipt reference is invalid") + if not SAFE_ID_RE.fullmatch(handoff_id) or not SAFE_ID_RE.fullmatch(review_id): + raise SystemExit("post-publication adoption proposed identity is unsafe") + + binding_path = _binding_path(control_root, plan_id, task_id) + binding = load_task_execution_binding(control_root, plan_id, task_id) + baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} + binding_id = str((binding.get("ownership") or {}).get("binding_id") or "") + binding_sha256 = hashlib.sha256(binding_path.read_bytes()).hexdigest() + execution_root = Path(str(binding.get("execution_path") or "")).resolve() + resolved_expected_head = _resolve_commit(execution_root, expected_base_head) + resolved_expected_tree = _git( + execution_root, "rev-parse", f"{resolved_expected_head}^{{tree}}" + ).strip() + if resolved_expected_tree != expected_base_tree: + raise SystemExit("post-publication adoption expected base Git identity is mismatched") + resolved_final_head = _resolve_commit(execution_root, final_head) + resolved_final_tree = _git( + execution_root, "rev-parse", f"{resolved_final_head}^{{tree}}" + ).strip() + if resolved_final_tree != final_tree: + raise SystemExit("post-publication adoption final Git identity is mismatched") + + prior_receipt_id = str(prior_receipt_reference["receipt_id"]) + prior_path = _recovery_receipt_path(control_root, plan_id, task_id, prior_receipt_id) + if not prior_path.is_file() or prior_path.is_symlink(): + raise SystemExit("post-publication adoption prior receipt is missing") + if hashlib.sha256(prior_path.read_bytes()).hexdigest() != str( + prior_receipt_reference["receipt_sha256"] + ): + raise SystemExit("post-publication adoption prior receipt digest is stale") + try: + prior = json.loads(prior_path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as error: + raise SystemExit("post-publication adoption prior receipt is invalid") from error + if not isinstance(prior, dict) or set(prior) != LEGACY_RECOVERY_RECEIPT_KEYS: + raise SystemExit("post-publication adoption requires the closed revision-11 receipt schema") + if ( + prior.get("receipt_id") != prior_receipt_id + or prior.get("schema") != RECOVERY_RECEIPT_SCHEMA + or prior.get("plan_id") != plan_id + or prior.get("task_id") != task_id + or prior.get("binding_id") != binding_id + or prior.get("binding_sha256") != binding_sha256 + or prior.get("baseline_head") != baseline.get("head") + or prior.get("baseline_tree") != baseline.get("tree") + or prior.get("expected_base_head") != resolved_expected_head + or prior.get("expected_base_tree") != resolved_expected_tree + or prior.get("absence_result") != "accepted_base_absent" + or prior.get("freshness") != "current_validation_attempt" + or not isinstance(prior.get("observed_at"), str) + ): + raise SystemExit("post-publication adoption prior receipt has a non-global defect") + + stores = prior.get("handoff_stores") + index_identity = prior.get("handoff_index") + if not isinstance(stores, Mapping) or set(stores) != {"active", "archived"}: + raise SystemExit("post-publication adoption prior receipt stores are invalid") + recorded_ids: set[str] = set() + for status in ("active", "archived"): + store = stores[status] + if not isinstance(store, Mapping) or set(store) != {"store_id", "records", "sha256"}: + raise SystemExit("post-publication adoption prior receipt store is invalid") + records = store.get("records") + if ( + store.get("store_id") != f"executor/{status}" + or not isinstance(records, list) + or store.get("sha256") != semantic_digest(records) + ): + raise SystemExit("post-publication adoption prior receipt store digest is invalid") + for record in records: + if not isinstance(record, Mapping) or set(record) != {"handoff_id", "path", "sha256"}: + raise SystemExit("post-publication adoption prior receipt record is invalid") + recorded_ids.add(str(record.get("handoff_id") or "")) + record_path = (control_root / str(record.get("path") or "")).resolve() + try: + record_path.relative_to(control_root) + except ValueError as error: + raise SystemExit("post-publication adoption prior receipt record path is unsafe") from error + if ( + not record_path.is_file() + or record_path.is_symlink() + or hashlib.sha256(record_path.read_bytes()).hexdigest() != record.get("sha256") + ): + raise SystemExit("post-publication adoption prior receipt record is stale") + try: + recorded_handoff, _ = _read_structured(record_path) + except (OSError, SystemExit, ValueError) as error: + raise SystemExit("post-publication adoption prior receipt record is invalid") from error + if _is_recoverable_accepted_base( + recorded_handoff, + plan_id, + task_id, + resolved_expected_head, + resolved_expected_tree, + ): + raise SystemExit("post-publication adoption prior expected-base query was not absent") + if handoff_id in recorded_ids: + raise SystemExit("post-publication adoption candidate was already present in the prior receipt") + if not isinstance(index_identity, Mapping) or set(index_identity) != { + "index_id", "path", "sha256", "projected_entries_sha256" + }: + raise SystemExit("post-publication adoption prior receipt index is invalid") + if ( + index_identity.get("index_id") != "handoff/index.jsonl" + or index_identity.get("path") != ".work-bundle/orchestration/handoff/index.jsonl" + or any( + not re.fullmatch(r"[0-9a-f]{64}", str(index_identity.get(field) or "")) + for field in ("sha256", "projected_entries_sha256") + ) + ): + raise SystemExit("post-publication adoption prior receipt index digest is invalid") + prior_revision = semantic_digest( + { + "expected_base_head": resolved_expected_head, + "expected_base_tree": resolved_expected_tree, + "handoff_stores": stores, + "handoff_index": index_identity, + } + ) + if prior.get("queried_historical_revision") != prior_revision: + raise SystemExit("post-publication adoption prior receipt historical revision is invalid") + expected_receipt_prefix = f"accepted-result-recovery-{task_id}-{prior_revision[:12]}-" + observed_suffix = hashlib.sha256(str(prior["observed_at"]).encode()).hexdigest()[:12] + if prior_receipt_id != f"{expected_receipt_prefix}{observed_suffix}": + raise SystemExit("post-publication adoption prior receipt identity is invalid") + + snapshot = _accepted_base_query_snapshot( + control_root, + plan_id, + task_id, + resolved_expected_head, + resolved_expected_tree, + handoff_id, + review_id, + resolved_final_head, + resolved_final_tree, + ) + if snapshot["recoverable_base_handoff_ids"]: + raise SystemExit("post-publication adoption rejected: recoverable accepted base exists") + if snapshot["proposal_state"] != "published": + raise SystemExit("post-publication adoption candidate is missing, mismatched, or ambiguous") + + handoff_root = control_root / ".work-bundle/orchestration/handoff" + _, candidate = _exact_handoff_reference( + handoff_root, {"handoff_id": handoff_id, "handoff_sha256": handoff_sha256} + ) + if not _valid_recovered_result(candidate, plan_id, task_id): + raise SystemExit("post-publication adoption candidate is not a valid recovered result") + for path in sorted(handoff_root.glob("executor/*/*")): + if not path.is_file() or path.is_symlink(): + continue + if hashlib.sha256(path.read_bytes()).hexdigest() == handoff_sha256: + continue + try: + other, _ = _read_structured(path) + except (OSError, SystemExit, ValueError): + continue + if _valid_recovered_result(other, plan_id, task_id): + raise SystemExit("post-publication adoption rejected: valid competing recovered result exists") + + current_legacy = _legacy_recovery_global_snapshot( + control_root, + plan_id, + task_id, + resolved_expected_head, + resolved_expected_tree, + ) + if all(prior.get(field) == current_legacy[field] for field in current_legacy): + raise SystemExit("post-publication adoption prior receipt is not globally stale") + return _write_query_scoped_recovery_receipt( + control_root, + plan_id, + task_id, + binding, + binding_path, + resolved_expected_head, + resolved_expected_tree, + handoff_id, + review_id, + resolved_final_head, + resolved_final_tree, + snapshot["expected_base_query"], + ) + + +def validate_accepted_base_absence_receipt( + control_root: Path, + plan_id: str, + task_id: str, + reference: object, +) -> dict[str, Any]: + if not isinstance(reference, Mapping) or set(reference) != {"receipt_id", "receipt_sha256"}: + raise SystemExit("accepted-result recovery receipt reference must use the closed identity shape") + receipt_id = str(reference["receipt_id"]) + path = _recovery_receipt_path(control_root, plan_id, task_id, receipt_id) + if not path.is_file() or path.is_symlink(): + raise SystemExit("accepted-result recovery receipt is missing") + if hashlib.sha256(path.read_bytes()).hexdigest() != str(reference["receipt_sha256"]): + raise SystemExit("accepted-result recovery receipt is stale") + try: + receipt = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as error: + raise SystemExit("accepted-result recovery receipt is invalid") from error + if not isinstance(receipt, dict) or set(receipt) != RECOVERY_RECEIPT_KEYS: + raise SystemExit("accepted-result recovery receipt must use the closed schema") + if ( + receipt.get("receipt_id") != receipt_id + or receipt.get("schema") != RECOVERY_RECEIPT_SCHEMA + or receipt.get("plan_id") != plan_id + or receipt.get("task_id") != task_id + or receipt.get("freshness") != "current_validation_attempt" + ): + raise SystemExit("accepted-result recovery receipt identity is mismatched") + binding_path = _binding_path(control_root, plan_id, task_id) + binding = load_task_execution_binding(control_root, plan_id, task_id) + baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} + if ( + receipt.get("binding_id") != (binding.get("ownership") or {}).get("binding_id") + or receipt.get("binding_sha256") != hashlib.sha256(binding_path.read_bytes()).hexdigest() + or receipt.get("baseline_head") != baseline.get("head") + or receipt.get("baseline_tree") != baseline.get("tree") + ): + raise SystemExit("accepted-result recovery receipt binding or baseline is stale") + execution_root = Path(str(binding.get("execution_path") or "")).resolve() + expected_head = _resolve_commit(execution_root, str(receipt.get("expected_base_head") or "")) + expected_tree = _git(execution_root, "rev-parse", f"{expected_head}^{{tree}}").strip() + if receipt.get("expected_base_tree") != expected_tree: + raise SystemExit("accepted-result recovery receipt expected base identity is stale") + proposal = receipt.get("proposed_recovered_result") + if not isinstance(proposal, Mapping) or set(proposal) != { + "handoff_id", "review_id", "final_head", "final_tree" + }: + raise SystemExit("accepted-result recovery receipt proposed result is invalid") + final_head = _resolve_commit(execution_root, str(proposal.get("final_head") or "")) + final_tree = _git(execution_root, "rev-parse", f"{final_head}^{{tree}}").strip() + if proposal.get("final_tree") != final_tree: + raise SystemExit("accepted-result recovery receipt proposed final identity is stale") + snapshot = _accepted_base_query_snapshot( + control_root, + plan_id, + task_id, + expected_head, + expected_tree, + str(proposal.get("handoff_id") or ""), + str(proposal.get("review_id") or ""), + final_head, + final_tree, + ) + if snapshot["recoverable_base_handoff_ids"]: + raise SystemExit("accepted-result recovery rejected: recoverable accepted base handoff exists") + if receipt.get("expected_base_query") != snapshot["expected_base_query"]: + raise SystemExit("accepted-result recovery receipt expected-base query is stale") + if snapshot["proposal_state"] in {"mismatch", "ambiguous"}: + raise SystemExit("accepted-result recovery proposed result is mismatched or ambiguous") + return {**receipt, "proposal_state": snapshot["proposal_state"]} + + +def _recovered_accepted_dependency_paths( + task: dict[str, Any], + execution_root: Path, + descriptors: list[Mapping[str, object]], +) -> set[str]: + required_fields = { + "task_id", + "execution_baseline_recovery", + "recovered_result", + "accepted_result_delta", + "integrated_base", + "integrated_head", + } + recovery_fields = { + "binding_id", + "binding_sha256", + "baseline_head", + "baseline_tree", + "recovery_receipt", + } + delta_fields = { + "expected_base_head", + "expected_base_tree", + "final_head", + "final_tree", + } + dependencies = {str(value) for value in _as_list(task.get("depends_on"))} + plan_id = str(task.get("plan_id") or "") + workspace = task.get("workspace") if isinstance(task.get("workspace"), dict) else {} + control_root = Path(str(workspace.get("root") or "")).resolve() + handoff_root = control_root / ".work-bundle/orchestration/handoff" + current_head = _resolve_commit(execution_root, "HEAD") + admitted: set[str] = set() + seen_dependencies: set[str] = set() + for descriptor in descriptors: + if set(descriptor) != required_fields: + raise SystemExit( + "accepted_dependency_deltas recovery entries must use the closed runtime identity shape" + ) + dependency_id = str(descriptor["task_id"]) + if dependency_id not in dependencies: + raise SystemExit(f"accepted_dependency_deltas names undeclared dependency: {dependency_id}") + if dependency_id in seen_dependencies: + raise SystemExit(f"accepted_dependency_deltas duplicates dependency: {dependency_id}") + seen_dependencies.add(dependency_id) + recovery = descriptor["execution_baseline_recovery"] + if not isinstance(recovery, Mapping) or set(recovery) != recovery_fields: + raise SystemExit("execution_baseline_recovery must use the closed runtime identity shape") + + binding_path = _binding_path(control_root, plan_id, dependency_id) + binding = load_task_execution_binding(control_root, plan_id, dependency_id) + baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} + binding_id = str((binding.get("ownership") or {}).get("binding_id") or "") + binding_digest = hashlib.sha256(binding_path.read_bytes()).hexdigest() + if ( + recovery.get("binding_id") != binding_id + or recovery.get("binding_sha256") != binding_digest + or recovery.get("baseline_head") != baseline.get("head") + or recovery.get("baseline_tree") != baseline.get("tree") + ): + raise SystemExit("execution_baseline_recovery binding or original baseline is mismatched") + receipt = validate_accepted_base_absence_receipt( + control_root, + plan_id, + dependency_id, + recovery.get("recovery_receipt"), + ) + if receipt.get("proposal_state") != "published": + raise SystemExit("accepted-result recovery proposed result is not published and indexed") + + delta = descriptor["accepted_result_delta"] + if not isinstance(delta, Mapping) or set(delta) != delta_fields: + raise SystemExit("accepted_result_delta must use the closed runtime identity shape") + proposal = receipt["proposed_recovered_result"] + if ( + delta.get("expected_base_head") != receipt.get("expected_base_head") + or delta.get("expected_base_tree") != receipt.get("expected_base_tree") + or delta.get("final_head") != proposal.get("final_head") + or delta.get("final_tree") != proposal.get("final_tree") + ): + raise SystemExit("accepted_result_delta is mismatched with the recovery receipt") + recovered_reference = descriptor["recovered_result"] + if ( + not isinstance(recovered_reference, Mapping) + or recovered_reference.get("handoff_id") != proposal.get("handoff_id") + ): + raise SystemExit("recovered result is mismatched with the recovery receipt proposal") + + _, recovered = _exact_handoff_reference(handoff_root, recovered_reference) + related = recovered.get("related") if isinstance(recovered.get("related"), Mapping) else {} + result = recovered.get("result") if isinstance(recovered.get("result"), Mapping) else {} + review = ( + recovered.get("acceptance_review") + if isinstance(recovered.get("acceptance_review"), Mapping) + else {} + ) + reset = review.get("review_reset") if isinstance(review.get("review_reset"), Mapping) else {} + evidence = review.get("evidence") if isinstance(review.get("evidence"), Mapping) else {} + reviewer = review.get("reviewer") if isinstance(review.get("reviewer"), Mapping) else {} + identity = review.get("target_identity") if isinstance(review.get("target_identity"), Mapping) else {} + if ( + related.get("plan") != plan_id + or related.get("task") != dependency_id + or result.get("state") != "completed" + or review.get("verdict") != "accept" + or review.get("review_mode") != "initial" + or review.get("review_target_kind") != "task" + or review.get("reviewer_independent") is not True + or review.get("repair_frontier") is not None + or reset.get("reason_class") != "authority" + or evidence.get("unavailable_evidence") != [] + or reviewer.get("capability") != "judgment" + or identity.get("artifact_id") != dependency_id + or review.get("reviewed_head") != identity.get("revision") + or review.get("review_id") != proposal.get("review_id") + ): + raise SystemExit( + "recovered result requires a fresh complete independent whole-task initial authority review with empty unavailable_evidence" + ) + try: + from review_runtime import ReviewContractError, validate_task_acceptance_review + + validate_task_acceptance_review(review) + except ReviewContractError as error: + raise SystemExit(f"recovered result task review is invalid: {error}") from error + try: + owner = normalize_subagent_provenance( + recovered.get("delegation_evidence") + if isinstance(recovered.get("delegation_evidence"), Mapping) + else None + ) + except OwnershipBlocker as error: + raise SystemExit(f"recovered result ownership is invalid: {error}") from error + if reviewer.get("agent_id") == owner.get("agent_id"): + raise SystemExit("recovered result reviewer is not independent from the task owner") + + source_base = _resolve_commit(execution_root, str(delta.get("expected_base_head") or "")) + source_head = _resolve_commit(execution_root, str(delta.get("final_head") or "")) + source_tree = _git(execution_root, "rev-parse", f"{source_head}^{{tree}}").strip() + source_base_tree = _git(execution_root, "rev-parse", f"{source_base}^{{tree}}").strip() + if delta.get("expected_base_tree") != source_base_tree: + raise SystemExit("accepted_result_delta expected base Git identity is mismatched") + if delta.get("final_tree") != source_tree or identity.get("source_tree") != source_tree: + raise SystemExit("recovered result Git identity is mismatched") + if subprocess.run( + ["git", "-C", str(execution_root), "merge-base", "--is-ancestor", source_base, source_head], + capture_output=True, + check=False, + ).returncode: + raise SystemExit("recovered result source chain is non-ancestral") + integrated_base = _resolve_commit(execution_root, str(descriptor["integrated_base"])) + integrated_head = _resolve_commit(execution_root, str(descriptor["integrated_head"])) + if subprocess.run( + ["git", "-C", str(execution_root), "merge-base", "--is-ancestor", integrated_head, current_head], + capture_output=True, + check=False, + ).returncode: + raise SystemExit("recovered result integration checkpoint is not current") + source_diff = _git(execution_root, "diff", "--binary", source_base, source_head, "--") + integrated_diff = _git( + execution_root, "diff", "--binary", integrated_base, integrated_head, "--" + ) + if source_diff != integrated_diff: + raise SystemExit("recovered result integration checkpoint is mismatched") + paths = { + path + for line in _git( + execution_root, "diff", "--name-status", source_base, source_head, "--" + ).splitlines() + for path in _paths_from_name_status(line) + } + if paths and _git( + execution_root, "diff", "--name-only", integrated_head, "--", *sorted(paths) + ).strip(): + raise SystemExit("recovered dependency path changed after integration") + admitted.update(paths) + return admitted + + +def _cumulative_accepted_dependency_paths( + task: dict[str, Any], + execution_root: Path, + descriptors: list[Mapping[str, object]], +) -> set[str]: + required_fields = { + "task_id", "accepted_result_base", "review_chain", "integrated_base", "integrated_head" + } + dependencies = {str(value) for value in _as_list(task.get("depends_on"))} + plan_id = str(task.get("plan_id") or "") + workspace = task.get("workspace") if isinstance(task.get("workspace"), dict) else {} + handoff_root = ( + Path(str(workspace.get("root") or "")).resolve() + / ".work-bundle/orchestration/handoff" + ) + current_head = _resolve_commit(execution_root, "HEAD") + admitted: set[str] = set() + seen_dependencies: set[str] = set() + for descriptor in descriptors: + if set(descriptor) != required_fields: + raise SystemExit( + "accepted_dependency_deltas cumulative entries must use the closed runtime identity shape" + ) + dependency_id = str(descriptor["task_id"]) + if dependency_id not in dependencies: + raise SystemExit(f"accepted_dependency_deltas names undeclared dependency: {dependency_id}") + if dependency_id in seen_dependencies: + raise SystemExit(f"accepted_dependency_deltas duplicates dependency: {dependency_id}") + seen_dependencies.add(dependency_id) + chain_references = descriptor["review_chain"] + if (not isinstance(chain_references, list) or not chain_references + or any(not isinstance(item, Mapping) for item in chain_references)): + raise SystemExit("accepted dependency review_chain must be non-empty and exact") + reference_ids = [str(item.get("handoff_id") or "") for item in chain_references] + if len(reference_ids) != len(set(reference_ids)): + raise SystemExit("accepted dependency review_chain cannot contain duplicated handoffs") + + _, base_handoff = _exact_handoff_reference( + handoff_root, descriptor["accepted_result_base"] + ) + base_related = base_handoff.get("related") if isinstance(base_handoff.get("related"), dict) else {} + base_result = base_handoff.get("result") if isinstance(base_handoff.get("result"), dict) else {} + base_review = base_handoff.get("acceptance_review") if isinstance(base_handoff.get("acceptance_review"), dict) else {} + if (base_related.get("plan") != plan_id or base_related.get("task") != dependency_id + or base_result.get("state") != "completed" or base_review.get("verdict") != "accept"): + raise SystemExit("accepted dependency result base is not an accepted plan/task result") + + chain: list[tuple[dict[str, Any], dict[str, Any], dict[str, Any]]] = [] + for reference in chain_references: + _, handoff = _exact_handoff_reference(handoff_root, reference) + related = handoff.get("related") if isinstance(handoff.get("related"), dict) else {} + result = handoff.get("result") if isinstance(handoff.get("result"), dict) else {} + review = handoff.get("acceptance_review") if isinstance(handoff.get("acceptance_review"), dict) else {} + if related.get("plan") != plan_id or related.get("task") != dependency_id: + raise SystemExit("accepted dependency review_chain plan/task identity is mismatched") + chain.append((handoff, result, review)) + + try: + from review_runtime import ( + ReviewContractError, + review_evidence_identity, + validate_task_acceptance_review, + ) + validate_task_acceptance_review(base_review) + base_identity = base_review.get("target_identity") + if (not isinstance(base_identity, Mapping) + or base_identity.get("artifact_id") != dependency_id + or base_review.get("reviewed_head") != base_identity.get("revision")): + raise SystemExit("accepted dependency result base identity is mismatched") + previous_review = base_review + seen_review_ids = {str(base_review.get("review_id") or "")} + for _, result, review in chain: + # The bounded previous_review embedded in each chain link lets the + # native sequence validator enforce repair continuity and material- + # change reset isolation without reacquiring older history. + validate_task_acceptance_review(review) + review_id = str(review.get("review_id") or "") + identity = review.get("target_identity") + if (review_id in seen_review_ids or not isinstance(identity, Mapping) + or identity.get("artifact_id") != dependency_id + or review.get("reviewed_head") != identity.get("revision")): + raise SystemExit("accepted dependency review_chain review identity is duplicated or mismatched") + seen_review_ids.add(review_id) + revision = _resolve_commit(execution_root, str(identity.get("revision") or "")) + tree = _git(execution_root, "rev-parse", f"{revision}^{{tree}}").strip() + if tree != identity.get("source_tree"): + raise SystemExit("accepted dependency review_chain Git identity is mismatched") + carried = review.get("previous_review") + if (not isinstance(carried, Mapping) or "previous_review" in carried + or _review_without_history(carried) != _review_without_history(previous_review)): + raise SystemExit("accepted dependency review_chain prior review is missing or mismatched") + mode = review.get("review_mode") + if mode == "repair": + frontier = review.get("repair_frontier") + if not isinstance(frontier, Mapping): + raise SystemExit("accepted dependency review_chain repair frontier is missing") + blocking = [ + str(item.get("finding_id")) + for item in _as_list(previous_review.get("findings")) + if isinstance(item, Mapping) and item.get("severity") == "blocking" + ] + if (frontier.get("prior_review_id") != previous_review.get("review_id") + or frontier.get("previous_reviewed_identity") != previous_review.get("target_identity") + or frontier.get("repaired_identity") != review.get("target_identity") + or list(frontier.get("blocking_finding_ids") or []) != blocking + or frontier.get("frozen_evidence_reference") != review_evidence_identity(previous_review)): + raise SystemExit("accepted dependency review_chain repair continuity is mismatched") + else: + reset = review.get("review_reset") + if not isinstance(reset, Mapping) or reset.get("prior_review_id") != previous_review.get("review_id"): + raise SystemExit("accepted dependency review_chain initial reset is non-contiguous") + if review.get("verdict") not in {"repair", "accept"}: + raise SystemExit("accepted dependency review_chain contains a blocked review") + if review.get("verdict") == "accept" and result.get("state") != "completed": + raise SystemExit("accepted dependency review_chain accepted result is incomplete") + previous_review = review + except ReviewContractError as error: + raise SystemExit(f"accepted dependency review_chain is invalid: {error}") from error + + _, final_result, final_review = chain[-1] + if final_review.get("verdict") != "accept" or final_result.get("state") != "completed": + raise SystemExit("accepted dependency review_chain final result is not accepted") + base_identity = base_review.get("target_identity") + final_identity = final_review.get("target_identity") + if not isinstance(base_identity, Mapping) or not isinstance(final_identity, Mapping): + raise SystemExit("accepted dependency cumulative result identity is incomplete") + for identity in (base_identity, final_identity): + revision = _resolve_commit(execution_root, str(identity.get("revision") or "")) + tree = _git(execution_root, "rev-parse", f"{revision}^{{tree}}").strip() + if tree != identity.get("source_tree"): + raise SystemExit("accepted dependency cumulative Git identity is mismatched") + + final_review_id = str(final_review.get("review_id") or "") + chain_ids = set(reference_ids) + successor_edges: list[tuple[str, str]] = [] + for path in sorted(handoff_root.glob("executor/*/*")): + if not path.is_file() or path.is_symlink(): + continue + try: + candidate, _ = _read_structured(path) + except (OSError, SystemExit, ValueError): + continue + if str(candidate.get("id") or "") in chain_ids: + continue + related = candidate.get("related") if isinstance(candidate.get("related"), dict) else {} + review = candidate.get("acceptance_review") if isinstance(candidate.get("acceptance_review"), dict) else {} + frontier = review.get("repair_frontier") if isinstance(review.get("repair_frontier"), dict) else {} + reset = review.get("review_reset") if isinstance(review.get("review_reset"), dict) else {} + prior_id = frontier.get("prior_review_id") or reset.get("prior_review_id") + if (related.get("plan") == plan_id and related.get("task") == dependency_id + and isinstance(prior_id, str) and prior_id): + successor_edges.append((prior_id, str(review.get("review_id") or ""))) + reachable = {final_review_id} + while True: + additions = {current for prior, current in successor_edges if prior in reachable} + if additions.issubset(reachable): + break + reachable.update(additions) + if any(prior in reachable for prior, _ in successor_edges): + raise SystemExit("accepted dependency review_chain terminal result is stale") + + source_base = _resolve_commit(execution_root, str(base_identity.get("revision") or "")) + source_head = _resolve_commit(execution_root, str(final_identity.get("revision") or "")) + if subprocess.run( + ["git", "-C", str(execution_root), "merge-base", "--is-ancestor", source_base, source_head], + capture_output=True, + check=False, + ).returncode: + raise SystemExit("accepted dependency cumulative source chain is non-ancestral") + integrated_base = _resolve_commit(execution_root, str(descriptor["integrated_base"])) + integrated_head = _resolve_commit(execution_root, str(descriptor["integrated_head"])) + if subprocess.run( + ["git", "-C", str(execution_root), "merge-base", "--is-ancestor", integrated_head, current_head], + capture_output=True, + check=False, + ).returncode: + raise SystemExit("accepted dependency cumulative integration checkpoint is not current") + source_diff = _git(execution_root, "diff", "--binary", source_base, source_head, "--") + integrated_diff = _git( + execution_root, "diff", "--binary", integrated_base, integrated_head, "--" + ) + if source_diff != integrated_diff: + raise SystemExit("accepted dependency cumulative integration checkpoint is mismatched") + paths = { + path + for line in _git(execution_root, "diff", "--name-status", source_base, source_head, "--").splitlines() + for path in _paths_from_name_status(line) + } + if paths and _git( + execution_root, "diff", "--name-only", integrated_head, "--", *sorted(paths) + ).strip(): + raise SystemExit("accepted dependency cumulative path changed after integration") + admitted.update(paths) + return admitted + + +def _accepted_dependency_paths( + task: dict[str, Any], + execution_root: Path, + accepted_dependency_deltas: Iterable[Mapping[str, object]] | None, +) -> set[str]: + descriptors = list(accepted_dependency_deltas or []) + if not descriptors: + return set() + cumulative_fields = { + "task_id", "accepted_result_base", "review_chain", "integrated_base", "integrated_head" + } + recovery_fields = { + "task_id", "execution_baseline_recovery", "recovered_result", "accepted_result_delta", + "integrated_base", "integrated_head" + } + cumulative = [ + item for item in descriptors if isinstance(item, Mapping) and set(item) == cumulative_fields + ] + recovered = [ + item for item in descriptors if isinstance(item, Mapping) and set(item) == recovery_fields + ] + if cumulative or recovered: + if len(cumulative) + len(recovered) != len(descriptors): + raise SystemExit("accepted_dependency_deltas cannot mix closed and legacy identities") + dependency_ids = [str(item["task_id"]) for item in [*cumulative, *recovered]] + if len(dependency_ids) != len(set(dependency_ids)): + raise SystemExit("accepted_dependency_deltas duplicates dependency") + return ( + _cumulative_accepted_dependency_paths(task, execution_root, cumulative) + | _recovered_accepted_dependency_paths(task, execution_root, recovered) + ) + dependencies = {str(value) for value in _as_list(task.get("depends_on"))} + plan_id = str(task.get("plan_id") or "") + workspace = task.get("workspace") if isinstance(task.get("workspace"), dict) else {} + control_root = Path(str(workspace.get("root") or "")).resolve() + handoff_root = control_root / ".work-bundle/orchestration/handoff" + admitted: set[str] = set() + chain_tail_by_dependency: dict[str, dict[str, Any]] = {} + chain_by_dependency: dict[str, dict[str, Any]] = {} + last_checkpoint_by_path: dict[str, tuple[str, str]] = {} + current_head = _resolve_commit(execution_root, "HEAD") + required_fields = { + "task_id", "handoff_id", "handoff_sha256", "integrated_base", "integrated_head" + } + for descriptor in descriptors: + if not isinstance(descriptor, Mapping) or set(descriptor) != required_fields: + raise SystemExit("accepted_dependency_deltas entries must use the closed runtime identity shape") + dependency_id = str(descriptor["task_id"]) + handoff_id = str(descriptor["handoff_id"]) + if dependency_id not in dependencies: + raise SystemExit(f"accepted_dependency_deltas names undeclared dependency: {dependency_id}") + matches: list[tuple[Path, dict[str, Any]]] = [] + for path in sorted(handoff_root.glob("executor/*/*")): + if not path.is_file() or path.is_symlink(): + continue + try: + document, _ = _read_structured(path) + except (OSError, SystemExit, ValueError): + continue + if document.get("id") == handoff_id: + matches.append((path, document)) + if len(matches) != 1: + raise SystemExit(f"accepted dependency handoff identity is missing or ambiguous: {handoff_id}") + handoff_path, handoff = matches[0] + actual_digest = hashlib.sha256(handoff_path.read_bytes()).hexdigest() + if actual_digest != str(descriptor["handoff_sha256"]): + raise SystemExit(f"accepted dependency handoff identity is stale: {handoff_id}") + related = handoff.get("related") if isinstance(handoff.get("related"), dict) else {} + result = handoff.get("result") if isinstance(handoff.get("result"), dict) else {} + review = handoff.get("acceptance_review") if isinstance(handoff.get("acceptance_review"), dict) else {} + frontier = review.get("repair_frontier") if isinstance(review.get("repair_frontier"), dict) else {} + previous = frontier.get("previous_reviewed_identity") if isinstance(frontier.get("previous_reviewed_identity"), dict) else {} + repaired = frontier.get("repaired_identity") if isinstance(frontier.get("repaired_identity"), dict) else {} + if related.get("plan") != plan_id or related.get("task") != dependency_id: + raise SystemExit( + f"accepted dependency handoff plan/task identity is mismatched: {handoff_id}" + ) + if (result.get("state") != "completed" or review.get("verdict") != "accept" + or review.get("review_mode") != "repair" + or repaired != review.get("target_identity")): + raise SystemExit(f"dependency handoff is not an accepted repair result: {handoff_id}") + source_base = str(previous.get("revision") or "") + source_head = str(repaired.get("revision") or "") + integrated_base = _resolve_commit(execution_root, str(descriptor["integrated_base"])) + integrated_head = _resolve_commit(execution_root, str(descriptor["integrated_head"])) + if not source_base or not source_head: + raise SystemExit(f"accepted dependency repair identity is incomplete: {handoff_id}") + for commit, identity in ((source_base, previous), (source_head, repaired)): + resolved = _resolve_commit(execution_root, commit) + tree = _git(execution_root, "rev-parse", f"{resolved}^{{tree}}").strip() + if tree != identity.get("source_tree"): + raise SystemExit(f"accepted dependency Git identity is mismatched: {handoff_id}") + prior_link = chain_tail_by_dependency.get(dependency_id) + if prior_link is not None: + if previous != prior_link["repaired_identity"]: + raise SystemExit( + "accepted dependency source chain is not ordered and contiguous: " + f"{handoff_id}" + ) + if integrated_base != prior_link["integrated_head"]: + raise SystemExit( + "accepted dependency integrated chain is non-adjacent: " + f"{handoff_id}" + ) + if subprocess.run( + ["git", "-C", str(execution_root), "merge-base", "--is-ancestor", integrated_head, current_head], + capture_output=True, + check=False, + ).returncode: + raise SystemExit(f"accepted dependency integration checkpoint is not current: {handoff_id}") + source_diff = _git(execution_root, "diff", "--binary", source_base, source_head, "--") + integrated_diff = _git( + execution_root, "diff", "--binary", integrated_base, integrated_head, "--" + ) + if source_diff != integrated_diff: + raise SystemExit(f"accepted dependency integration checkpoint is mismatched: {handoff_id}") + paths = { + path + for line in _git(execution_root, "diff", "--name-status", source_base, source_head, "--").splitlines() + for path in _paths_from_name_status(line) + } + chain_tail_by_dependency[dependency_id] = { + "repaired_identity": repaired, + "integrated_head": integrated_head, + } + chain = chain_by_dependency.setdefault( + dependency_id, + { + "first_previous_identity": previous, + "last_repaired_identity": repaired, + "accepted_edges": set(), + }, + ) + chain["last_repaired_identity"] = repaired + chain["accepted_edges"].add( + semantic_digest({"previous": previous, "repaired": repaired}) + ) + for path in paths: + last_checkpoint_by_path[path] = (integrated_head, handoff_id) + for dependency_id, chain in chain_by_dependency.items(): + for path in sorted(handoff_root.glob("executor/*/*")): + if not path.is_file() or path.is_symlink(): + continue + try: + candidate, _ = _read_structured(path) + except (OSError, SystemExit, ValueError): + continue + related = candidate.get("related") if isinstance(candidate.get("related"), dict) else {} + result = candidate.get("result") if isinstance(candidate.get("result"), dict) else {} + review = candidate.get("acceptance_review") if isinstance(candidate.get("acceptance_review"), dict) else {} + frontier = review.get("repair_frontier") if isinstance(review.get("repair_frontier"), dict) else {} + previous = frontier.get("previous_reviewed_identity") if isinstance(frontier.get("previous_reviewed_identity"), dict) else {} + repaired = frontier.get("repaired_identity") if isinstance(frontier.get("repaired_identity"), dict) else {} + if (related.get("plan") != plan_id or related.get("task") != dependency_id + or result.get("state") != "completed" + or review.get("verdict") != "accept" or review.get("review_mode") != "repair" + or repaired != review.get("target_identity") or not previous or not repaired): + continue + edge = semantic_digest({"previous": previous, "repaired": repaired}) + if edge in chain["accepted_edges"]: + continue + if repaired == chain["first_previous_identity"]: + raise SystemExit( + "accepted dependency repair chain is not anchored at its known accepted start: " + f"{dependency_id}" + ) + if previous == chain["last_repaired_identity"]: + raise SystemExit( + "accepted dependency repair chain is incomplete at its known accepted head: " + f"{dependency_id}" + ) + for path, (integrated_head, handoff_id) in last_checkpoint_by_path.items(): + if _git(execution_root, "diff", "--name-only", integrated_head, "--", path).strip(): + raise SystemExit(f"accepted dependency path changed after integration: {handoff_id}") + admitted.add(path) + return admitted + + def _observe_completed_validation( handoff: dict[str, Any], task: dict[str, Any], @@ -1297,6 +2818,7 @@ def _observe_completed_validation( execution_id: str | None = None, repository_id: str | None = None, execution_runtime_root: str | None = None, + accepted_dependency_deltas: Iterable[Mapping[str, object]] | None = None, ) -> list[dict[str, Any]]: if "harness_receipt" in handoff or ( isinstance(handoff.get("validation"), dict) and "harness_receipt" in handoff["validation"] @@ -1329,7 +2851,12 @@ def _observe_completed_validation( raise SystemExit(str(error)) from error observed_items: list[dict[str, Any]] = [] task_files = task.get("files") if isinstance(task.get("files"), dict) else {} - _assert_task_caused_delta_in_write_scope(task_caused_paths(baseline, pre_batch, execution_root), task_files) + accepted_paths = _accepted_dependency_paths(task, execution_root, accepted_dependency_deltas) + _assert_task_caused_delta_in_write_scope( + task_caused_paths(baseline, pre_batch, execution_root), + task_files, + accepted_dependency_paths=accepted_paths, + ) for item in required_items: observed = _completion_provenance_module().observe_validation( binding, task, item, pre_batch, @@ -1361,7 +2888,9 @@ def _observe_completed_validation( ) caused = task_caused_paths(baseline, post_batch, execution_root) task_files = task.get("files") if isinstance(task.get("files"), dict) else {} - _assert_task_caused_delta_in_write_scope(caused, task_files) + _assert_task_caused_delta_in_write_scope( + caused, task_files, accepted_dependency_paths=accepted_paths + ) if binding.get("mutating") is True: updated = dict(binding) updated["mutating"] = False @@ -1460,6 +2989,7 @@ def _observe_repository_and_codegraph_evidence( codegraph_entries: list[dict[str, Any]], *, codegraph_required: bool, + accepted_dependency_deltas: Iterable[Mapping[str, object]] | None = None, ) -> None: workspace = task.get("workspace") if isinstance(task.get("workspace"), dict) else {} control_root_raw = workspace.get("root") @@ -1484,7 +3014,13 @@ def _observe_repository_and_codegraph_evidence( raise SystemExit("Task execution binding is missing harness provenance baseline") caused = task_caused_paths(baseline, observed_repository, execution_root) task_files = task.get("files") if isinstance(task.get("files"), dict) else {} - _assert_task_caused_delta_in_write_scope(caused, task_files) + _assert_task_caused_delta_in_write_scope( + caused, + task_files, + accepted_dependency_paths=_accepted_dependency_paths( + task, execution_root, accepted_dependency_deltas + ), + ) metadata = repository.get("metadata") if isinstance(metadata, dict): @@ -1569,6 +3105,11 @@ def validate_executor_result_for_task( execution_id: str | None = None, repository_id: str | None = None, execution_runtime_root: str | None = None, + mutation_events: Iterable[Mapping[str, object]] | None = None, + accepted_dependency_deltas: Iterable[Mapping[str, object]] | None = None, + prior_ownership: Mapping[str, Mapping[str, object]] | None = None, + repair_continuity: Mapping[str, Mapping[str, object] | RepairContinuity] | None = None, + authorized_replacements: Iterable[str] | None = None, ) -> dict[str, Any]: if handoff.get("type") != "executor-result": raise SystemExit("Handoff is not executor-result") @@ -1601,7 +3142,7 @@ def validate_executor_result_for_task( if state in {"completed", "partial"}: _assert_task_fit_check(handoff, task_id, state) _assert_changed_paths_in_write_scope(handoff, task_files) - _assert_handoff_review_matches_task(handoff, task, state) + acceptance_review_sequence = _assert_handoff_review_matches_task(handoff, task, state) required_items = [ item for item in _as_list(task.get("validation")) @@ -1662,10 +3203,11 @@ def validate_executor_result_for_task( if observe and (repository_entries or codegraph_entries): _observe_repository_and_codegraph_evidence( task, - repository_entries, - codegraph_entries, - codegraph_required=evidence_applicability["codegraph"]["required"], - ) + repository_entries, + codegraph_entries, + codegraph_required=evidence_applicability["codegraph"]["required"], + accepted_dependency_deltas=accepted_dependency_deltas, + ) if state == "completed" and required_items and observe: observed_validation = _observe_completed_validation( @@ -1677,6 +3219,7 @@ def validate_executor_result_for_task( execution_id=execution_id, repository_id=repository_id, execution_runtime_root=execution_runtime_root, + accepted_dependency_deltas=accepted_dependency_deltas, ) evidence_closure = _validate_evidence_closure( handoff, task, state, reported_commands, observed_validation @@ -1686,16 +3229,123 @@ def validate_executor_result_for_task( raise SystemExit( "evidence-closure-blocked: completed mapped invariants require produced harness observations and passed evidence closure" ) + task_ownership = None + if state == "completed": + if mutation_events is None: + raise AcceptanceOwnershipError( + "review-blocked: completed task requires harness-owned mutation_events evidence" + ) + runtime_mutation_events = list(mutation_events) + if any(not isinstance(event, Mapping) for event in runtime_mutation_events): + raise AcceptanceOwnershipError("Runtime mutation_events must contain mappings") + fit = handoff.get("task_fit_check") if isinstance(handoff.get("task_fit_check"), dict) else {} + operation = "repair" if fit.get("result") == "repaired" else "implementation" + try: + task_ownership = validate_task_acceptance_ownership( + delegation_evidence=( + handoff.get("delegation_evidence") + if isinstance(handoff.get("delegation_evidence"), dict) + else None + ), + mutation_events=runtime_mutation_events, + write_scope=_as_list(task_files.get("write")), + validations_passed=True, + operation=operation, + ) + if operation == "repair" and acceptance_review_sequence != "initial-reset": + _validate_repair_acceptance_continuity( + task=task, + handoff=handoff, + current_ownership=task_ownership, + prior_ownership=prior_ownership, + repair_continuity=repair_continuity, + authorized_replacements=authorized_replacements, + ) + except OwnershipBlocker as error: + raise AcceptanceOwnershipError(str(error)) from error return { "knowledge_disposition": knowledge_disposition, "unresolved": unresolved, "result_state": state, "evidence_applicability": evidence_applicability, "evidence_closure": evidence_closure, + **({"task_ownership": task_ownership} if task_ownership is not None else {}), **({"observed_validation": observed_validation} if observed_validation is not None else {}), } +def _validate_repair_acceptance_continuity( + *, + task: Mapping[str, object], + handoff: Mapping[str, object], + current_ownership: Mapping[str, object], + prior_ownership: Mapping[str, Mapping[str, object]] | None, + repair_continuity: Mapping[str, Mapping[str, object] | RepairContinuity] | None, + authorized_replacements: Iterable[str] | None, +) -> None: + """Bind repair acceptance to the scheduler's original owner and identities.""" + + task_id = str(task.get("task_id") or "") + if prior_ownership is not None and not isinstance(prior_ownership, Mapping): + raise OwnershipBlocker("review-blocked", "repair prior_ownership must be a task mapping") + if repair_continuity is not None and not isinstance(repair_continuity, Mapping): + raise OwnershipBlocker("review-blocked", "repair_continuity must be a task mapping") + previous_value = (prior_ownership or {}).get(task_id) + continuity_value = (repair_continuity or {}).get(task_id) + if previous_value is None: + raise OwnershipBlocker("review-blocked", f"{task_id} repair lacks prior owner") + if continuity_value is None: + raise OwnershipBlocker("review-blocked", f"{task_id} repair lacks continuity identities") + previous = normalize_subagent_provenance(previous_value) + if isinstance(continuity_value, RepairContinuity): + continuity = continuity_value + else: + if not isinstance(continuity_value, Mapping) or set(continuity_value) != { + "binding_id", + "baseline_identity", + "evidence_identity", + "previous_review_identity", + }: + raise OwnershipBlocker("review-blocked", f"{task_id} repair continuity is not closed") + try: + continuity = RepairContinuity(**{key: str(value) for key, value in continuity_value.items()}) + except (TypeError, ValueError) as error: + raise OwnershipBlocker("review-blocked", f"{task_id} repair continuity is invalid") from error + + workspace = task.get("workspace") if isinstance(task.get("workspace"), Mapping) else {} + control_root = Path(str(workspace.get("root") or "")).expanduser().resolve() + if not control_root.is_dir(): + raise OwnershipBlocker("review-blocked", f"{task_id} repair control root is unavailable") + binding = load_task_execution_binding(control_root, str(task.get("plan_id") or ""), task_id) + baseline = binding.get("baseline") + if not isinstance(baseline, Mapping) or not baseline.get("head"): + raise OwnershipBlocker("review-blocked", f"{task_id} repair baseline identity is unavailable") + review = handoff.get("acceptance_review") if isinstance(handoff.get("acceptance_review"), Mapping) else {} + frontier = review.get("repair_frontier") if isinstance(review.get("repair_frontier"), Mapping) else {} + ownership = binding.get("ownership") if isinstance(binding.get("ownership"), Mapping) else {} + try: + expected = RepairContinuity( + binding_id=str(ownership.get("binding_id") or ""), + baseline_identity=semantic_digest(dict(baseline)), + evidence_identity=str(frontier.get("frozen_evidence_reference") or ""), + previous_review_identity=str(frontier.get("prior_review_id") or ""), + ) + except ValueError as error: + raise OwnershipBlocker( + "review-blocked", f"{task_id} repair frontier continuity is unavailable" + ) from error + if continuity != expected: + raise OwnershipBlocker("review-blocked", f"{task_id} repair continuity identities do not match") + if isinstance(authorized_replacements, (str, bytes, Mapping)): + raise OwnershipBlocker("review-blocked", "authorized_replacements must be task IDs") + replacements = {str(value) for value in (authorized_replacements or [])} + replaced = current_ownership.get("agent_id") != previous.get("agent_id") + if replaced and task_id not in replacements: + raise OwnershipBlocker( + "review-blocked", f"{task_id} repair owner replacement is not authorized" + ) + + def _acceptable_validation_results(item: dict[str, Any]) -> set[str]: raw = item.get("acceptable_results") if isinstance(raw, list) and raw: @@ -1735,7 +3385,9 @@ def _assert_changed_paths_in_write_scope(handoff: dict[str, Any], task_files: di raise SystemExit(f"Executor result changed path is outside task write scope: {path}") -def _assert_handoff_review_matches_task(handoff: dict[str, Any], task: dict[str, Any], state: str) -> None: +def _assert_handoff_review_matches_task( + handoff: dict[str, Any], task: dict[str, Any], state: str +) -> str | None: compiled_required = task.get("review_required") is True review = handoff.get("acceptance_review") if isinstance(handoff.get("acceptance_review"), dict) else {} handoff_required = review.get("required") is True @@ -1743,9 +3395,48 @@ def _assert_handoff_review_matches_task(handoff: dict[str, Any], task: dict[str, raise SystemExit("Executor result acceptance_review.required must match compiled review_required") if compiled_required and state == "completed" and review.get("verdict") != "accept": raise SystemExit("Review-required task cannot complete without acceptance_review.verdict: accept") + if not compiled_required or state != "completed": + return None + fit = handoff.get("task_fit_check") if isinstance(handoff.get("task_fit_check"), dict) else {} + repaired = fit.get("result") == "repaired" + mode = review.get("review_mode") + reset_initial = mode == "initial" and review.get("review_reset") is not None + if repaired and mode != "repair" and not reset_initial: + raise SystemExit("Repaired task completion requires a sequenced repair acceptance_review") + if mode is not None: + try: + from review_runtime import ReviewContractError, validate_task_acceptance_review + validated = validate_task_acceptance_review(review) + except (ReviewContractError, TypeError, ValueError) as error: + raise SystemExit(f"Task acceptance review is invalid: {error}") from error + if validated.verdict != "accepted": + raise SystemExit("Review-required task cannot complete without an accepted task review") + if validated.target_identity["artifact_id"] != str(task.get("task_id")): + raise SystemExit("Task acceptance review target identity does not match the completed task") + if validated.review_mode == "repair": + repositories = [ + item for item in _as_list(handoff.get("repository")) + if isinstance(item, dict) and item.get("target_kind") == "git-backed" + ] + if len(repositories) != 1: + raise SystemExit("Task repair review requires one observed Git repository identity") + root = Path(str(repositories[0].get("root") or "")).expanduser().resolve() + head = subprocess.run(["git", "-C", str(root), "rev-parse", "HEAD"], capture_output=True, text=True) + tree = subprocess.run(["git", "-C", str(root), "rev-parse", "HEAD^{tree}"], capture_output=True, text=True) + if head.returncode or tree.returncode: + raise SystemExit("Task repair review Git identity is unavailable") + if (review.get("reviewed_head") != head.stdout.strip() + or validated.target_identity["source_tree"] != tree.stdout.strip()): + raise SystemExit("Task repair review does not match the observed Git head/tree identity") + if validated.review_mode == "initial" and validated.review_reset is not None: + return "initial-reset" + return validated.review_mode + return None def _yaml_scalar(value: Any) -> str: + if isinstance(value, _SemanticReference): + return value.reference_id if value is True: return "true" if value is False: @@ -1960,7 +3651,12 @@ def _compile_task_brief(args: argparse.Namespace) -> tuple[Path, dict[str, Any]] truth_basis = _compile_truth_basis(task, records, source_paths) validation = [ { - key: value if key in {"id", "invariant_ids"} else _resolve_reference(value, records, source_paths) + key: ( + _compile_validation_references(value, records) + if key == "proves" + else value if key in {"id", "invariant_ids"} + else _resolve_reference(value, records, source_paths) + ) for key, value in item.items() } for item in validation_value @@ -1975,30 +3671,49 @@ def _compile_task_brief(args: argparse.Namespace) -> tuple[Path, dict[str, Any]] review_required = acceptance_review.get("required", False) if not isinstance(review_required, bool): raise SystemExit(f"Task acceptance_review.required must be boolean: {task_path}") - brief = { - "task_brief": { + semantic_authority = _compile_semantic_authority(source_ids, records, truth_basis) + capability_projection = _compile_capability_projection(executor_profile, rules, skill_names) + task_brief = { "task_id": task_id, "plan_id": plan_id, + "depends_on": [str(value) for value in _as_list(task.get("depends_on"))], "source_ids": source_ids, "goal": resolved_goal, "truth_basis": truth_basis, + "semantic_authority": semantic_authority, "requirements": source_by_kind["requirements"], "constraints": source_by_kind["constraints"], "interfaces": { - "consumes": _resolve_reference(_as_list(interfaces.get("consumes")), records, source_paths), - "produces": _resolve_reference(_as_list(interfaces.get("produces")), records, source_paths), + "consumes": _retain_source_references(_as_list(interfaces.get("consumes")), records), + "produces": _retain_source_references(_as_list(interfaces.get("produces")), records), }, "files": {"read": read_files, "write": write_files, "forbidden": forbidden_files}, "methodology": {"primary": methodology.get("primary", "direct"), "skills": skill_names}, "allocated_rules": rules, + "capability_projection": capability_projection, "executor_profile": executor_profile, "evidence_applicability": evidence_applicability, "workspace": {"root": str(root)}, + "runtime_context": { + "authority": "compiled", + "histories": "runtime_lazy", + "executor_retrieval": "forbidden", + }, "validation": validation, "evidence_capability": evidence_capability, "handoff_contract": "executor-result-v1", "review_required": review_required, - } + } + omitted = 0 + serialized_task = json.dumps(task, sort_keys=True, ensure_ascii=False) + for identifier in source_ids: + duplicate_references = max(0, serialized_task.count(identifier) - 1) + omitted += duplicate_references * max(0, len(records[identifier].encode("utf-8")) - len(identifier.encode("utf-8"))) + brief = { + "task_brief": task_brief, + "compiled_context_metrics": compiled_context_metrics( + task_brief, omitted_by_reference_bytes=omitted + ), } for identifier in source_ids: if not _contains_resolved_source_record(brief, records[identifier]): @@ -2162,6 +3877,19 @@ def build_review_package(args: argparse.Namespace) -> Path: handoff, _ = _read_structured(handoff_path) validated = validate_executor_result_for_task(handoff, task, observe=True, **_observation_kwargs(args)) knowledge_disposition = validated["knowledge_disposition"] + review_request = handoff.get("acceptance_review") if isinstance(handoff.get("acceptance_review"), dict) else {} + review_mode = str(review_request.get("review_mode") or "initial") + if review_mode not in {"initial", "repair"}: + raise SystemExit("review-blocked: review_mode must be initial or repair") + repair_frontier: dict[str, Any] | None = None + if review_mode == "repair": + try: + from review_runtime import ReviewContractError, _repair_frontier + repair_frontier = dict(_repair_frontier(review_request.get("repair_frontier"))) + except (ReviewContractError, TypeError, ValueError) as error: + raise SystemExit(f"review-blocked: invalid repair frontier: {error}") from error + elif review_request.get("repair_frontier") not in (None, {}): + raise SystemExit("review-blocked: initial review cannot carry repair_frontier") binding = load_task_execution_binding(root, plan_id, task_id) execution_root = Path(str(binding["execution_path"])).resolve() @@ -2170,16 +3898,47 @@ def build_review_package(args: argparse.Namespace) -> Path: head, diff, name_status, out_of_scope = _review_diff( execution_root, base, str(args.head), write_paths ) + if repair_frontier is not None: + base_tree = _git(execution_root, "rev-parse", f"{base}^{{tree}}").strip() + if head.startswith("worktree:"): + raise SystemExit("review-blocked: repair review requires a committed repaired identity") + head_tree = _git(execution_root, "rev-parse", f"{head}^{{tree}}").strip() + if repair_frontier["previous_reviewed_identity"]["source_tree"] != base_tree: + raise SystemExit("review-blocked: repair base does not match previous reviewed identity") + if repair_frontier["repaired_identity"]["source_tree"] != head_tree: + raise SystemExit("review-blocked: repair head does not match repaired identity") + omitted_diff_bytes = 0 if len(diff.encode("utf-8")) > MAX_DIFF_BYTES or diff.count("\n") > MAX_DIFF_LINES: oversized = ", ".join( sorted({path for line in name_status for path in _paths_from_name_status(line)}) ) or "unknown" - raise SystemExit( - "review-blocked: task-local review diff exceeds the bounded package limit " - f"(oversized paths: {oversized}). This is a compiler-or-plan defect, " - "not a reason to add implementation tasks." + if head.startswith("worktree:"): + raise SystemExit( + "review-blocked: oversized uncommitted task-local diff has no immutable " + f"source identity (oversized paths: {oversized})" + ) + omitted_diff_bytes = len(diff.encode("utf-8")) + base_tree = _git(execution_root, "rev-parse", f"{base}^{{tree}}").strip() + head_tree = _git(execution_root, "rev-parse", f"{head}^{{tree}}").strip() + diff_digest = hashlib.sha256(diff.encode("utf-8")).hexdigest() + changed_paths = sorted( + {path for line in name_status for path in _paths_from_name_status(line)} ) - diff = _redact_diff(diff) + diff = "\n".join( + [ + "Exact source diff reference (content omitted from this bounded packet).", + f"Base commit: {base}", + f"Base tree: {base_tree}", + f"Head commit: {head}", + f"Head tree: {head_tree}", + f"Task-local diff SHA-256: {diff_digest}", + "Changed paths:", + *[f"- {path}" for path in changed_paths], + "Review the exact Git diff between these commits, restricted to the changed paths above.", + ] + ) + else: + diff = _redact_diff(diff) changes = handoff.get("changes") if isinstance(handoff.get("changes"), dict) else {} handoff_files = [item for item in _as_list(changes.get("files")) if isinstance(item, dict)] @@ -2188,11 +3947,26 @@ def build_review_package(args: argparse.Namespace) -> Path: ) validation = handoff.get("validation") if isinstance(handoff.get("validation"), dict) else {} validation_commands = [item for item in _as_list(validation.get("commands")) if isinstance(item, dict)] + compiled_validation = { + str(item.get("command") or ""): item + for item in _as_list(task.get("validation")) + if isinstance(item, dict) + } + normalized_validation = [] + for position, item in enumerate(validation_commands, start=1): + compiled = compiled_validation.get(str(item.get("command") or ""), {}) + normalized_validation.append({**item, "id": item.get("id") or compiled.get("id") or f"validation-{position:03d}"}) + evidence_projection = project_validation_evidence( + normalized_validation, + evidence_capability=task.get("evidence_capability") if isinstance(task.get("evidence_capability"), dict) else {}, + observed=validated.get("observed_validation"), + expansion_reason=("failed_validation" if any(item.get("result") == "failed" for item in normalized_validation) else None), + ) unresolved = _as_list(handoff.get("unresolved")) evidence = { "changed_files": name_status, "changed_symbols": symbols, - "validation": validation_commands, + "validation": evidence_projection, "unresolved": unresolved, "knowledge_disposition": knowledge_disposition, } @@ -2214,6 +3988,7 @@ def build_review_package(args: argparse.Namespace) -> Path: f"Task: {task_id}", f"Base: {base}", f"Head: {head}", + f"Review mode: {review_mode}", "", "## Required behavior", *_markdown_items(required), @@ -2221,6 +3996,9 @@ def build_review_package(args: argparse.Namespace) -> Path: "## Accepted Truth Basis", *_markdown_items([task.get("truth_basis", {})]), "", + "## Semantic authority", + *_markdown_items([task.get("semantic_authority", {})]), + "", "## Evidence capability", *_markdown_items([task.get("evidence_capability", {})]), "", @@ -2234,7 +4012,7 @@ def build_review_package(args: argparse.Namespace) -> Path: *_markdown_items(symbols), "", "## Validation reported", - *_markdown_items(validation_commands), + *_markdown_items(evidence_projection), "", "## Knowledge disposition", *_markdown_items([knowledge_disposition]), @@ -2250,6 +4028,23 @@ def build_review_package(args: argparse.Namespace) -> Path: diff.rstrip(), "```", ] + if repair_frontier is not None: + lines.extend( + [ + "", + "## Repair frontier", + *_markdown_items( + [{ + "prior_review_id": repair_frontier["prior_review_id"], + "blocking_finding_ids": repair_frontier["blocking_finding_ids"], + "previous_reviewed_identity": repair_frontier["previous_reviewed_identity"], + "repaired_identity": repair_frontier["repaired_identity"], + "affected_boundaries": repair_frontier["affected_boundaries"], + "frozen_evidence_reference": repair_frontier["frozen_evidence_reference"], + }] + ), + ] + ) if out_of_scope: lines.extend( [ @@ -2278,6 +4073,20 @@ def build_review_package(args: argparse.Namespace) -> Path: review_target = target.with_name("review-package.md") review_target.parent.mkdir(parents=True, exist_ok=True) review_target.write_text(package, encoding="utf-8") + metrics = compiled_context_metrics( + task, + review_package=package, + evidence_projection=evidence_projection, + omitted_by_reference_bytes=omitted_diff_bytes, + expansion_reason=next( + (item.get("expansion_reason") for item in evidence_projection if item.get("expansion_reason")), + None, + ), + ) + review_target.with_name("review-package-metrics.json").write_text( + json.dumps({"compiled_context_metrics": metrics}, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) return review_target @@ -2294,7 +4103,23 @@ def cmd_build_review_package(args: argparse.Namespace) -> None: def cmd_observe_task_validation(args: argparse.Namespace) -> None: _, brief_document = _compile_task_brief(args) task = brief_document["task_brief"] - observed = _observe_completed_validation({}, task, task["validation"], None, **_observation_kwargs(args)) + runtime = _observation_kwargs(args) + observed = _observe_completed_validation( + {}, + task, + task["validation"], + None, + **{ + key: runtime[key] + for key in ( + "workspace_id", + "execution_id", + "repository_id", + "execution_runtime_root", + "accepted_dependency_deltas", + ) + }, + ) print(json.dumps({"validation": observed}, sort_keys=True)) @@ -2311,12 +4136,56 @@ def cmd_validate_executor_result(args: argparse.Namespace) -> None: print(handoff_path.relative_to(root).as_posix()) -def _observation_kwargs(args: argparse.Namespace) -> dict[str, str | None]: +def cmd_create_accepted_base_absence_receipt(args: argparse.Namespace) -> None: + root = resolve_workspace_root(args) + reference = create_accepted_base_absence_receipt( + root, + str(args.plan_id), + str(args.task_id), + str(args.expected_head), + str(args.expected_tree), + str(args.proposed_handoff_id), + str(args.proposed_review_id), + str(args.final_head), + str(args.final_tree), + ) + print(json.dumps(reference, sort_keys=True)) + + +def cmd_adopt_existing_recovered_result(args: argparse.Namespace) -> None: + root = resolve_workspace_root(args) + reference = adopt_existing_recovered_result( + root, + str(args.plan_id), + str(args.task_id), + str(args.expected_head), + str(args.expected_tree), + str(args.handoff_id), + str(args.handoff_sha256), + str(args.review_id), + str(args.final_head), + str(args.final_tree), + { + "receipt_id": str(args.prior_receipt_id), + "receipt_sha256": str(args.prior_receipt_sha256), + }, + ) + print(json.dumps(reference, sort_keys=True)) + + +def _observation_kwargs(args: argparse.Namespace) -> dict[str, Any]: return { "workspace_id": getattr(args, "workspace_id", None) or None, "execution_id": getattr(args, "execution_id", None) or None, "repository_id": getattr(args, "repository_id", None) or None, "execution_runtime_root": getattr(args, "execution_runtime_root", None) or None, + "accepted_dependency_deltas": ( + getattr(args, "accepted_dependency_deltas", None) or None + ), + "mutation_events": getattr(args, "mutation_events", None), + "prior_ownership": getattr(args, "prior_ownership", None) or None, + "repair_continuity": getattr(args, "repair_continuity", None) or None, + "authorized_replacements": getattr(args, "authorized_replacements", None) or None, } diff --git a/scripts/orchestration/plans.py b/scripts/orchestration/plans.py index ebc5b0d..21cdaef 100644 --- a/scripts/orchestration/plans.py +++ b/scripts/orchestration/plans.py @@ -5,6 +5,7 @@ from core import * from core import _member_roots from execution_context import ( + AcceptanceOwnershipError, cmd_validate_executor_result, evaluate_knowledge_closure_state, read_structured_artifact, @@ -124,6 +125,8 @@ def _try_validate_task_handoff( observe=capability.get("result") == "mapped", **_observation_kwargs(args), ) + except AcceptanceOwnershipError: + raise except SystemExit: return None return handoff, brief diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index 44c65d1..9113720 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -23,6 +23,11 @@ TERMINAL_FINDING_DISPOSITIONS = frozenset({"accepted", "rejected"}) STAGE_REVIEW_STAGES = frozenset({"specification", "plan", "integrated_implementation"}) REVIEW_VERDICTS = frozenset({"accepted", "repair", "blocked"}) +REVIEW_MODES = frozenset({"initial", "repair"}) +REVIEW_TARGET_KINDS = frozenset({"task", "stage"}) +MATERIAL_CHANGE_CLASSES = frozenset( + {"material_redesign", "authority", "scope", "acceptance", "decomposition", "validation_allocation"} +) PARTICIPATION_FIELDS = ( "authorship", "repair_participation", @@ -58,8 +63,13 @@ TARGET_KEYS = frozenset({"artifact_id", "revision", "sha256", "source_tree"}) EVIDENCE_ITEM_KEYS = frozenset({"kind", "locator", "digest_or_identity", "observation"}) STAGE_REVIEW_KEYS = frozenset( - {"review_id", "stage", "target_identity", "reviewer", "evidence", "verdict", "findings", "started_at", "completed_at", "staleness"} + {"review_id", "review_mode", "review_target_kind", "repair_frontier", "review_reset", "stage", "target_identity", "reviewer", "evidence", "verdict", "findings", "started_at", "completed_at", "staleness"} +) +LEGACY_STAGE_REVIEW_KEYS = STAGE_REVIEW_KEYS - {"review_mode", "review_target_kind", "repair_frontier", "review_reset"} +REPAIR_FRONTIER_KEYS = frozenset( + {"prior_review_id", "blocking_finding_ids", "previous_reviewed_identity", "repaired_identity", "affected_boundaries", "frozen_evidence_reference"} ) +REVIEW_RESET_KEYS = frozenset({"prior_review_id", "reason_class", "reason"}) REVIEWER_KEYS = frozenset( {"agent_id", "capability", *PARTICIPATION_FIELDS, "context_origin"} ) @@ -237,8 +247,15 @@ def stage_evidence_manifest(root: Path, source_root: Path, context: Mapping[str, if not locator.startswith("control:"): raise ReviewContractError("stage evidence target must be control-local") target = root / locator[8:] - required, missing = stage_evidence_requirements(root, str(context["stage"]), target) - source = source_snapshot_entries(source_root) if context["stage"] == "integrated_implementation" else [] + if context.get("review_mode", "initial") == "repair": + # Repair packets carry the repaired target and a compact immutable + # reference to prior evidence. Full durable history remains lazy. + required, missing = {locator: "target"}, [] + else: + required, missing = stage_evidence_requirements(root, str(context["stage"]), target) + source = (source_snapshot_entries(source_root) + if context["stage"] == "integrated_implementation" and context.get("review_mode", "initial") == "initial" + else []) for entry in source: required.setdefault(entry["locator"], "source_tree") available = {item["locator"]: item for item in artifacts} @@ -259,7 +276,9 @@ def stage_evidence_manifest(root: Path, source_root: Path, context: Mapping[str, entries.append(entry) return {"schema": "stage-evidence-manifest-v1", "stage": context["stage"], "target_identity": context["target_identity"], "entries": entries, - "source_tree": source, "missing": sorted(set(missing))} + "source_tree": source, "missing": sorted(set(missing)), + **({"repair_frontier_reference": context["repair_frontier"]["frozen_evidence_reference"]} + if context.get("review_mode") == "repair" else {})} def validate_stage_evidence(root: Path, context: Mapping[str, Any], packet: Mapping[str, Any]) -> None: @@ -271,9 +290,15 @@ def validate_stage_evidence(root: Path, context: Mapping[str, Any], packet: Mapp target = str(context["target_locator"]) if not target.startswith("control:"): raise ReviewContractError("stage evidence target must be control-local") - required, missing = stage_evidence_requirements(root, str(context["stage"]), root / target[8:]) + if context.get("review_mode", "initial") == "repair": + frontier = _repair_frontier(context.get("repair_frontier")) + if manifest.get("repair_frontier_reference") != frontier["frozen_evidence_reference"]: + raise ReviewContractError("repair stage evidence does not bind frozen evidence") + required, missing = {target: "target"}, [] + else: + required, missing = stage_evidence_requirements(root, str(context["stage"]), root / target[8:]) source = manifest.get("source_tree", []) - if context["stage"] == "integrated_implementation": + if context["stage"] == "integrated_implementation" and context.get("review_mode", "initial") == "initial": if snapshot_tree_identity(source) != context["target_identity"]["source_tree"]: raise ReviewContractError("stage evidence source snapshot is incomplete") for entry in source: @@ -344,6 +369,18 @@ def canonical(value: Any) -> str: result = {key: value for key, value in review.items() if key != "reviewer_run"} context = _mapping(receipt.get("stage_review_context", {}), "reviewer-run provenance context") mode = "direct_source" if review["evidence"]["mode"] == "direct" else review["evidence"]["mode"] + review_context = { + "review_mode": review.get("review_mode", "initial"), + "review_target_kind": review.get("review_target_kind", "stage"), + "repair_frontier": review.get("repair_frontier"), + "review_reset": review.get("review_reset"), + } + receipt_context = { + "review_mode": context.get("review_mode", "initial"), + "review_target_kind": context.get("review_target_kind", "stage"), + "repair_frontier": context.get("repair_frontier"), + "review_reset": context.get("review_reset"), + } if (receipt.get("schema") != "reviewer-process-receipt-v1" or receipt.get("run_id") != run_id or receipt.get("review_id") != review["review_id"] or receipt.get("status") != "passed" or receipt.get("exit_code") != 0 or receipt.get("review_result_sha256") != canonical(result) @@ -352,6 +389,7 @@ def canonical(value: Any) -> str: or context.get("agent_id") != review["reviewer"]["agent_id"] or context.get("evidence_mode") != review["reviewer"]["context_origin"] or context.get("capability") != review["reviewer"]["capability"] or context.get("evidence_mode") != mode + or review_context != receipt_context or receipt.get("isolation") != {"mechanism": "sandbox-exec", "network": "denied", "write_scope": "scratch"} or not context.get("execution_id") or receipt.get("sandbox_profile_sha256") != hashlib.sha256(immutable_file(path.with_suffix(".profile.sb"))).hexdigest() @@ -389,8 +427,10 @@ def _require_current_review(root: Path, stage: str, identity: Mapping[str, Any]) accepted = [] matching_values = [] review_ids: set[str] = set() + historical: dict[str, Mapping[str, Any]] = {} review_root = root / ".work-bundle/orchestration/reviews" try: + documents = [] for path in sorted(review_root.rglob("*")): if path.suffix not in {".json", ".yaml", ".yml"} or not path.is_file(): continue @@ -399,12 +439,33 @@ def _require_current_review(root: Path, stage: str, identity: Mapping[str, Any]) value = _read_document(path) if not isinstance(value, dict) or "review_id" not in value or "stage" not in value: continue + if value["stage"] == stage: + review_key = str(value["review_id"]) + if review_key in historical: + raise ReviewContractError("stage review IDs must be globally unique") + historical[review_key] = value + documents.append(value) + for value in documents: # Old target records remain history, not current acceptance candidates. # In particular, a superseded legacy evidence mode must not poison a # valid replacement review for the actual current artifact. if value["stage"] != stage or value.get("target_identity") != identity: continue record = validate_stage_review(value) + if record.review_mode == "repair": + frontier = record.repair_frontier + assert frontier is not None + prior = historical.get(str(frontier["prior_review_id"])) + if prior is None: + raise ReviewContractError("repair review predecessor is missing") + record = validate_review_sequence(value, previous_review=prior) + elif record.review_reset is not None: + prior = historical.get(str(record.review_reset["prior_review_id"])) + if prior is None: + raise ReviewContractError("initial review reset predecessor is missing") + record = validate_review_sequence( + value, previous_review=prior, material_change=str(record.review_reset["reason_class"]) + ) if record.review_id in review_ids: raise ReviewContractError("stage review IDs must be globally unique") review_ids.add(record.review_id) @@ -490,6 +551,10 @@ class ReviewFindingV1: @dataclass(frozen=True) class StageReviewV1: review_id: str + review_mode: str + review_target_kind: str + repair_frontier: Mapping[str, Any] | None + review_reset: Mapping[str, Any] | None stage: str target_identity: Mapping[str, Any] reviewer: Mapping[str, Any] @@ -564,6 +629,44 @@ def _target_identity(value: Any, name: str = "target_identity") -> Mapping[str, return target +def review_evidence_identity(value: Mapping[str, Any]) -> str: + """Return a compact immutable identity for reusable review evidence.""" + record = _mapping(value, "review") + evidence = _mapping(record.get("evidence"), "evidence") + payload = {"review_id": record.get("review_id"), "evidence": evidence, + "reviewer_run": record.get("reviewer_run")} + return hashlib.sha256( + json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode() + ).hexdigest() + + +def _repair_frontier(value: Any) -> Mapping[str, Any]: + frontier = _mapping(value, "repair_frontier") + _closed(frontier, REPAIR_FRONTIER_KEYS, "repair_frontier") + _identifier(frontier["prior_review_id"], "repair_frontier.prior_review_id") + finding_ids = _string_list(frontier["blocking_finding_ids"], "repair_frontier.blocking_finding_ids") + if not finding_ids or len(finding_ids) != len(set(finding_ids)): + raise ReviewContractError("repair_frontier.blocking_finding_ids must be non-empty and unique") + _target_identity(frontier["previous_reviewed_identity"], "repair_frontier.previous_reviewed_identity") + _target_identity(frontier["repaired_identity"], "repair_frontier.repaired_identity") + boundaries = _string_list(frontier["affected_boundaries"], "repair_frontier.affected_boundaries") + if not boundaries or len(boundaries) != len(set(boundaries)): + raise ReviewContractError("repair_frontier.affected_boundaries must be non-empty and unique") + reference = frontier["frozen_evidence_reference"] + if not isinstance(reference, str) or not SHA256_RE.fullmatch(reference): + raise ReviewContractError("repair_frontier.frozen_evidence_reference must be a lowercase SHA-256") + return frontier + + +def _review_reset(value: Any) -> Mapping[str, Any]: + reset = _mapping(value, "review_reset") + _closed(reset, REVIEW_RESET_KEYS, "review_reset") + _identifier(reset["prior_review_id"], "review_reset.prior_review_id") + _enum(reset["reason_class"], MATERIAL_CHANGE_CLASSES, "review_reset.reason_class") + _nonempty(reset["reason"], "review_reset.reason") + return reset + + def classify_first_broken_owner(finding_class: str) -> tuple[str, str, str]: try: return ROUTES[finding_class] @@ -657,7 +760,10 @@ def validate_stage_review( value: Mapping[str, Any], *, current_target_identity: Mapping[str, Any] | None = None ) -> StageReviewV1: record = _mapping(value, "stage_review_v1") - _closed({key: item for key, item in record.items() if key != "reviewer_run"}, STAGE_REVIEW_KEYS, "stage_review_v1") + envelope = {key: item for key, item in record.items() if key != "reviewer_run"} + keys = set(envelope) + if not LEGACY_STAGE_REVIEW_KEYS.issubset(keys) or not keys.issubset(STAGE_REVIEW_KEYS): + _closed(envelope, STAGE_REVIEW_KEYS, "stage_review_v1") if "reviewer_run" in record: reference = _mapping(record["reviewer_run"], "reviewer_run") _closed(reference, frozenset({"run_id", "sha256"}), "reviewer_run") @@ -665,8 +771,28 @@ def validate_stage_review( if not isinstance(reference["sha256"], str) or not SHA256_RE.fullmatch(reference["sha256"]): raise ReviewContractError("reviewer_run.sha256 must be a lowercase SHA-256") review_id = _identifier(record["review_id"], "review_id") + review_mode = _enum(record.get("review_mode", "initial"), REVIEW_MODES, "review_mode") + review_target_kind = _enum(record.get("review_target_kind", "stage"), REVIEW_TARGET_KINDS, "review_target_kind") + raw_frontier = record.get("repair_frontier") + raw_reset = record.get("review_reset") + if review_mode == "repair": + if raw_frontier is None: + raise ReviewContractError("repair review requires repair_frontier") + if raw_reset is not None: + raise ReviewContractError("repair review cannot carry review_reset; require a fresh initial review") + repair_frontier = _repair_frontier(raw_frontier) + review_reset = None + else: + if raw_frontier is not None: + raise ReviewContractError("initial review cannot carry repair_frontier") + repair_frontier = None + review_reset = _review_reset(raw_reset) if raw_reset is not None else None stage = _enum(record["stage"], STAGE_REVIEW_STAGES, "stage") + if review_target_kind != "stage": + raise ReviewContractError("stage_review_v1 review_target_kind must be stage") target = _target_identity(record["target_identity"]) + if repair_frontier is not None and repair_frontier["repaired_identity"] != target: + raise ReviewContractError("repair frontier repaired identity must equal target_identity") reviewer = _mapping(record["reviewer"], "reviewer") _closed(reviewer, REVIEWER_KEYS, "reviewer") _identifier(reviewer["agent_id"], "reviewer.agent_id") @@ -746,6 +872,10 @@ def validate_stage_review( raise ReviewContractError("snapshot review requires explicit reproducible_snapshot artifacts") return StageReviewV1( review_id, + review_mode, + review_target_kind, + repair_frontier, + review_reset, stage, target, reviewer, @@ -758,6 +888,108 @@ def validate_stage_review( ) +def validate_review_sequence( + value: Mapping[str, Any], *, previous_review: Mapping[str, Any] | None = None, + material_change: str | None = None, +) -> StageReviewV1: + """Bind a re-review to its exact predecessor without replaying history.""" + current = validate_stage_review(value) + if previous_review is None: + if current.review_mode == "repair" or current.review_reset is not None: + raise ReviewContractError("re-review requires the exact previous review") + return current + previous = validate_stage_review(previous_review) + if current.review_mode == "repair": + if material_change is not None: + _enum(material_change, MATERIAL_CHANGE_CLASSES, "material_change") + raise ReviewContractError("material change requires a fresh initial review") + frontier = current.repair_frontier + assert frontier is not None + if previous.verdict != "repair" or frontier["prior_review_id"] != previous.review_id: + raise ReviewContractError("repair frontier must bind the exact prior repair review") + if frontier["previous_reviewed_identity"] != previous.target_identity: + raise ReviewContractError("repair frontier previous reviewed identity does not match prior review") + expected_findings = {item.finding_id for item in previous.findings if item.severity == "blocking"} + if set(frontier["blocking_finding_ids"]) != expected_findings: + raise ReviewContractError("repair frontier blocking finding IDs do not match prior review") + if frontier["frozen_evidence_reference"] != review_evidence_identity(previous_review): + raise ReviewContractError("repair frontier frozen evidence reference does not match prior review") + return current + if material_change is not None: + _enum(material_change, MATERIAL_CHANGE_CLASSES, "material_change") + reset = current.review_reset + if (reset is None or reset["prior_review_id"] != previous.review_id + or reset["reason_class"] != material_change): + raise ReviewContractError("material change requires a recorded fresh initial review reset") + if current.reviewer["agent_id"] == previous.reviewer["agent_id"] or current.reviewer["capability"] != "judgment": + raise ReviewContractError("reset requires a fresh capable independent reviewer identity") + elif current.review_reset is not None: + raise ReviewContractError("review_reset requires a classified material change") + return current + + +def _task_review_as_stage(value: Mapping[str, Any]) -> dict[str, Any]: + """Adapt the existing task acceptance record to the native review validator.""" + record = _mapping(value, "task acceptance_review") + verdict = {"accept": "accepted", "repair": "repair", "blocked": "blocked"}.get(record.get("verdict")) + if verdict is None: + raise ReviewContractError("task acceptance_review verdict must be accept, repair, or blocked") + return { + "review_id": record.get("review_id"), + "review_mode": record.get("review_mode"), + "review_target_kind": "stage", + "repair_frontier": record.get("repair_frontier"), + "review_reset": record.get("review_reset"), + # This is an adapter discriminator, not a fourth stage-review seat. + "stage": "plan", + "target_identity": record.get("target_identity"), + "reviewer": record.get("reviewer"), + "evidence": record.get("evidence"), + "verdict": verdict, + "findings": record.get("findings"), + "started_at": record.get("started_at"), + "completed_at": record.get("completed_at"), + "staleness": record.get("staleness"), + **({"reviewer_run": record["reviewer_run"]} if "reviewer_run" in record else {}), + } + + +def validate_task_acceptance_review(value: Mapping[str, Any]) -> StageReviewV1: + """Validate a task acceptance review and its one bounded predecessor.""" + record = _mapping(value, "task acceptance_review") + current = validate_task_review_record(record) + mode = current.review_mode + previous = record.get("previous_review") + if mode == "repair": + if not isinstance(previous, Mapping): + raise ReviewContractError("task repair review requires its exact previous_review") + if "previous_review" in previous: + raise ReviewContractError("task repair review may carry exactly one previous_review; older history stays lazy") + validate_task_review_record(previous) + return validate_review_sequence(_task_review_as_stage(record), previous_review=_task_review_as_stage(previous)) + if record.get("review_reset") is not None: + if not isinstance(previous, Mapping): + raise ReviewContractError("task initial reset requires its exact previous_review") + validate_task_review_record(previous) + reset = _mapping(record["review_reset"], "review_reset") + return validate_review_sequence( + _task_review_as_stage(record), previous_review=_task_review_as_stage(previous), material_change=str(reset.get("reason_class")) + ) + return validate_review_sequence(_task_review_as_stage(record)) + + +def validate_task_review_record(value: Mapping[str, Any]) -> StageReviewV1: + """Validate one native task-review record without traversing history.""" + record = _mapping(value, "task acceptance_review") + if (record.get("required") is not True or record.get("review_target_kind") != "task" + or record.get("reviewer_independent") is not True): + raise ReviewContractError( + "task acceptance_review requires required: true, reviewer_independent: true, and review_target_kind: task" + ) + _enum(record.get("review_mode"), REVIEW_MODES, "task acceptance_review.review_mode") + return validate_stage_review(_task_review_as_stage(record)) + + def validate_stage_reviews( values: Sequence[Mapping[str, Any]], *, current_target_identities: Mapping[str, Mapping[str, Any]] | None = None, @@ -768,8 +1000,25 @@ def validate_stage_reviews( ids = [record.review_id for record in records] if len(ids) != len(set(ids)): raise ReviewContractError("stage review IDs must be globally unique") + raw_by_id = {record.review_id: value for record, value in zip(records, values)} + sequenced = [] + for record, value in zip(records, values): + if record.review_mode == "repair": + assert record.repair_frontier is not None + prior = raw_by_id.get(str(record.repair_frontier["prior_review_id"])) + if prior is None: + raise ReviewContractError("repair review predecessor is missing") + record = validate_review_sequence(value, previous_review=prior) + elif record.review_reset is not None: + prior = raw_by_id.get(str(record.review_reset["prior_review_id"])) + if prior is None: + raise ReviewContractError("initial review reset predecessor is missing") + record = validate_review_sequence( + value, previous_review=prior, material_change=str(record.review_reset["reason_class"]) + ) + sequenced.append(record) countable: dict[str, StageReviewV1] = {} - for record in sorted(records, key=lambda item: item.completed_at): + for record in sorted((item for item in sequenced if item.review_target_kind == "stage"), key=lambda item: item.completed_at): current = _target_identity(current_target_identities[record.stage], "current_target_identity") if record.target_identity == current: countable.pop(record.stage, None) diff --git a/scripts/orchestration/task_ownership.py b/scripts/orchestration/task_ownership.py new file mode 100644 index 0000000..72e8e9f --- /dev/null +++ b/scripts/orchestration/task_ownership.py @@ -0,0 +1,299 @@ +"""Provider-neutral subagent ownership and ready-wave scheduling.""" + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import PurePosixPath +from typing import Iterable, Mapping, Protocol, Sequence + + +PROVENANCE_FIELDS = frozenset({"delegated", "owner_kind", "agent_id", "run_id", "mechanism"}) +SUBAGENT_MECHANISMS = frozenset({"host-native", "execution-flow"}) +MUTATION_EVENT_FIELDS = frozenset({"actor_kind", "paths"}) +MUTATION_ACTOR_KINDS = frozenset({"controller", "subagent"}) + + +class OwnershipBlocker(RuntimeError): + """Typed orchestration blocker raised before mutation or acceptance.""" + + def __init__(self, code: str, reason: str) -> None: + self.code = code + self.reason = reason + super().__init__(f"{code}: {reason}") + + +@dataclass(frozen=True) +class TaskCandidate: + task_id: str + dependencies: tuple[str, ...] + write_scope: tuple[str, ...] + execution_workspace: str + common_contract: str | None = None + barrier: str | None = None + convergence_owner: str | None = None + barrier_participants: tuple[str, ...] = () + binding_id: str | None = None + + def __post_init__(self) -> None: + if not self.task_id.strip(): + raise ValueError("task_id must be non-empty") + if not self.write_scope or any(not str(path).strip() for path in self.write_scope): + raise ValueError("write_scope must contain explicit paths") + if not self.execution_workspace.strip(): + raise ValueError("execution_workspace must be non-empty") + + +@dataclass(frozen=True) +class RepairContinuity: + """Harness-owned identities a repair must carry without reacquisition.""" + + binding_id: str + baseline_identity: str + evidence_identity: str + previous_review_identity: str + + def __post_init__(self) -> None: + for field in ( + "binding_id", + "baseline_identity", + "evidence_identity", + "previous_review_identity", + ): + if not str(getattr(self, field)).strip(): + raise ValueError(f"{field} must be non-empty") + + +@dataclass(frozen=True) +class DispatchWaveResult: + dispatched: tuple[str, ...] + results: tuple[object, ...] + ownership: tuple[dict[str, object], ...] + operation: str + repair_continuity: tuple[RepairContinuity, ...] = () + + +@dataclass(frozen=True) +class SubagentDispatch: + handle: object + delegation_evidence: Mapping[str, object] + + +class SubagentAdapter(Protocol): + def available(self) -> bool: ... + + def dispatch(self, task: TaskCandidate, *, operation: str) -> SubagentDispatch: ... + + def wait(self, handle: object) -> object: ... + + +def _identifier(value: object, field: str) -> str: + normalized = str(value or "").strip() + if not normalized: + raise OwnershipBlocker("workspace-blocked", f"subagent {field} is missing") + return normalized + + +def normalize_subagent_provenance( + evidence: Mapping[str, object] | None, + *, + operation: str = "implementation", +) -> dict[str, object]: + """Validate the closed, provider-neutral task ownership receipt.""" + + if operation not in {"implementation", "repair"}: + raise ValueError("operation must be implementation or repair") + if not isinstance(evidence, Mapping): + raise OwnershipBlocker("workspace-blocked", f"{operation} requires subagent ownership") + if set(evidence) != PROVENANCE_FIELDS: + raise OwnershipBlocker("workspace-blocked", "subagent ownership provenance is not closed") + if evidence.get("delegated") is not True or evidence.get("owner_kind") != "subagent": + raise OwnershipBlocker("workspace-blocked", f"{operation} requires subagent ownership") + mechanism = _identifier(evidence.get("mechanism"), "mechanism") + if mechanism not in SUBAGENT_MECHANISMS: + raise OwnershipBlocker("workspace-blocked", "subagent mechanism is unsupported") + return { + "delegated": True, + "owner_kind": "subagent", + "agent_id": _identifier(evidence.get("agent_id"), "agent_id"), + "run_id": _identifier(evidence.get("run_id"), "run_id"), + "mechanism": mechanism, + } + + +def _scope_matches(path: str, scope: str) -> bool: + left = path.strip().removeprefix("./").rstrip("/") + right = scope.strip().removeprefix("./").rstrip("/") + return left == right or left.startswith(right + "/") or right.startswith(left + "/") + + +def _scopes_overlap(left: Sequence[str], right: Sequence[str]) -> bool: + return any(_scope_matches(a, b) for a in left for b in right) + + +def validate_task_acceptance_ownership( + *, + delegation_evidence: Mapping[str, object] | None, + mutation_events: Iterable[Mapping[str, object]], + write_scope: Sequence[str], + validations_passed: bool, + operation: str = "implementation", +) -> dict[str, object]: + """Reject controller-authored task-scope changes even after green validation.""" + + provenance = normalize_subagent_provenance(delegation_evidence, operation=operation) + for event in mutation_events: + if set(event) != MUTATION_EVENT_FIELDS: + raise OwnershipBlocker("review-blocked", "mutation event fields are not closed and complete") + actor_kind = event.get("actor_kind") + if not isinstance(actor_kind, str) or actor_kind not in MUTATION_ACTOR_KINDS: + raise OwnershipBlocker("review-blocked", "mutation event actor_kind is invalid") + paths = event.get("paths") + if not isinstance(paths, list) or not paths: + raise OwnershipBlocker("review-blocked", "mutation event paths must be a non-empty list") + changed: list[str] = [] + for path in paths: + if not isinstance(path, str): + raise OwnershipBlocker("review-blocked", "mutation event paths must contain strings") + normalized = path.strip().removeprefix("./") + parsed = PurePosixPath(normalized) + if (not normalized or parsed.is_absolute() or ".." in parsed.parts + or normalized in {".", "./"} or "\\" in normalized + or any(character in normalized for character in "*?[]")): + raise OwnershipBlocker("review-blocked", "mutation event path is unsafe") + changed.append(parsed.as_posix()) + if actor_kind != "controller": + continue + if _scopes_overlap(changed, write_scope): + raise OwnershipBlocker( + "review-blocked", + "controller mutated task-owned implementation scope", + ) + if not validations_passed: + raise OwnershipBlocker("validation-blocked", "task validation has not passed") + return provenance + + +def _validate_topology(task: TaskCandidate) -> None: + participant_fields = (task.common_contract, task.barrier, task.convergence_owner) + if any(participant_fields) and not all(participant_fields) and not task.barrier_participants: + raise OwnershipBlocker( + "workspace-blocked", + f"{task.task_id} has incomplete common-contract/barrier topology", + ) + if task.barrier_participants: + if not task.common_contract or not task.barrier: + raise OwnershipBlocker( + "workspace-blocked", + f"{task.task_id} convergence admission lacks common contract or barrier", + ) + if not set(task.barrier_participants).issubset(task.dependencies): + raise OwnershipBlocker( + "workspace-blocked", + f"{task.task_id} convergence dependencies omit barrier participants", + ) + + +def _ready_wave( + tasks: Sequence[TaskCandidate], + completed: set[str], + accepted_handoffs: set[str], +) -> list[TaskCandidate]: + for task in tasks: + _validate_topology(task) + ready = [ + task + for task in tasks + if task.task_id not in completed and set(task.dependencies).issubset(completed) + and set(task.barrier_participants).issubset(accepted_handoffs) + ] + wave: list[TaskCandidate] = [] + for task in ready: + if any(task.execution_workspace == selected.execution_workspace for selected in wave): + continue + if any(_scopes_overlap(task.write_scope, selected.write_scope) for selected in wave): + continue + wave.append(task) + return wave + + +class TaskOwnershipScheduler: + """Production admission and scheduling entry for ``orch-execute-plan``.""" + + def __init__(self, adapter: SubagentAdapter) -> None: + self._adapter = adapter + + def run_wave( + self, + tasks: Sequence[TaskCandidate], + *, + completed: set[str], + operation: str = "implementation", + accepted_handoffs: set[str] | None = None, + prior_ownership: Mapping[str, Mapping[str, object]] | None = None, + repair_continuity: Mapping[str, RepairContinuity] | None = None, + authorized_replacements: set[str] | None = None, + ) -> DispatchWaveResult: + if operation not in {"implementation", "repair"}: + raise ValueError("operation must be implementation or repair") + if not self._adapter.available(): + raise OwnershipBlocker("workspace-blocked", "subagent execution is unavailable") + wave = _ready_wave(tasks, completed, accepted_handoffs or set()) + if operation == "repair": + for task in wave: + if (prior_ownership or {}).get(task.task_id) is None: + raise OwnershipBlocker( + "review-blocked", f"{task.task_id} repair lacks prior owner" + ) + if (repair_continuity or {}).get(task.task_id) is None: + raise OwnershipBlocker( + "review-blocked", f"{task.task_id} repair lacks continuity identities" + ) + dispatched: list[SubagentDispatch] = [] + ownership: list[dict[str, object]] = [] + retained: list[RepairContinuity] = [] + for task in wave: + receipt = self._adapter.dispatch(task, operation=operation) + if not isinstance(receipt, SubagentDispatch): + raise OwnershipBlocker("workspace-blocked", "subagent dispatch receipt is invalid") + current_owner = normalize_subagent_provenance( + receipt.delegation_evidence, operation=operation + ) + if operation == "repair": + previous = (prior_ownership or {}).get(task.task_id) + continuity = (repair_continuity or {}).get(task.task_id) + assert previous is not None and continuity is not None + previous_owner = normalize_subagent_provenance(previous) + replaced = current_owner["agent_id"] != previous_owner["agent_id"] + if replaced and task.task_id not in (authorized_replacements or set()): + raise OwnershipBlocker( + "review-blocked", + f"{task.task_id} repair owner replacement is not authorized", + ) + retained.append(continuity) + ownership.append(current_owner) + dispatched.append(receipt) + results = tuple(self._adapter.wait(receipt.handle) for receipt in dispatched) + return DispatchWaveResult( + dispatched=tuple(task.task_id for task in wave), + results=results, + ownership=tuple(ownership), + operation=operation, + repair_continuity=tuple(retained), + ) + + def validate_acceptance( + self, + *, + delegation_evidence: Mapping[str, object] | None, + mutation_events: Iterable[Mapping[str, object]], + write_scope: Sequence[str], + validations_passed: bool, + operation: str = "implementation", + ) -> dict[str, object]: + return validate_task_acceptance_ownership( + delegation_evidence=delegation_evidence, + mutation_events=mutation_events, + write_scope=write_scope, + validations_passed=validations_passed, + operation=operation, + ) diff --git a/scripts/work-bundle/control_plane.py b/scripts/work-bundle/control_plane.py index 5ab1bfe..2b52d0c 100644 --- a/scripts/work-bundle/control_plane.py +++ b/scripts/work-bundle/control_plane.py @@ -658,7 +658,6 @@ def _render_v4( " operation_policy: inherit", ] ) - lines.append(f"prefer_subagent: {str(_parse_bool(_yaml_scalar(v3_text, 'prefer_subagent'))).lower()}") lines.append(_agents_contract_block()) lines.extend(_portable_unknown_blocks(v3_text)) return "\n".join(lines).rstrip() + "\n", workspace_id, repositories @@ -1079,7 +1078,6 @@ def _render_new_v4_for_mode( f" required: {str(bool(repository.get('required', True))).lower()}", " operation_policy: inherit", ]) - lines.append("prefer_subagent: false") lines.append(_agents_contract_block()) return "\n".join(lines) + "\n", workspace_id diff --git a/scripts/work-bundle/core.py b/scripts/work-bundle/core.py index 064dfa0..a3b4c2d 100644 --- a/scripts/work-bundle/core.py +++ b/scripts/work-bundle/core.py @@ -56,7 +56,6 @@ execution-workspace-cleanup-owned --runtime-root --workspace-id ... execution-workspace-doctor-stale [--runtime-root ] [--cleanup] instruction-audit --root [--soft-threshold-words ] - set-prefer-subagent --scope [--project-root ] generate-project-metadata-profile --input --output merge-project-metadata-profile --current --incoming --output validate-project-metadata-profile @@ -138,17 +137,6 @@ def compact_yaml_map(text: str) -> dict[str, str]: return data -def yaml_bool(value: object, default: bool = False) -> bool: - if value is None: - return default - normalized = str(value).strip().lower() - if normalized in {'true', 'yes', 'on', '1'}: - return True - if normalized in {'false', 'no', 'off', '0', ''}: - return False - return default - - def utc_now_rfc3339() -> str: return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace('+00:00', 'Z') @@ -176,37 +164,13 @@ def resolve_bootstrap_runtime() -> dict[str, object]: config_root = work_bundle_config_root() bootstrap_path = config_root / GLOBAL_BOOTSTRAP_FILE_NAME resolved = resolve_work_bundle_root() - bootstrap = compact_yaml_map(read(bootstrap_path)) if bootstrap_path.is_file() else {} - global_prefer_subagent = yaml_bool(bootstrap.get('prefer_subagent'), False) return { 'work_bundle_config_root': str(config_root), 'global_bootstrap_path': str(bootstrap_path), 'global_bootstrap_exists': bootstrap_path.is_file(), 'resolved_work_bundle_root': str(resolved) if resolved else None, - 'prefer_subagent': global_prefer_subagent, } def project_metadata_path(project_root: Path) -> Path: return project_root.expanduser().resolve() / '.work-bundle' / 'project.yaml' - - -def resolve_effective_prefer_subagent(project_root: Path | None = None) -> dict[str, object]: - config_root = work_bundle_config_root() - bootstrap_path = config_root / GLOBAL_BOOTSTRAP_FILE_NAME - bootstrap = compact_yaml_map(read(bootstrap_path)) if bootstrap_path.is_file() else {} - global_prefer_subagent = yaml_bool(bootstrap.get('prefer_subagent'), False) - - project_path = project_metadata_path(project_root) if project_root is not None else None - project_metadata = compact_yaml_map(read(project_path)) if project_path and project_path.is_file() else {} - has_project_override = 'prefer_subagent' in project_metadata - effective = yaml_bool(project_metadata.get('prefer_subagent'), global_prefer_subagent) if has_project_override else global_prefer_subagent - source = 'project' if has_project_override else ('global' if 'prefer_subagent' in bootstrap else 'default') - return { - 'prefer_subagent': effective, - 'source': source, - 'project_prefer_subagent': yaml_bool(project_metadata.get('prefer_subagent'), False) if has_project_override else None, - 'global_prefer_subagent': global_prefer_subagent, - 'global_bootstrap_path': str(bootstrap_path), - 'project_metadata_path': str(project_path) if project_path else None, - } diff --git a/scripts/work-bundle/dispatcher.py b/scripts/work-bundle/dispatcher.py index 8486660..851c83d 100644 --- a/scripts/work-bundle/dispatcher.py +++ b/scripts/work-bundle/dispatcher.py @@ -11,7 +11,7 @@ from legacy import cmd_legacy_command_removed from metadata_profile import cmd_domain_profile from bootstrap_config import cmd_migrate_work_bundle_config -from project import cmd_cleanup_member, cmd_doctor_project, cmd_init_project, cmd_migrate_project, cmd_migrate_to_multi_repository, cmd_project, cmd_provision_member, cmd_register_project_command, cmd_session_start, cmd_set_prefer_subagent, cmd_show_project, cmd_validate_project +from project import cmd_cleanup_member, cmd_doctor_project, cmd_init_project, cmd_migrate_project, cmd_migrate_to_multi_repository, cmd_project, cmd_provision_member, cmd_register_project_command, cmd_session_start, cmd_show_project, cmd_validate_project from rules import cmd_create_rules, cmd_validate_rules from skill_registry import cmd_merge_skill_hints, cmd_registry from stage_events import cmd_stage_events @@ -97,7 +97,7 @@ def _load_evaluation_identity(): 'defer-workspace-member', 'attach-deferred-remote', 'doctor-project', 'provision-member', 'cleanup-member', 'credential-list', 'instruction-audit', 'session-start', 'inspect-project-initialization', - 'validate-project', 'set-prefer-subagent', 'create-rules', 'validate-rules', + 'validate-project', 'create-rules', 'validate-rules', 'defect-ensure-store', 'defect-create-evidence', 'defect-build-index', 'defect-write-index', 'defect-archive-evidence', 'defect-migrate-store', 'validate-contract', 'assert-migration-stop', @@ -207,8 +207,6 @@ def main() -> int: return cmd_project(parsed.args, inspect_only=True, repo_model=True) if command == 'validate-project': return cmd_validate_project(parsed.args) - if command == 'set-prefer-subagent': - return cmd_set_prefer_subagent(parsed.args) if command == 'create-rules': return cmd_create_rules(parsed.args) if command == 'validate-rules': diff --git a/scripts/work-bundle/migration.py b/scripts/work-bundle/migration.py index 220a1e9..8b58cd8 100644 --- a/scripts/work-bundle/migration.py +++ b/scripts/work-bundle/migration.py @@ -405,7 +405,6 @@ def _metadata_text( 'workspace_mode: multi-repository', f'project_root: {_yaml_string(member_root)}', f'industry: {_yaml_string(repository_id)}', - 'prefer_subagent: false', 'metadata_compatibility:', ' readable_versions: [2, 3]', ' migration_requires_explicit_apply: true', diff --git a/scripts/work-bundle/project.py b/scripts/work-bundle/project.py index acbe427..d642ca9 100644 --- a/scripts/work-bundle/project.py +++ b/scripts/work-bundle/project.py @@ -1135,7 +1135,6 @@ def inspect_project(project_root: Path) -> dict: 'global_bootstrap_path': runtime.get('global_bootstrap_path'), 'global_bootstrap_exists': runtime.get('global_bootstrap_exists'), 'resolved_work_bundle_root': runtime.get('resolved_work_bundle_root'), - 'prefer_subagent': resolve_effective_prefer_subagent(project_root), } @@ -1368,17 +1367,6 @@ def _set_yaml_scalar(path: Path, key: str, value: str) -> bool: return changed -def set_prefer_subagent(project_root: Path, scope: str, enabled: bool) -> tuple[Path, bool]: - value = 'true' if enabled else 'false' - if scope == 'global': - path = work_bundle_config_root() / GLOBAL_BOOTSTRAP_FILE_NAME - elif scope == 'project': - path = project_metadata_path(project_root) - else: - raise ValueError(f'unsupported prefer_subagent scope: {scope}') - return path, _set_yaml_scalar(path, 'prefer_subagent', value) - - def _ensure_registry_schema_version(text: str, version: str = REGISTRY_SCHEMA_VERSION) -> str: rendered = text if text.endswith('\n') else text + '\n' for line in rendered.splitlines(): @@ -2575,29 +2563,6 @@ def cmd_migrate_to_multi_repository(args: list[str]) -> int: return 0 -def cmd_set_prefer_subagent(args: list[str]) -> int: - parser = argparse.ArgumentParser(prog="wb.py set-prefer-subagent") - parser.add_argument("value", choices=["true", "false", "enable", "disable", "enabled", "disabled", "on", "off"]) - parser.add_argument("--scope", choices=["global", "project"], required=True) - parser.add_argument("--project-root", default=".") - parsed = parser.parse_args(args) - enabled = parsed.value in {"true", "enable", "enabled", "on"} - project_root = Path(parsed.project_root).expanduser().resolve() - target_path, changed = set_prefer_subagent(project_root, parsed.scope, enabled) - effective = resolve_effective_prefer_subagent(project_root) - out({ - "command": "set-prefer-subagent", - "status": "updated" if changed else "skipped", - "scope": parsed.scope, - "prefer_subagent": enabled, - "target_path": str(target_path), - "project_root": str(project_root), - "effective_prefer_subagent": effective, - "changed_files": [str(target_path)] if changed else [], - }) - return 0 - - def apply_layout_v2_to_v3( project_root: Path, name: str | None = None, diff --git a/scripts/work-bundle/reviewer_workspace.py b/scripts/work-bundle/reviewer_workspace.py index 00a5d11..a5149ca 100644 --- a/scripts/work-bundle/reviewer_workspace.py +++ b/scripts/work-bundle/reviewer_workspace.py @@ -47,7 +47,8 @@ def _review_runtime(): def _validate_stage_context(context: object) -> dict[str, object]: fields = {"stage", "target_identity", "target_locator", "agent_id", "capability", "execution_id", "evidence_mode"} - if not isinstance(context, dict) or set(context) != fields: + repair_fields = {"review_mode", "review_target_kind", "repair_frontier", "review_reset"} + if not isinstance(context, dict) or frozenset(context) not in {frozenset(fields), frozenset(fields | repair_fields)}: raise ReviewerWorkspaceError("WB_REVIEW_STAGE_CONTEXT_INVALID") if (context["stage"] not in {"specification", "plan", "integrated_implementation"} or context["capability"] not in {"standard", "judgment"} @@ -55,6 +56,18 @@ def _validate_stage_context(context: object) -> dict[str, object]: or not all(isinstance(context[key], str) and context[key] for key in ("agent_id", "execution_id"))): raise ReviewerWorkspaceError("WB_REVIEW_STAGE_CONTEXT_INVALID") _review_runtime()._target_identity(context["target_identity"]) + if repair_fields.issubset(context): + try: + mode = _review_runtime()._enum(context["review_mode"], _review_runtime().REVIEW_MODES, "review_mode") + _review_runtime()._enum(context["review_target_kind"], _review_runtime().REVIEW_TARGET_KINDS, "review_target_kind") + if mode == "repair": + _review_runtime()._repair_frontier(context["repair_frontier"]) + if context["review_reset"] is not None: + raise ValueError("repair reset") + elif context["repair_frontier"] is not None: + raise ValueError("initial frontier") + except (ValueError, TypeError): + raise ReviewerWorkspaceError("WB_REVIEW_STAGE_CONTEXT_INVALID") from None return context @@ -668,11 +681,24 @@ def run_sandboxed_reviewer(workspace: Path, argv: list[str]) -> dict[str, object except (ValueError, TypeError) as error: raise ReviewerWorkspaceError("WB_REVIEW_STAGE_OUTPUT_INVALID") from error mode = "direct_source" if validated.evidence["mode"] == "direct" else validated.evidence["mode"] + review_context = { + "review_mode": validated.review_mode, + "review_target_kind": validated.review_target_kind, + "repair_frontier": validated.repair_frontier, + "review_reset": validated.review_reset, + } + packet_context = { + "review_mode": context.get("review_mode", "initial"), + "review_target_kind": context.get("review_target_kind", "stage"), + "repair_frontier": context.get("repair_frontier"), + "review_reset": context.get("review_reset"), + } if ("reviewer_run" in review or validated.review_id != review_id or validated.stage != context["stage"] or validated.target_identity != context["target_identity"] or validated.reviewer["agent_id"] != context["agent_id"] or validated.reviewer["context_origin"] != context["evidence_mode"] - or validated.reviewer["capability"] != context["capability"] or mode != context["evidence_mode"]): + or validated.reviewer["capability"] != context["capability"] or mode != context["evidence_mode"] + or review_context != packet_context): raise ReviewerWorkspaceError("WB_REVIEW_STAGE_OUTPUT_MISMATCH") if validated.verdict == "accepted": try: diff --git a/scripts/work-bundle/stage_events.py b/scripts/work-bundle/stage_events.py index fa70359..430a9b6 100644 --- a/scripts/work-bundle/stage_events.py +++ b/scripts/work-bundle/stage_events.py @@ -48,7 +48,23 @@ ) IDENTITY_FIELDS = frozenset({"product_tree", "artifact_digest", "mutation_epoch"}) CLOCK_FIELDS = frozenset({"wall_ms", "active_ms", "billed_ms"}) -EVENT_FIELDS = frozenset( +COMPILED_CONTEXT_METRIC_FIELDS = frozenset( + { + "task_brief_bytes", + "semantic_authority_bytes", + "allocated_rule_bytes", + "allocated_skill_bytes", + "capability_projection_bytes", + "evidence_projection_bytes", + "review_package_bytes", + "omitted_by_reference_bytes", + "expansion_reason", + } +) +CONTEXT_EXPANSION_REASONS = frozenset( + {None, "failed_validation", "ambiguity", "reviewer_request", "authority_gap"} +) +LEGACY_EVENT_FIELDS = frozenset( { "event_id", "timestamp", @@ -66,6 +82,7 @@ "privacy", } ) +EVENT_FIELDS = LEGACY_EVENT_FIELDS | {"compiled_context_metrics"} _ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]*$") _OID_RE = re.compile(r"^[0-9a-f]{40}$") @@ -103,9 +120,13 @@ class StageEventV1: owner: str | None identity: dict[str, str | int | None] privacy: str + compiled_context_metrics: dict[str, int | str | None] | None = None def to_dict(self) -> dict[str, object]: - return asdict(self) + result = asdict(self) + if self.compiled_context_metrics is None: + result.pop("compiled_context_metrics") + return result def _fail(code: str) -> None: @@ -133,7 +154,7 @@ def _parse_timestamp(value: object) -> datetime: def _scan_for_sensitive_content(value: object, *, key: str | None = None) -> None: - if key is not None and _SENSITIVE_KEY_RE.search(key): + if key is not None and key != "compiled_context_metrics" and _SENSITIVE_KEY_RE.search(key): _fail("WB_STAGE_EVENT_PRIVACY_INVALID") if isinstance(value, Mapping): for nested_key, nested_value in value.items(): @@ -157,7 +178,10 @@ def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: """Validate one closed API-004 event without retaining caller-owned containers.""" _scan_for_sensitive_content(payload) - value = _validate_exact_fields(payload, EVENT_FIELDS, "WB_STAGE_EVENT_FIELDS_INVALID") + if set(payload) == LEGACY_EVENT_FIELDS: + value = payload + else: + value = _validate_exact_fields(payload, EVENT_FIELDS, "WB_STAGE_EVENT_FIELDS_INVALID") for field in ("event_id", "process_id", "attempt_id"): if not _is_id(value[field]): _fail("WB_STAGE_EVENT_ID_INVALID") @@ -206,6 +230,21 @@ def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: if value["privacy"] != "operational_metadata_only": _fail("WB_STAGE_EVENT_PRIVACY_INVALID") + metrics_value = value.get("compiled_context_metrics") + metrics: dict[str, int | str | None] | None = None + if metrics_value is not None: + checked = _validate_exact_fields( + metrics_value, + COMPILED_CONTEXT_METRIC_FIELDS, + "WB_STAGE_EVENT_CONTEXT_METRICS_INVALID", + ) + for field in COMPILED_CONTEXT_METRIC_FIELDS - {"expansion_reason"}: + if not _is_nonnegative_int(checked[field]): + _fail("WB_STAGE_EVENT_CONTEXT_METRICS_INVALID") + if checked["expansion_reason"] not in CONTEXT_EXPANSION_REASONS: + _fail("WB_STAGE_EVENT_CONTEXT_METRICS_INVALID") + metrics = {field: checked[field] for field in sorted(COMPILED_CONTEXT_METRIC_FIELDS)} + return StageEventV1( event_id=str(value["event_id"]), timestamp=str(value["timestamp"]), @@ -221,6 +260,7 @@ def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: owner=value["owner"] if isinstance(value["owner"], str) else None, identity={field: identity[field] for field in sorted(IDENTITY_FIELDS)}, privacy="operational_metadata_only", + compiled_context_metrics=metrics, ) diff --git a/skills/orch-create-handoff/SKILL.md b/skills/orch-create-handoff/SKILL.md index 6720e12..9525eff 100644 --- a/skills/orch-create-handoff/SKILL.md +++ b/skills/orch-create-handoff/SKILL.md @@ -54,7 +54,7 @@ Always include identity, related artifacts, result state, and concise summary. I - `task_fit_check` for completed or partial task results, covering the compiled task brief and assigned task. Escalate to full source artifacts only for inconsistent compiled context or a reviewer-reported source-contract problem. - `repository` when repository preflight, accepted baseline, changed paths, or blocker state matters. - `codegraph` when source-code inspection or edits were in scope; keep it to `root`, `applicable`, `up_to_date`, and fallback/blocker facts unless more detail is needed. -- `delegation_evidence` when task, phase, or plan ownership was delegated or fallback proof is required; record `internal_spawn_used_for_task_delegation: false`. +- `delegation_evidence` for task ownership; record only delegated state, `owner_kind: subagent`, minimum agent/run identity, and provider-neutral mechanism. ## Hard Rules @@ -66,7 +66,7 @@ Always include identity, related artifacts, result state, and concise summary. I - Do not include durable-knowledge recommendations, orchestration review recommendations, executor advice fields, or strategy advice in executor-result handoffs. - Stop if source artifact paths or current state are unknown. - Executor-result handoffs must list changed files or inspected artifacts, validation, unresolved blockers when present, and compact `task_fit_check` when applicable. -- Executor-result handoffs must not omit applicable `codegraph:` or `delegation_evidence:` and must not record contradictory delegation evidence such as `internal_spawn_used_for_task_delegation: true`. +- Executor-result handoffs must not omit applicable `codegraph:` or task `delegation_evidence:` and must not include UI, visibility, fallback, or controller-ownership fields. ## Status and Index @@ -87,7 +87,7 @@ Load only when creating or validating: ## Validation -Confirm required sparse YAML metadata exists, referenced specs/plans/phases/tasks/files are listed when applicable, unresolved blockers are explicit when present, executor-result fields are complete by applicability rather than fixed section presence, forbidden executor advice fields are absent, applicable `codegraph:` evidence includes compact up-to-date or fallback/blocker facts, delegated executor-result handoffs include `delegation_evidence:`, `visible_reference` when available, and `internal_spawn_used_for_task_delegation: false`, raw chat is excluded, no handoff is written under `.work-bundle/knowledge/`, no active orchestration handoff is created, and execution-completion handoffs did not invoke retrieval. +Confirm required sparse YAML metadata exists, referenced specs/plans/phases/tasks/files are listed when applicable, unresolved blockers are explicit when present, executor-result fields are complete by applicability rather than fixed section presence, forbidden executor advice fields are absent, applicable `codegraph:` evidence includes compact up-to-date or fallback/blocker facts, task executor-result handoffs include neutral `delegation_evidence` with agent/run identity and mechanism, raw chat is excluded, no handoff is written under `.work-bundle/knowledge/`, no active orchestration handoff is created, and execution-completion handoffs did not invoke retrieval. ## Runtime Rules diff --git a/skills/orch-doctor/SKILL.md b/skills/orch-doctor/SKILL.md index 299f89c..0985f2b 100644 --- a/skills/orch-doctor/SKILL.md +++ b/skills/orch-doctor/SKILL.md @@ -55,14 +55,14 @@ Verify: 11. orchestration evals that require v3 role labels are backed by orch skill documentation; 12. specification and plan contracts allocate `dev-semantic-convergence`, caller-specific lenses, compact `semantic_loop` evidence, and the body-level `Quality gate: verified|blocked` result where applicable; 13. task contracts carry source IDs, methodology, provider-neutral capability, compiled-brief context, and acceptance-review fields; -14. execution contracts preserve no-retrieval execution, compile bounded task/review packets, require fresh task validation plus acceptance review, and keep reduced-independence fallback explicit; +14. execution contracts preserve no-retrieval execution, compile bounded task/review packets, require mandatory subagent task ownership, and require fresh task validation plus acceptance review; 15. the CodeGraph-first rule remains conditional on an indexed target and requires a recorded fallback reason when unavailable; 16. active orchestration contracts do not depend on `HABITS.md` or the deprecated role-selection subsystem. 17. executor-result contracts default to sparse YAML, require fields by applicability, reject forbidden executor advice fields, and do not require active orchestration handoffs; 18. compact CodeGraph evidence retains `root`, `applicable`, `up_to_date`, and required fallback or blocker facts, including `no-index` and `sync-failed` where applicable; -19. compact visible delegation evidence uses `delegation_evidence`, `visible_reference` when available, and `internal_spawn_used_for_task_delegation: false`; -20. no active orch contract reintroduces invisible internal spawn work as a valid task-delegation vehicle; -21. handoff and review contracts validate compact CodeGraph and visible delegation outcomes by applicability rather than fixed prose sections. +19. compact neutral ownership evidence uses `delegation_evidence` with only delegated state, subagent owner kind, agent/run identity, and provider-neutral mechanism; +20. no active orch contract permits controller or single-agent task mutation; +21. handoff and review contracts validate compact CodeGraph and neutral ownership outcomes by applicability rather than fixed prose sections. ## Workflow Integrity Checks @@ -70,9 +70,9 @@ Verify: - `orch-create-specification`, `orch-create-implementation-plan`, `orch-create-handoff`, `orch-execute-plan`, `orch-review-plan`, and `orch-doctor` keep distinct responsibilities; - artifact creation modes do not execute implementation work; -- `orch-execute-plan` checks sub-agent support before delegation; -- `orch-execute-plan` preserves the single-agent fallback and does not fail only because sub-agents are unavailable; -- layered `prefer_subagent` remains permission-only and cannot bypass preflight, dependency, scope, or handoff safety checks; +- `orch-execute-plan` requires subagent support before any implementation or repair mutation and fails closed when unavailable; +- the orchestration thread schedules, compiles, coordinates, validates, reviews, and manages lifecycle without mutating task write scope; +- planner-proven independent disjoint tasks dispatch before waiting while dependent, overlapping, and same-workspace tasks serialize; - executor-result handoffs require local task-fit evidence against the compiled brief and assigned task, with full lifecycle artifacts reserved for inconsistent context or source-contract escalation; - executor-result handoffs default to sparse YAML, omit non-applicable fields, reject forbidden executor advice fields, and require compact `codegraph:` and `delegation_evidence:` only when applicable; - active orchestration handoffs are unavailable and continuation uses active specs, plans, tasks, indexes, and executor-result handoffs; @@ -85,13 +85,13 @@ Verify: Look for one-sided or conflicting instructions that would bias execution toward a single path when alternatives are required: -- sub-agent scheduler must not be mandatory when sub-agents are unavailable or unsafe; -- single-agent fallback must not silently bypass required handoffs or status updates; +- mandatory subagent ownership must fail closed before mutation when no safe subagent path is available; +- controller scheduling must not become controller implementation or repair; - review must not be treated as execution; - execution completion must not imply archival; - durable knowledge extraction must not be implied by executor-result handoffs; - CodeGraph sync evidence must not be optional when `.codegraph/` exists and graph-derived source work is in scope; -- visible delegation wording must not allow invisible internal spawn work to own delegated plan, phase, or task execution. +- ownership wording must remain provider-neutral and must not depend on UI visibility. Deterministic doctor checks are limited to bounded file presence, JSON shape, required contract terms, and forbidden active dependencies. They must not judge @@ -119,7 +119,7 @@ Files changed: none ## Validation -Confirm `dev-rules-doctor` was used first, diagnostics stayed read-only, orch skill coverage was checked, skill front matter was checked, semantic-convergence and compiled-context terms were present, sparse YAML and applicability terms were present, forbidden executor advice fields and active orchestration handoffs were rejected, compact CodeGraph and visible delegation safety terms were present, invisible internal spawn task-delegation regressions were absent, forbidden active dependencies were absent, workflow responsibilities remained distinct, required fallback paths were present, archival remained isolated to `orch-review-plan`, and no files were changed. +Confirm `dev-rules-doctor` was used first, diagnostics stayed read-only, orch skill coverage was checked, skill front matter was checked, semantic-convergence and compiled-context terms were present, sparse YAML and applicability terms were present, forbidden executor advice fields and active orchestration handoffs were rejected, compact CodeGraph and neutral subagent ownership terms were present, controller mutation and single-agent fallback regressions were absent, forbidden active dependencies were absent, workflow responsibilities remained distinct, fail-closed paths were present, archival remained isolated to `orch-review-plan`, and no files were changed. ## Runtime Rules @@ -137,12 +137,12 @@ Diagnose develop-rules installation health and orchestrator workflow consistency - Stop and report the blocker if `dev-rules-doctor` cannot run; do not treat installation health as passed. - Perform a read-only orchestrator audit across orchestrator skill files, workflow reference, orchestration evals, and helper commands in `scripts/orch.py`. - Verify skill coverage, front matter consistency, workflow responsibility separation, retrieval-policy mappings, helper command availability or declared fallback behavior, and required execution fallback paths. -- Verify `orch-execute-plan` compiles bounded task/review packets, preserves visible sub-agent and reduced-independence fallback paths, requires fresh validation plus task acceptance, and does not archive artifacts during execution. +- Verify `orch-execute-plan` compiles bounded task/review packets, requires mandatory subagent ownership for implementation and repair, fails closed before mutation when unavailable, requires fresh validation plus task acceptance, and does not archive artifacts during execution. - Verify `orch-review-plan` is the only skill that archives completed specification, plan, and handoff artifacts. - Verify knowledge-using orch skills route through `keep-summarizing` rather than direct `.work-bundle/knowledge/` browsing. - Verify executor-result contracts default to sparse YAML, require fields by applicability, omit non-applicable fields, reject forbidden executor advice fields, and do not require active orchestration handoffs. - Verify compact CodeGraph evidence includes `root`, `applicable`, `up_to_date`, and accepted fallback or blocker facts such as `no-index` and `sync-failed` where applicable. -- Verify compact visible delegation evidence uses `delegation_evidence`, `visible_reference` when available, and `internal_spawn_used_for_task_delegation: false`, and does not permit invisible internal spawn work to own delegated task execution. +- Verify compact neutral ownership evidence uses `delegation_evidence` with minimum agent/run identity and mechanism, rejects UI/visibility fields, and does not permit controller-owned task execution. - Look for workflow bias such as false review independence, skipped handoffs, scheduler-owned code review, mandatory full lifecycle context for a valid brief, or handoff conclusions treated as persisted knowledge. - Report findings as concrete repair actions with cited conflicting artifacts when issues are found. - Emit doctor output with `Files changed: none`. diff --git a/skills/orch-execute-plan/SKILL.md b/skills/orch-execute-plan/SKILL.md index 2362f54..876efa0 100644 --- a/skills/orch-execute-plan/SKILL.md +++ b/skills/orch-execute-plan/SKILL.md @@ -1,6 +1,6 @@ --- name: orch-execute-plan -description: 'Execute a WorkBundle task, phase, or plan through compiled task briefs, task-local methodology, optional independent acceptance review, and dependency-aware scheduling.' +description: 'Execute a WorkBundle task, phase, or plan through mandatory subagent task ownership, compiled task briefs, task-local methodology, optional independent acceptance review, and dependency-aware scheduling.' --- # orch-execute-plan @@ -23,9 +23,9 @@ python3 scripts/orch.py build-task-brief --task ``` Missing source IDs; decision authority other than `none-relevant` or an `AUTH-NNN` alias whose carried constraint was reconciled in the verified specification; `conflict_status: escalate`; inconsistent scope; or unsafe workspace state fails closed with the existing typed blocker. Truth Basis conflict uses `decision-blocked`. The compiled brief includes `AUTH-NNN: `, not the alias alone. -7. Choose the provider-neutral capability from the task profile. Partition only independent tasks with disjoint write scopes. Contract-decoupled participants validate against the common contract, accepted prior handoffs, and task-local files; they reach the named barrier before convergence work. -8. When user/environment policy permits delegation, use visible multi-agent subagents for task ownership. Invisible helper workers may support bounded analysis only. If independent subagents are unavailable, use single-agent execution and mark later review `reviewer_independent: false`. -9. Always validate the executor-result with the shared helper. For a mapped capability task, report each validation under its compiled evidence ID and invariant IDs, and add `evidence_closure` using the allocated boundary, freshness, and evidence IDs. The helper observes required process/inspection items in the bound worktree as one Git-state-neutral batch, reuses the compiled identities, and rejects missing, incapable, contradictory, stale, wrong-boundary, failed, or unexecuted evidence before authorizing from post-execution task-caused delta. Executor closure claims are corroboration, not harness proof. Compile `build-review-package` and assign `dev-code-review` only when `acceptance_review.required: true` or compiled `review_required: true`. The scheduler does not perform code-quality review. +7. Choose the provider-neutral capability from the task profile. Consume planner-proven dependencies, write scopes, common-contract groups, barriers, convergence ownership, and isolation requirements. Dispatch every ready independent task with disjoint write scope to a separate execution workspace before awaiting any result. Serialize dependent, overlapping, or same-workspace mutation. Contract-decoupled participants validate against the common contract, accepted prior handoffs, and task-local files; they reach the named barrier before convergence work. +8. Selecting `orch-execute-plan` makes every implementation and repair task subagent-owned. Use the production `TaskOwnershipScheduler` entry in `scripts/orchestration/task_ownership.py` with a host-native or Execution-Flow adapter; do not reproduce its admission logic in prompts or detached helpers. Before any task mutation, it confirms adapter availability and binds each dispatched task to validated neutral agent/run provenance. If no subagent is available, it fails closed with `workspace-blocked`; there is no controller or single-agent fallback. Do not substitute `reviewer_independent: false` for a missing task owner. The orchestration thread may schedule, compile briefs, coordinate barriers, validate results, route reviews, and manage lifecycle, but it must not implement or repair task write scope. +9. Use `TaskOwnershipScheduler.validate_acceptance` to validate neutral `delegation_evidence` and reject acceptance when mutation provenance shows the controller changed task-owned write scope, even if validation is green. Then validate the executor-result with the shared helper. For a mapped capability task, report each validation under its compiled evidence ID and invariant IDs, and add `evidence_closure` using the allocated boundary, freshness, and evidence IDs. The helper observes required process/inspection items in the bound worktree as one Git-state-neutral batch, reuses the compiled identities, and rejects missing, incapable, contradictory, stale, wrong-boundary, failed, or unexecuted evidence before authorizing from post-execution task-caused delta. Executor closure claims are corroboration, not harness proof. Compile `build-review-package` and assign `dev-code-review` only when `acceptance_review.required: true` or compiled `review_required: true`. The scheduler does not perform code-quality review. ```bash python3 scripts/orch.py validate-executor-result --task --handoff @@ -34,6 +34,7 @@ python3 scripts/orch.py validate-executor-result --task --handoff --repository-id --remote --name --path --default-branch (--dry-run|--accepted-proposal-id --apply)` | | Provision member | `provision-member --workspace-root [--workspace-slug ] --origin --repository-id --working-branch --base-ref [--dry-run|--apply]` | | Cleanup member | `cleanup-member --workspace-root --repository-id (--dry-run|--apply)` | -| Set sub-agent preference | `set-prefer-subagent --scope [--project-root ]` | `initialize-project` remains a compatibility alias for `init-project`; prefer `init-project` in new instructions. -The explicit `--workspace-root` and `--project-root` selectors remain available only on commands whose live help lists them, such as `show-project` and project-scoped `set-prefer-subagent`. New creation must reject a missing or contradictory mode/root combination rather than silently infer topology. Single-repository mode is current and fully supported, not legacy or transitional. +The explicit `--workspace-root` and `--project-root` selectors remain available only on commands whose live help lists them, such as `show-project`. New creation must reject a missing or contradictory mode/root combination rather than silently infer topology. Single-repository mode is current and fully supported, not legacy or transitional. **Portable v4 migration guardrails:** before `migrate-control-plane`, load every applicable rule body in full, including project context, registry authority, lifecycle, repository boundary, security exclusion, and defect routing. Do not sample those rules by keyword. Resolve canonical remotes from explicit `--repository-remote` input, registry locator authority, and the live origin chain. When authoritative network remotes conflict, stop and ask the user which remote is canonical; rerun the exact dry-run with `--repository-remote` after the decision. During this workflow, do not edit the project registry directly and do not change an external repository's Git config. `show-project`, `validate-project`, and `doctor-project` route metadata-version-4 workspaces to v4 control-plane validation; repair must never rewrite portable v4 metadata into v3 shape. @@ -79,20 +76,6 @@ An exact workspace-local checkout created by an older WorkBundle version may hav **`init-project --dry-run` / `validate-project --dry-run`:** inspect and report mechanical failures without writing project files. -**`prefer_subagent` management:** - -- `prefer_subagent` is a boolean preference only; it does not bypass orchestration preflight, dependency, write-scope, handoff, or fallback rules. -- Effective value resolves as project metadata first, then global bootstrap, then `false`: - - project: `$workspace_root/.work-bundle/project.yaml` -> `prefer_subagent`; - - global: `$work_bundle_config_root/bootstrap.yaml` -> `prefer_subagent`; - - default: `false`. -- Use `set-prefer-subagent true --scope global` for requests such as "Enable global `prefer_subagent`". -- Use `set-prefer-subagent false --scope global` for requests such as "Disable global `prefer_subagent`". -- Use `set-prefer-subagent false --scope project --project-root ` for requests such as "Disable `prefer_subagent` for current workspace". -- Use `set-prefer-subagent true --scope project --project-root ` for requests such as "Enable `prefer_subagent` for current workspace". -- The command updates only the selected YAML file and reports `target_path`, `changed_files`, and `effective_prefer_subagent` in JSON. -- Do not hand-edit either YAML file for this preference when the dispatcher command is available. - **Registry and slug (per `wb-project-registry`):** - Resolve the project registry path from `bootstrap.yaml` field `project_registry`. @@ -137,7 +120,6 @@ An exact workspace-local checkout created by an older WorkBundle version may hav - In multi-repository mode place runtime Git control stores beneath `$workspace_root/.work-bundle/git/` and exclude them from workspace-management commits and broad scans. - Render `.work-bundle/project.yaml` from `references/assets/template/project.yaml`. - Render `.work-bundle/project.yaml` with metadata v3 workspace/member state from mechanical Git and per-member `.codegraph/` inspection. -- Render `.work-bundle/project.yaml` with a `prefer_subagent: false` default unless a future template version explicitly changes the default. - Render `.work-bundle/project.yaml` with an `agents_sync` section that owns WorkBundle `AGENTS.md` checksum and sync-status state. - Create, append, or refresh `AGENTS.md` with the WorkBundle managed section from `references/assets/template/AGENTS.md`; do not overwrite user-authored content outside the managed section. - Create or preserve required `.gitignore` entries. @@ -193,7 +175,6 @@ Use `migrate-project` only for unambiguous single-repository legacy layout upgra - Initialized, doctored, validated, registered, or migrated project workspace. - Updated bootstrap-resolved project-registry locator or metadata-v4 device binding when its lifecycle command runs. - JSON command output with `status`, `failures`, registry status, metadata version/mode/resources/member evidence, redacted lifecycle transaction state, AGENTS sync evidence, and changed files where applicable. Compatibility reads retain existing v2 evidence fields until explicit migration. -- For `set-prefer-subagent`, JSON command output with `status`, `scope`, `prefer_subagent`, `target_path`, `changed_files`, and `effective_prefer_subagent`. - Migration report and optional legacy-bootstrap archive paths under `.work-bundle/orchestration/docs/` when migration retires legacy artifacts. ## On Failure diff --git a/tests/test_control_plane_v4.py b/tests/test_control_plane_v4.py index 378da49..8e5b3ba 100644 --- a/tests/test_control_plane_v4.py +++ b/tests/test_control_plane_v4.py @@ -13,7 +13,12 @@ REPO_ROOT = Path(__file__).resolve().parents[1] -sys.path.insert(0, str(REPO_ROOT / "scripts/work-bundle")) +WORK_BUNDLE_SCRIPTS = REPO_ROOT / "scripts/work-bundle" +loaded_core = sys.modules.get("core") +loaded_core_path = Path(getattr(loaded_core, "__file__", "")) if loaded_core is not None else None +if loaded_core_path is not None and WORK_BUNDLE_SCRIPTS not in loaded_core_path.parents: + sys.modules.pop("core", None) +sys.path.insert(0, str(WORK_BUNDLE_SCRIPTS)) from workspace_resources import CREDENTIAL_TEMPLATE, SCRIPT_INDEX_TEMPLATE from control_plane import ( ControlPlaneError, @@ -66,7 +71,6 @@ def config_root(tmp_path: Path) -> Path: f"work_bundle_root: {REPO_ROOT}", 'project_registry: "$work_bundle_config_root/registry/projects.yaml"', 'skill_registry: "$work_bundle_config_root/registry/skill-registry.yaml"', - "prefer_subagent: false", "", ] ), @@ -269,7 +273,7 @@ def test_v3_to_v4_migration_is_deterministic_and_splits_local_state(tmp_path: Pa assert "workspace:\n id: wb-" in metadata assert f"canonical: {remote}" in metadata assert "custom_portable:" in metadata - for forbidden in ("workspace_root:", "project_root:", "observed_head:", "observation_time:", "git_control_root:"): + for forbidden in ("workspace_root:", "project_root:", "observed_head:", "observation_time:", "git_control_root:", "prefer_subagent:"): assert forbidden not in metadata registry = (config / "registry/projects.yaml").read_text(encoding="utf-8") assert str(workspace) in registry @@ -1383,8 +1387,8 @@ def test_v4_schema_rejects_duplicate_repository_ids_and_invalid_mode(tmp_path: P assert run_wb(config, "init-workspace", str(workspace), "--slug", "demo", "--repository", f"source-main={remote}", "--apply").returncode == 0 metadata = workspace / ".work-bundle/project.yaml" text = metadata.read_text(encoding="utf-8") - duplicate = text[text.index(" - id: source-main"):text.index("prefer_subagent:")] - metadata.write_text(text.replace(" mode: multi-repository", " mode: invalid").replace("prefer_subagent:", duplicate + "prefer_subagent:"), encoding="utf-8") + duplicate = text[text.index(" - id: source-main"):text.index("agents_sync:")] + metadata.write_text(text.replace(" mode: multi-repository", " mode: invalid").replace("agents_sync:", duplicate + "agents_sync:", 1), encoding="utf-8") doctor = run_wb(config, "doctor-workspace", str(workspace)) failures = json.loads(doctor.stdout)["portable"]["failures"] assert "WB_CONTROL_PLANE_WORKSPACE_MODE_INVALID" in failures @@ -1765,7 +1769,7 @@ def write_composite_metadata(workspace: Path, *, include_root: bool = True, memb "", ] ) - text = text.replace("prefer_subagent:", member + "prefer_subagent:") + text = text.replace("agents_sync:", member + "agents_sync:", 1) metadata.write_text(text, encoding="utf-8") @@ -1933,7 +1937,7 @@ def test_v4_composite_schema_rejects_duplicate_member_paths(self) -> None: ] ) metadata.write_text( - metadata.read_text(encoding="utf-8").replace("prefer_subagent:", extra + "prefer_subagent:"), + metadata.read_text(encoding="utf-8").replace("agents_sync:", extra + "agents_sync:", 1), encoding="utf-8", ) doctor = run_wb(config, "doctor-workspace", str(workspace)) @@ -1945,7 +1949,7 @@ def test_v4_existing_modes_still_reject_crossed_bindings(self) -> None: metadata = workspace / ".work-bundle/project.yaml" text = metadata.read_text(encoding="utf-8") metadata.write_text( - text.replace("prefer_subagent:", "\n".join([ + text.replace("agents_sync:", "\n".join([ " - id: extra-member", " role: source", " remote:", @@ -1958,8 +1962,8 @@ def test_v4_existing_modes_still_reject_crossed_bindings(self) -> None: " materialization:", " required: true", " operation_policy: inherit", - "prefer_subagent:", - ])), + "agents_sync:", + ]), 1), encoding="utf-8", ) doctor = run_wb(config, "doctor-workspace", str(workspace)) diff --git a/tests/test_multi_repository_member.py b/tests/test_multi_repository_member.py index 0e38da9..f0fd544 100644 --- a/tests/test_multi_repository_member.py +++ b/tests/test_multi_repository_member.py @@ -231,7 +231,7 @@ def test_multi_member_add_preserves_mode_and_replays_without_root_git(multi, ado registry = config / "registry/projects.yaml" original = metadata.read_text() # Extra top-level fields must not capture the appended repository block. - original = original.replace("prefer_subagent: false\n", "") + "custom_owner_field: retained\n" + original = original + "custom_owner_field: retained\n" metadata.write_text(original) before = metadata.read_bytes(), registry.read_bytes() proposal = propose(config, workspace, remote) @@ -399,9 +399,9 @@ def test_member_add_preserves_yaml_section_boundaries(multi, tmp_path, monkeypat elif shape == "anchored-header": text = text.replace("source_repositories:", "source_repositories: &sources") elif shape == "quoted-key": - text = text.replace("prefer_subagent:", "'custom_owner_field': retained\nprefer_subagent:") + text = text.replace("agents_sync:", "'custom_owner_field': retained\nagents_sync:", 1) elif shape == "quoted-nested": - text = text.replace("prefer_subagent:", "'owner_settings':\n contact:\n team: engineering\nprefer_subagent:") + text = text.replace("agents_sync:", "'owner_settings':\n contact:\n team: engineering\nagents_sync:", 1) elif shape == "commented-control": text = text.replace("control_plane:", "control_plane: # portable settings") elif shape == "owner-flow": diff --git a/tests/test_orchestration_execution_context.py b/tests/test_orchestration_execution_context.py index ed78dde..cf3e3a0 100644 --- a/tests/test_orchestration_execution_context.py +++ b/tests/test_orchestration_execution_context.py @@ -2,6 +2,7 @@ import argparse import hashlib +import importlib.util import json import re import shlex @@ -23,6 +24,20 @@ from execution_context import build_review_package, build_task_brief # noqa: E402 +def _load_orchestration_dispatcher(): + path = ORCHESTRATION / "dispatcher.py" + spec = importlib.util.spec_from_file_location( + "test_orchestration_execution_context_dispatcher", path + ) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +orchestration_dispatcher = _load_orchestration_dispatcher() + + def _counted_validation(tmp_path: Path, reuse_seconds: int = 3600, suffix: str = ""): root, _, task = workspace(tmp_path) _ensure_source_file(root) @@ -244,6 +259,148 @@ def test_observe_task_validation_is_dispatched() -> None: assert "--task" in completed.stdout +@pytest.mark.parametrize("command", ["validate-executor-result", "set-plan-status"]) +def test_normal_cli_projects_controller_mutation_events_into_acceptance( + tmp_path: Path, command: str +) -> None: + root, task, brief, handoff, _ = _counted_validation(tmp_path) + handoff_path = root / ".work-bundle/orchestration/handoff/executor/active/handoff-task-004.yaml" + handoff_path.write_text( + "\n".join(execution_context._dump_yaml(handoff)) + "\n", encoding="utf-8" + ) + runtime = json.dumps( + [{"actor_kind": "controller", "paths": [brief["files"]["write"][0]]}] + ) + if command == "validate-executor-result": + operation = [command, "--task", str(task), "--handoff", str(handoff_path)] + else: + operation = [ + command, + "--id", + "task-004", + "--kind", + "task", + "--plan-id", + "plan-001", + "--status", + "Completed", + "--handoff", + str(handoff_path), + ] + parsed = orchestration_dispatcher.build_parser().parse_args( + [*operation, "--project-root", str(root), "--mutation-events", runtime] + ) + with pytest.raises(SystemExit, match="controller mutated task-owned implementation scope"): + parsed.func(parsed) + task_data, _ = execution_context._read_structured(task) + assert task_data.get("status") != "Completed" + + +@pytest.mark.parametrize("command", ["validate-executor-result", "set-plan-status"]) +def test_completed_task_cli_rejects_missing_controller_mutation_evidence( + tmp_path: Path, command: str +) -> None: + root, task, _, handoff, _ = _counted_validation(tmp_path) + handoff_path = root / ".work-bundle/orchestration/handoff/executor/active/handoff-task-004.yaml" + handoff_path.write_text( + "\n".join(execution_context._dump_yaml(handoff)) + "\n", encoding="utf-8" + ) + if command == "validate-executor-result": + operation = [command, "--task", str(task), "--handoff", str(handoff_path)] + else: + operation = [ + command, + "--id", + "task-004", + "--kind", + "task", + "--plan-id", + "plan-001", + "--status", + "Completed", + "--handoff", + str(handoff_path), + ] + parsed = orchestration_dispatcher.build_parser().parse_args( + [*operation, "--project-root", str(root)] + ) + with pytest.raises(SystemExit, match="mutation.*evidence|mutation_events"): + parsed.func(parsed) + task_data, _ = execution_context._read_structured(task) + assert task_data.get("status") != "Completed" + + +@pytest.mark.parametrize("command", ["validate-executor-result", "set-plan-status"]) +def test_completed_task_cli_accepts_complete_no_controller_mutation_evidence( + tmp_path: Path, command: str +) -> None: + root, task, _, handoff, _ = _counted_validation(tmp_path) + handoff_path = root / ".work-bundle/orchestration/handoff/executor/active/handoff-task-004.yaml" + handoff_path.write_text( + "\n".join(execution_context._dump_yaml(handoff)) + "\n", encoding="utf-8" + ) + if command == "validate-executor-result": + operation = [command, "--task", str(task), "--handoff", str(handoff_path)] + else: + operation = [ + command, + "--id", + "task-004", + "--kind", + "task", + "--plan-id", + "plan-001", + "--status", + "Completed", + "--handoff", + str(handoff_path), + ] + parsed = orchestration_dispatcher.build_parser().parse_args( + [*operation, "--project-root", str(root), "--mutation-events", "[]"] + ) + parsed.func(parsed) + if command == "set-plan-status": + task_data, _ = execution_context._read_structured(task) + assert task_data["status"] == "Completed" + + +@pytest.mark.parametrize( + "command", ["validate-executor-result", "set-plan-status", "build-review-package"] +) +def test_acceptance_cli_projects_all_controller_owned_runtime_inputs(command: str) -> None: + expected = { + "mutation_events": [{"actor_kind": "controller", "paths": ["src/a.py"]}], + "accepted_dependency_deltas": [{"task_id": "task-a"}], + "prior_ownership": {"task-b": {"agent_id": "agent-a"}}, + "repair_continuity": {"task-b": {"binding_id": "binding-b"}}, + "authorized_replacements": ["task-b"], + } + if command == "validate-executor-result": + operation = [command, "--task", "task.md", "--handoff", "handoff.yaml"] + elif command == "set-plan-status": + operation = [command, "--id", "task-b", "--status", "Completed"] + else: + operation = [ + command, + "--task", + "task.md", + "--handoff", + "handoff.yaml", + "--base", + "base", + "--head", + "head", + ] + argv = list(operation) + for name, value in expected.items(): + argv.extend(["--" + name.replace("_", "-"), json.dumps(value)]) + + parsed = orchestration_dispatcher.build_parser().parse_args(argv) + + projected = execution_context._observation_kwargs(parsed) + assert {name: projected[name] for name in expected} == expected + + def test_source_records_keep_letter_suffixed_ids_distinct(tmp_path: Path) -> None: specification = tmp_path / "spec.md" body = ( @@ -658,6 +815,7 @@ def args(root: Path, task: Path, **overrides: object) -> argparse.Namespace: "execution_id": None, "repository_id": None, "execution_runtime_root": None, + "mutation_events": [], } values.update(overrides) return argparse.Namespace(**values) @@ -714,6 +872,12 @@ def evidence_blocks(root: Path, *, codegraph: str = "no-index") -> str: "codegraph:\n" f" - root: {root.resolve()}\n" f"{codegraph_block}" + "delegation_evidence:\n" + " delegated: true\n" + " owner_kind: subagent\n" + " agent_id: execution-context-fixture-agent\n" + " run_id: execution-context-fixture-run\n" + " mechanism: host-native\n" ) @@ -1899,7 +2063,7 @@ def test_review_package_keeps_sibling_and_rename_paths_as_out_of_scope_diagnosti assert "No out-of-scope change is present" not in package -def test_review_package_overflow_fails_closed_on_write_scope_diff_only( +def test_review_package_projects_committed_oversized_diff_by_exact_source_identity( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: root, _, task = workspace(tmp_path) @@ -1915,14 +2079,27 @@ def test_review_package_overflow_fails_closed_on_write_scope_diff_only( base = git(root, "rev-parse", "HEAD") scoped.write_text("def compile_task():\n return 'IN-SCOPE-OVERFLOW-PAYLOAD'\n", encoding="utf-8") outsider.write_text("OUTSIDE = '" + ("Y" * 400) + "'\n", encoding="utf-8") + git(root, "add", ".") + git(root, "commit", "-qm", "oversized committed task result") + head = git(root, "rev-parse", "HEAD") + head_tree = git(root, "rev-parse", "HEAD^{tree}") handoff = _bind_passing_observation(root, task) - with pytest.raises(SystemExit, match="review-blocked|bounded package limit") as error: - build_review_package(args(root, task, handoff=str(handoff), base=base, head="worktree")) - - message = str(error.value) - assert WRITE_SCOPE_FILE in message - assert "implementation task" not in message.lower() or "not a reason to add implementation" in message + target = build_review_package( + args(root, task, handoff=str(handoff), base=base, head=head) + ) + package = target.read_text(encoding="utf-8") + metrics = json.loads( + target.with_name("review-package-metrics.json").read_text(encoding="utf-8") + )["compiled_context_metrics"] + + assert "Exact source diff reference" in package + assert f"Base commit: {base}" in package + assert f"Head commit: {head}" in package + assert f"Head tree: {head_tree}" in package + assert WRITE_SCOPE_FILE in package + assert "IN-SCOPE-OVERFLOW-PAYLOAD" not in package + assert metrics["omitted_by_reference_bytes"] > 0 def test_review_package_does_not_overflow_on_out_of_scope_payload( @@ -2124,7 +2301,9 @@ def test_validate_executor_result_accepts_explicit_shaped_no_index_evidence(tmp_ handoff = _read_handoff(_completed_handoff_payload(root)) brief = _compiled_brief(root, task) - validated = execution_context.validate_executor_result_for_task(handoff, brief) + validated = execution_context.validate_executor_result_for_task( + handoff, brief, mutation_events=[] + ) assert validated["evidence_applicability"] == brief["evidence_applicability"] @@ -2168,7 +2347,9 @@ def test_helper_observation_accepts_metadata_only_applicability_without_codegrap "baseline_status": "current", } - validated = execution_context.validate_executor_result_for_task(handoff, brief, observe=True) + validated = execution_context.validate_executor_result_for_task( + handoff, brief, observe=True, mutation_events=[] + ) assert validated["evidence_applicability"]["codegraph"]["required"] is False @@ -2381,6 +2562,283 @@ def test_validate_executor_result_rejects_review_required_upgrade(tmp_path: Path _validate_observed(_read_handoff(handoff), brief) +def _repair_completion_fixture() -> tuple[dict, dict]: + brief = { + "task_id": "task-rf", "plan_id": "plan-rf", "review_required": True, + "source_ids": [], "files": {"read": [], "write": [], "forbidden": []}, + "truth_basis": {}, "validation": [], + "evidence_applicability": { + "metadata": {"required": False, "reasons": []}, + "repository": {"required": False, "reasons": []}, + "codegraph": {"required": False, "reasons": []}, + }, + "evidence_capability": {"result": "no_validation_bearing_obligation", "invariants": []}, + } + handoff = { + "type": "executor-result", "related": {"plan": "plan-rf", "task": "task-rf"}, + "result": {"state": "completed"}, + "task_fit_check": {"task": "task-rf", "result": "repaired"}, + "acceptance_review": {"required": True, "verdict": "accept"}, + "delegation_evidence": { + "delegated": True, + "owner_kind": "subagent", + "agent_id": "repair-fixture-agent", + "run_id": "repair-fixture-run", + "mechanism": "host-native", + }, + "knowledge_disposition": {"action": "none", "reason": "No authority change.", "affected_authority": []}, + } + return brief, handoff + + +def test_rf_task_repair_completion_rejects_unsequenced_acceptance_review() -> None: + brief, handoff = _repair_completion_fixture() + with pytest.raises(SystemExit, match="repair.*review|review.*repair"): + execution_context.validate_executor_result_for_task(handoff, brief) + + +def _material_reset_task_review() -> dict: + prior_identity = { + "artifact_id": "task-rf", "revision": "1", "sha256": "1" * 64, "source_tree": None, + } + current_identity = { + "artifact_id": "task-rf", "revision": "1", "sha256": "2" * 64, "source_tree": None, + } + prior = { + "required": True, + "reviewer_independent": True, + "verdict": "repair", + "review_id": "review-prior-material", + "review_mode": "initial", + "review_target_kind": "task", + "repair_frontier": None, + "review_reset": None, + "target_identity": prior_identity, + "reviewer": { + "agent_id": "reviewer-prior", + "capability": "judgment", + "authorship": "none", + "repair_participation": "none", + "decision_participation": "none", + "deliberation_participation": "none", + "context_origin": "direct_source", + }, + "evidence": { + "mode": "direct", "capabilities": ["source inspection"], + "unavailable_evidence": [], "commands": [], "artifacts": [], + }, + "findings": [{ + "finding_id": "RF-MATERIAL-1", + "stage": "implementation", + "class": "implementation_defect", + "severity": "blocking", + "first_broken_artifact": "implementation", + "obligation_basis": "accepted_requirement", + "evidence": [{ + "kind": "test", "locator": "RF", "digest_or_identity": "RF-RED", + "observation": "failed", + }], + "target_identity": prior_identity, + "summary": "Accepted boundary changed materially.", + "recommended_owner": "task_owner", + "disposition": "repair_task", + }], + "started_at": "2026-09-06T00:00:00Z", + "completed_at": "2026-09-06T00:01:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + return { + "required": True, + "reviewer_independent": True, + "verdict": "accept", + "review_id": "review-current-material-reset", + "review_mode": "initial", + "review_target_kind": "task", + "repair_frontier": None, + "review_reset": { + "prior_review_id": prior["review_id"], + "reason_class": "scope", + "reason": "Accepted write scope changed.", + }, + "target_identity": current_identity, + "reviewer": {**prior["reviewer"], "agent_id": "reviewer-reset"}, + "evidence": { + "mode": "direct", "capabilities": ["source inspection"], + "unavailable_evidence": [], "commands": [], "artifacts": [], + }, + "findings": [], + "previous_review": prior, + "started_at": "2026-09-06T00:02:00Z", + "completed_at": "2026-09-06T00:03:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + + +def test_rf_repaired_task_completion_accepts_native_fresh_initial_review_reset() -> None: + brief, handoff = _repair_completion_fixture() + handoff["acceptance_review"] = _material_reset_task_review() + + accepted = execution_context.validate_executor_result_for_task( + handoff, brief, mutation_events=[] + ) + + assert accepted["result_state"] == "completed" + assert accepted["task_ownership"]["agent_id"] == "repair-fixture-agent" + + +def test_rf_repaired_task_completion_rejects_unclassified_initial_review_reset() -> None: + brief, handoff = _repair_completion_fixture() + handoff["acceptance_review"] = _material_reset_task_review() + handoff["acceptance_review"]["review_reset"]["reason_class"] = "fixture_recovery" + + with pytest.raises(SystemExit, match="review_reset|material_change"): + execution_context.validate_executor_result_for_task( + handoff, brief, mutation_events=[] + ) + + +@pytest.mark.parametrize( + "mutation_events", + [ + [{}], + [{"paths": ["src/a.py"]}], + [{"actor_kind": "controller"}], + [{"actor_kind": 42, "paths": ["src/a.py"]}], + [{"actor_kind": "unknown", "paths": ["src/a.py"]}], + [{"actor_kind": "subagent", "paths": "src/a.py"}], + [{"actor_kind": "subagent", "paths": []}], + [{"actor_kind": "subagent", "paths": [42]}], + [{"actor_kind": "subagent", "paths": ["../src/a.py"]}], + [{"actor_kind": "subagent", "paths": ["/tmp/src/a.py"]}], + [{"actor_kind": "subagent", "paths": ["src/a.py"], "extra": True}], + ], +) +def test_completed_task_rejects_malformed_mutation_evidence( + tmp_path: Path, mutation_events: object +) -> None: + _, _, brief, handoff, _ = _counted_validation(tmp_path) + + with pytest.raises(SystemExit, match="mutation_events|mutation event"): + execution_context.validate_executor_result_for_task( + handoff, + brief, + observe=True, + mutation_events=mutation_events, + ) + + +def test_completed_task_accepts_complete_subagent_mutation_evidence(tmp_path: Path) -> None: + _, _, brief, handoff, _ = _counted_validation(tmp_path) + + accepted = execution_context.validate_executor_result_for_task( + handoff, + brief, + observe=True, + mutation_events=[{"actor_kind": "subagent", "paths": [WRITE_SCOPE_FILE]}], + ) + + assert accepted["result_state"] == "completed" + + +def test_completed_task_rejects_controller_mutation_with_dot_segment() -> None: + brief, handoff = _repair_completion_fixture() + brief["review_required"] = False + brief["files"]["write"] = ["src/a.py"] + handoff["task_fit_check"]["result"] = "clean" + handoff["acceptance_review"] = {"required": False} + + with pytest.raises(SystemExit, match="controller mutated task-owned implementation scope"): + execution_context.validate_executor_result_for_task( + handoff, + brief, + mutation_events=[{"actor_kind": "controller", "paths": ["src/./a.py"]}], + ) + + +def test_completed_task_rejects_controller_mutation_with_repeated_separator() -> None: + brief, handoff = _repair_completion_fixture() + brief["review_required"] = False + brief["files"]["write"] = ["src/a.py"] + handoff["task_fit_check"]["result"] = "clean" + handoff["acceptance_review"] = {"required": False} + + with pytest.raises(SystemExit, match="controller mutated task-owned implementation scope"): + execution_context.validate_executor_result_for_task( + handoff, + brief, + mutation_events=[{"actor_kind": "controller", "paths": ["src//a.py"]}], + ) + + +def test_rf_task_repair_completion_rejects_stale_repair_frontier(tmp_path: Path) -> None: + brief, handoff = _repair_completion_fixture() + git(tmp_path, "init", "-q") + git(tmp_path, "config", "user.name", "Test") + git(tmp_path, "config", "user.email", "test@example.com") + source = tmp_path / "source.py" + source.write_text("OLD = True\n") + git(tmp_path, "add", ".") + git(tmp_path, "commit", "-qm", "old") + old_tree = git(tmp_path, "rev-parse", "HEAD^{tree}") + source.write_text("NEW = True\n") + git(tmp_path, "add", ".") + git(tmp_path, "commit", "-qm", "repaired") + repaired_head = git(tmp_path, "rev-parse", "HEAD") + repaired_tree = git(tmp_path, "rev-parse", "HEAD^{tree}") + old = {"artifact_id": "task-rf", "revision": "1", "sha256": "1" * 64, "source_tree": old_tree} + new = {"artifact_id": "task-rf", "revision": "1", "sha256": "2" * 64, "source_tree": repaired_tree} + prior = { + "required": True, "reviewer_independent": True, "verdict": "repair", "review_id": "review-prior", + "review_mode": "initial", "review_target_kind": "task", "repair_frontier": None, "review_reset": None, + "target_identity": old, + "reviewer": {"agent_id": "reviewer-prior", "capability": "judgment", "authorship": "none", "repair_participation": "none", "decision_participation": "none", "deliberation_participation": "none", "context_origin": "direct_source"}, + "evidence": {"mode": "direct", "capabilities": ["source inspection"], "unavailable_evidence": [], "commands": [], "artifacts": []}, + "findings": [{ + "finding_id": "RF-FINDING-1", "stage": "implementation", "class": "implementation_defect", "severity": "blocking", + "first_broken_artifact": "implementation", "obligation_basis": "accepted_requirement", + "evidence": [{"kind": "test", "locator": "RF", "digest_or_identity": "RF-RED", "observation": "failed"}], + "target_identity": old, "summary": "repair", "recommended_owner": "task_owner", "disposition": "repair_task", + }], + "started_at": "2026-09-06T00:00:00Z", "completed_at": "2026-09-06T00:01:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + from review_runtime import review_evidence_identity + handoff["acceptance_review"] = { + "required": True, "reviewer_independent": True, "verdict": "accept", "review_id": "review-repair", + "reviewed_head": repaired_head, + "review_mode": "repair", "review_target_kind": "task", "review_reset": None, + "repair_frontier": { + "prior_review_id": "review-prior", "blocking_finding_ids": ["RF-FINDING-1"], + "previous_reviewed_identity": old, "repaired_identity": new, + "affected_boundaries": ["scripts/orchestration/execution_context.py:_assert_handoff_review_matches_task"], + "frozen_evidence_reference": review_evidence_identity(prior), + }, + "target_identity": new, + "reviewer": {**prior["reviewer"], "agent_id": "reviewer-new"}, + "evidence": {"mode": "direct", "capabilities": ["bounded source inspection"], "unavailable_evidence": [], "commands": [], "artifacts": []}, + "findings": [], "previous_review": prior, + "started_at": "2026-09-06T00:02:00Z", "completed_at": "2026-09-06T00:03:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + handoff["repository"] = [{"root": str(tmp_path), "target_kind": "git-backed", "preflight_kind": "git-clean-worktree", "baseline": "initial", "status": "clean"}] + execution_context._assert_handoff_review_matches_task(handoff, brief, "completed") + stale = deepcopy(handoff) + stale["acceptance_review"]["target_identity"] = {**new, "sha256": "3" * 64} + with pytest.raises(SystemExit, match="repaired identity|frontier"): + execution_context._assert_handoff_review_matches_task(stale, brief, "completed") + for field, value in (("required", False), ("reviewer_independent", False), ("review_target_kind", "wrong-kind")): + invalid_prior = deepcopy(handoff) + invalid_prior["acceptance_review"]["previous_review"][field] = value + with pytest.raises(SystemExit, match="previous|task acceptance|review_target_kind"): + execution_context._assert_handoff_review_matches_task(invalid_prior, brief, "completed") + fabricated = deepcopy(handoff) + fabricated_identity = {**new, "sha256": "3" * 64, "source_tree": "f" * 40} + fabricated["acceptance_review"]["target_identity"] = fabricated_identity + fabricated["acceptance_review"]["repair_frontier"]["repaired_identity"] = fabricated_identity + with pytest.raises(SystemExit, match="head|tree|Git identity"): + execution_context._assert_handoff_review_matches_task(fabricated, brief, "completed") + + def test_no_review_update_stays_closure_eligible_when_handoff_self_upgrades() -> None: handoffs = [ { @@ -2444,6 +2902,7 @@ def test_set_plan_status_completed_requires_validated_handoff(tmp_path: Path) -> status="Completed", kind="task", handoff=str(handoff), + mutation_events=[], ) ) data, _ = execution_context._read_structured(task) @@ -2763,7 +3222,9 @@ def _enable_passing_observation(root: Path, task: Path, handoff: Path) -> None: def _validate_observed(handoff: dict, brief: dict) -> dict: - return execution_context.validate_executor_result_for_task(handoff, brief, observe=True) + return execution_context.validate_executor_result_for_task( + handoff, brief, observe=True, mutation_events=[] + ) def _set_process_validation(task: Path, command: str, **fields: object) -> None: @@ -3137,6 +3598,7 @@ def test_set_plan_status_completed_observes_bound_worktree(tmp_path: Path) -> No execution_id=None, repository_id=None, execution_runtime_root=None, + mutation_events=[], ) ) data, _ = execution_context._read_structured(task) diff --git a/tests/test_orchestration_skill_rule_boundary.py b/tests/test_orchestration_skill_rule_boundary.py index cbd42dd..bda6d3c 100644 --- a/tests/test_orchestration_skill_rule_boundary.py +++ b/tests/test_orchestration_skill_rule_boundary.py @@ -122,7 +122,11 @@ def test_execute_skill_uses_compiler_independent_review_and_typed_blockers() -> "build-review-package", "dev-code-review", "The scheduler does not perform code-quality review", - "reviewer_independent: false", + "every implementation and repair task subagent-owned", + "TaskOwnershipScheduler", + "TaskOwnershipScheduler.validate_acceptance", + "there is no controller or single-agent fallback", + "must not implement or repair task write scope", "After two failed repair rounds", "acceptance_review.required: true", "review_required: true", @@ -144,6 +148,19 @@ def test_execute_skill_uses_compiler_independent_review_and_typed_blockers() -> assert token in text +def test_task_ownership_contract_is_provider_neutral_and_not_visibility_specific() -> None: + for relative in [ + "skills/orch-create-handoff/SKILL.md", + "rules/orchestration/orch-handoff-required.md", + "references/assets/orchestration/contract/handoff-executor-result-v1.md", + ]: + text = read(relative) + for token in ["delegation_evidence", "owner_kind", "agent", "run", "mechanism"]: + assert token in text, f"{relative}: {token}" + for retired in ["visible_reference", "internal_spawn_used_for_task_delegation", "single-agent-fallback"]: + assert retired not in text, f"{relative}: {retired}" + + def test_final_review_is_workflow_audit_not_code_review() -> None: text = read("skills/orch-review-plan/SKILL.md") for token in [ diff --git a/tests/test_orchestration_workflow_contracts.py b/tests/test_orchestration_workflow_contracts.py index 3e90f00..b4db039 100644 --- a/tests/test_orchestration_workflow_contracts.py +++ b/tests/test_orchestration_workflow_contracts.py @@ -59,6 +59,16 @@ def handoff_args(tmp_path: Path, **overrides: object) -> argparse.Namespace: return argparse.Namespace(**values) +def archive_args(project_root: Path, plan_id: str, **overrides: object) -> argparse.Namespace: + values: dict[str, object] = { + "project_root": str(project_root), + "id": plan_id, + "mutation_events": [], + } + values.update(overrides) + return argparse.Namespace(**values) + + def _task_brief(*, plan_id: str = "plan-001", task_id: str = "task-001") -> dict[str, object]: return { "task_id": task_id, @@ -90,6 +100,13 @@ def _completed_executor_result( "related": related, "result": {"state": "completed"}, "task_fit_check": {"task": task, "result": "clean"}, + "delegation_evidence": { + "delegated": True, + "owner_kind": "subagent", + "agent_id": "workflow-contract-fixture-agent", + "run_id": "workflow-contract-fixture-run", + "mechanism": "host-native", + }, "knowledge_disposition": { "action": "none", "reason": "No stable authority changed.", @@ -445,11 +462,81 @@ def test_archive_plan_uses_accepted_execution_dispositions_as_knowledge_gate(tmp ) with pytest.raises(SystemExit, match="knowledge-blocked"): + cmd_archive_plan(archive_args(root, "plan-001")) + + assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() + + +def test_archive_plan_completed_handoff_rejects_missing_harness_mutation_evidence( + tmp_path: Path, +) -> None: + from plans import cmd_archive_plan + from test_orchestration_execution_context import workspace, write_executor_handoff + + root, _, _ = workspace(tmp_path) + _append_plan_knowledge(root, closure_return="missing") + write_executor_handoff( + root, + " action: none\n" + " reason: No stable authority changed.\n" + " affected_authority: []\n", + ) + + with pytest.raises(SystemExit, match="mutation.*evidence|mutation_events"): cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() +def test_archive_plan_cli_accepts_explicit_harness_mutation_evidence() -> None: + from dispatcher import build_parser + + parsed = build_parser().parse_args( + [ + "archive-plan", + "--id", + "plan-001", + "--mutation-events", + "[]", + ] + ) + + assert parsed.mutation_events == [] + + +def test_archive_plan_rejects_controller_task_scope_mutation_evidence( + tmp_path: Path, +) -> None: + from plans import cmd_archive_plan + from test_orchestration_execution_context import ( + WRITE_SCOPE_FILE, + workspace, + write_executor_handoff, + ) + + root, _, _ = workspace(tmp_path) + _append_plan_knowledge(root, closure_return="missing") + write_executor_handoff( + root, + " action: none\n" + " reason: No stable authority changed.\n" + " affected_authority: []\n", + ) + + with pytest.raises(SystemExit, match="controller mutated task-owned implementation scope"): + cmd_archive_plan( + archive_args( + root, + "plan-001", + mutation_events=[ + {"actor_kind": "controller", "paths": [WRITE_SCOPE_FILE]} + ], + ) + ) + + assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() + + @pytest.mark.parametrize( ("verdict", "action", "closure_return"), [ @@ -485,7 +572,7 @@ def test_archive_plan_allows_only_resolved_or_non_triggering_dispositions( encoding="utf-8", ) - cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-001")) + cmd_archive_plan(archive_args(tmp_path, "plan-001")) assert (tmp_path / ".work-bundle/orchestration/plan/archived/plan.md").is_file() @@ -698,7 +785,7 @@ def test_archive_plan_ignores_foreign_plan_handoff_with_colliding_task_id(tmp_pa _write_archive_plan(tmp_path, "plan-B") _write_archive_handoff(tmp_path, "plan-a.yaml", "{plan: plan-A, task: task-001}") - cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-B")) + cmd_archive_plan(archive_args(tmp_path, "plan-B")) assert (tmp_path / ".work-bundle/orchestration/plan/archived/plan-B.md").is_file() assert (tmp_path / ".work-bundle/orchestration/plan/active/plan-A.md").is_file() @@ -730,7 +817,7 @@ def test_archive_plan_skips_unrelated_unparseable_executor_yaml(tmp_path: Path) encoding="utf-8", ) - cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-B")) + cmd_archive_plan(archive_args(tmp_path, "plan-B")) assert (tmp_path / ".work-bundle/orchestration/plan/archived/plan-B.md").is_file() assert (tmp_path / ".work-bundle/orchestration/plan/active/plan-A.md").is_file() @@ -742,7 +829,7 @@ def test_archive_plan_ignores_task_only_handoff_with_ambiguous_task_id(tmp_path: _write_archive_plan(tmp_path, "plan-B") _write_archive_handoff(tmp_path, "task-only.yaml", "{task: task-001}") - cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-B")) + cmd_archive_plan(archive_args(tmp_path, "plan-B")) assert (tmp_path / ".work-bundle/orchestration/plan/archived/plan-B.md").is_file() @@ -756,7 +843,7 @@ def test_archive_plan_ignores_archived_foreign_handoff_with_colliding_task_id(tm tmp_path, "historical.yaml", "{plan: plan-A, task: task-001}", location="archived" ) - cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-B")) + cmd_archive_plan(archive_args(tmp_path, "plan-B")) assert (tmp_path / ".work-bundle/orchestration/plan/archived/plan-B.md").is_file() @@ -773,7 +860,7 @@ def test_archive_plan_same_plan_accepted_update_still_blocks_unresolved_closure( ) with pytest.raises(SystemExit, match="knowledge-blocked"): - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() @@ -784,7 +871,7 @@ def test_archive_plan_same_plan_resolved_closure_allows_archive(tmp_path: Path) _write_archive_plan(tmp_path, "plan-B", closure_return="completed") _write_archive_handoff(tmp_path, "plan-b.yaml", "{plan: plan-B, task: task-001}") - cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-B")) + cmd_archive_plan(archive_args(tmp_path, "plan-B")) assert (tmp_path / ".work-bundle/orchestration/plan/archived/plan-B.md").is_file() @@ -869,7 +956,9 @@ def test_workflow_assigns_review_ownership_and_repair_loop() -> None: "Reviewers own acceptance judgment", "Schedulers own dependencies", "they do not perform code-quality review", - "reviewer_independent: false", + "requires a subagent owner for every task", + "fails closed before task mutation", + "dispatch before any wait", "After two failed low-cost repair rounds", "A task becomes `Completed` only when", "`Completed` does not require `verdict: accept` unless review was required", @@ -977,7 +1066,9 @@ def test_no_review_completed_handoff_does_not_require_accept_or_reviewer() -> No assert token in execute assert "verdict: accept" not in str(_completed_executor_result()) - validated = validate_executor_result_for_task(_completed_executor_result(), _task_brief()) + validated = validate_executor_result_for_task( + _completed_executor_result(), _task_brief(), mutation_events=[] + ) assert validated["result_state"] == "completed" assert validated["knowledge_disposition"]["action"] == "none" @@ -1088,7 +1179,7 @@ def test_failing_declared_plan_acceptance_blocks_archive_without_second_reviewer ) with pytest.raises(SystemExit, match="acceptance-blocked"): - cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-B")) + cmd_archive_plan(archive_args(tmp_path, "plan-B")) assert (tmp_path / ".work-bundle/orchestration/plan/active/plan-B.md").is_file() @@ -1152,7 +1243,7 @@ def test_archive_plan_accepts_mapped_invariant_handoff_with_harness_observation( root, _binding, _command = _mapped_archive_workspace(tmp_path) - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/archived/compiler-plan.md").is_file() @@ -1163,9 +1254,9 @@ def test_archive_plan_forwards_execution_binding_into_task_revalidation(tmp_path root, binding, command = _mapped_archive_workspace(tmp_path) cmd_archive_plan( - argparse.Namespace( - project_root=str(root), - id="plan-001", + archive_args( + root, + "plan-001", workspace_id=binding["workspace_id"], execution_id=binding["execution_id"], repository_id=binding["repository_id"], @@ -1179,9 +1270,9 @@ def test_archive_plan_forwards_execution_binding_into_task_revalidation(tmp_path restored_root, restored_binding, _command = _mapped_archive_workspace(restored) with pytest.raises(SystemExit, match=rf"acceptance-blocked: declared plan-level acceptance {command} is missing"): cmd_archive_plan( - argparse.Namespace( - project_root=str(restored_root), - id="plan-001", + archive_args( + restored_root, + "plan-001", workspace_id="wrong-workspace", execution_id=restored_binding["execution_id"], repository_id=restored_binding["repository_id"], @@ -1227,7 +1318,7 @@ def test_passing_declared_plan_acceptance_allows_archive_without_second_reviewer encoding="utf-8", ) - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/archived/compiler-plan.md").is_file() @@ -1264,7 +1355,7 @@ def test_unvalidated_handoff_cannot_satisfy_declared_plan_acceptance(tmp_path: P ) with pytest.raises(SystemExit, match="acceptance-blocked"): - cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-B")) + cmd_archive_plan(archive_args(tmp_path, "plan-B")) def _record_tree_fresh_integration_pass(root: Path, command: str) -> str: @@ -1297,7 +1388,7 @@ def test_tree_fresh_executor_pass_without_harness_observation_blocks_archive(tmp _record_tree_fresh_integration_pass(root, command) with pytest.raises(SystemExit, match="acceptance-blocked"): - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() @@ -1318,7 +1409,7 @@ def test_task_worktree_command_pass_cannot_archive_different_final_workspace(tmp _write_earlier_integration_pass(root, command, created_at="2026-08-17") with pytest.raises(SystemExit, match="acceptance-blocked"): - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() assert not (root / "worktree-only-marker").exists() @@ -1335,7 +1426,7 @@ def test_passing_mutating_integration_command_blocks_archive(tmp_path: Path) -> _record_tree_fresh_integration_pass(root, command) with pytest.raises(SystemExit, match="acceptance-blocked"): - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() @@ -1381,7 +1472,7 @@ def test_contradictory_validated_plan_acceptance_blocks_archive(tmp_path: Path) ) with pytest.raises(SystemExit, match="acceptance-blocked"): - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) def test_stale_plan_acceptance_after_later_material_task_blocks_archive(tmp_path: Path) -> None: @@ -1408,7 +1499,7 @@ def test_stale_plan_acceptance_after_later_material_task_blocks_archive(tmp_path ) with pytest.raises(SystemExit, match="acceptance-blocked:.*is stale"): - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() @@ -1438,7 +1529,7 @@ def test_fresh_plan_acceptance_rerun_after_later_task_allows_archive(tmp_path: P actual_commit=later, ) - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/archived/compiler-plan.md").is_file() @@ -1453,7 +1544,7 @@ def test_archive_moves_plan_directory_named_for_root_artifact(tmp_path: Path) -> (active / "compiler-plan.md").rename(active / "plan-001-feature.md") (active / "plan-001").rename(active / "plan-001-feature") - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) archived = root / ".work-bundle/orchestration/plan/archived" assert (archived / "plan-001-feature.md").is_file() @@ -1474,7 +1565,7 @@ def test_archive_reconciles_archived_root_with_active_plan_directory(tmp_path: P (active / "compiler-plan.md").rename(archived / "plan-001-feature.md") (active / "plan-001").rename(active / "plan-001-feature") - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (archived / "plan-001-feature.md").is_file() assert (archived / "plan-001-feature").is_dir() @@ -1514,7 +1605,7 @@ def test_same_day_out_of_id_order_stale_plan_acceptance_blocks_archive(tmp_path: ) with pytest.raises(SystemExit, match="acceptance-blocked:.*is stale"): - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() @@ -1552,7 +1643,7 @@ def test_same_day_out_of_id_order_fresh_rerun_allows_archive(tmp_path: Path) -> actual_commit=later, ) - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/archived/compiler-plan.md").is_file() @@ -1591,7 +1682,7 @@ def test_historical_failed_plan_acceptance_does_not_poison_fresh_head_pass(tmp_p actual_commit=later, ) - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/archived/compiler-plan.md").is_file() @@ -1625,7 +1716,7 @@ def test_same_tree_contradictory_plan_acceptance_still_blocks_archive(tmp_path: ) with pytest.raises(SystemExit, match="acceptance-blocked:.*contradictory"): - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) def test_precommit_tree_pass_survives_same_tree_finalization_commit(tmp_path: Path) -> None: @@ -1658,7 +1749,7 @@ def test_precommit_tree_pass_survives_same_tree_finalization_commit(tmp_path: Pa ) git(root, "commit", "-qm", "finalize") - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/archived/compiler-plan.md").is_file() @@ -1677,7 +1768,7 @@ def test_archive_plan_no_review_completed_update_blocks_until_closure_return( ) with pytest.raises(SystemExit, match="knowledge-blocked"): - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() @@ -1694,7 +1785,7 @@ def test_archive_plan_ignores_unvalidated_update_handoff(tmp_path: Path) -> None result_state="completed", ) - cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-B")) + cmd_archive_plan(archive_args(tmp_path, "plan-B")) assert (tmp_path / ".work-bundle/orchestration/plan/archived/plan-B.md").is_file() @@ -1717,7 +1808,7 @@ def test_archive_plan_review_required_cannot_downgrade_via_omitted_required(tmp_ f" action: update\n reason: Task-local evidence.\n affected_authority: [{ACCEPTED_AUTHORITY}]\n", ) - cmd_archive_plan(argparse.Namespace(project_root=str(root), id="plan-001")) + cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/archived/compiler-plan.md").is_file() @@ -1776,7 +1867,11 @@ def test_review_required_task_fails_closed_until_independent_accept() -> None: accepted = _completed_executor_result( acceptance_review={"required": True, "verdict": "accept"} ) - validated = validate_executor_result_for_task(accepted, {**_task_brief(), "review_required": True}) + validated = validate_executor_result_for_task( + accepted, + {**_task_brief(), "review_required": True}, + mutation_events=[], + ) assert validated["result_state"] == "completed" @@ -1785,9 +1880,11 @@ def test_overlapping_writes_are_not_parallelizable() -> None: create = read("skills/orch-create-implementation-plan/SKILL.md") workflow = read("references/assets/orchestration/workflow.md") plan = read("references/assets/orchestration/contract/plan-v1.md") - assert "Partition only independent tasks with disjoint write scopes" in execute + assert "planner-proven dependencies, write scopes" in execute + assert "Dispatch every ready independent task with disjoint write scope" in execute + assert "Serialize dependent, overlapping, or same-workspace mutation" in execute assert "disjoint write scopes" in create - assert "write scopes are disjoint" in workflow + assert "Independent disjoint tasks in distinct execution workspaces dispatch before any wait" in workflow assert "disjoint write scopes" in workflow assert "assign parallel tasks only when dependencies are satisfied and write scopes are disjoint" in plan assert "unsafe parallelization is explicitly blocked by dependency or scope evidence" in plan diff --git a/tests/test_project_initialization.py b/tests/test_project_initialization.py index bd850ec..5181ae0 100644 --- a/tests/test_project_initialization.py +++ b/tests/test_project_initialization.py @@ -85,7 +85,6 @@ def bootstrap_config(tmp_path: Path, work_bundle_root: Path | None = None) -> Pa f"work_bundle_root: {work_bundle_root or REPO_ROOT}", 'project_registry: "$work_bundle_config_root/registry/projects.yaml"', 'skill_registry: "$work_bundle_config_root/registry/skill-registry.yaml"', - "prefer_subagent: false", "", ] ), @@ -1050,8 +1049,7 @@ def test_validate_project_omits_pointer_diagnostics(tmp_path: Path) -> None: assert result.returncode == 0, result.stdout + result.stderr data = json.loads(result.stdout) assert data["path_model"]["work_bundle_root"] - assert data["prefer_subagent"]["prefer_subagent"] is False - assert data["prefer_subagent"]["source"] == "project" + assert "prefer_subagent" not in data for key in ( "work_bundle_root_pointer_path", "work_bundle_root_pointer_exists", @@ -1140,19 +1138,17 @@ def test_migrate_work_bundle_config_migrates_legacy_bootstrap(tmp_path: Path) -> assert Path(data["retired_root_pointer"]).is_file() -def test_templates_include_layered_prefer_subagent_defaults() -> None: +def test_templates_omit_retired_subagent_preference() -> None: bootstrap_text = (REPO_ROOT / "references/assets/template/bootstrap.yaml").read_text(encoding="utf-8") project_text = (REPO_ROOT / "references/assets/template/project.yaml").read_text(encoding="utf-8") agents_text = (REPO_ROOT / "references/assets/template/AGENTS.md").read_text(encoding="utf-8") - assert "prefer_subagent: false" in bootstrap_text - assert "prefer_subagent: false" in project_text + assert "prefer_subagent" not in bootstrap_text + assert "prefer_subagent" not in project_text assert "agents_sync:" in project_text assert "template_checksum_sha256: \"\"" in project_text assert "status: never-synced" in project_text - assert ".work-bundle/project.yaml` -> `prefer_subagent`" in agents_text - assert "then `$work_bundle_config_root/bootstrap.yaml` -> `prefer_subagent`, then `false`" in agents_text - assert "bypass repository preflight" in agents_text + assert "prefer_subagent" not in agents_text def test_project_metadata_v3_records_git_and_codegraph_state(tmp_path: Path) -> None: @@ -1879,7 +1875,7 @@ def test_provision_member_resumes_matching_verified_checkout(tmp_path: Path, mon assert json.loads(resumed.stdout)["transaction"]["state"] == "published" -def test_resolve_effective_prefer_subagent_uses_project_global_default_order(tmp_path: Path, monkeypatch) -> None: +def test_runtime_has_no_subagent_preference_resolver(tmp_path: Path, monkeypatch) -> None: script_root = REPO_ROOT / "scripts" / "work-bundle" module = sys.modules.get("core") module_file = Path(getattr(module, "__file__", "")) if module is not None else None @@ -1889,27 +1885,8 @@ def test_resolve_effective_prefer_subagent_uses_project_global_default_order(tmp try: import core # type: ignore[import-not-found] - config_root = tmp_path / "config" - config_root.mkdir() - monkeypatch.setenv("WB_CONFIG_ROOT", str(config_root)) - project_root = tmp_path / "project" - metadata_path = project_root / ".work-bundle/project.yaml" - metadata_path.parent.mkdir(parents=True) - - assert core.resolve_effective_prefer_subagent(project_root)["prefer_subagent"] is False - assert core.resolve_effective_prefer_subagent(project_root)["source"] == "default" - - (config_root / "bootstrap.yaml").write_text("prefer_subagent: true\n", encoding="utf-8") - assert core.resolve_effective_prefer_subagent(project_root)["prefer_subagent"] is True - assert core.resolve_effective_prefer_subagent(project_root)["source"] == "global" - - metadata_path.write_text("prefer_subagent: false\n", encoding="utf-8") - resolved = core.resolve_effective_prefer_subagent(project_root) - assert resolved["prefer_subagent"] is False - assert resolved["source"] == "project" - - metadata_path.write_text("prefer_subagent: true\n", encoding="utf-8") - assert core.resolve_effective_prefer_subagent(project_root)["prefer_subagent"] is True + assert not hasattr(core, "resolve_effective_prefer_subagent") + assert "prefer_subagent" not in core.resolve_bootstrap_runtime() finally: if sys.path and sys.path[0] == str(REPO_ROOT / "scripts" / "work-bundle"): sys.path.pop(0) @@ -2057,46 +2034,22 @@ def test_init_project_metadata_records_agents_sync_checksum(tmp_path: Path) -> N assert json.loads(rerun.stdout)["changed_files"] == [] -def test_set_prefer_subagent_updates_global_bootstrap(tmp_path: Path) -> None: +def test_retired_subagent_preference_command_is_not_exposed(tmp_path: Path) -> None: config_root, project = _init_fixture_project(tmp_path) result = run_wb(config_root, "set-prefer-subagent", "enable", "--scope", "global", "--project-root", str(project)) - assert result.returncode == 0, result.stdout + result.stderr - data = json.loads(result.stdout) - - assert data["command"] == "set-prefer-subagent" - assert data["scope"] == "global" - assert data["prefer_subagent"] is True - assert data["status"] == "updated" - assert data["target_path"] == str((config_root / "bootstrap.yaml").resolve()) - assert "prefer_subagent: true" in (config_root / "bootstrap.yaml").read_text(encoding="utf-8") - assert data["effective_prefer_subagent"]["prefer_subagent"] is False - assert data["effective_prefer_subagent"]["source"] == "project" + assert result.returncode == 2 + assert "unknown command" in result.stderr - project_text = (project / ".work-bundle/project.yaml").read_text(encoding="utf-8") - assert "prefer_subagent: false" in project_text - -def test_set_prefer_subagent_updates_current_workspace_override(tmp_path: Path) -> None: +def test_legacy_subagent_preference_input_does_not_appear_in_show_output(tmp_path: Path) -> None: config_root, project = _init_fixture_project(tmp_path) - assert run_wb(config_root, "set-prefer-subagent", "enable", "--scope", "global", "--project-root", str(project)).returncode == 0 - - result = run_wb(config_root, "set-prefer-subagent", "disable", "--scope", "project", "--project-root", str(project)) - assert result.returncode == 0, result.stdout + result.stderr - data = json.loads(result.stdout) - - assert data["scope"] == "project" - assert data["prefer_subagent"] is False - assert data["target_path"] == str((project / ".work-bundle/project.yaml").resolve()) - assert data["effective_prefer_subagent"]["prefer_subagent"] is False - assert data["effective_prefer_subagent"]["source"] == "project" - assert "prefer_subagent: false" in (project / ".work-bundle/project.yaml").read_text(encoding="utf-8") - + metadata = project / ".work-bundle/project.yaml" + metadata.write_text(metadata.read_text(encoding="utf-8") + "prefer_subagent: true\n", encoding="utf-8") show = run_wb(config_root, "show-project", "--project-root", str(project)) assert show.returncode == 0, show.stdout + show.stderr show_data = json.loads(show.stdout) - assert show_data["prefer_subagent"]["global_prefer_subagent"] is True - assert show_data["prefer_subagent"]["project_prefer_subagent"] is False + assert "prefer_subagent" not in show_data def test_migrate_work_bundle_config_resolves_legacy_pointer_without_toolkit_flag(tmp_path: Path) -> None: diff --git a/tests/test_registry_layout_migration.py b/tests/test_registry_layout_migration.py index 425766a..acc9f18 100644 --- a/tests/test_registry_layout_migration.py +++ b/tests/test_registry_layout_migration.py @@ -29,6 +29,20 @@ ) +def restore_work_bundle_import_boundary() -> None: + """Keep direct migration calls isolated from orchestration's `core` module.""" + + if _WB_SCRIPTS in sys.path: + sys.path.remove(_WB_SCRIPTS) + sys.path.insert(0, _WB_SCRIPTS) + core_module = sys.modules.get("core") + core_path = Path(getattr(core_module, "__file__", "")) if core_module is not None else None + if core_path is not None and Path(_WB_SCRIPTS) not in core_path.parents: + sys.modules.pop("core", None) + for name in ("project", "bootstrap_config"): + sys.modules.pop(name, None) + + def run_wb(config_root: Path, *args: str) -> subprocess.CompletedProcess[str]: env = os.environ.copy() env.update( @@ -76,7 +90,6 @@ def bootstrap_config(tmp_path: Path) -> Path: f"work_bundle_root: {REPO_ROOT}", 'project_registry: "$work_bundle_config_root/registry/projects.yaml"', 'skill_registry: "$work_bundle_config_root/registry/skill-registry.yaml"', - "prefer_subagent: false", "", ] ), @@ -471,6 +484,7 @@ def test_blocked_multi_repository_v2(tmp_path: Path) -> None: def test_validation_failure_after_transformation_restores_state(tmp_path: Path, monkeypatch) -> None: + restore_work_bundle_import_boundary() config = bootstrap_config(tmp_path) workspace, remote, _, repo_id = prepare_workspace(tmp_path, "validate-fail", "v3-single.yaml") registry = write_registry(config, [project_block("validate-fail", workspace, repo_id, str(remote))]) @@ -503,6 +517,7 @@ def failing_validate(root: Path, version: str) -> list[str]: def test_intermediate_step_failure_restores_pre_migration_state(tmp_path: Path, monkeypatch) -> None: + restore_work_bundle_import_boundary() config = bootstrap_config(tmp_path) workspace, remote, _, repo_id = prepare_workspace(tmp_path, "mid-fail", "v2-project.yaml") registry = write_registry(config, [project_block("mid-fail", workspace, repo_id, str(remote))]) @@ -541,6 +556,7 @@ def fail_v4(step, root, entry): def test_failed_migration_preserves_symlink_and_nested_credentials( tmp_path: Path, monkeypatch ) -> None: + restore_work_bundle_import_boundary() config = bootstrap_config(tmp_path) workspace, remote, _, repo_id = prepare_workspace(tmp_path, "user-data", "v3-single.yaml") registry = write_registry(config, [project_block("user-data", workspace, repo_id, str(remote))]) diff --git a/tests/test_wor108_closure.py b/tests/test_wor108_closure.py new file mode 100644 index 0000000..4eba67a --- /dev/null +++ b/tests/test_wor108_closure.py @@ -0,0 +1,108 @@ +from __future__ import annotations + +from copy import deepcopy +import importlib.util +import json +from pathlib import Path + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +EVAL_ROOT = REPO_ROOT / "evals" / "wor108" +SPEC = importlib.util.spec_from_file_location("wor108_verify", EVAL_ROOT / "verify.py") +assert SPEC is not None and SPEC.loader is not None +wor108_verify = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(wor108_verify) + + +def test_wor108_closure_registry_has_exact_public_fixture_identities() -> None: + result = wor108_verify.verify() + assert result == { + "evaluation_id": "wor108-legacy-closure-v1", + "fixtures": 22, + "changed_surfaces": 44, + "verdict": "accepted", + } + + +def test_wor108_closure_registry_binds_linear_exact_production_oracles() -> None: + package = json.loads((EVAL_ROOT / "fixtures.json").read_text(encoding="utf-8")) + nodes_by_id = { + fixture["fixture_id"]: fixture["pytest_nodes"] + for fixture in package["fixtures"] + } + assert nodes_by_id | { + "RF-01": [ + "tests/test_wor108_review_frontier.py::test_rf_01_h1_repairs_recorded_a_without_reacquiring_unrecorded_latent_b" + ], + "RF-03": [ + "tests/test_wor108_review_frontier.py::test_rf_03_capable_untouched_invariant_stays_blocking_during_narrow_repair" + ], + "RF-06": [ + "tests/test_wor108_review_frontier.py::test_rf_06_final_broad_integrated_review_rediscovers_and_classifies_latent_b" + ], + "SG-04": [ + "tests/test_wor108_context_projection.py::test_completed_task_acceptance_rejects_controller_task_scope_mutation" + ], + } == nodes_by_id + + +def test_wor108_fixture_registry_rejects_missing_identity(tmp_path: Path) -> None: + fixtures = json.loads((EVAL_ROOT / "fixtures.json").read_text(encoding="utf-8")) + fixtures["fixtures"] = fixtures["fixtures"][:-1] + tampered = tmp_path / "fixtures.json" + tampered.write_text(json.dumps(fixtures), encoding="utf-8") + + with pytest.raises(wor108_verify.VerificationError, match="exactly RF-01..08"): + wor108_verify.verify(fixtures_path=tampered) + + +def test_wor108_fixture_schema_rejects_non_object_entries_and_numeric_ids( + tmp_path: Path, +) -> None: + schema = json.loads( + (EVAL_ROOT / "contracts-v1.schema.json").read_text(encoding="utf-8") + ) + fixture_schema = schema["$defs"]["fixture"] + assert fixture_schema["type"] == "object" + assert fixture_schema["properties"]["fixture_id"]["type"] == "string" + + fixtures = json.loads((EVAL_ROOT / "fixtures.json").read_text(encoding="utf-8")) + malformed_entry = deepcopy(fixtures) + malformed_entry["fixtures"][0] = 7 + malformed_entry_path = tmp_path / "malformed-entry.json" + malformed_entry_path.write_text(json.dumps(malformed_entry), encoding="utf-8") + with pytest.raises(wor108_verify.VerificationError, match="exactly RF-01..08"): + wor108_verify.verify(fixtures_path=malformed_entry_path) + + numeric_identity = deepcopy(fixtures) + numeric_identity["fixtures"][0]["fixture_id"] = 1 + numeric_identity_path = tmp_path / "numeric-identity.json" + numeric_identity_path.write_text(json.dumps(numeric_identity), encoding="utf-8") + with pytest.raises(wor108_verify.VerificationError, match="exactly RF-01..08"): + wor108_verify.verify(fixtures_path=numeric_identity_path) + + +def test_wor108_migration_impact_rejects_final_git_identity(tmp_path: Path) -> None: + manifest = json.loads((EVAL_ROOT / "migration-impact.json").read_text(encoding="utf-8")) + tampered_manifest = deepcopy(manifest) + tampered_manifest["final_tree"] = "0" * 40 + tampered = tmp_path / "migration-impact.json" + tampered.write_text(json.dumps(tampered_manifest), encoding="utf-8") + + with pytest.raises(wor108_verify.VerificationError, match="closed shape"): + wor108_verify.verify(migration_path=tampered) + + +def test_wor108_migration_impact_rejects_incomplete_baseline_delta( + tmp_path: Path, +) -> None: + manifest = json.loads((EVAL_ROOT / "migration-impact.json").read_text(encoding="utf-8")) + operations = manifest["changed_surfaces"]["operations"] + operations.remove("scripts/orchestration/execution_context.py") + tampered = tmp_path / "migration-impact.json" + tampered.write_text(json.dumps(manifest), encoding="utf-8") + + with pytest.raises(wor108_verify.VerificationError, match="changed surface completeness"): + wor108_verify.verify(migration_path=tampered) diff --git a/tests/test_wor108_context_projection.py b/tests/test_wor108_context_projection.py new file mode 100644 index 0000000..16a1546 --- /dev/null +++ b/tests/test_wor108_context_projection.py @@ -0,0 +1,1688 @@ +from __future__ import annotations + +import builtins +import hashlib +import json +import subprocess +import sys +from copy import deepcopy +from pathlib import Path + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +ORCHESTRATION = REPO_ROOT / "scripts" / "orchestration" +WORK_BUNDLE = REPO_ROOT / "scripts" / "work-bundle" +for path in (WORK_BUNDLE, ORCHESTRATION): + if str(path) not in sys.path: + sys.path.insert(0, str(path)) + +import execution_context # noqa: E402 +import review_runtime # noqa: E402 +from stage_events import StageEventError, validate_stage_event # noqa: E402 +from test_orchestration_execution_context import ( # noqa: E402 + _bind_task_execution, + _compiled_brief, + _ensure_source_file, + args, + build_review_package, + build_task_brief, + git, + workspace, +) +from test_stage_events import event # noqa: E402 + + +def _document(root: Path, task: Path) -> dict: + return execution_context._read_structured(build_task_brief(args(root, task)))[0] + + +def _delegation_evidence() -> dict[str, object]: + return { + "delegated": True, + "owner_kind": "subagent", + "agent_id": "ctx-fixture-agent", + "run_id": "ctx-fixture-run", + "mechanism": "host-native", + } + + +def _without_terminal_evidence(brief: dict[str, object], reason: str) -> dict[str, object]: + projected = deepcopy(brief) + projected["validation"] = [] + projected["evidence_capability"] = { + "result": "no_validation_bearing_obligation", + "reason": reason, + "invariants": [], + } + projected["evidence_applicability"] = { + kind: {"required": False, "reasons": []} + for kind in ("metadata", "repository", "codegraph") + } + return projected + + +def test_ctx_01_unrelated_runtime_history_does_not_inflate_unchanged_task_brief( + tmp_path: Path, +) -> None: + root, _, task = workspace(tmp_path) + first = build_task_brief(args(root, task)).read_bytes() + history = root / ".work-bundle/runtime/history/unrelated.jsonl" + history.parent.mkdir(parents=True) + history.write_text("{\"provenance\":\"x\"}\n" * 50_000, encoding="utf-8") + (history.parent / "accepted-handoffs.json").write_text("[]\n", encoding="utf-8") + + second = build_task_brief(args(root, task)).read_bytes() + + assert second == first + + +def test_ctx_01_repair_package_uses_frontier_without_reacquiring_review_history( + tmp_path: Path, +) -> None: + root, _, task = workspace(tmp_path) + task.write_text( + task.read_text(encoding="utf-8").replace( + "acceptance_review:\n required: false\n", + "acceptance_review:\n required: true\n", + ), + encoding="utf-8", + ) + scoped = _ensure_source_file(root) + git(root, "add", ".") + git(root, "commit", "-qm", "reviewed") + base = git(root, "rev-parse", "HEAD") + base_tree = git(root, "rev-parse", "HEAD^{tree}") + brief = _compiled_brief(root, task) + _bind_task_execution(root, brief) + scoped.write_text("def compile_task():\n return 'repaired'\n", encoding="utf-8") + git(root, "add", str(scoped.relative_to(root))) + git(root, "commit", "-qm", "repaired") + head = git(root, "rev-parse", "HEAD") + head_tree = git(root, "rev-parse", "HEAD^{tree}") + identity = lambda tree, digest: { + "artifact_id": "task-004", "revision": "rev-1", "sha256": digest, "source_tree": tree + } + handoff = { + "id": "handoff-task-004", + "type": "executor-result", + "related": {"plan": "plan-001", "task": "task-004"}, + "result": {"state": "partial"}, + "task_fit_check": {"task": "task-004", "result": "repaired"}, + "acceptance_review": { + "required": True, + "verdict": "pending", + "review_mode": "repair", + "repair_frontier": { + "prior_review_id": "review-prior", + "blocking_finding_ids": ["RF-FINDING-1"], + "previous_reviewed_identity": identity(base_tree, execution_context.semantic_digest("old")), + "repaired_identity": identity(head_tree, execution_context.semantic_digest("new")), + "affected_boundaries": ["compile_task"], + "frozen_evidence_reference": execution_context.semantic_digest("frozen"), + }, + }, + "changes": {"files": [{"path": "scripts/orchestration/execution_context.py", "action": "modified"}]}, + "repository": [{ + "root": str(root.resolve()), "target_kind": "git-backed", + "preflight_kind": "git-clean-worktree", "baseline": "initial", "status": "clean", + }], + "codegraph": [{"root": str(root.resolve()), "applicable": False, "up_to_date": False, "reason": "no-index"}], + "knowledge_disposition": {"action": "none", "reason": "No stable authority changed.", "affected_authority": []}, + "previous_review_history": "MUST-NOT-BE-PROJECTED", + } + handoff_path = root / ".work-bundle/orchestration/handoff/executor/active/handoff-task-004.yaml" + handoff_path.parent.mkdir(parents=True) + handoff_path.write_text("\n".join(execution_context._dump_yaml(handoff)) + "\n", encoding="utf-8") + + package = build_review_package( + args(root, task, handoff=str(handoff_path), base=base, head=head) + ).read_text(encoding="utf-8") + + assert "Review mode: repair" in package + assert "RF-FINDING-1" in package + assert execution_context.semantic_digest("frozen") in package + assert "MUST-NOT-BE-PROJECTED" not in package + assert f"Base: {base}" in package and f"Head: {head}" in package + + +def test_ctx_02_semantic_authority_is_retained_once_and_referenced_by_id( + tmp_path: Path, +) -> None: + root, _, task = workspace(tmp_path) + document = _document(root, task) + brief = document["task_brief"] + rendered = build_task_brief(args(root, task)).read_text(encoding="utf-8") + + assert brief["semantic_authority"]["records"]["REQ-003"]["canonical_field"] == "requirements" + assert brief["semantic_authority"]["records"]["API-002"]["canonical_field"] == "interface_semantics" + assert brief["interfaces"] == {"consumes": ["API-002"], "produces": ["API-002"]} + assert brief["validation"][0]["proves"] == "TEST-004" + for meaning in ( + "Retry exactly three times before returning failure.", + "Never write outside the assigned files.", + "`compile_task(task: Path) -> dict[str, object]`", + "Focused pytest exits with status 0.", + ): + assert rendered.count(meaning) == 1 + + +def test_ctx_03_executor_capability_projection_contains_only_allocated_capabilities( + tmp_path: Path, +) -> None: + root, _, task = workspace(tmp_path) + brief = _document(root, task)["task_brief"] + + assert brief["capability_projection"] == { + "executor": {"capability": "mechanical", "reason": "task executor_profile allocation"}, + "rules": [{"id": "scoped-rule", "reason": "Keep the executor packet bounded."}], + "skills": [{"id": "dev-test-driven-development", "reason": "task methodology allocation"}], + "traversal": "runtime_only", + } + assert "parent-rule" not in json.dumps(brief) + + +def test_ctx_04_success_evidence_projects_compact_receipt_not_history_or_stdout() -> None: + projected = execution_context.project_validation_evidence( + [{"id": "VAL-001", "command": "pytest -q", "result": "passed", "stdout": "large output", "history": [1, 2]}], + evidence_capability={ + "invariants": [{"boundary": "component", "freshness": "current_task_batch", "evidence_ids": ["VAL-001"]}] + }, + observed=[{"id": "VAL-001", "observation_id": "observation-001", "result": "passed"}], + ) + + assert projected == [{ + "id": "VAL-001", + "digest": execution_context.semantic_digest({"command": "pytest -q", "result": "passed"}), + "result": "passed", + "boundary": "component", + "freshness": "current_task_batch", + "invalidation_receipt": "observation-001", + "expansion_reason": None, + }] + assert "stdout" not in json.dumps(projected) + assert "history" not in json.dumps(projected) + + +def test_ctx_05_failed_evidence_expands_only_with_allowed_reason() -> None: + item = {"id": "VAL-001", "command": "pytest -q", "result": "failed", "stderr": "assertion failed"} + projected = execution_context.project_validation_evidence( + [item], evidence_capability={"invariants": []}, expansion_reason="failed_validation" + ) + + assert projected[0]["expansion_reason"] == "failed_validation" + assert projected[0]["details"] == item + with pytest.raises(SystemExit, match="expansion_reason"): + execution_context.project_validation_evidence( + [item], evidence_capability={"invariants": []}, expansion_reason="whole_history" + ) + + +def test_ctx_06_no_retrieval_escape_hatch_and_context_metrics_use_existing_telemetry( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + root, _, task = workspace(tmp_path) + document = _document(root, task) + brief = document["task_brief"] + metrics = document["compiled_context_metrics"] + + assert brief["runtime_context"] == { + "authority": "compiled", + "histories": "runtime_lazy", + "executor_retrieval": "forbidden", + } + assert all(isinstance(value, int) and value >= 0 for key, value in metrics.items() if key != "expansion_reason") + assert metrics["expansion_reason"] is None + assert "hard_limit" not in json.dumps(document) + + compiled_packet = _without_terminal_evidence( + brief, "CTX-06 executes only from its already-compiled packet." + ) + handoff = { + "type": "executor-result", + "related": {"plan": brief["plan_id"], "task": brief["task_id"]}, + "result": {"state": "completed"}, + "task_fit_check": {"task": brief["task_id"], "result": "clean"}, + "delegation_evidence": _delegation_evidence(), + "knowledge_disposition": { + "action": "none", + "reason": "No stable authority changed.", + "affected_authority": [], + }, + } + original_open = builtins.open + original_read_text = Path.read_text + original_read_bytes = Path.read_bytes + + def retrieval_is_denied(file: object) -> bool: + candidate = str(file) + return ( + ".work-bundle/knowledge" in candidate + or ".work-bundle/orchestration" in candidate + ) + + def deny_runtime_retrieval(file: object, *args: object, **kwargs: object): + if retrieval_is_denied(file): + raise AssertionError(f"executor retrieval attempted: {file}") + return original_open(file, *args, **kwargs) + + def deny_path_text(file: Path, *args: object, **kwargs: object) -> str: + if retrieval_is_denied(file): + raise AssertionError(f"executor retrieval attempted: {file}") + return original_read_text(file, *args, **kwargs) + + def deny_path_bytes(file: Path) -> bytes: + if retrieval_is_denied(file): + raise AssertionError(f"executor retrieval attempted: {file}") + return original_read_bytes(file) + + monkeypatch.setattr(builtins, "open", deny_runtime_retrieval) + monkeypatch.setattr(Path, "read_text", deny_path_text) + monkeypatch.setattr(Path, "read_bytes", deny_path_bytes) + accepted = execution_context.validate_executor_result_for_task( + handoff, compiled_packet, mutation_events=[] + ) + assert accepted["result_state"] == "completed" + assert accepted["task_ownership"]["agent_id"] == "ctx-fixture-agent" + + payload = event() + payload["compiled_context_metrics"] = metrics + assert validate_stage_event(payload).compiled_context_metrics == metrics + invalid = deepcopy(payload) + invalid["compiled_context_metrics"]["expansion_reason"] = "whole_history" + with pytest.raises(StageEventError, match="WB_STAGE_EVENT_CONTEXT_METRICS_INVALID"): + validate_stage_event(invalid) + + +def test_completed_task_acceptance_requires_subagent_delegation_evidence(tmp_path: Path) -> None: + root, _, task = workspace(tmp_path) + brief = _without_terminal_evidence( + _document(root, task)["task_brief"], "Ownership-only acceptance fixture." + ) + handoff = { + "type": "executor-result", + "related": {"plan": brief["plan_id"], "task": brief["task_id"]}, + "result": {"state": "completed"}, + "task_fit_check": {"task": brief["task_id"], "result": "clean"}, + "knowledge_disposition": { + "action": "none", + "reason": "No stable authority changed.", + "affected_authority": [], + }, + } + + with pytest.raises(SystemExit, match="workspace-blocked.*subagent ownership"): + execution_context.validate_executor_result_for_task( + handoff, brief, mutation_events=[] + ) + + +def test_completed_task_acceptance_rejects_controller_task_scope_mutation( + tmp_path: Path, +) -> None: + root, _, task = workspace(tmp_path) + brief = _without_terminal_evidence( + _document(root, task)["task_brief"], "Ownership-only acceptance fixture." + ) + handoff = { + "type": "executor-result", + "related": {"plan": brief["plan_id"], "task": brief["task_id"]}, + "result": {"state": "completed"}, + "task_fit_check": {"task": brief["task_id"], "result": "clean"}, + "delegation_evidence": _delegation_evidence(), + "knowledge_disposition": { + "action": "none", + "reason": "No stable authority changed.", + "affected_authority": [], + }, + } + + with pytest.raises(SystemExit, match="review-blocked.*controller mutated"): + execution_context.validate_executor_result_for_task( + handoff, + brief, + mutation_events=[{ + "actor_kind": "controller", + "paths": [brief["files"]["write"][0]], + }], + ) + assert "mutation_events" not in handoff + durable_history = {**handoff, "mutation_events": []} + with pytest.raises(SystemExit, match="forbidden field mutation_events"): + execution_context.validate_executor_result_for_task(durable_history, brief) + + +def test_repair_acceptance_requires_exact_runtime_owner_and_continuity( + tmp_path: Path, +) -> None: + root, _, task = workspace(tmp_path) + scoped = _ensure_source_file(root) + git(root, "add", ".") + git(root, "commit", "-qm", "baseline") + brief = _without_terminal_evidence( + _document(root, task)["task_brief"], "Repair ownership fixture." + ) + binding = _bind_task_execution(root, brief) + scoped.write_text("def compile_task():\n return 'repair'\n", encoding="utf-8") + frontier = { + "prior_review_id": "review-prior", + "frozen_evidence_reference": execution_context.semantic_digest("evidence"), + } + handoff = { + "type": "executor-result", + "related": {"plan": brief["plan_id"], "task": brief["task_id"]}, + "result": {"state": "completed"}, + "task_fit_check": {"task": brief["task_id"], "result": "repaired"}, + "delegation_evidence": _delegation_evidence(), + "acceptance_review": {"required": False, "repair_frontier": frontier}, + "knowledge_disposition": { + "action": "none", + "reason": "No stable authority changed.", + "affected_authority": [], + }, + } + continuity = { + brief["task_id"]: { + "binding_id": binding["ownership"]["binding_id"], + "baseline_identity": execution_context.semantic_digest(binding["baseline"]), + "evidence_identity": frontier["frozen_evidence_reference"], + "previous_review_identity": frontier["prior_review_id"], + } + } + prior = {brief["task_id"]: _delegation_evidence()} + + accepted = execution_context.validate_executor_result_for_task( + handoff, + brief, + mutation_events=[], + prior_ownership=prior, + repair_continuity=continuity, + ) + assert accepted["task_ownership"]["agent_id"] == "ctx-fixture-agent" + + replacement = deepcopy(handoff) + replacement["delegation_evidence"] = { + **_delegation_evidence(), + "agent_id": "unrelated-agent", + "run_id": "unrelated-run", + } + with pytest.raises(SystemExit, match="replacement is not authorized"): + execution_context.validate_executor_result_for_task( + replacement, + brief, + mutation_events=[], + prior_ownership=prior, + repair_continuity=continuity, + ) + execution_context.validate_executor_result_for_task( + replacement, + brief, + mutation_events=[], + prior_ownership=prior, + repair_continuity=continuity, + authorized_replacements={brief["task_id"]}, + ) + + stale = deepcopy(continuity) + stale[brief["task_id"]]["baseline_identity"] = "stale" + with pytest.raises(SystemExit, match="continuity identities do not match"): + execution_context.validate_executor_result_for_task( + handoff, + brief, + mutation_events=[], + prior_ownership=prior, + repair_continuity=stale, + ) + + +def test_accepted_dependency_deltas_use_exact_handoff_and_observed_checkpoint( + tmp_path: Path, +) -> None: + root, _, task = workspace(tmp_path) + task.write_text( + task.read_text(encoding="utf-8").replace( + "phase_id: phase-001\n", + "phase_id: phase-001\ndepends_on: [task-dependency]\n", + ), + encoding="utf-8", + ) + scoped = _ensure_source_file(root) + dependency = root / "references/assets/orchestration/workflow.md" + dependency.parent.mkdir(parents=True, exist_ok=True) + dependency.write_text("before\n", encoding="utf-8") + git(root, "add", ".") + git(root, "commit", "-qm", "baseline") + baseline = git(root, "rev-parse", "HEAD") + baseline_tree = git(root, "rev-parse", "HEAD^{tree}") + + brief = _document(root, task)["task_brief"] + _bind_task_execution(root, brief) + + dependency.write_text("accepted dependency\n", encoding="utf-8") + git(root, "add", str(dependency.relative_to(root))) + git(root, "commit", "-qm", "accepted dependency") + checkpoint = git(root, "rev-parse", "HEAD") + checkpoint_tree = git(root, "rev-parse", "HEAD^{tree}") + scoped.write_text("def compile_task():\n return 'task repair'\n", encoding="utf-8") + git(root, "add", str(scoped.relative_to(root))) + git(root, "commit", "-qm", "task repair") + + identity = lambda commit, tree: { + "artifact_id": "task-dependency", + "revision": commit, + "sha256": execution_context.semantic_digest({"commit": commit, "tree": tree}), + "source_tree": tree, + } + dependency_handoff = { + "id": "handoff-accepted-dependency", + "type": "executor-result", + "related": {"plan": brief["plan_id"], "task": "task-dependency"}, + "result": {"state": "completed"}, + "acceptance_review": { + "required": True, + "verdict": "accept", + "review_mode": "repair", + "target_identity": identity(checkpoint, checkpoint_tree), + "repair_frontier": { + "previous_reviewed_identity": identity(baseline, baseline_tree), + "repaired_identity": identity(checkpoint, checkpoint_tree), + }, + }, + } + accepted_path = ( + root + / ".work-bundle/orchestration/handoff/executor/active/handoff-accepted-dependency.yaml" + ) + accepted_path.parent.mkdir(parents=True, exist_ok=True) + accepted_path.write_text( + "\n".join(execution_context._dump_yaml(dependency_handoff)) + "\n", + encoding="utf-8", + ) + descriptor = { + "task_id": "task-dependency", + "handoff_id": "handoff-accepted-dependency", + "handoff_sha256": hashlib.sha256(accepted_path.read_bytes()).hexdigest(), + "integrated_base": baseline, + "integrated_head": checkpoint, + } + + current = _without_terminal_evidence(brief, "Dependency attribution fixture.") + current["evidence_applicability"] = { + "metadata": {"required": False, "reasons": []}, + "repository": {"required": True, "reasons": ["accepted dependency delta"]}, + "codegraph": {"required": False, "reasons": []}, + } + handoff = { + "type": "executor-result", + "related": {"plan": brief["plan_id"], "task": brief["task_id"]}, + "result": {"state": "completed"}, + "task_fit_check": {"task": brief["task_id"], "result": "clean"}, + "delegation_evidence": _delegation_evidence(), + "repository": [{ + "root": str(root.resolve()), + "target_kind": "git-backed", + "preflight_kind": "git-clean-worktree", + "baseline": "initial", + "status": "clean", + }], + "knowledge_disposition": { + "action": "none", + "reason": "No stable authority changed.", + "affected_authority": [], + }, + } + + with pytest.raises(SystemExit, match="workflow.md"): + execution_context.validate_executor_result_for_task( + handoff, current, observe=True, mutation_events=[] + ) + accepted = execution_context.validate_executor_result_for_task( + handoff, + current, + observe=True, + mutation_events=[], + accepted_dependency_deltas=[descriptor], + ) + assert accepted["result_state"] == "completed" + + cli_handoff = deepcopy(handoff) + cli_handoff["result"]["state"] = "partial" + cli_handoff["changes"] = { + "files": [ + { + "path": "scripts/orchestration/execution_context.py", + "action": "modified", + } + ] + } + cli_handoff["codegraph"] = [ + { + "root": str(root.resolve()), + "applicable": False, + "up_to_date": False, + "reason": "no-index", + } + ] + cli_handoff_path = ( + root / ".work-bundle/orchestration/handoff/executor/active/handoff-task-004.yaml" + ) + cli_handoff_path.write_text( + "\n".join(execution_context._dump_yaml(cli_handoff)) + "\n", encoding="utf-8" + ) + cli = subprocess.run( + [ + sys.executable, + str(REPO_ROOT / "scripts/orch.py"), + "build-review-package", + "--project-root", + str(root), + "--task", + str(task), + "--handoff", + str(cli_handoff_path), + "--base", + baseline, + "--head", + git(root, "rev-parse", "HEAD"), + "--accepted-dependency-deltas", + json.dumps([descriptor]), + ], + capture_output=True, + text=True, + ) + assert cli.returncode == 0, cli.stderr + assert "review-package.md" in cli.stdout + + accepted_bytes = accepted_path.read_bytes() + wrong_plan_handoff = deepcopy(dependency_handoff) + wrong_plan_handoff["related"]["plan"] = "plan-WRONG" + accepted_path.write_text( + "\n".join(execution_context._dump_yaml(wrong_plan_handoff)) + "\n", + encoding="utf-8", + ) + wrong_plan = { + **descriptor, + "handoff_sha256": hashlib.sha256(accepted_path.read_bytes()).hexdigest(), + } + with pytest.raises(SystemExit, match="plan"): + execution_context.validate_executor_result_for_task( + handoff, current, observe=True, accepted_dependency_deltas=[wrong_plan] + ) + accepted_path.write_bytes(accepted_bytes) + + stale = {**descriptor, "handoff_sha256": "0" * 64} + with pytest.raises(SystemExit, match="handoff identity is stale"): + execution_context.validate_executor_result_for_task( + handoff, current, observe=True, accepted_dependency_deltas=[stale] + ) + mismatched = {**descriptor, "integrated_head": git(root, "rev-parse", "HEAD")} + with pytest.raises(SystemExit, match="checkpoint is mismatched"): + execution_context.validate_executor_result_for_task( + handoff, current, observe=True, accepted_dependency_deltas=[mismatched] + ) + unaccepted_handoff = deepcopy(dependency_handoff) + unaccepted_handoff["acceptance_review"]["verdict"] = "pending" + accepted_path.write_text( + "\n".join(execution_context._dump_yaml(unaccepted_handoff)) + "\n", + encoding="utf-8", + ) + unaccepted = { + **descriptor, + "handoff_sha256": hashlib.sha256(accepted_path.read_bytes()).hexdigest(), + } + with pytest.raises(SystemExit, match="not an accepted repair result"): + execution_context.validate_executor_result_for_task( + handoff, current, observe=True, accepted_dependency_deltas=[unaccepted] + ) + accepted_path.write_bytes(accepted_bytes) + dependency.write_text("later task mutation\n", encoding="utf-8") + with pytest.raises(SystemExit, match="changed after integration"): + execution_context.validate_executor_result_for_task( + handoff, current, observe=True, accepted_dependency_deltas=[descriptor] + ) + + +def test_accepted_dependency_repair_chain_is_ordered_contiguous_and_last_wins( + tmp_path: Path, +) -> None: + root, _, _ = workspace(tmp_path) + dependency_path = "references/assets/orchestration/workflow.md" + dependency = root / dependency_path + dependency.parent.mkdir(parents=True, exist_ok=True) + dependency.write_text("version zero\n", encoding="utf-8") + git(root, "add", ".") + git(root, "commit", "-qm", "chain baseline") + commits = [git(root, "rev-parse", "HEAD")] + trees = [git(root, "rev-parse", "HEAD^{tree}")] + for number in range(1, 4): + dependency.write_text(f"version {number}\n", encoding="utf-8") + git(root, "add", dependency_path) + git(root, "commit", "-qm", f"accepted dependency repair {number}") + commits.append(git(root, "rev-parse", "HEAD")) + trees.append(git(root, "rev-parse", "HEAD^{tree}")) + unrelated = root / "tests/task-local.txt" + unrelated.parent.mkdir(parents=True, exist_ok=True) + unrelated.write_text("dependent task change\n", encoding="utf-8") + git(root, "add", str(unrelated.relative_to(root))) + git(root, "commit", "-qm", "dependent task change") + + task = { + "task_id": "dependent-task", + "plan_id": "plan-001", + "depends_on": ["task-dependency"], + "workspace": {"root": str(root)}, + } + handoff_root = root / ".work-bundle/orchestration/handoff/executor/active" + handoff_root.mkdir(parents=True, exist_ok=True) + + def identity(index: int) -> dict[str, object]: + return { + "artifact_id": "task-dependency", + "revision": commits[index], + "sha256": execution_context.semantic_digest( + {"commit": commits[index], "tree": trees[index]} + ), + "source_tree": trees[index], + } + + handoffs: list[dict[str, object]] = [] + descriptors: list[dict[str, object]] = [] + paths: list[Path] = [] + for index in range(1, 4): + handoff_id = f"handoff-chain-{index}" + handoff = { + "id": handoff_id, + "type": "executor-result", + "related": {"plan": "plan-001", "task": "task-dependency"}, + "result": {"state": "completed"}, + "acceptance_review": { + "required": True, + "verdict": "accept", + "review_mode": "repair", + "target_identity": identity(index), + "repair_frontier": { + "previous_reviewed_identity": identity(index - 1), + "repaired_identity": identity(index), + }, + }, + } + path = handoff_root / f"{handoff_id}.yaml" + path.write_text("\n".join(execution_context._dump_yaml(handoff)) + "\n", encoding="utf-8") + handoffs.append(handoff) + paths.append(path) + descriptors.append( + { + "task_id": "task-dependency", + "handoff_id": handoff_id, + "handoff_sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + "integrated_base": commits[index - 1], + "integrated_head": commits[index], + } + ) + + assert execution_context._accepted_dependency_paths(task, root, descriptors) == { + dependency_path + } + + with pytest.raises(SystemExit, match="anchored|incomplete"): + execution_context._accepted_dependency_paths(task, root, descriptors[1:]) + with pytest.raises(SystemExit, match="anchored|incomplete"): + execution_context._accepted_dependency_paths(task, root, descriptors[-1:]) + + with pytest.raises(SystemExit, match="ordered|source.*contiguous"): + execution_context._accepted_dependency_paths( + task, root, [descriptors[1], descriptors[0], descriptors[2]] + ) + with pytest.raises(SystemExit, match="source.*contiguous|incomplete"): + execution_context._accepted_dependency_paths(task, root, [descriptors[0], descriptors[2]]) + nonadjacent = deepcopy(descriptors) + nonadjacent[1] = {**nonadjacent[1], "integrated_base": commits[0]} + with pytest.raises(SystemExit, match="integrated.*contiguous|non-adjacent"): + execution_context._accepted_dependency_paths(task, root, nonadjacent) + missing = deepcopy(descriptors) + missing[1] = {**missing[1], "handoff_id": "handoff-chain-missing"} + with pytest.raises(SystemExit, match="missing or ambiguous"): + execution_context._accepted_dependency_paths(task, root, missing) + + rejected = deepcopy(handoffs[1]) + rejected["acceptance_review"]["verdict"] = "pending" + paths[1].write_text( + "\n".join(execution_context._dump_yaml(rejected)) + "\n", encoding="utf-8" + ) + unaccepted = deepcopy(descriptors) + unaccepted[1] = { + **unaccepted[1], + "handoff_sha256": hashlib.sha256(paths[1].read_bytes()).hexdigest(), + } + with pytest.raises(SystemExit, match="not an accepted repair"): + execution_context._accepted_dependency_paths(task, root, unaccepted) + paths[1].write_text( + "\n".join(execution_context._dump_yaml(handoffs[1])) + "\n", encoding="utf-8" + ) + + dependency.write_text("unaccepted later mutation\n", encoding="utf-8") + with pytest.raises(SystemExit, match="changed after integration"): + execution_context._accepted_dependency_paths(task, root, descriptors) + + +def test_cumulative_accepted_result_delta_requires_complete_final_review_chain( + tmp_path: Path, +) -> None: + root, _, _ = workspace(tmp_path) + dependency_path = "references/assets/orchestration/workflow.md" + repair_path = "scripts/orchestration/dependency_repair.py" + final_path = "tests/dependency_result.md" + dependency = root / dependency_path + dependency.parent.mkdir(parents=True, exist_ok=True) + dependency.write_text("accepted base\n", encoding="utf-8") + git(root, "add", ".") + git(root, "commit", "-qm", "accepted base") + commits = [git(root, "rev-parse", "HEAD")] + trees = [git(root, "rev-parse", "HEAD^{tree}")] + for label, changed_path in ( + ("initial repair target", dependency_path), + ("accepted repair", repair_path), + ("fresh accepted result", final_path), + ): + changed = root / changed_path + changed.parent.mkdir(parents=True, exist_ok=True) + changed.write_text(label + "\n", encoding="utf-8") + git(root, "add", changed_path) + git(root, "commit", "-qm", label) + commits.append(git(root, "rev-parse", "HEAD")) + trees.append(git(root, "rev-parse", "HEAD^{tree}")) + local = root / "tests/task-local.txt" + local.parent.mkdir(parents=True, exist_ok=True) + local.write_text("dependent task\n", encoding="utf-8") + git(root, "add", str(local.relative_to(root))) + git(root, "commit", "-qm", "dependent task") + + def identity(index: int) -> dict[str, object]: + return { + "artifact_id": "task-dependency", + "revision": commits[index], + "sha256": execution_context.semantic_digest( + {"commit": commits[index], "tree": trees[index]} + ), + "source_tree": trees[index], + } + + def reviewer(agent: str) -> dict[str, object]: + return { + "agent_id": agent, + "capability": "judgment", + "authorship": "none", + "repair_participation": "none", + "decision_participation": "none", + "deliberation_participation": "none", + "context_origin": "direct_source", + } + + def review(review_id: str, index: int, verdict: str) -> dict[str, object]: + return { + "required": True, + "reviewer_independent": True, + "verdict": verdict, + "reviewed_head": commits[index], + "review_id": review_id, + "review_mode": "initial", + "review_target_kind": "task", + "repair_frontier": None, + "review_reset": None, + "target_identity": identity(index), + "reviewer": reviewer("reviewer-" + review_id), + "evidence": { + "mode": "direct", + "capabilities": ["source inspection"], + "unavailable_evidence": [], + "commands": [], + "artifacts": [], + }, + "findings": [], + "started_at": f"2026-09-06T00:0{index}:00Z", + "completed_at": f"2026-09-06T00:0{index}:30Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + + base_review = review("review-base", 0, "accept") + initial_repair = review("review-initial-repair", 1, "repair") + initial_repair["review_reset"] = { + "prior_review_id": "review-base", + "reason_class": "validation_allocation", + "reason": "Validation authority changed.", + } + initial_repair["previous_review"] = base_review + initial_repair["findings"] = [{ + "finding_id": "CHAIN-FINDING", + "stage": "implementation", + "class": "implementation_defect", + "severity": "blocking", + "first_broken_artifact": "implementation", + "obligation_basis": "accepted_requirement", + "evidence": [{ + "kind": "test", "locator": "CHAIN", "digest_or_identity": "red", + "observation": "repair required", + }], + "target_identity": identity(1), + "summary": "Repair the chain.", + "recommended_owner": "task_owner", + "disposition": "repair_task", + }] + accepted_repair = review("review-accepted-repair", 2, "accept") + accepted_repair["review_mode"] = "repair" + accepted_repair["repair_frontier"] = { + "prior_review_id": "review-initial-repair", + "blocking_finding_ids": ["CHAIN-FINDING"], + "previous_reviewed_identity": identity(1), + "repaired_identity": identity(2), + "affected_boundaries": [dependency_path], + "frozen_evidence_reference": review_runtime.review_evidence_identity(initial_repair), + } + accepted_repair["previous_review"] = { + key: value for key, value in initial_repair.items() if key != "previous_review" + } + final_review = review("review-final", 3, "accept") + final_review["review_reset"] = { + "prior_review_id": "review-accepted-repair", + "reason_class": "validation_allocation", + "reason": "Cumulative result validation changed.", + } + final_review["previous_review"] = { + key: value for key, value in accepted_repair.items() if key != "previous_review" + } + + handoff_root = root / ".work-bundle/orchestration/handoff/executor/active" + handoff_root.mkdir(parents=True, exist_ok=True) + records = [ + ("handoff-base", base_review, "completed"), + ("handoff-initial-repair", initial_repair, "partial"), + ("handoff-accepted-repair", accepted_repair, "completed"), + ("handoff-final", final_review, "completed"), + ] + references: dict[str, dict[str, str]] = {} + for handoff_id, acceptance, state in records: + path = handoff_root / f"{handoff_id}.yaml" + document = { + "id": handoff_id, + "type": "executor-result", + "related": {"plan": "plan-001", "task": "task-dependency"}, + "result": {"state": state}, + "acceptance_review": acceptance, + } + rendered = "\n".join(execution_context._dump_yaml(document)) + "\n" + path.write_text(rendered.replace(": none\n", ': "none"\n'), encoding="utf-8") + references[handoff_id] = { + "handoff_id": handoff_id, + "handoff_sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + } + descriptor = { + "task_id": "task-dependency", + "accepted_result_base": references["handoff-base"], + "review_chain": [ + references["handoff-initial-repair"], + references["handoff-accepted-repair"], + references["handoff-final"], + ], + "integrated_base": commits[0], + "integrated_head": commits[3], + } + task = { + "task_id": "dependent-task", + "plan_id": "plan-001", + "depends_on": ["task-dependency"], + "workspace": {"root": str(root)}, + } + + final_handoff_path = handoff_root / "handoff-final.yaml" + original_final_bytes = final_handoff_path.read_bytes() + same_reviewer_document = deepcopy(document) + same_reviewer_document["acceptance_review"]["reviewer"] = deepcopy( + accepted_repair["reviewer"] + ) + rendered = "\n".join(execution_context._dump_yaml(same_reviewer_document)) + "\n" + final_handoff_path.write_text( + rendered.replace(": none\n", ': "none"\n'), encoding="utf-8" + ) + same_reviewer = deepcopy(descriptor) + same_reviewer["review_chain"][-1]["handoff_sha256"] = hashlib.sha256( + final_handoff_path.read_bytes() + ).hexdigest() + with pytest.raises(SystemExit, match="fresh|reviewer|independent"): + execution_context._accepted_dependency_paths(task, root, [same_reviewer]) + final_handoff_path.write_bytes(original_final_bytes) + + assert execution_context._accepted_dependency_paths(task, root, [descriptor]) == { + dependency_path, repair_path, final_path + } + missing = deepcopy(descriptor) + missing["review_chain"] = missing["review_chain"][1:] + with pytest.raises(SystemExit, match="chain|prior"): + execution_context._accepted_dependency_paths(task, root, [missing]) + reordered = deepcopy(descriptor) + reordered["review_chain"][0], reordered["review_chain"][1] = ( + reordered["review_chain"][1], reordered["review_chain"][0] + ) + with pytest.raises(SystemExit, match="chain|prior|ordered"): + execution_context._accepted_dependency_paths(task, root, [reordered]) + intermediate = deepcopy(descriptor) + intermediate["review_chain"] = intermediate["review_chain"][:1] + intermediate["integrated_head"] = commits[1] + with pytest.raises(SystemExit, match="final|accept"): + execution_context._accepted_dependency_paths(task, root, [intermediate]) + wrong_checkpoint = deepcopy(descriptor) + wrong_checkpoint["integrated_head"] = commits[2] + with pytest.raises(SystemExit, match="checkpoint|mismatched"): + execution_context._accepted_dependency_paths(task, root, [wrong_checkpoint]) + stale_identity = deepcopy(descriptor) + stale_identity["review_chain"][-1]["handoff_sha256"] = "0" * 64 + with pytest.raises(SystemExit, match="stale"): + execution_context._accepted_dependency_paths(task, root, [stale_identity]) + + commits.append(git(root, "rev-parse", "HEAD")) + trees.append(git(root, "rev-parse", "HEAD^{tree}")) + later_review = review("review-later", 4, "repair") + later_review["review_reset"] = { + "prior_review_id": "review-final", + "reason_class": "validation_allocation", + "reason": "A later accepted result superseded the terminal result.", + } + later_review["previous_review"] = { + key: value for key, value in final_review.items() if key != "previous_review" + } + later_review["findings"] = [deepcopy(initial_repair["findings"][0])] + later_review["findings"][0]["finding_id"] = "LATER-FINDING" + later_review["findings"][0]["target_identity"] = identity(4) + later_path = handoff_root / "handoff-later.yaml" + later_document = { + "id": "handoff-later", + "type": "executor-result", + "related": {"plan": "plan-001", "task": "task-dependency"}, + "result": {"state": "partial"}, + "acceptance_review": later_review, + } + rendered = "\n".join(execution_context._dump_yaml(later_document)) + "\n" + later_path.write_text(rendered.replace(": none\n", ': "none"\n'), encoding="utf-8") + with pytest.raises(SystemExit, match="terminal result is stale"): + execution_context._accepted_dependency_paths(task, root, [descriptor]) + + +def test_authority_recovery_receipt_is_helper_created_fresh_and_rechecked( + tmp_path: Path, +) -> None: + root, _, task_path = workspace(tmp_path) + dependency_path = "references/assets/orchestration/workflow.md" + dependency = root / dependency_path + dependency.parent.mkdir(parents=True, exist_ok=True) + dependency.write_text("original accepted baseline\n", encoding="utf-8") + git(root, "add", ".") + git(root, "commit", "-qm", "original dependency baseline") + baseline = git(root, "rev-parse", "HEAD") + baseline_tree = git(root, "rev-parse", "HEAD^{tree}") + + dependent = _compiled_brief(root, task_path) + dependency_brief = deepcopy(dependent) + dependency_brief["task_id"] = "task-dependency" + binding = _bind_task_execution( + root, + dependency_brief, + execution_id="dependency-exec", + write_scope=[dependency_path], + ) + binding_path = ( + root + / ".work-bundle/runtime/execution/plan-001/task-dependency/execution-binding.json" + ) + binding_digest = hashlib.sha256(binding_path.read_bytes()).hexdigest() + + dependency.write_text("missing accepted result base\n", encoding="utf-8") + git(root, "add", dependency_path) + git(root, "commit", "-qm", "missing accepted result base") + expected_base_head = git(root, "rev-parse", "HEAD") + expected_base_tree = git(root, "rev-parse", "HEAD^{tree}") + + dependency.write_text("fresh whole-task accepted result\n", encoding="utf-8") + git(root, "add", dependency_path) + git(root, "commit", "-qm", "fresh recovered dependency result") + recovered_head = git(root, "rev-parse", "HEAD") + recovered_tree = git(root, "rev-parse", "HEAD^{tree}") + + def identity(commit: str, tree: str) -> dict[str, object]: + return { + "artifact_id": "task-dependency", + "revision": commit, + "sha256": execution_context.semantic_digest({"commit": commit, "tree": tree}), + "source_tree": tree, + } + + def reviewer(agent_id: str) -> dict[str, object]: + return { + "agent_id": agent_id, + "capability": "judgment", + "authorship": "none", + "repair_participation": "none", + "decision_participation": "none", + "deliberation_participation": "none", + "context_origin": "direct_source", + } + + previous = { + "required": True, + "reviewer_independent": True, + "verdict": "accept", + "reviewed_head": expected_base_head, + "review_id": "review-dependency-accepted-historical", + "review_mode": "initial", + "review_target_kind": "task", + "repair_frontier": None, + "review_reset": None, + "target_identity": identity(expected_base_head, expected_base_tree), + "reviewer": reviewer("historical-reviewer"), + "evidence": { + "mode": "direct", + "capabilities": ["whole-task source review"], + "unavailable_evidence": [], + "commands": [], + "artifacts": [], + }, + "findings": [], + "started_at": "2026-09-06T01:00:00Z", + "completed_at": "2026-09-06T01:01:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + recovered_review = { + **deepcopy(previous), + "review_id": "review-dependency-authority-recovery", + "reviewed_head": recovered_head, + "target_identity": identity(recovered_head, recovered_tree), + "reviewer": reviewer("fresh-recovery-reviewer"), + "review_reset": { + "prior_review_id": previous["review_id"], + "reason_class": "authority", + "reason": "The accepted-result handoff bytes are unavailable.", + }, + "previous_review": previous, + "started_at": "2026-09-06T01:02:00Z", + "completed_at": "2026-09-06T01:03:00Z", + } + recovered_handoff = { + "id": "handoff-recovered-dependency", + "type": "executor-result", + "status": "active", + "project": "fixture", + "created_at": "2026-09-06", + "updated_at": "2026-09-06", + "related": {"plan": "plan-001", "task": "task-dependency"}, + "result": {"state": "completed"}, + "delegation_evidence": { + "delegated": True, + "owner_kind": "subagent", + "agent_id": "dependency-owner", + "run_id": "dependency-run", + "mechanism": "host-native", + }, + "acceptance_review": recovered_review, + } + handoff_dir = root / ".work-bundle/orchestration/handoff/executor/active" + handoff_dir.mkdir(parents=True, exist_ok=True) + recovered_path = handoff_dir / "handoff-recovered-dependency.yaml" + index = root / ".work-bundle/orchestration/handoff/index.jsonl" + index.write_text("", encoding="utf-8") + create_receipt = subprocess.run( + [ + sys.executable, + str(REPO_ROOT / "scripts/orch.py"), + "create-accepted-base-absence-receipt", + "--project-root", + str(root), + "--plan-id", + "plan-001", + "--task-id", + "task-dependency", + "--expected-head", + expected_base_head, + "--expected-tree", + expected_base_tree, + "--proposed-handoff-id", + recovered_handoff["id"], + "--proposed-review-id", + recovered_review["review_id"], + "--final-head", + recovered_head, + "--final-tree", + recovered_tree, + ], + capture_output=True, + text=True, + ) + assert create_receipt.returncode == 0, create_receipt.stderr + receipt_reference = json.loads(create_receipt.stdout) + recovered_path.write_text( + ("\n".join(execution_context._dump_yaml(recovered_handoff)) + "\n").replace( + ": none\n", ': "none"\n' + ), + encoding="utf-8", + ) + index.write_text( + json.dumps({ + "id": recovered_handoff["id"], + "type": "executor-result", + "status": "active", + "path": str(recovered_path.relative_to(root)), + "related_plan": "plan-001", + "related_task": "task-dependency", + }) + "\n", + encoding="utf-8", + ) + + assert set(receipt_reference) == {"receipt_id", "receipt_sha256"} + receipt_path = execution_context._recovery_receipt_path( + root, "plan-001", "task-dependency", receipt_reference["receipt_id"] + ) + receipt = json.loads(receipt_path.read_text(encoding="utf-8")) + assert receipt["schema"] == "accepted-result-recovery-receipt-v1" + assert receipt["binding_id"] == binding["ownership"]["binding_id"] + assert receipt["binding_sha256"] == binding_digest + assert receipt["baseline_head"] == baseline + assert receipt["baseline_tree"] == baseline_tree + assert receipt["expected_base_query"]["result"] == "absent" + assert receipt["freshness"] == "current_validation_attempt" + assert receipt["proposed_recovered_result"] == { + "handoff_id": recovered_handoff["id"], + "review_id": recovered_review["review_id"], + "final_head": recovered_head, + "final_tree": recovered_tree, + } + + recovered_reference = { + "handoff_id": recovered_handoff["id"], + "handoff_sha256": hashlib.sha256(recovered_path.read_bytes()).hexdigest(), + } + descriptor = { + "task_id": "task-dependency", + "execution_baseline_recovery": { + "binding_id": binding["ownership"]["binding_id"], + "binding_sha256": binding_digest, + "baseline_head": baseline, + "baseline_tree": baseline_tree, + "recovery_receipt": receipt_reference, + }, + "recovered_result": recovered_reference, + "accepted_result_delta": { + "expected_base_head": expected_base_head, + "expected_base_tree": expected_base_tree, + "final_head": recovered_head, + "final_tree": recovered_tree, + }, + "integrated_base": expected_base_head, + "integrated_head": recovered_head, + } + dependent["depends_on"] = ["task-dependency"] + assert execution_context._accepted_dependency_paths(dependent, root, [descriptor]) == { + dependency_path + } + + empty_records: list[dict[str, str]] = [] + legacy_stores = { + status: { + "store_id": f"executor/{status}", + "records": empty_records, + "sha256": execution_context.semantic_digest(empty_records), + } + for status in ("active", "archived") + } + legacy_index = { + "index_id": "handoff/index.jsonl", + "path": ".work-bundle/orchestration/handoff/index.jsonl", + "sha256": hashlib.sha256(b"").hexdigest(), + "projected_entries_sha256": execution_context.semantic_digest([]), + } + legacy_revision = execution_context.semantic_digest({ + "expected_base_head": expected_base_head, + "expected_base_tree": expected_base_tree, + "handoff_stores": legacy_stores, + "handoff_index": legacy_index, + }) + legacy_observed_at = "2026-09-06T01:01:00Z" + legacy_receipt_id = ( + f"accepted-result-recovery-task-dependency-{legacy_revision[:12]}-" + f"{hashlib.sha256(legacy_observed_at.encode()).hexdigest()[:12]}" + ) + legacy_receipt = { + "receipt_id": legacy_receipt_id, + "schema": "accepted-result-recovery-receipt-v1", + "plan_id": "plan-001", + "task_id": "task-dependency", + "binding_id": binding["ownership"]["binding_id"], + "binding_sha256": binding_digest, + "baseline_head": baseline, + "baseline_tree": baseline_tree, + "expected_base_head": expected_base_head, + "expected_base_tree": expected_base_tree, + "queried_historical_revision": legacy_revision, + "handoff_stores": legacy_stores, + "handoff_index": legacy_index, + "absence_result": "accepted_base_absent", + "observed_at": legacy_observed_at, + "freshness": "current_validation_attempt", + } + legacy_receipt_path = execution_context._recovery_receipt_path( + root, "plan-001", "task-dependency", legacy_receipt_id + ) + legacy_receipt_path.write_text( + json.dumps(legacy_receipt, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + legacy_reference = { + "receipt_id": legacy_receipt_id, + "receipt_sha256": hashlib.sha256(legacy_receipt_path.read_bytes()).hexdigest(), + } + handoff_bytes = recovered_path.read_bytes() + index_bytes = index.read_bytes() + adopted_reference = execution_context.adopt_existing_recovered_result( + root, + "plan-001", + "task-dependency", + expected_base_head, + expected_base_tree, + recovered_handoff["id"], + recovered_reference["handoff_sha256"], + recovered_review["review_id"], + recovered_head, + recovered_tree, + legacy_reference, + ) + assert recovered_path.read_bytes() == handoff_bytes + assert index.read_bytes() == index_bytes + adopted_descriptor = deepcopy(descriptor) + adopted_descriptor["execution_baseline_recovery"]["recovery_receipt"] = adopted_reference + assert execution_context._accepted_dependency_paths( + dependent, root, [adopted_descriptor] + ) == {dependency_path} + + adopt_cli = subprocess.run( + [ + sys.executable, + str(REPO_ROOT / "scripts/orch.py"), + "adopt-existing-recovered-result", + "--project-root", + str(root), + "--plan-id", + "plan-001", + "--task-id", + "task-dependency", + "--expected-head", + expected_base_head, + "--expected-tree", + expected_base_tree, + "--handoff-id", + recovered_handoff["id"], + "--handoff-sha256", + recovered_reference["handoff_sha256"], + "--review-id", + recovered_review["review_id"], + "--final-head", + recovered_head, + "--final-tree", + recovered_tree, + "--prior-receipt-id", + legacy_reference["receipt_id"], + "--prior-receipt-sha256", + legacy_reference["receipt_sha256"], + ], + capture_output=True, + text=True, + ) + assert adopt_cli.returncode == 0, adopt_cli.stderr + assert set(json.loads(adopt_cli.stdout)) == {"receipt_id", "receipt_sha256"} + assert recovered_path.read_bytes() == handoff_bytes + assert index.read_bytes() == index_bytes + + with pytest.raises(SystemExit, match="prior receipt.*missing"): + execution_context.adopt_existing_recovered_result( + root, + "plan-001", + "task-dependency", + expected_base_head, + expected_base_tree, + recovered_handoff["id"], + recovered_reference["handoff_sha256"], + recovered_review["review_id"], + recovered_head, + recovered_tree, + {"receipt_id": "missing-prior-receipt", "receipt_sha256": "0" * 64}, + ) + with pytest.raises(SystemExit, match="stale"): + execution_context.adopt_existing_recovered_result( + root, + "plan-001", + "task-dependency", + expected_base_head, + expected_base_tree, + recovered_handoff["id"], + "0" * 64, + recovered_review["review_id"], + recovered_head, + recovered_tree, + legacy_reference, + ) + + invalid_legacy = deepcopy(legacy_receipt) + invalid_legacy["baseline_head"] = expected_base_head + invalid_observed_at = "2026-09-06T01:01:01Z" + invalid_legacy["observed_at"] = invalid_observed_at + invalid_legacy_id = ( + f"accepted-result-recovery-task-dependency-{legacy_revision[:12]}-" + f"{hashlib.sha256(invalid_observed_at.encode()).hexdigest()[:12]}" + ) + invalid_legacy["receipt_id"] = invalid_legacy_id + invalid_legacy_path = execution_context._recovery_receipt_path( + root, "plan-001", "task-dependency", invalid_legacy_id + ) + invalid_legacy_path.write_text( + json.dumps(invalid_legacy, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + with pytest.raises(SystemExit, match="prior receipt|baseline|non-global"): + execution_context.adopt_existing_recovered_result( + root, + "plan-001", + "task-dependency", + expected_base_head, + expected_base_tree, + recovered_handoff["id"], + recovered_reference["handoff_sha256"], + recovered_review["review_id"], + recovered_head, + recovered_tree, + { + "receipt_id": invalid_legacy_id, + "receipt_sha256": hashlib.sha256(invalid_legacy_path.read_bytes()).hexdigest(), + }, + ) + + wrong_delta_base = deepcopy(descriptor) + wrong_delta_base["accepted_result_delta"]["expected_base_head"] = baseline + wrong_delta_base["accepted_result_delta"]["expected_base_tree"] = baseline_tree + wrong_delta_base["integrated_base"] = baseline + with pytest.raises(SystemExit, match="accepted_result_delta.*mismatch"): + execution_context._accepted_dependency_paths(dependent, root, [wrong_delta_base]) + + caller_assertion = deepcopy(descriptor) + caller_assertion["execution_baseline_recovery"]["accepted_base_absent"] = True + with pytest.raises(SystemExit, match="closed|shape"): + execution_context._accepted_dependency_paths(dependent, root, [caller_assertion]) + + recovered_review_with_gap = deepcopy(recovered_review) + recovered_review_with_gap["evidence"]["unavailable_evidence"] = ["historical handoff"] + recovered_handoff_with_gap = deepcopy(recovered_handoff) + recovered_handoff_with_gap["acceptance_review"] = recovered_review_with_gap + recovered_path.write_text( + ("\n".join(execution_context._dump_yaml(recovered_handoff_with_gap)) + "\n").replace( + ": none\n", ': "none"\n' + ), + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="unavailable_evidence|complete"): + execution_context._accepted_dependency_paths(dependent, root, [{ + **descriptor, + "recovered_result": { + **recovered_reference, + "handoff_sha256": hashlib.sha256(recovered_path.read_bytes()).hexdigest(), + }, + }]) + recovered_path.write_text( + ("\n".join(execution_context._dump_yaml(recovered_handoff)) + "\n").replace( + ": none\n", ': "none"\n' + ), + encoding="utf-8", + ) + + extra = handoff_dir / "unrelated-record.yaml" + extra.write_text( + "id: unrelated-record\ntype: executor-result\n" + "related: {plan: plan-other, task: task-other}\nresult: {state: partial}\n", + encoding="utf-8", + ) + index.write_text( + index.read_text(encoding="utf-8") + + json.dumps({"id": "unrelated-record", "related_plan": "plan-other", "related_task": "task-other"}) + + "\n", + encoding="utf-8", + ) + assert execution_context._accepted_dependency_paths(dependent, root, [descriptor]) == { + dependency_path + } + + competitor = deepcopy(recovered_handoff) + competitor["id"] = "handoff-competing-recovered-dependency" + competitor["acceptance_review"]["review_id"] = "review-competing-recovery" + competitor_path = handoff_dir / "handoff-competing-recovered-dependency.yaml" + competitor_path.write_text( + ("\n".join(execution_context._dump_yaml(competitor)) + "\n").replace( + ": none\n", ': "none"\n' + ), + encoding="utf-8", + ) + before_competitor_index = index.read_bytes() + index.write_text( + index.read_text(encoding="utf-8") + + json.dumps({ + "id": competitor["id"], + "type": "executor-result", + "status": "active", + "path": str(competitor_path.relative_to(root)), + "related_plan": "plan-001", + "related_task": "task-dependency", + }) + + "\n", + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="competing recovered result"): + execution_context.adopt_existing_recovered_result( + root, + "plan-001", + "task-dependency", + expected_base_head, + expected_base_tree, + recovered_handoff["id"], + recovered_reference["handoff_sha256"], + recovered_review["review_id"], + recovered_head, + recovered_tree, + legacy_reference, + ) + competitor_path.unlink() + index.write_bytes(before_competitor_index) + + archived_dir = root / ".work-bundle/orchestration/handoff/executor/archived" + archived_dir.mkdir(parents=True, exist_ok=True) + expected_base_handoff = deepcopy(recovered_handoff) + expected_base_handoff["id"] = "handoff-restored-accepted-base" + expected_base_handoff["acceptance_review"] = previous + expected_base_path = archived_dir / "handoff-restored-accepted-base.yaml" + expected_base_path.write_text( + ("\n".join(execution_context._dump_yaml(expected_base_handoff)) + "\n").replace( + ": none\n", ': "none"\n' + ), + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="recoverable accepted base"): + execution_context._accepted_dependency_paths(dependent, root, [descriptor]) + with pytest.raises(SystemExit, match="recoverable accepted base"): + execution_context.adopt_existing_recovered_result( + root, + "plan-001", + "task-dependency", + expected_base_head, + expected_base_tree, + recovered_handoff["id"], + recovered_reference["handoff_sha256"], + recovered_review["review_id"], + recovered_head, + recovered_tree, + legacy_reference, + ) + expected_base_path.unlink() + + duplicate_path = archived_dir / "handoff-recovered-dependency-duplicate.yaml" + duplicate_path.write_bytes(recovered_path.read_bytes()) + with pytest.raises(SystemExit, match="ambiguous"): + execution_context._accepted_dependency_paths(dependent, root, [descriptor]) + with pytest.raises(SystemExit, match="ambiguous"): + execution_context.adopt_existing_recovered_result( + root, + "plan-001", + "task-dependency", + expected_base_head, + expected_base_tree, + recovered_handoff["id"], + recovered_reference["handoff_sha256"], + recovered_review["review_id"], + recovered_head, + recovered_tree, + legacy_reference, + ) + duplicate_path.unlink() + + mismatched = deepcopy(recovered_handoff) + mismatched["acceptance_review"]["review_id"] = "different-proposed-review" + recovered_path.write_text( + ("\n".join(execution_context._dump_yaml(mismatched)) + "\n").replace( + ": none\n", ': "none"\n' + ), + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="proposal|proposed|mismatch"): + execution_context._accepted_dependency_paths(dependent, root, [{ + **descriptor, + "recovered_result": { + **recovered_reference, + "handoff_sha256": hashlib.sha256(recovered_path.read_bytes()).hexdigest(), + }, + }]) + + +def test_authority_recovery_receipt_rejects_recoverable_accepted_base(tmp_path: Path) -> None: + root, _, task_path = workspace(tmp_path) + git(root, "add", ".") + git(root, "commit", "-qm", "baseline") + brief = _compiled_brief(root, task_path) + brief["task_id"] = "task-dependency" + _bind_task_execution(root, brief, execution_id="dependency-exec") + handoff_dir = root / ".work-bundle/orchestration/handoff/executor/archived" + handoff_dir.mkdir(parents=True, exist_ok=True) + commit = git(root, "rev-parse", "HEAD") + tree = git(root, "rev-parse", "HEAD^{tree}") + accepted = { + "id": "handoff-historical-accepted-base", + "type": "executor-result", + "related": {"plan": "plan-001", "task": "task-dependency"}, + "result": {"state": "completed"}, + "acceptance_review": { + "required": True, + "reviewer_independent": True, + "verdict": "accept", + "reviewed_head": commit, + "review_id": "review-historical-accepted-base", + "review_mode": "initial", + "review_target_kind": "task", + "repair_frontier": None, + "review_reset": None, + "target_identity": { + "artifact_id": "task-dependency", + "revision": commit, + "sha256": execution_context.semantic_digest({"commit": commit, "tree": tree}), + "source_tree": tree, + }, + "reviewer": { + "agent_id": "historical-reviewer", + "capability": "judgment", + "authorship": "none", + "repair_participation": "none", + "decision_participation": "none", + "deliberation_participation": "none", + "context_origin": "direct_source", + }, + "evidence": { + "mode": "direct", + "capabilities": ["whole-task source review"], + "unavailable_evidence": [], + "commands": [], + "artifacts": [], + }, + "findings": [], + "started_at": "2026-09-06T01:00:00Z", + "completed_at": "2026-09-06T01:01:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + }, + } + accepted_path = handoff_dir / "handoff-historical-accepted-base.yaml" + accepted_path.write_text( + ("\n".join(execution_context._dump_yaml(accepted)) + "\n").replace( + ": none\n", ': "none"\n' + ), + encoding="utf-8", + ) + index = root / ".work-bundle/orchestration/handoff/index.jsonl" + index.write_text("", encoding="utf-8") + + with pytest.raises(SystemExit, match="recoverable accepted base"): + execution_context.create_accepted_base_absence_receipt( + root, + "plan-001", + "task-dependency", + commit, + tree, + "handoff-proposed-recovery", + "review-proposed-recovery", + commit, + tree, + ) + + distinct = root / "tests/distinct-expected-base.txt" + distinct.parent.mkdir(parents=True, exist_ok=True) + distinct.write_text("distinct expected identity\n", encoding="utf-8") + git(root, "add", str(distinct.relative_to(root))) + git(root, "commit", "-qm", "distinct expected base identity") + distinct_head = git(root, "rev-parse", "HEAD") + distinct_tree = git(root, "rev-parse", "HEAD^{tree}") + reference = execution_context.create_accepted_base_absence_receipt( + root, + "plan-001", + "task-dependency", + distinct_head, + distinct_tree, + "handoff-proposed-recovery", + "review-proposed-recovery", + distinct_head, + distinct_tree, + ) + receipt_path = execution_context._recovery_receipt_path( + root, "plan-001", "task-dependency", reference["receipt_id"] + ) + receipt = json.loads(receipt_path.read_text(encoding="utf-8")) + assert receipt["expected_base_head"] == distinct_head + assert receipt["expected_base_tree"] == distinct_tree + + +def test_rf_07_brief_rebuild_retains_original_execution_binding_and_baseline( + tmp_path: Path, +) -> None: + root, _, task = workspace(tmp_path) + scoped = _ensure_source_file(root) + git(root, "add", ".") + git(root, "commit", "-qm", "baseline") + brief = _compiled_brief(root, task) + original = _bind_task_execution(root, brief) + scoped.write_text("def compile_task():\n return 'repair'\n", encoding="utf-8") + git(root, "add", str(scoped.relative_to(root))) + git(root, "commit", "-qm", "repair") + + build_task_brief(args(root, task)) + retained = execution_context.capture_task_baseline_once( + execution_context.load_task_execution_binding(root, "plan-001", "task-004") + ) + + assert retained["ownership"]["binding_id"] == original["ownership"]["binding_id"] + assert retained["baseline"] == original["baseline"] + assert retained["baseline"]["head"] != git(root, "rev-parse", "HEAD") diff --git a/tests/test_wor108_review_frontier.py b/tests/test_wor108_review_frontier.py new file mode 100644 index 0000000..242585e --- /dev/null +++ b/tests/test_wor108_review_frontier.py @@ -0,0 +1,297 @@ +from __future__ import annotations + +import hashlib +import json +import subprocess +import sys +from copy import deepcopy +from pathlib import Path + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(REPO_ROOT / "scripts" / "orchestration")) +sys.path.insert(0, str(REPO_ROOT / "scripts" / "work-bundle")) + +import review_runtime # noqa: E402 +import reviewer_workspace # noqa: E402 + + +ZERO_SHA = "0" * 64 + + +def identity(name: str, digest: str) -> dict[str, object]: + return {"artifact_id": name, "revision": "1", "sha256": digest, "source_tree": None} + + +def finding(target: dict[str, object], finding_id: str = "RF-FINDING-1") -> dict[str, object]: + return { + "finding_id": finding_id, + "stage": "implementation", + "class": "implementation_defect", + "severity": "blocking", + "first_broken_artifact": "implementation", + "obligation_basis": "accepted_requirement", + "evidence": [{"kind": "test", "locator": "RF", "digest_or_identity": "RF-RED", "observation": "failed"}], + "target_identity": target, + "summary": "Repair this bounded defect.", + "recommended_owner": "task_owner", + "disposition": "repair_task", + } + + +def review(*, target: dict[str, object], mode: str = "initial", kind: str = "stage", agent: str = "reviewer-new") -> dict[str, object]: + return { + "review_id": f"review-{mode}-{agent}", + "review_mode": mode, + "review_target_kind": kind, + "repair_frontier": None, + "review_reset": None, + "stage": "plan", + "target_identity": target, + "reviewer": { + "agent_id": agent, + "capability": "judgment", + "authorship": "none", + "repair_participation": "none", + "decision_participation": "none", + "deliberation_participation": "none", + "context_origin": "direct_source", + }, + "evidence": {"mode": "direct", "capabilities": ["bounded source inspection"], "unavailable_evidence": [], "commands": [], "artifacts": []}, + "verdict": "accepted", + "findings": [], + "started_at": "2026-09-06T00:00:00Z", + "completed_at": "2026-09-06T00:01:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + + +def repair_pair() -> tuple[dict[str, object], dict[str, object]]: + old = identity("task-rf", "1" * 64) + new = identity("task-rf", "2" * 64) + prior = review(target=old, agent="reviewer-prior") + prior["review_id"] = "review-prior" + prior["verdict"] = "repair" + prior["findings"] = [finding(old)] + current = review(target=new, mode="repair") + current["repair_frontier"] = { + "prior_review_id": prior["review_id"], + "blocking_finding_ids": ["RF-FINDING-1"], + "previous_reviewed_identity": old, + "repaired_identity": new, + "affected_boundaries": ["scripts/orchestration/review_runtime.py:validate_stage_review"], + "frozen_evidence_reference": review_runtime.review_evidence_identity(prior), + } + return prior, current + + +def test_rf_01_initial_and_repair_modes_are_native_and_closed() -> None: + initial = review(target=identity("task-rf", ZERO_SHA)) + validated = review_runtime.validate_stage_review(initial) + assert (validated.review_mode, validated.review_target_kind, validated.repair_frontier) == ("initial", "stage", None) + schema = json.loads((REPO_ROOT / "references/assets/orchestration/contract/stage-review-v1.schema.json").read_text()) + assert schema["$defs"]["reviewMode"]["enum"] == ["initial", "repair"] + assert schema["$defs"]["reviewTargetKind"]["enum"] == ["task", "stage"] + initial["repair_frontier"] = {"unexpected": True} + with pytest.raises(review_runtime.ReviewContractError, match="initial review cannot carry"): + review_runtime.validate_stage_review(initial) + + +def test_rf_02_repair_frontier_binds_prior_findings_evidence_and_exact_identities() -> None: + prior, current = repair_pair() + validated = review_runtime.validate_review_sequence(current, previous_review=prior) + assert validated.repair_frontier["previous_reviewed_identity"] == prior["target_identity"] + schema = json.loads((REPO_ROOT / "references/assets/orchestration/contract/stage-review-v1.schema.json").read_text()) + assert set(schema["$defs"]["repairFrontier"]["required"]) == set(current["repair_frontier"]) + tampered = deepcopy(current) + tampered["repair_frontier"]["blocking_finding_ids"] = ["UNKNOWN"] + with pytest.raises(review_runtime.ReviewContractError, match="blocking finding IDs"): + review_runtime.validate_review_sequence(tampered, previous_review=prior) + + +def test_rf_03_repair_reuses_frozen_evidence_without_copying_history() -> None: + prior, current = repair_pair() + prior["evidence"]["artifacts"] = [{"path": f"history/{index}.json", "sha256": ZERO_SHA} for index in range(100)] + current["repair_frontier"]["frozen_evidence_reference"] = review_runtime.review_evidence_identity(prior) + validated = review_runtime.validate_review_sequence(current, previous_review=prior) + assert validated.evidence["artifacts"] == [] + assert len(json.dumps(validated.repair_frontier)) < len(json.dumps(prior["evidence"])) + + +@pytest.mark.parametrize("reason_class", ["material_redesign", "authority", "scope", "acceptance", "decomposition", "validation_allocation"]) +def test_rf_04_material_change_requires_fresh_initial_review(reason_class: str) -> None: + prior, repair = repair_pair() + with pytest.raises(review_runtime.ReviewContractError, match="fresh initial review"): + review_runtime.validate_review_sequence(repair, previous_review=prior, material_change=reason_class) + reset = review(target=repair["target_identity"], agent="reviewer-reset") + reset["review_reset"] = {"prior_review_id": prior["review_id"], "reason_class": reason_class, "reason": "accepted boundary changed"} + assert review_runtime.validate_review_sequence(reset, previous_review=prior, material_change=reason_class).review_mode == "initial" + + +def test_rf_05_reset_rejects_reused_repair_reviewer_identity() -> None: + prior, repair = repair_pair() + reset = review(target=repair["target_identity"], agent=prior["reviewer"]["agent_id"]) + reset["review_reset"] = {"prior_review_id": prior["review_id"], "reason_class": "scope", "reason": "scope changed"} + with pytest.raises(review_runtime.ReviewContractError, match="fresh capable independent reviewer"): + review_runtime.validate_review_sequence(reset, previous_review=prior, material_change="scope") + + +def test_rf_06_repair_rejects_stale_or_relabelled_identity() -> None: + prior, current = repair_pair() + current["target_identity"] = identity("task-rf", "3" * 64) + with pytest.raises(review_runtime.ReviewContractError, match="repaired identity"): + review_runtime.validate_review_sequence(current, previous_review=prior) + + +def test_rf_07_repair_still_detects_regression_in_affected_boundary() -> None: + prior, current = repair_pair() + current["findings"] = [finding(current["target_identity"], "RF-REGRESSION")] + with pytest.raises(review_runtime.ReviewContractError, match="accepted review cannot contain blocking"): + review_runtime.validate_review_sequence(current, previous_review=prior) + + +def test_rf_08_repair_packet_is_bounded_for_task_and_stage_targets(tmp_path: Path) -> None: + prior, current = repair_pair() + target = tmp_path / "target.md" + target.write_text("---\nid: task-rf\n---\nrepaired\n") + current["target_identity"] = review_runtime.artifact_review_identity(target) + current["repair_frontier"]["repaired_identity"] = current["target_identity"] + context = { + "stage": "plan", "target_identity": current["target_identity"], "target_locator": "control:target.md", + "agent_id": "reviewer-new", "capability": "judgment", "execution_id": "exec-rf", + "evidence_mode": "reproducible_snapshot", "review_mode": "repair", "review_target_kind": "stage", + "repair_frontier": current["repair_frontier"], "review_reset": None, + } + artifact = {"locator": "control:target.md", "sha256": hashlib.sha256(target.read_bytes()).hexdigest(), "content_base64": ""} + manifest = review_runtime.stage_evidence_manifest(tmp_path, tmp_path, context, [artifact]) + assert manifest["missing"] == [] + assert [entry["locator"] for entry in manifest["entries"]] == ["control:target.md"] + assert manifest["repair_frontier_reference"] == current["repair_frontier"]["frozen_evidence_reference"] + assert reviewer_workspace._validate_stage_context(context)["review_target_kind"] == "stage" + + task_prior = {key: value for key, value in prior.items() if key != "stage"} + task_prior.update(required=True, reviewer_independent=True, review_target_kind="task") + task_current = {key: value for key, value in current.items() if key != "stage"} + task_current.update(required=True, reviewer_independent=True, verdict="accept", review_target_kind="task", previous_review=task_prior) + assert review_runtime.validate_task_acceptance_review(task_current).target_identity == current["target_identity"] + + +def test_rf_01_h1_repairs_recorded_a_without_reacquiring_unrecorded_latent_b(tmp_path: Path) -> None: + target = tmp_path / "repair-a.md" + latent = tmp_path / "latent-b.md" + old_content = "---\nid: artifact-rf01\nversion: 1\n---\nA is broken\n" + new_content = old_content.replace("broken", "repaired") + target.write_text(new_content) + latent.write_text("B is latent and outside H1's recorded surface.\n") + old_identity = review_runtime.artifact_review_identity(target, content=old_content) + new_identity = review_runtime.artifact_review_identity(target) + + initial_h1 = review(target=old_identity, agent="reviewer-h1") + initial_h1.update(review_id="review-h1", verdict="repair", findings=[finding(old_identity, "RF-A")]) + repair_a = review(target=new_identity, mode="repair", agent="reviewer-repair-a") + repair_a["repair_frontier"] = { + "prior_review_id": "review-h1", "blocking_finding_ids": ["RF-A"], + "previous_reviewed_identity": old_identity, "repaired_identity": new_identity, + "affected_boundaries": ["control:repair-a.md"], + "frozen_evidence_reference": review_runtime.review_evidence_identity(initial_h1), + } + assert review_runtime.validate_review_sequence(repair_a, previous_review=initial_h1).verdict == "accepted" + + context = { + "stage": "plan", "target_identity": new_identity, "target_locator": "control:repair-a.md", + "agent_id": "reviewer-repair-a", "capability": "judgment", "execution_id": "exec-rf01", + "evidence_mode": "reproducible_snapshot", "review_mode": "repair", "review_target_kind": "stage", + "repair_frontier": repair_a["repair_frontier"], "review_reset": None, + } + manifest = review_runtime.stage_evidence_manifest( + tmp_path, tmp_path, context, + [{"locator": "control:repair-a.md", "sha256": hashlib.sha256(target.read_bytes()).hexdigest()}], + ) + assert manifest["missing"] == [] + assert [entry["locator"] for entry in manifest["entries"]] == ["control:repair-a.md"] + assert "latent-b.md" not in json.dumps(manifest) + + +@pytest.mark.parametrize( + ("invariant", "finding_class", "obligation", "first_broken"), + [ + ("security", "implementation_defect", "essential_safety", "implementation"), + ("ownership", "implementation_defect", "accepted_requirement", "implementation"), + ("destructive-safety", "implementation_defect", "essential_safety", "implementation"), + ("evidence-integrity", "validation_oracle_defect", "evidence_integrity", "validation_oracle"), + ], +) +def test_rf_03_capable_untouched_invariant_stays_blocking_during_narrow_repair( + invariant: str, finding_class: str, obligation: str, first_broken: str +) -> None: + prior, narrow = repair_pair() + safety = finding(narrow["target_identity"], f"RF-SAFETY-{invariant}") + safety.update( + **{ + "class": finding_class, + "obligation_basis": obligation, + "first_broken_artifact": first_broken, + "recommended_owner": review_runtime.classify_first_broken_owner(finding_class)[1], + "disposition": review_runtime.classify_first_broken_owner(finding_class)[2], + } + ) + safety["evidence"] = [{ + "kind": "test", "locator": f"accepted:{invariant}", + "digest_or_identity": f"RF-03-{invariant}", "observation": "capable evidence still fails", + }] + narrow.update(verdict="repair", findings=[safety]) + assert review_runtime.validate_review_sequence(narrow, previous_review=prior).verdict == "repair" + routed = review_runtime.route_review_verdict(safety) + assert routed["first_broken_artifact"] == first_broken + assert routed["preserve_valid_work_and_evidence"] is True + + +def test_rf_06_final_broad_integrated_review_rediscovers_and_classifies_latent_b(tmp_path: Path) -> None: + for args in (("init", "-q"), ("config", "user.name", "Test"), ("config", "user.email", "test@example.com")): + subprocess.run(["git", "-C", str(tmp_path), *args], check=True) + (tmp_path / ".gitignore").write_text(".work-bundle/\n") + (tmp_path / "direct-a.py").write_text("A = 'repaired'\n") + (tmp_path / "latent-b.py").write_text("B = 'deferred-non-load-bearing'\n") + subprocess.run(["git", "-C", str(tmp_path), "add", "."], check=True) + subprocess.run(["git", "-C", str(tmp_path), "commit", "-qm", "integrated"], check=True) + spec = tmp_path / ".work-bundle/orchestration/spec/active/spec-rf06.md" + spec.parent.mkdir(parents=True) + spec.write_text("---\nid: spec-rf06\nversion: 1\nstatus: verified\n---\nAccepted scope.\n") + plan = tmp_path / ".work-bundle/orchestration/plan/active/plan-rf06.md" + plan.parent.mkdir(parents=True) + plan.write_text("---\nid: plan-rf06\nversion: 1\nstatus: In progress\nsource_spec: [.work-bundle/orchestration/spec/active/spec-rf06.md]\n---\nFinal broad review.\n") + target_identity = review_runtime.stage_target_identity( + tmp_path, "integrated_implementation", plan, source_root=tmp_path + ) + artifacts = [ + {"locator": "control:.work-bundle/orchestration/plan/active/plan-rf06.md", "sha256": hashlib.sha256(plan.read_bytes()).hexdigest()}, + {"locator": "control:.work-bundle/orchestration/spec/active/spec-rf06.md", "sha256": hashlib.sha256(spec.read_bytes()).hexdigest()}, + ] + for entry in review_runtime.source_snapshot_entries(tmp_path): + path = tmp_path / entry["locator"].removeprefix("source:") + artifacts.append({"locator": entry["locator"], "sha256": hashlib.sha256(path.read_bytes()).hexdigest()}) + context = { + "stage": "integrated_implementation", "target_identity": target_identity, + "target_locator": "control:.work-bundle/orchestration/plan/active/plan-rf06.md", + "agent_id": "reviewer-final", "capability": "judgment", "execution_id": "exec-rf06", + "evidence_mode": "reproducible_snapshot", "review_mode": "initial", "review_target_kind": "stage", + "repair_frontier": None, "review_reset": None, + } + manifest = review_runtime.stage_evidence_manifest(tmp_path, tmp_path, context, artifacts) + assert manifest["missing"] == [] + assert "source:latent-b.py" in {entry["locator"] for entry in manifest["source_tree"]} + assert next(entry for entry in manifest["entries"] if entry["locator"] == "source:latent-b.py")["role"] == "source_tree" + + latent_finding = finding(target_identity, "RF-LATENT-B") + latent_finding.update( + **{"class": "advisory_enhancement", "severity": "advisory", "obligation_basis": "none", + "first_broken_artifact": "implementation", "recommended_owner": "backlog_owner", "disposition": "record_advisory"} + ) + latent_finding["evidence"] = [{ + "kind": "source", "locator": "source:latent-b.py", "digest_or_identity": "RF-06-B", + "observation": "Final broad review rediscovered deferred non-load-bearing B.", + }] + assert review_runtime.validate_review_finding(latent_finding).finding_id == "RF-LATENT-B" + assert review_runtime.route_review_verdict(latent_finding)["return_to"] == "backlog_owner" diff --git a/tests/test_wor108_subagent_ownership.py b/tests/test_wor108_subagent_ownership.py new file mode 100644 index 0000000..2d6cfc4 --- /dev/null +++ b/tests/test_wor108_subagent_ownership.py @@ -0,0 +1,328 @@ +from __future__ import annotations + +from pathlib import Path +import json +import os +import subprocess +import sys + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(REPO_ROOT / "scripts" / "orchestration")) + +from task_ownership import ( # type: ignore[import-not-found] + OwnershipBlocker, + RepairContinuity, + SubagentDispatch, + TaskCandidate, + TaskOwnershipScheduler, + normalize_subagent_provenance, +) + + +def provenance(*, mechanism: str = "host-native") -> dict[str, object]: + return { + "delegated": True, + "owner_kind": "subagent", + "agent_id": "agent-task-001", + "run_id": "run-task-001-a", + "mechanism": mechanism, + } + + +class RecordingAdapter: + def __init__(self, *, available: bool = True, mechanism: str = "host-native") -> None: + self.is_available = available + self.mechanism = mechanism + self.events: list[str] = [] + + def available(self) -> bool: + return self.is_available + + def dispatch(self, task: TaskCandidate, *, operation: str) -> SubagentDispatch: + self.events.append(f"dispatch:{task.task_id}:{operation}") + return SubagentDispatch(task.task_id, provenance(mechanism=self.mechanism)) + + def wait(self, handle: object) -> object: + self.events.append(f"wait:{handle}") + return handle + + +class HeldOpenAdapter(RecordingAdapter): + """Workers remain pending until wait; waiting early exposes lost fan-out.""" + + def __init__(self, expected_dispatches: int) -> None: + super().__init__() + self.expected_dispatches = expected_dispatches + + def wait(self, handle: object) -> object: + dispatches = [event for event in self.events if event.startswith("dispatch:")] + assert len(dispatches) == self.expected_dispatches + self.events.append(f"wait:{handle}") + return {"handoff": f"handoff-{handle}", "accepted": True} + + +def run_wb(config_root: Path, *args: str, cwd: Path) -> subprocess.CompletedProcess[str]: + env = os.environ.copy() + env["WB_CONFIG_ROOT"] = str(config_root) + return subprocess.run( + [sys.executable, str(REPO_ROOT / "scripts" / "wb.py"), *args], + cwd=cwd, + env=env, + check=False, + capture_output=True, + text=True, + ) + + +def test_sg01_execute_plan_requires_implicit_subagent_ownership() -> None: + adapter = RecordingAdapter() + result = TaskOwnershipScheduler(adapter).run_wave( + [TaskCandidate("task-001", (), ("src/a.py",), "workspace-a")], completed=set() + ) + assert result.dispatched == ("task-001",) + assert result.ownership[0]["owner_kind"] == "subagent" + assert adapter.events == ["dispatch:task-001:implementation", "wait:task-001"] + + +def test_sg02_no_subagent_fails_closed_before_mutation() -> None: + adapter = RecordingAdapter(available=False) + + with pytest.raises(OwnershipBlocker, match="workspace-blocked"): + TaskOwnershipScheduler(adapter).run_wave( + [TaskCandidate("task-001", (), ("src/a.py",), "workspace-a")], + completed=set(), + ) + + assert adapter.events == [] + + +@pytest.mark.parametrize("legacy_value", [True, False, None]) +def test_sg03_legacy_preference_has_no_behavioral_effect( + tmp_path: Path, legacy_value: bool | None +) -> None: + config_root = tmp_path / "config" + registry = config_root / "registry" + registry.mkdir(parents=True) + preference = "" if legacy_value is None else f"prefer_subagent: {str(legacy_value).lower()}\n" + (config_root / "bootstrap.yaml").write_text( + "bootstrap_version: v1\n" + f"work_bundle_root: {REPO_ROOT}\n" + 'project_registry: "$work_bundle_config_root/registry/projects.yaml"\n' + 'skill_registry: "$work_bundle_config_root/registry/skill-registry.yaml"\n' + + preference, + encoding="utf-8", + ) + (registry / "projects.yaml").write_text("projects: []\n", encoding="utf-8") + (registry / "skill-registry.yaml").write_text("skills: []\n", encoding="utf-8") + project = tmp_path / "project" + project.mkdir() + initialized = run_wb( + config_root, + "init-project", + str(project), + "--mode", + "single-repository", + cwd=project, + ) + assert initialized.returncode == 0, initialized.stdout + initialized.stderr + metadata = project / ".work-bundle" / "project.yaml" + metadata.write_text(metadata.read_text(encoding="utf-8") + preference, encoding="utf-8") + + shown = run_wb(config_root, "show-project", "--project-root", str(project), cwd=project) + assert shown.returncode == 0, shown.stdout + shown.stderr + assert "prefer_subagent" not in json.loads(shown.stdout) + assert "prefer_subagent" not in (project / "AGENTS.md").read_text(encoding="utf-8") + assert "prefer_subagent" not in json.loads(initialized.stdout) + + adapter = RecordingAdapter() + TaskOwnershipScheduler(adapter).run_wave( + [TaskCandidate("task-001", (), ("src/a.py",), "workspace-a")], + completed=set(), + ) + assert adapter.events == ["dispatch:task-001:implementation", "wait:task-001"] + + +def test_sg04_controller_task_mutation_is_rejected_even_when_validation_passes() -> None: + scheduler = TaskOwnershipScheduler(RecordingAdapter()) + with pytest.raises(OwnershipBlocker, match="review-blocked") as error: + scheduler.validate_acceptance( + delegation_evidence=provenance(), + mutation_events=[{"actor_kind": "controller", "paths": ["src/a.py"]}], + write_scope=["src/a.py"], + validations_passed=True, + ) + assert error.value.code == "review-blocked" + + +def test_sg05_independent_disjoint_tasks_dispatch_before_wait_and_convergence() -> None: + adapter = HeldOpenAdapter(expected_dispatches=2) + tasks = [ + TaskCandidate( + "task-a", (), ("src/a.py",), "workspace-a", + common_contract="CG-001", barrier="BAR-001", convergence_owner="task-c", + ), + TaskCandidate( + "task-b", (), ("src/b.py",), "workspace-b", + common_contract="CG-001", barrier="BAR-001", convergence_owner="task-c", + ), + ] + + result = TaskOwnershipScheduler(adapter).run_wave( + tasks, + completed=set(), + ) + + assert result.dispatched == ("task-a", "task-b") + assert adapter.events == [ + "dispatch:task-a:implementation", + "dispatch:task-b:implementation", + "wait:task-a", + "wait:task-b", + ] + assert result.results == ( + {"handoff": "handoff-task-a", "accepted": True}, + {"handoff": "handoff-task-b", "accepted": True}, + ) + + convergence = TaskCandidate( + "task-c", ("task-a", "task-b"), ("tests/integration.py",), "workspace-c", + common_contract="CG-001", barrier="BAR-001", barrier_participants=("task-a", "task-b"), + ) + closed = TaskOwnershipScheduler(RecordingAdapter()).run_wave( + [convergence], completed={"task-a", "task-b"}, accepted_handoffs={"task-a"} + ) + assert closed.dispatched == () + released = TaskOwnershipScheduler(RecordingAdapter()).run_wave( + [convergence], + completed={"task-a", "task-b"}, + accepted_handoffs={"task-a", "task-b"}, + ) + assert released.dispatched == ("task-c",) + + +def test_sg05_rejects_incomplete_common_contract_barrier_topology() -> None: + incomplete = TaskCandidate( + "task-a", (), ("src/a.py",), "workspace-a", common_contract="CG-001" + ) + with pytest.raises(OwnershipBlocker, match="workspace-blocked"): + TaskOwnershipScheduler(RecordingAdapter()).run_wave([incomplete], completed=set()) + + +def test_sg06_dependent_or_overlapping_tasks_are_serialized() -> None: + adapter = RecordingAdapter() + tasks = [ + TaskCandidate("task-a", (), ("src/shared.py",), "workspace-a"), + TaskCandidate("task-b", (), ("src/shared.py",), "workspace-b"), + TaskCandidate("task-c", ("task-a",), ("src/c.py",), "workspace-c"), + ] + result = TaskOwnershipScheduler(adapter).run_wave( + tasks, + completed=set(), + ) + + assert result.dispatched == ("task-a",) + assert adapter.events == ["dispatch:task-a:implementation", "wait:task-a"] + + +def test_sg06_same_execution_workspace_is_never_fanned_out() -> None: + tasks = [ + TaskCandidate("task-a", (), ("src/a.py",), "workspace-shared"), + TaskCandidate("task-b", (), ("src/b.py",), "workspace-shared"), + ] + result = TaskOwnershipScheduler(RecordingAdapter()).run_wave( + tasks, + completed=set(), + ) + assert result.dispatched == ("task-a",) + + +def test_sg06_serialized_tasks_progress_across_successive_waves() -> None: + tasks = [ + TaskCandidate("task-a", (), ("src/shared.py",), "workspace-a"), + TaskCandidate("task-b", (), ("src/shared.py",), "workspace-b"), + ] + scheduler = TaskOwnershipScheduler(RecordingAdapter()) + first = scheduler.run_wave( + tasks, + completed=set(), + ) + second = scheduler.run_wave( + tasks, + completed=set(first.dispatched), + ) + + assert first.dispatched == ("task-a",) + assert second.dispatched == ("task-b",) + + +def test_sg07_repair_retains_owner_binding_baseline_evidence_and_frontier() -> None: + adapter = RecordingAdapter() + continuity = RepairContinuity( + binding_id="binding-task-001", + baseline_identity="base-tree-001", + evidence_identity="evidence-batch-001", + previous_review_identity="review-head-001", + ) + result = TaskOwnershipScheduler(adapter).run_wave( + [TaskCandidate("task-001", (), ("src/a.py",), "workspace-a")], + completed=set(), + operation="repair", + prior_ownership={"task-001": provenance()}, + repair_continuity={"task-001": continuity}, + ) + assert result.operation == "repair" + assert result.ownership[0]["owner_kind"] == "subagent" + assert result.ownership[0]["agent_id"] == provenance()["agent_id"] + assert result.repair_continuity == (continuity,) + assert adapter.events == ["dispatch:task-001:repair", "wait:task-001"] + + +def test_sg07_repair_allows_only_an_authorized_replacement_owner() -> None: + class ReplacementAdapter(RecordingAdapter): + def dispatch(self, task: TaskCandidate, *, operation: str) -> SubagentDispatch: + replacement = {**provenance(), "agent_id": "replacement-agent", "run_id": "repair-run"} + return SubagentDispatch(task.task_id, replacement) + + task = TaskCandidate("task-001", (), ("src/a.py",), "workspace-a") + continuity = RepairContinuity("binding", "baseline", "evidence", "review-head") + scheduler = TaskOwnershipScheduler(ReplacementAdapter()) + with pytest.raises(OwnershipBlocker, match="review-blocked"): + scheduler.run_wave( + [task], completed=set(), operation="repair", + prior_ownership={"task-001": provenance()}, + repair_continuity={"task-001": continuity}, + ) + accepted = scheduler.run_wave( + [task], completed=set(), operation="repair", + prior_ownership={"task-001": provenance()}, + repair_continuity={"task-001": continuity}, + authorized_replacements={"task-001"}, + ) + assert accepted.repair_continuity == (continuity,) + + +def test_sg08_native_mechanisms_preserve_task_binding_handoff_and_validation_semantics() -> None: + observed: list[tuple[object, ...]] = [] + for mechanism in ("host-native", "execution-flow"): + adapter = HeldOpenAdapter(expected_dispatches=1) + adapter.mechanism = mechanism + task = TaskCandidate("task-001", (), ("src/a.py",), "workspace-a", binding_id="binding-001") + scheduler = TaskOwnershipScheduler(adapter) + result = scheduler.run_wave([task], completed=set()) + validated = scheduler.validate_acceptance( + delegation_evidence=result.ownership[0], mutation_events=[], + write_scope=task.write_scope, validations_passed=True, + ) + observed.append( + (result.dispatched, task.binding_id, result.results, validated["owner_kind"], set(validated)) + ) + assert result.ownership[0]["mechanism"] == mechanism + assert observed[0] == observed[1] + + +def test_visibility_specific_provenance_is_rejected() -> None: + with pytest.raises(OwnershipBlocker, match="workspace-blocked"): + normalize_subagent_provenance({**provenance(), "visible_reference": "thread-123"})