diff --git a/docs/G2-EVIDENCE.md b/docs/G2-EVIDENCE.md index a3c1f0f..516dde2 100644 --- a/docs/G2-EVIDENCE.md +++ b/docs/G2-EVIDENCE.md @@ -1,8 +1,8 @@ # G2 Contract Foundation Evidence **Status:** passed -**Tested source commit:** `17acb56ff06c4af0a15ed52d61bed28042e85319` -**CI attestation:** `https://github.com/OpenCoven/psyche/actions/runs/31462840301` +**Tested source commit:** `4808bac17ebd95366446b778bde919268613b59b` +**CI attestation:** `https://github.com/OpenCoven/psyche/actions/runs/31561161614` **Coven plan source commit:** `5f22ebef1e23d045a10f2ec0a3c87be029446cf6` **Coven plan URL:** `https://github.com/OpenCoven/coven/blob/5f22ebef1e23d045a10f2ec0a3c87be029446cf6/docs/superpowers/plans/2026-08-05-psyche-w2-g2-foundation.md` **Coven plan SHA-256:** `sha256:4fba002ad9f969cd01866ea08f270654f82b53c7d90b73d28643a9abb12cba68` @@ -18,35 +18,35 @@ | Criterion | Command | Result | Artifact | |---|---|---|---| -| Canonical ID prefixes and execution-binding identity | `cargo test -p psyche-core --test contracts -- --exact delivery_keeps_the_canonical_del_prefix && cargo test -p psyche-core --test contracts -- --exact delegation_uses_the_distinct_dlg_prefix && cargo test -p psyche-core --test contracts -- --exact execution_binding_uses_attempt_as_its_only_record_kind` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Complete canonical error enum | `cargo test -p psyche-core --test contracts -- --exact all_canonical_error_codes_decode` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Canonical delivery v1 shape | `cargo test -p psyche-core --test contracts -- --exact delivery_v1_fixture_round_trips_canonically && cargo test -p psyche-store --test records -- --exact delivery_direct_insert_round_trips_canonically` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Surface and quarantine owned types | `cargo test -p psyche-core --test contracts -- --exact surface_event_and_effect_fixtures_round_trip && cargo test -p psyche-store --test retention -- --exact quarantine_id_constructor_parser_and_serde_round_trip` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Package-local nullable-binding fixtures | `cargo test -p psyche-core --test contracts -- --exact graph_and_node_accept_only_the_two_frozen_nullable_bindings` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Exhaustive registered decode | `cargo test -p psyche-core --test decode -- --exact recognized_error_envelope_decodes_exhaustively` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Unknown kind/version/enum denial and quarantine | `cargo test -p psyche-core --test decode -- --exact unknown_typed_enum_is_a_quarantinable_decode_failure && cargo test -p psyche-store --test retention -- --exact unknown_enum_is_quarantined_without_dispatchable_record` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Quarantine resolution | `cargo test -p psyche-store --test retention -- --exact quarantine_resolution_is_durable_and_idempotent && cargo test -p psyche-store --test retention -- --exact concurrent_quarantine_resolution_has_one_durable_winner` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Direct typed insert validation | `cargo test -p psyche-store --test records -- --exact direct_insert_rejects_wrong_field_id_kind_without_writing && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_acknowledged_cancellation_without_evidence && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_acknowledged_state_without_termination_correlation && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_mismatched_cancellation_evidence && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_wrong_termination_request_id && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_termination_before_execution_request && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_acknowledgement_outside_termination_window && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_acknowledgement_before_termination_window && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_unresolved_outside_termination_window && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_unresolved_before_termination_window && cargo test -p psyche-store --test records -- --exact direct_insert_accepts_acknowledgement_at_termination_window_boundaries && cargo test -p psyche-store --test records -- --exact direct_insert_accepts_unresolved_at_termination_window_boundaries && cargo test -p psyche-store --test records -- --exact direct_insert_accepts_termination_window_after_execution_deadline && cargo test -p psyche-store --test records -- --exact direct_insert_accepts_termination_at_execution_creation_boundary` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Append-only execution-binding revisions | `cargo test -p psyche-store --test records -- --exact execution_binding_revision_appends_termination_outcomes_without_record_conflict && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_forks_gaps_and_changed_correlation && cargo test -p psyche-store --test records -- --exact execution_binding_revision_replay_is_idempotent && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_same_revision_changed_bytes && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_changed_reason_replay && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_every_frozen_execution_field_change && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_session_and_termination_rebinding && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_termination_correlation_removal && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_timestamp_regression && cargo test -p psyche-store --test retention -- --exact pruning_preserves_unresolved_quarantine_binding_revisions_and_transitions` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Transition contract and append-only rules | `cargo test -p psyche-store --test records -- --exact transition_versions_are_monotonic_and_append_only` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Checkpoint-failure shutdown | `cargo test -p psyche-runtime --lib -- --exact tests::checkpoint_failure_stops_and_releases_every_shutdown_waiter` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Migrations | `cargo test -p psyche-store --test migrations -- --exact fresh_store_applies_v1_once_and_reopens` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| State-machine/property | `cargo test -p psyche-test-support --test state_machine -- --exact model_and_store_agree_after_any_foundation_operation_sequence` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Crash/restart | `cargo test -p psyche-store --features test-fault-injection --test crash -- --exact killed_writer_exposes_only_committed_state_after_reopen` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Fake boundaries and durable termination ordering | `cargo test -p psyche-test-support --test fakes -- --exact advertised_adoption_requires_a_scripted_adoption_step && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_requires_durable_session_bound_revision && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_persists_acknowledged_outcome_before_success && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_persists_unresolved_outcome_before_success && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_exact_replay_is_idempotent && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_crash_after_response_leaves_recoverable_request && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_restart_recovers_missing_outcome && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_rejects_conflicting_replay_response && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_rejects_invalid_outcome_evidence && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_rejects_unresolved_outside_termination_window && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_reports_indeterminate_outcome_persistence && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_accepts_concurrent_exact_outcome_replay && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_rejects_concurrent_divergent_outcome && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_rejects_outcome_byte_attestation_mismatch` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Execution request RFC3339 golden bytes | `cargo test -p psyche-coven --test request_digest -- --exact execution_request_launch_matches_golden_bytes_and_digest && cargo test -p psyche-coven --test request_digest -- --exact execution_request_input_matches_golden_bytes_and_digest` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Validated termination dispatch | `cargo test -p psyche-coven --test bindings -- --exact termination_dispatch_rejects_invalid_request_before_persistence` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| G2 cancellation-state vocabulary | `cargo test -p psyche-core --test contracts -- --exact cancellation_state_vocabulary_requires_matching_o5_evidence` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| Full execution-request digest binding | `cargo test -p psyche-test-support --test state_machine -- --exact request_digest_binds_every_typed_field` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S1 scripted contract negotiation | `cargo test -p psyche-test-support --test conformance -- --exact c_s1_contract_negotiation` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S2 scripted session lifecycle | `cargo test -p psyche-test-support --test conformance -- --exact c_s2_session_lifecycle` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S3 scripted snapshot/attempt binding | `cargo test -p psyche-test-support --test conformance -- --exact c_s3_snapshot_attempt_binding` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S4 scripted stable adoption | `cargo test -p psyche-test-support --test conformance -- --exact c_s4_stable_adoption` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S5 scripted non-adoption proof | `cargo test -p psyche-test-support --test conformance -- --exact c_s5_non_adoption_proof` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S6 scripted ambiguity reconciliation/fence | `cargo test -p psyche-test-support --test state_machine -- --exact c_s6_model_never_redispatches_without_fence && cargo test -p psyche-test-support --test conformance -- --exact c_s6_ambiguity_fence` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S7 scripted ordered cursor | `cargo test -p psyche-test-support --test conformance -- --exact c_s7_ordered_cursor` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S8 scripted terminal authority | `cargo test -p psyche-test-support --test conformance -- --exact c_s8_terminal_authority` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S9 scripted O5 cancellation acknowledgement | `cargo test -p psyche-test-support --test conformance -- --exact c_s9_cancellation_acknowledgement` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S10 scripted result/artifact binding | `cargo test -p psyche-coven --test bindings -- --exact result_bundle_fixture_round_trips_complete_content_references && cargo test -p psyche-coven --test bindings -- --exact result_bundle_fixture_uses_launch_request_correlation && cargo test -p psyche-coven --test bindings -- --exact content_reference_rejects_digest_size_media_type_and_lifetime_mismatch && cargo test -p psyche-test-support --test conformance -- --exact c_s10_result_artifact_binding` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S11 scripted restart persistence | `cargo test -p psyche-test-support --test conformance -- --exact c_s11_restart_persistence` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | -| C-S12 scripted structured denial | `cargo test -p psyche-test-support --test conformance -- --exact c_s12_structured_denial` | passed | https://github.com/OpenCoven/psyche/actions/runs/31462840301 | +| Canonical ID prefixes and execution-binding identity | `cargo test -p psyche-core --test contracts -- --exact delivery_keeps_the_canonical_del_prefix && cargo test -p psyche-core --test contracts -- --exact delegation_uses_the_distinct_dlg_prefix && cargo test -p psyche-core --test contracts -- --exact execution_binding_uses_attempt_as_its_only_record_kind` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Complete canonical error enum | `cargo test -p psyche-core --test contracts -- --exact all_canonical_error_codes_decode` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Canonical delivery v1 shape | `cargo test -p psyche-core --test contracts -- --exact delivery_v1_fixture_round_trips_canonically && cargo test -p psyche-store --test records -- --exact delivery_direct_insert_round_trips_canonically` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Surface and quarantine owned types | `cargo test -p psyche-core --test contracts -- --exact surface_event_and_effect_fixtures_round_trip && cargo test -p psyche-store --test retention -- --exact quarantine_id_constructor_parser_and_serde_round_trip` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Package-local nullable-binding fixtures | `cargo test -p psyche-core --test contracts -- --exact graph_and_node_accept_only_the_two_frozen_nullable_bindings` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Exhaustive registered decode | `cargo test -p psyche-core --test decode -- --exact recognized_error_envelope_decodes_exhaustively` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Unknown kind/version/enum denial and quarantine | `cargo test -p psyche-core --test decode -- --exact unknown_typed_enum_is_a_quarantinable_decode_failure && cargo test -p psyche-store --test retention -- --exact unknown_enum_is_quarantined_without_dispatchable_record` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Quarantine resolution | `cargo test -p psyche-store --test retention -- --exact quarantine_resolution_is_durable_and_idempotent && cargo test -p psyche-store --test retention -- --exact concurrent_quarantine_resolution_has_one_durable_winner` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Direct typed insert validation | `cargo test -p psyche-store --test records -- --exact direct_insert_rejects_wrong_field_id_kind_without_writing && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_acknowledged_cancellation_without_evidence && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_acknowledged_state_without_termination_correlation && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_mismatched_cancellation_evidence && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_wrong_termination_request_id && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_termination_before_execution_request && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_acknowledgement_outside_termination_window && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_acknowledgement_before_termination_window && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_unresolved_outside_termination_window && cargo test -p psyche-store --test records -- --exact direct_insert_rejects_unresolved_before_termination_window && cargo test -p psyche-store --test records -- --exact direct_insert_accepts_acknowledgement_at_termination_window_boundaries && cargo test -p psyche-store --test records -- --exact direct_insert_accepts_unresolved_at_termination_window_boundaries && cargo test -p psyche-store --test records -- --exact direct_insert_accepts_termination_window_after_execution_deadline && cargo test -p psyche-store --test records -- --exact direct_insert_accepts_termination_at_execution_creation_boundary` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Append-only execution-binding revisions | `cargo test -p psyche-store --test records -- --exact execution_binding_revision_appends_termination_outcomes_without_record_conflict && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_forks_gaps_and_changed_correlation && cargo test -p psyche-store --test records -- --exact execution_binding_revision_replay_is_idempotent && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_same_revision_changed_bytes && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_changed_reason_replay && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_every_frozen_execution_field_change && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_session_and_termination_rebinding && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_termination_correlation_removal && cargo test -p psyche-store --test records -- --exact execution_binding_revision_rejects_timestamp_regression && cargo test -p psyche-store --test retention -- --exact pruning_preserves_unresolved_quarantine_binding_revisions_and_transitions` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Transition contract and append-only rules | `cargo test -p psyche-store --test records -- --exact transition_versions_are_monotonic_and_append_only` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Checkpoint-failure shutdown | `cargo test -p psyche-runtime --lib -- --exact tests::checkpoint_failure_stops_and_releases_every_shutdown_waiter` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Migrations | `cargo test -p psyche-store --test migrations -- --exact fresh_store_applies_v1_once_and_reopens` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| State-machine/property | `cargo test -p psyche-test-support --test state_machine -- --exact model_and_store_agree_after_any_foundation_operation_sequence` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Crash/restart | `cargo test -p psyche-store --features test-fault-injection --test crash -- --exact killed_writer_exposes_only_committed_state_after_reopen` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Fake boundaries and durable termination ordering | `cargo test -p psyche-test-support --test fakes -- --exact advertised_adoption_requires_a_scripted_adoption_step && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_requires_durable_session_bound_revision && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_persists_acknowledged_outcome_before_success && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_persists_unresolved_outcome_before_success && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_exact_replay_is_idempotent && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_crash_after_response_leaves_recoverable_request && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_restart_recovers_missing_outcome && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_rejects_conflicting_replay_response && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_rejects_invalid_outcome_evidence && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_rejects_unresolved_outside_termination_window && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_reports_indeterminate_outcome_persistence && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_accepts_concurrent_exact_outcome_replay && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_rejects_concurrent_divergent_outcome && cargo test -p psyche-test-support --test fakes -- --exact termination_dispatch_rejects_outcome_byte_attestation_mismatch` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Execution request RFC3339 golden bytes | `cargo test -p psyche-coven --test request_digest -- --exact execution_request_launch_matches_golden_bytes_and_digest && cargo test -p psyche-coven --test request_digest -- --exact execution_request_input_matches_golden_bytes_and_digest` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Validated termination dispatch | `cargo test -p psyche-coven --test bindings -- --exact termination_dispatch_rejects_invalid_request_before_persistence` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| G2 cancellation-state vocabulary | `cargo test -p psyche-core --test contracts -- --exact cancellation_state_vocabulary_requires_matching_o5_evidence` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| Full execution-request digest binding | `cargo test -p psyche-test-support --test state_machine -- --exact request_digest_binds_every_typed_field` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S1 scripted contract negotiation | `cargo test -p psyche-test-support --test conformance -- --exact c_s1_contract_negotiation` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S2 scripted session lifecycle | `cargo test -p psyche-test-support --test conformance -- --exact c_s2_session_lifecycle` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S3 scripted snapshot/attempt binding | `cargo test -p psyche-test-support --test conformance -- --exact c_s3_snapshot_attempt_binding` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S4 scripted stable adoption | `cargo test -p psyche-test-support --test conformance -- --exact c_s4_stable_adoption` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S5 scripted non-adoption proof | `cargo test -p psyche-test-support --test conformance -- --exact c_s5_non_adoption_proof` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S6 scripted ambiguity reconciliation/fence | `cargo test -p psyche-test-support --test state_machine -- --exact c_s6_model_never_redispatches_without_fence && cargo test -p psyche-test-support --test conformance -- --exact c_s6_ambiguity_fence` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S7 scripted ordered cursor | `cargo test -p psyche-test-support --test conformance -- --exact c_s7_ordered_cursor` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S8 scripted terminal authority | `cargo test -p psyche-test-support --test conformance -- --exact c_s8_terminal_authority` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S9 scripted O5 cancellation acknowledgement | `cargo test -p psyche-test-support --test conformance -- --exact c_s9_cancellation_acknowledgement` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S10 scripted result/artifact binding | `cargo test -p psyche-coven --test bindings -- --exact result_bundle_fixture_round_trips_complete_content_references && cargo test -p psyche-coven --test bindings -- --exact result_bundle_fixture_uses_launch_request_correlation && cargo test -p psyche-coven --test bindings -- --exact content_reference_rejects_digest_size_media_type_and_lifetime_mismatch && cargo test -p psyche-test-support --test conformance -- --exact c_s10_result_artifact_binding` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S11 scripted restart persistence | `cargo test -p psyche-test-support --test conformance -- --exact c_s11_restart_persistence` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | +| C-S12 scripted structured denial | `cargo test -p psyche-test-support --test conformance -- --exact c_s12_structured_denial` | passed | https://github.com/OpenCoven/psyche/actions/runs/31561161614 | diff --git a/scripts/check-g2-evidence-test.py b/scripts/check-g2-evidence-test.py index a793d18..7586900 100644 --- a/scripts/check-g2-evidence-test.py +++ b/scripts/check-g2-evidence-test.py @@ -135,6 +135,38 @@ def pull_request_event(self, *, repository="OpenCoven/psyche", head=None): }, } + def squash_merge_push_event(self): + before = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + after = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + return { + "after": after, + "before": before, + "created": False, + "deleted": False, + "distinct_size": 1, + "forced": False, + "head_commit": {"id": after}, + "ref": "refs/heads/main", + "repository": {"full_name": "OpenCoven/psyche"}, + "size": 1, + "commits": [{"id": after, "message": "squash merge"}], + } + + def merged_pull_request(self): + return { + "base": { + "ref": "main", + "repo": {"full_name": "OpenCoven/psyche"}, + }, + "head": { + "sha": "fedcba9876543210fedcba9876543210fedcba98", + "repo": {"full_name": "OpenCoven/psyche"}, + }, + "merge_commit_sha": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "merged_at": "2026-08-12T03:04:34Z", + "state": "closed", + } + def compare_response(self, *, merge_base=None, files=None): tested = "0123456789abcdef0123456789abcdef01234567" head = "fedcba9876543210fedcba9876543210fedcba98" @@ -552,6 +584,49 @@ def test_shallow_actions_verifier_uses_event_and_compare_without_local_git(self) ], ) + def test_shallow_actions_verifier_accepts_reviewed_squash_merge_push(self) -> None: + tested = "0123456789abcdef0123456789abcdef01234567" + event = json.dumps(self.squash_merge_push_event()) + tree = "cccccccccccccccccccccccccccccccccccccccc" + merge_commit = { + "parents": [{ + "sha": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "url": "https://api.github.com/repos/OpenCoven/psyche/git/commits/parent", + }], + "sha": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "tree": {"sha": tree}, + } + terminal_commit = { + "sha": "fedcba9876543210fedcba9876543210fedcba98", + "tree": {"sha": tree}, + } + environment = {"GITHUB_ACTIONS": "true", "GITHUB_EVENT_PATH": "/tmp/event.json"} + with mock.patch.dict(os.environ, environment, clear=False), mock.patch.object( + self.checker.pathlib.Path, "read_text", return_value=event + ), mock.patch.object( + self.checker, + "run_json", + side_effect=([self.merged_pull_request()], merge_commit, terminal_commit, self.compare_response()), + ) as run_json: + self.checker.verify_actions_source_relationship(ROOT, tested) + self.assertEqual( + [call.args[0] for call in run_json.call_args_list], + [ + [ + "gh", "api", "-H", "Accept: application/vnd.github+json", + "repos/OpenCoven/psyche/commits/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/pulls", + ], + ["gh", "api", "repos/OpenCoven/psyche/git/commits/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"], + ["gh", "api", "repos/OpenCoven/psyche/git/commits/fedcba9876543210fedcba9876543210fedcba98"], + [ + "gh", "api", + "repos/OpenCoven/psyche/compare/" + "0123456789abcdef0123456789abcdef01234567..." + "fedcba9876543210fedcba9876543210fedcba98", + ], + ], + ) + def test_shallow_actions_verifier_rejects_malformed_event_payload(self) -> None: passed = self.passed_evidence() environment = {"GITHUB_ACTIONS": "true", "GITHUB_EVENT_PATH": "/tmp/event.json"} diff --git a/scripts/check-g2-evidence.py b/scripts/check-g2-evidence.py index 4cff009..d10074f 100644 --- a/scripts/check-g2-evidence.py +++ b/scripts/check-g2-evidence.py @@ -800,36 +800,7 @@ def verify_local_source_relationship(root: pathlib.Path, tested: str) -> None: fail(f"passed source-to-HEAD diff is not evidence-only: {changed}") -def verify_actions_source_relationship(root: pathlib.Path, tested: str) -> None: - event_path = os.environ.get("GITHUB_EVENT_PATH") - if not event_path: - fail("GitHub Actions event payload path is absent") - try: - event = json.loads(pathlib.Path(event_path).read_text(encoding="utf-8")) - except (OSError, json.JSONDecodeError) as error: - fail(f"GitHub Actions event payload is invalid: {error}") - if not isinstance(event, dict): - fail("GitHub Actions event payload is not an object") - - repository = event.get("repository") - pull_request = event.get("pull_request") - head = pull_request.get("head") if isinstance(pull_request, dict) else None - head_repository = head.get("repo") if isinstance(head, dict) else None - pr_head = head.get("sha") if isinstance(head, dict) else None - if ( - not isinstance(repository, dict) - or repository.get("full_name") != "OpenCoven/psyche" - or not isinstance(head_repository, dict) - or head_repository.get("full_name") != "OpenCoven/psyche" - ): - fail("GitHub Actions pull-request repository provenance is invalid") - if not isinstance(pr_head, str) or not re.fullmatch(r"[0-9a-f]{40}", pr_head): - fail("GitHub Actions pull-request head provenance is invalid") - - compare = run_json( - ["gh", "api", f"repos/OpenCoven/psyche/compare/{tested}...{pr_head}"], - root, - ) +def validate_evidence_only_compare(compare: object, tested: str, terminal: str) -> None: if not isinstance(compare, dict): fail("GitHub compare response is invalid") base = compare.get("base_commit") @@ -841,7 +812,7 @@ def verify_actions_source_relationship(root: pathlib.Path, tested: str) -> None: not isinstance(commits, list) or not commits or not isinstance(commits[-1], dict) - or commits[-1].get("sha") != pr_head + or commits[-1].get("sha") != terminal ): fail("GitHub compare response does not end at the pull-request terminal commit") if compare.get("ahead_by") != len(commits) or compare.get("total_commits") != len(commits): @@ -863,6 +834,126 @@ def verify_actions_source_relationship(root: pathlib.Path, tested: str) -> None: fail(f"passed source-to-pull-request diff is not one modified evidence file: {files}") +def squash_merge_terminal(root: pathlib.Path, event: dict[str, object]) -> str: + before = event.get("before") + after = event.get("after") + commits = event.get("commits") + head_commit = event.get("head_commit") + if ( + event.get("ref") != "refs/heads/main" + or event.get("created") is not False + or event.get("deleted") is not False + or event.get("forced") is not False + or event.get("size") != 1 + or event.get("distinct_size") != 1 + or not isinstance(before, str) + or not re.fullmatch(r"[0-9a-f]{40}", before) + or not isinstance(after, str) + or not re.fullmatch(r"[0-9a-f]{40}", after) + or not isinstance(commits, list) + or len(commits) != 1 + or not isinstance(commits[0], dict) + or commits[0].get("id") != after + or not isinstance(head_commit, dict) + or head_commit.get("id") != after + ): + fail("GitHub Actions main push provenance is invalid") + + pulls = run_json( + [ + "gh", "api", "-H", "Accept: application/vnd.github+json", + f"repos/OpenCoven/psyche/commits/{after}/pulls", + ], + root, + ) + if not isinstance(pulls, list) or len(pulls) != 1 or not isinstance(pulls[0], dict): + fail("GitHub squash commit must belong to exactly one pull request") + pull = pulls[0] + base = pull.get("base") + head = pull.get("head") + base_repository = base.get("repo") if isinstance(base, dict) else None + head_repository = head.get("repo") if isinstance(head, dict) else None + terminal = head.get("sha") if isinstance(head, dict) else None + if ( + pull.get("state") != "closed" + or not isinstance(pull.get("merged_at"), str) + or not pull["merged_at"] + or pull.get("merge_commit_sha") != after + or not isinstance(base, dict) + or base.get("ref") != "main" + or not isinstance(base_repository, dict) + or base_repository.get("full_name") != "OpenCoven/psyche" + or not isinstance(head_repository, dict) + or head_repository.get("full_name") != "OpenCoven/psyche" + or not isinstance(terminal, str) + or not re.fullmatch(r"[0-9a-f]{40}", terminal) + ): + fail("GitHub squash pull-request provenance is invalid") + + merge_commit = run_json(["gh", "api", f"repos/OpenCoven/psyche/git/commits/{after}"], root) + terminal_commit = run_json( + ["gh", "api", f"repos/OpenCoven/psyche/git/commits/{terminal}"], + root, + ) + merge_tree = merge_commit.get("tree") if isinstance(merge_commit, dict) else None + terminal_tree = terminal_commit.get("tree") if isinstance(terminal_commit, dict) else None + parents = merge_commit.get("parents") if isinstance(merge_commit, dict) else None + if ( + not isinstance(merge_commit, dict) + or merge_commit.get("sha") != after + or not isinstance(parents, list) + or len(parents) != 1 + or not isinstance(parents[0], dict) + or parents[0].get("sha") != before + or not isinstance(merge_tree, dict) + or not isinstance(terminal_commit, dict) + or terminal_commit.get("sha") != terminal + or not isinstance(terminal_tree, dict) + or not isinstance(merge_tree.get("sha"), str) + or not re.fullmatch(r"[0-9a-f]{40}", merge_tree["sha"]) + or merge_tree.get("sha") != terminal_tree.get("sha") + ): + fail("GitHub squash commit tree does not match the reviewed pull-request tree") + return terminal + + +def verify_actions_source_relationship(root: pathlib.Path, tested: str) -> None: + event_path = os.environ.get("GITHUB_EVENT_PATH") + if not event_path: + fail("GitHub Actions event payload path is absent") + try: + event = json.loads(pathlib.Path(event_path).read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as error: + fail(f"GitHub Actions event payload is invalid: {error}") + if not isinstance(event, dict): + fail("GitHub Actions event payload is not an object") + + repository = event.get("repository") + if not isinstance(repository, dict) or repository.get("full_name") != "OpenCoven/psyche": + fail("GitHub Actions repository provenance is invalid") + + pull_request = event.get("pull_request") + if isinstance(pull_request, dict): + head = pull_request.get("head") + head_repository = head.get("repo") if isinstance(head, dict) else None + terminal = head.get("sha") if isinstance(head, dict) else None + if ( + not isinstance(head_repository, dict) + or head_repository.get("full_name") != "OpenCoven/psyche" + ): + fail("GitHub Actions pull-request repository provenance is invalid") + if not isinstance(terminal, str) or not re.fullmatch(r"[0-9a-f]{40}", terminal): + fail("GitHub Actions pull-request head provenance is invalid") + else: + terminal = squash_merge_terminal(root, event) + + compare = run_json( + ["gh", "api", f"repos/OpenCoven/psyche/compare/{tested}...{terminal}"], + root, + ) + validate_evidence_only_compare(compare, tested, terminal) + + def verify_passed(root: pathlib.Path, markdown: str, source_rows: list[list[str]]) -> None: tested = field(markdown, "Tested source commit") run_url = field(markdown, "CI attestation")