From 23e598d4bab383123d18f1fdd3d2f57bff8be6c4 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 19:53:27 +0200 Subject: [PATCH 01/10] feat: publish applicable participant-control contracts --- .../context-invalid/missing-obligation.json | 877 +++++ .../invalid/unknown-field.json | 893 +++++ .../valid/dependency-chain.json | 892 +++++ .../valid/later-phase-denial.json | 1108 ++++++ .../valid/optional-failure.json | 977 +++++ .../valid/disjoint-sinks.json | 227 ++ .../entries/backend-manifest-v2.json | 6 +- .../entries/backend-profile-v1.json | 6 +- .../participant-control-evaluation-v2.json | 10 + .../participant-control-selection-v2.json | 10 + .../backend-manifest/backend-manifest-v2.json | 2 + .../participant-control-evaluation-v2.json | 3157 +++++++++++++++++ .../participant-control-selection-v2.json | 600 ++++ .../schemas/profiles/backend-profile-v1.json | 2 + ...applicable-provider-contracts-preflight.md | 110 + .../modular-participant-control-contracts.md | 44 +- docs/governance/requirement-scopes/1365.json | 115 + .../control-applicability-and-evaluation.md | 71 +- docs/public/participant-control.md | 11 + docs/requirements/API-424/requirement.md | 11 + .../raes/observability_plane_semantics.py | 4 + .../raes_backend_protocols/protocols.py | 15 + .../conformance/validators.py | 26 +- .../contracts/_participant_control_exports.py | 17 + .../contracts/bundle_runtime.py | 17 +- .../participant_control_applicability.py | 219 ++ .../participant_control_decisions_v2.py | 250 ++ .../participant_control_evaluation_v2.py | 457 +++ .../participant_control_invocation.py | 256 ++ .../participant_control_resolution.py | 140 + .../participant_control_support_v2.py | 54 + .../raes_contracts/manifest_authority.py | 2 + .../test_issue_1365_applicable_contracts.py | 1776 ++++++++++ tools/generate_contract_schemas.py | 5 +- tools/policy/requirement_order.yaml | 19 + 35 files changed, 12360 insertions(+), 26 deletions(-) create mode 100644 contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json create mode 100644 contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json create mode 100644 contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json create mode 100644 contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json create mode 100644 contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json create mode 100644 contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json create mode 100644 contracts/schema-publication/entries/participant-control-evaluation-v2.json create mode 100644 contracts/schema-publication/entries/participant-control-selection-v2.json create mode 100644 contracts/schemas/participant-runtime/participant-control-evaluation-v2.json create mode 100644 contracts/schemas/participant-runtime/participant-control-selection-v2.json create mode 100644 docs/decisions/issue-1365-applicable-provider-contracts-preflight.md create mode 100644 docs/governance/requirement-scopes/1365.json create mode 100644 implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py create mode 100644 implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py create mode 100644 implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py create mode 100644 implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py create mode 100644 implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py create mode 100644 implementations/python/tests/test_issue_1365_applicable_contracts.py diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json new file mode 100644 index 000000000..461bb8a20 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json @@ -0,0 +1,877 @@ +{ + "commit": { + "coverage_digest": "sha256:f966a4b4c9845f6824c751af4cceede0c0f50b3095ec336c425b5c259d8745ac", + "decision_digest": "sha256:d1c72c0c53778b391603d2fad4f7a89a2e784920b0bdfad99bf882430d4237d1", + "dispatchable_effect_ids": [], + "effect_intent_digests": [], + "effects_consumed": 0, + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "receipt": null, + "result_digests": [ + "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "sha256:aacb4994403c2d24525b94e19eea45e05f50be437e224a7887c2f54e59286e33" + ], + "state_proposal_digests": [], + "status": "prepared" + }, + "composition": { + "blockers": [], + "contributing_result_ids": [ + "result-assessment", + "result-fact", + "result-rule" + ], + "disposition": "eligible", + "lost_advice_slot_ids": [], + "rule_revision": "participant-control-applicability/rev1" + }, + "effect_plan": { + "decisions": [ + { + "decision_id": "result-rule", + "disposition": "permit", + "reasons": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "decision-basis", + "revision": "rev1" + } + ], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + } + ], + "effect_outcomes": [], + "effects": [], + "incumbent_gate_disposition": "permit", + "parent_admission_receipt": null, + "parent_application_receipt": null, + "parent_applied": false, + "parent_crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "parent_disposition": "permit", + "realized_effect_ids": [], + "runnable_effect_ids": [] + }, + "evaluation_id": "evaluation-v2", + "invocations": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-fact", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "fact" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "other", + "invocation_id": "invoke-assessment", + "predecessor_inputs": [ + { + "kind": "ifc-fact", + "result_digest": "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "result_id": "result-fact", + "slot_id": "fact" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "assessment" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-rule", + "predecessor_inputs": [ + { + "kind": "advisory", + "result_digest": "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "result_id": "result-assessment", + "slot_id": "assessment" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "rule" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + } + ], + "lost_advice": [], + "request": { + "applicability": { + "applicable_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "coverage": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-here", + "revision": "rev1" + } + ], + "obligation_id": "teaching-here", + "slot_ids": [ + "fact" + ], + "status": "applies" + } + ], + "derivation_evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "subset-derivation", + "revision": "rev1" + } + ], + "required_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "selection_digest": "sha256:c4b3238b76a9c3a2f4a7677251f03f94bf44bf1bc2de9997dd3668c409b4038c" + }, + "context": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "attempt": 1, + "audience_ref": "student-audience", + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "clock": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "clock", + "ref": "clock-1", + "revision": "rev1" + }, + "controller_ref": "teacher", + "crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "depth": 0, + "destination_ref": "student", + "direction": "ingress", + "effects_consumed": 0, + "episode_id": "episode-1", + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "inputs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "order": 1, + "participant_address": "participants.student", + "phase_ref": "practice", + "policy": { + "decision_cut_ref": "cut-1", + "effective_order": 1, + "policy_decision_ref": "crossing-decision-1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "valid_from_order": 0, + "valid_until_order": 20 + }, + "predecessors": [], + "prior_effect_claims": [], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + }, + { + "instance_id": "other", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + ], + "rule_firings": [], + "run": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "run", + "ref": "run-1", + "revision": "rev1" + }, + "sink_ref": "teaching-observation", + "state_cut": { + "anchor_event_ref": "event-1", + "anchor_order": 1, + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "history_prefix_length": 2, + "order_model": "behavior_history_order", + "predecessor_event_refs": [ + "event-0" + ] + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "event-1", + "revision": "rev1" + }, + "trigger_root": "root-1" + }, + "selection": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "decision", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "ifc-fact", + "slot_id": "fact" + } + ], + "instance_id": "other", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "assessment" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "advisory", + "slot_id": "assessment" + } + ], + "instance_id": "influence", + "kind": "decision", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "rule" + } + ] + } + }, + "results": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:6eedb8ca7540290c79c772ed7bd65961992721cfbd58ed0665ac7428ca6f7f7f", + "next_provider_state": null, + "payload": { + "domain": "teaching-influence-domain/rev1", + "kind": "ifc-fact", + "source_refs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "tokens": [ + "coached-hint" + ] + }, + "result_id": "result-fact", + "rule_outcome": null, + "slot_id": "fact", + "status": "resolved" + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "other", + "invocation_digest": "sha256:36aaff3fa0fbf53ddf2cbfcbff3a36160204a9bdc7cd49d587d8b35ed78c76b5", + "next_provider_state": null, + "payload": { + "assessment": "negative", + "kind": "advisory", + "sample": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "assessment-sample", + "revision": "rev1" + }, + "score": 0.2 + }, + "result_id": "result-assessment", + "rule_outcome": null, + "slot_id": "assessment", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:c55bec711126640624b0115ec1e5eb58b9cad00c317a5ebced1bfb206ed2a73c", + "next_provider_state": null, + "payload": { + "disposition": "permit", + "kind": "decision", + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + }, + "result_id": "result-rule", + "rule_outcome": null, + "slot_id": "rule", + "status": "resolved" + } + ], + "schema_version": "participant-control-evaluation/v2", + "state_proposals": [], + "support": [ + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-here", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-there", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "other", + "obligation_id": "input:assessment", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json new file mode 100644 index 000000000..7cca6838a --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json @@ -0,0 +1,893 @@ +{ + "commit": { + "coverage_digest": "sha256:80caf84d4b4e2b39429afdcea3de084248a77a33b2a3e6570a9025ce9491eed1", + "decision_digest": "sha256:d1c72c0c53778b391603d2fad4f7a89a2e784920b0bdfad99bf882430d4237d1", + "dispatchable_effect_ids": [], + "effect_intent_digests": [], + "effects_consumed": 0, + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "receipt": null, + "result_digests": [ + "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "sha256:aacb4994403c2d24525b94e19eea45e05f50be437e224a7887c2f54e59286e33" + ], + "state_proposal_digests": [], + "status": "prepared" + }, + "composition": { + "blockers": [], + "contributing_result_ids": [ + "result-assessment", + "result-fact", + "result-rule" + ], + "disposition": "eligible", + "lost_advice_slot_ids": [], + "rule_revision": "participant-control-applicability/rev1" + }, + "effect_plan": { + "decisions": [ + { + "decision_id": "result-rule", + "disposition": "permit", + "reasons": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "decision-basis", + "revision": "rev1" + } + ], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + } + ], + "effect_outcomes": [], + "effects": [], + "incumbent_gate_disposition": "permit", + "parent_admission_receipt": null, + "parent_application_receipt": null, + "parent_applied": false, + "parent_crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "parent_disposition": "permit", + "realized_effect_ids": [], + "runnable_effect_ids": [] + }, + "evaluation_id": "evaluation-v2", + "invocations": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-fact", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "fact" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "other", + "invocation_id": "invoke-assessment", + "predecessor_inputs": [ + { + "kind": "ifc-fact", + "result_digest": "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "result_id": "result-fact", + "slot_id": "fact" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "assessment" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-rule", + "predecessor_inputs": [ + { + "kind": "advisory", + "result_digest": "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "result_id": "result-assessment", + "slot_id": "assessment" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "rule" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + } + ], + "lost_advice": [], + "request": { + "applicability": { + "applicable_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "coverage": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-here", + "revision": "rev1" + } + ], + "obligation_id": "teaching-here", + "slot_ids": [ + "fact" + ], + "status": "applies" + }, + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-there", + "revision": "rev1" + } + ], + "obligation_id": "teaching-there", + "slot_ids": [ + "rule" + ], + "status": "applies" + } + ], + "derivation_evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "subset-derivation", + "revision": "rev1" + } + ], + "required_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "selection_digest": "sha256:c4b3238b76a9c3a2f4a7677251f03f94bf44bf1bc2de9997dd3668c409b4038c" + }, + "context": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "attempt": 1, + "audience_ref": "student-audience", + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "clock": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "clock", + "ref": "clock-1", + "revision": "rev1" + }, + "controller_ref": "teacher", + "crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "depth": 0, + "destination_ref": "student", + "direction": "ingress", + "effects_consumed": 0, + "episode_id": "episode-1", + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "inputs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "order": 1, + "participant_address": "participants.student", + "phase_ref": "practice", + "policy": { + "decision_cut_ref": "cut-1", + "effective_order": 1, + "policy_decision_ref": "crossing-decision-1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "valid_from_order": 0, + "valid_until_order": 20 + }, + "predecessors": [], + "prior_effect_claims": [], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + }, + { + "instance_id": "other", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + ], + "rule_firings": [], + "run": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "run", + "ref": "run-1", + "revision": "rev1" + }, + "sink_ref": "teaching-observation", + "state_cut": { + "anchor_event_ref": "event-1", + "anchor_order": 1, + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "history_prefix_length": 2, + "order_model": "behavior_history_order", + "predecessor_event_refs": [ + "event-0" + ] + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "event-1", + "revision": "rev1" + }, + "trigger_root": "root-1" + }, + "selection": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "decision", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "ifc-fact", + "slot_id": "fact" + } + ], + "instance_id": "other", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "assessment" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "advisory", + "slot_id": "assessment" + } + ], + "instance_id": "influence", + "kind": "decision", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "rule" + } + ] + } + }, + "results": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:6eedb8ca7540290c79c772ed7bd65961992721cfbd58ed0665ac7428ca6f7f7f", + "next_provider_state": null, + "payload": { + "domain": "teaching-influence-domain/rev1", + "kind": "ifc-fact", + "source_refs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "tokens": [ + "coached-hint" + ] + }, + "result_id": "result-fact", + "rule_outcome": null, + "slot_id": "fact", + "status": "resolved" + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "other", + "invocation_digest": "sha256:36aaff3fa0fbf53ddf2cbfcbff3a36160204a9bdc7cd49d587d8b35ed78c76b5", + "next_provider_state": null, + "payload": { + "assessment": "negative", + "kind": "advisory", + "sample": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "assessment-sample", + "revision": "rev1" + }, + "score": 0.2 + }, + "result_id": "result-assessment", + "rule_outcome": null, + "slot_id": "assessment", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:c55bec711126640624b0115ec1e5eb58b9cad00c317a5ebced1bfb206ed2a73c", + "next_provider_state": null, + "payload": { + "disposition": "permit", + "kind": "decision", + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + }, + "result_id": "result-rule", + "rule_outcome": null, + "slot_id": "rule", + "status": "resolved" + } + ], + "schema_version": "participant-control-evaluation/v2", + "state_proposals": [], + "support": [ + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-here", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-there", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "other", + "obligation_id": "input:assessment", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + } + ], + "unknown_field": "closed-contract-violation" +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json new file mode 100644 index 000000000..b3692c03f --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json @@ -0,0 +1,892 @@ +{ + "commit": { + "coverage_digest": "sha256:80caf84d4b4e2b39429afdcea3de084248a77a33b2a3e6570a9025ce9491eed1", + "decision_digest": "sha256:d1c72c0c53778b391603d2fad4f7a89a2e784920b0bdfad99bf882430d4237d1", + "dispatchable_effect_ids": [], + "effect_intent_digests": [], + "effects_consumed": 0, + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "receipt": null, + "result_digests": [ + "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "sha256:aacb4994403c2d24525b94e19eea45e05f50be437e224a7887c2f54e59286e33" + ], + "state_proposal_digests": [], + "status": "prepared" + }, + "composition": { + "blockers": [], + "contributing_result_ids": [ + "result-assessment", + "result-fact", + "result-rule" + ], + "disposition": "eligible", + "lost_advice_slot_ids": [], + "rule_revision": "participant-control-applicability/rev1" + }, + "effect_plan": { + "decisions": [ + { + "decision_id": "result-rule", + "disposition": "permit", + "reasons": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "decision-basis", + "revision": "rev1" + } + ], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + } + ], + "effect_outcomes": [], + "effects": [], + "incumbent_gate_disposition": "permit", + "parent_admission_receipt": null, + "parent_application_receipt": null, + "parent_applied": false, + "parent_crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "parent_disposition": "permit", + "realized_effect_ids": [], + "runnable_effect_ids": [] + }, + "evaluation_id": "evaluation-v2", + "invocations": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-fact", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "fact" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "other", + "invocation_id": "invoke-assessment", + "predecessor_inputs": [ + { + "kind": "ifc-fact", + "result_digest": "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "result_id": "result-fact", + "slot_id": "fact" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "assessment" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-rule", + "predecessor_inputs": [ + { + "kind": "advisory", + "result_digest": "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "result_id": "result-assessment", + "slot_id": "assessment" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "rule" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + } + ], + "lost_advice": [], + "request": { + "applicability": { + "applicable_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "coverage": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-here", + "revision": "rev1" + } + ], + "obligation_id": "teaching-here", + "slot_ids": [ + "fact" + ], + "status": "applies" + }, + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-there", + "revision": "rev1" + } + ], + "obligation_id": "teaching-there", + "slot_ids": [ + "rule" + ], + "status": "applies" + } + ], + "derivation_evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "subset-derivation", + "revision": "rev1" + } + ], + "required_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "selection_digest": "sha256:c4b3238b76a9c3a2f4a7677251f03f94bf44bf1bc2de9997dd3668c409b4038c" + }, + "context": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "attempt": 1, + "audience_ref": "student-audience", + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "clock": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "clock", + "ref": "clock-1", + "revision": "rev1" + }, + "controller_ref": "teacher", + "crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "depth": 0, + "destination_ref": "student", + "direction": "ingress", + "effects_consumed": 0, + "episode_id": "episode-1", + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "inputs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "order": 1, + "participant_address": "participants.student", + "phase_ref": "practice", + "policy": { + "decision_cut_ref": "cut-1", + "effective_order": 1, + "policy_decision_ref": "crossing-decision-1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "valid_from_order": 0, + "valid_until_order": 20 + }, + "predecessors": [], + "prior_effect_claims": [], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + }, + { + "instance_id": "other", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + ], + "rule_firings": [], + "run": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "run", + "ref": "run-1", + "revision": "rev1" + }, + "sink_ref": "teaching-observation", + "state_cut": { + "anchor_event_ref": "event-1", + "anchor_order": 1, + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "history_prefix_length": 2, + "order_model": "behavior_history_order", + "predecessor_event_refs": [ + "event-0" + ] + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "event-1", + "revision": "rev1" + }, + "trigger_root": "root-1" + }, + "selection": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "decision", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "ifc-fact", + "slot_id": "fact" + } + ], + "instance_id": "other", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "assessment" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "advisory", + "slot_id": "assessment" + } + ], + "instance_id": "influence", + "kind": "decision", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "rule" + } + ] + } + }, + "results": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:6eedb8ca7540290c79c772ed7bd65961992721cfbd58ed0665ac7428ca6f7f7f", + "next_provider_state": null, + "payload": { + "domain": "teaching-influence-domain/rev1", + "kind": "ifc-fact", + "source_refs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "tokens": [ + "coached-hint" + ] + }, + "result_id": "result-fact", + "rule_outcome": null, + "slot_id": "fact", + "status": "resolved" + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "other", + "invocation_digest": "sha256:36aaff3fa0fbf53ddf2cbfcbff3a36160204a9bdc7cd49d587d8b35ed78c76b5", + "next_provider_state": null, + "payload": { + "assessment": "negative", + "kind": "advisory", + "sample": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "assessment-sample", + "revision": "rev1" + }, + "score": 0.2 + }, + "result_id": "result-assessment", + "rule_outcome": null, + "slot_id": "assessment", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:c55bec711126640624b0115ec1e5eb58b9cad00c317a5ebced1bfb206ed2a73c", + "next_provider_state": null, + "payload": { + "disposition": "permit", + "kind": "decision", + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + }, + "result_id": "result-rule", + "rule_outcome": null, + "slot_id": "rule", + "status": "resolved" + } + ], + "schema_version": "participant-control-evaluation/v2", + "state_proposals": [], + "support": [ + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-here", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-there", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "other", + "obligation_id": "input:assessment", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json new file mode 100644 index 000000000..dfb0778fd --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json @@ -0,0 +1,1108 @@ +{ + "commit": { + "coverage_digest": "sha256:7aca381ff112ead69ee1204c3de0a8b9f7043d01917b0cc61a72e2c650bba59f", + "decision_digest": "sha256:de5a5245590de9fbeabae3e94e0a1b1425203473daf81ad1346ab7cdaf909937", + "dispatchable_effect_ids": [], + "effect_intent_digests": [ + "sha256:b84526d8397cec429d8786f0e2aad150080fc987fbf6f62cf9a65fcd16756554" + ], + "effects_consumed": 0, + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "receipt": null, + "result_digests": [ + "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "sha256:0258af11a92b4eb5b479936958b406b2839ad06d54400572b7ace1316f72e7d7", + "sha256:8304557e6402345ed746a896904815c3f906113fde75e1ec7a2866780a4a5ad9" + ], + "state_proposal_digests": [], + "status": "prepared" + }, + "composition": { + "blockers": [ + "parent:deny" + ], + "contributing_result_ids": [ + "result-assessment", + "result-audit", + "result-fact", + "result-rule" + ], + "disposition": "deny", + "lost_advice_slot_ids": [], + "rule_revision": "participant-control-applicability/rev1" + }, + "effect_plan": { + "decisions": [ + { + "decision_id": "result-rule", + "disposition": "deny", + "reasons": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "decision-basis", + "revision": "rev1" + } + ], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + } + ], + "effect_outcomes": [], + "effects": [ + { + "allowed_parent_dispositions": [ + "deny" + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "effect_id": "effect-1", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1" + } + ], + "key": { + "firing_epoch": "epoch-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "run_ref": "run-1", + "slot": "followup", + "trigger_root": "root-1" + }, + "kind": "effect-request", + "originating_principal_ref": "teacher", + "parent_outcome": "none", + "phase": "independent", + "predecessor_effect_ids": [], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1" + }, + "target": { + "audience_ref": "reviewers", + "audit_record": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "audit", + "ref": "audit-1", + "revision": "rev1" + }, + "kind": "audit", + "retention_policy": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "policy", + "ref": "retention", + "revision": "rev1" + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + } + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "trigger-1", + "revision": "rev1" + } + } + ], + "incumbent_gate_disposition": "permit", + "parent_admission_receipt": null, + "parent_application_receipt": null, + "parent_applied": false, + "parent_crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "parent_disposition": "deny", + "realized_effect_ids": [], + "runnable_effect_ids": [ + "effect-1" + ] + }, + "evaluation_id": "evaluation-v2", + "invocations": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-fact", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "fact" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "other", + "invocation_id": "invoke-assessment", + "predecessor_inputs": [ + { + "kind": "ifc-fact", + "result_digest": "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "result_id": "result-fact", + "slot_id": "fact" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "assessment" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-rule", + "predecessor_inputs": [ + { + "kind": "advisory", + "result_digest": "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "result_id": "result-assessment", + "slot_id": "assessment" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "rule" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-audit", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "audit-slot" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + } + ], + "lost_advice": [], + "request": { + "applicability": { + "applicable_slot_ids": [ + "fact", + "assessment", + "rule", + "audit-slot" + ], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "coverage": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-here", + "revision": "rev1" + } + ], + "obligation_id": "teaching-here", + "slot_ids": [ + "fact" + ], + "status": "applies" + }, + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-there", + "revision": "rev1" + } + ], + "obligation_id": "teaching-there", + "slot_ids": [ + "rule" + ], + "status": "applies" + } + ], + "derivation_evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "subset-derivation", + "revision": "rev1" + } + ], + "required_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "selection_digest": "sha256:96a1703e7777fa442be425a58ee3402f85d0a655cbbf31882d74b2b509cc196e" + }, + "context": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "attempt": 1, + "audience_ref": "student-audience", + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "clock": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "clock", + "ref": "clock-1", + "revision": "rev1" + }, + "controller_ref": "teacher", + "crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "depth": 0, + "destination_ref": "student", + "direction": "ingress", + "effects_consumed": 0, + "episode_id": "episode-1", + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "inputs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "order": 1, + "participant_address": "participants.student", + "phase_ref": "practice", + "policy": { + "decision_cut_ref": "cut-1", + "effective_order": 1, + "policy_decision_ref": "crossing-decision-1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "valid_from_order": 0, + "valid_until_order": 20 + }, + "predecessors": [], + "prior_effect_claims": [], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + }, + { + "instance_id": "other", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + ], + "rule_firings": [], + "run": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "run", + "ref": "run-1", + "revision": "rev1" + }, + "sink_ref": "teaching-observation", + "state_cut": { + "anchor_event_ref": "event-1", + "anchor_order": 1, + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "history_prefix_length": 2, + "order_model": "behavior_history_order", + "predecessor_event_refs": [ + "event-0" + ] + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "event-1", + "revision": "rev1" + }, + "trigger_root": "root-1" + }, + "selection": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "decision", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "ifc-fact", + "slot_id": "fact" + } + ], + "instance_id": "other", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "assessment" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "advisory", + "slot_id": "assessment" + } + ], + "instance_id": "influence", + "kind": "decision", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "rule" + }, + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "effect-request", + "required_strength": "exact", + "role": "advisory", + "slot_id": "audit-slot" + } + ] + } + }, + "results": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:6eedb8ca7540290c79c772ed7bd65961992721cfbd58ed0665ac7428ca6f7f7f", + "next_provider_state": null, + "payload": { + "domain": "teaching-influence-domain/rev1", + "kind": "ifc-fact", + "source_refs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "tokens": [ + "coached-hint" + ] + }, + "result_id": "result-fact", + "rule_outcome": null, + "slot_id": "fact", + "status": "resolved" + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "other", + "invocation_digest": "sha256:36aaff3fa0fbf53ddf2cbfcbff3a36160204a9bdc7cd49d587d8b35ed78c76b5", + "next_provider_state": null, + "payload": { + "assessment": "negative", + "kind": "advisory", + "sample": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "assessment-sample", + "revision": "rev1" + }, + "score": 0.2 + }, + "result_id": "result-assessment", + "rule_outcome": null, + "slot_id": "assessment", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:c55bec711126640624b0115ec1e5eb58b9cad00c317a5ebced1bfb206ed2a73c", + "next_provider_state": null, + "payload": { + "disposition": "deny", + "kind": "decision", + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + }, + "result_id": "result-rule", + "rule_outcome": null, + "slot_id": "rule", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:d8a044cdacfa3bde7d79afeb0ed27f76ab9f089168b87d801e56f811b259967f", + "next_provider_state": null, + "payload": { + "allowed_parent_dispositions": [ + "deny" + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "effect_id": "effect-1", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1" + } + ], + "key": { + "firing_epoch": "epoch-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "run_ref": "run-1", + "slot": "followup", + "trigger_root": "root-1" + }, + "kind": "effect-request", + "originating_principal_ref": "teacher", + "parent_outcome": "none", + "phase": "independent", + "predecessor_effect_ids": [], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1" + }, + "target": { + "audience_ref": "reviewers", + "audit_record": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "audit", + "ref": "audit-1", + "revision": "rev1" + }, + "kind": "audit", + "retention_policy": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "policy", + "ref": "retention", + "revision": "rev1" + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + } + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "trigger-1", + "revision": "rev1" + } + }, + "result_id": "result-audit", + "rule_outcome": null, + "slot_id": "audit-slot", + "status": "resolved" + } + ], + "schema_version": "participant-control-evaluation/v2", + "state_proposals": [], + "support": [ + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-here", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-there", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "other", + "obligation_id": "input:assessment", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json new file mode 100644 index 000000000..bf1c46131 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json @@ -0,0 +1,977 @@ +{ + "commit": { + "coverage_digest": "sha256:8853edfae9b3fd063aa76a0ea108752f51783330386a69a8bae3db935a42c6ec", + "decision_digest": "sha256:d1c72c0c53778b391603d2fad4f7a89a2e784920b0bdfad99bf882430d4237d1", + "dispatchable_effect_ids": [], + "effect_intent_digests": [], + "effects_consumed": 0, + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "receipt": null, + "result_digests": [ + "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "sha256:aacb4994403c2d24525b94e19eea45e05f50be437e224a7887c2f54e59286e33", + "sha256:a9e2dcea23396437cb731f5945d8622f2403172b366ed34a655b9e2ed307d466" + ], + "state_proposal_digests": [], + "status": "prepared" + }, + "composition": { + "blockers": [], + "contributing_result_ids": [ + "result-assessment", + "result-fact", + "result-monitor", + "result-rule" + ], + "disposition": "eligible", + "lost_advice_slot_ids": [ + "monitor" + ], + "rule_revision": "participant-control-applicability/rev1" + }, + "effect_plan": { + "decisions": [ + { + "decision_id": "result-rule", + "disposition": "permit", + "reasons": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "decision-basis", + "revision": "rev1" + } + ], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + } + ], + "effect_outcomes": [], + "effects": [], + "incumbent_gate_disposition": "permit", + "parent_admission_receipt": null, + "parent_application_receipt": null, + "parent_applied": false, + "parent_crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "parent_disposition": "permit", + "realized_effect_ids": [], + "runnable_effect_ids": [] + }, + "evaluation_id": "evaluation-v2", + "invocations": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-fact", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "fact" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "other", + "invocation_id": "invoke-assessment", + "predecessor_inputs": [ + { + "kind": "ifc-fact", + "result_digest": "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "result_id": "result-fact", + "slot_id": "fact" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "assessment" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-rule", + "predecessor_inputs": [ + { + "kind": "advisory", + "result_digest": "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "result_id": "result-assessment", + "slot_id": "assessment" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "rule" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-monitor", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "monitor" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + } + ], + "lost_advice": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "optional-loss", + "revision": "rev1" + } + ], + "reason": "malformed", + "slot_id": "monitor" + } + ], + "request": { + "applicability": { + "applicable_slot_ids": [ + "fact", + "assessment", + "rule", + "monitor" + ], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "coverage": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-here", + "revision": "rev1" + } + ], + "obligation_id": "teaching-here", + "slot_ids": [ + "fact" + ], + "status": "applies" + }, + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-there", + "revision": "rev1" + } + ], + "obligation_id": "teaching-there", + "slot_ids": [ + "rule" + ], + "status": "applies" + } + ], + "derivation_evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "subset-derivation", + "revision": "rev1" + } + ], + "required_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "selection_digest": "sha256:ef4fe3704f1c847b39731eb423bc8fbdfb5b7c3ad447bf63b7ca70a635f06b34" + }, + "context": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "attempt": 1, + "audience_ref": "student-audience", + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "clock": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "clock", + "ref": "clock-1", + "revision": "rev1" + }, + "controller_ref": "teacher", + "crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "depth": 0, + "destination_ref": "student", + "direction": "ingress", + "effects_consumed": 0, + "episode_id": "episode-1", + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "inputs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "order": 1, + "participant_address": "participants.student", + "phase_ref": "practice", + "policy": { + "decision_cut_ref": "cut-1", + "effective_order": 1, + "policy_decision_ref": "crossing-decision-1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "valid_from_order": 0, + "valid_until_order": 20 + }, + "predecessors": [], + "prior_effect_claims": [], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + }, + { + "instance_id": "other", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + ], + "rule_firings": [], + "run": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "run", + "ref": "run-1", + "revision": "rev1" + }, + "sink_ref": "teaching-observation", + "state_cut": { + "anchor_event_ref": "event-1", + "anchor_order": 1, + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "history_prefix_length": 2, + "order_model": "behavior_history_order", + "predecessor_event_refs": [ + "event-0" + ] + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "event-1", + "revision": "rev1" + }, + "trigger_root": "root-1" + }, + "selection": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "decision", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "ifc-fact", + "slot_id": "fact" + } + ], + "instance_id": "other", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "assessment" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "advisory", + "slot_id": "assessment" + } + ], + "instance_id": "influence", + "kind": "decision", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "rule" + }, + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "monitor" + } + ] + } + }, + "results": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:6eedb8ca7540290c79c772ed7bd65961992721cfbd58ed0665ac7428ca6f7f7f", + "next_provider_state": null, + "payload": { + "domain": "teaching-influence-domain/rev1", + "kind": "ifc-fact", + "source_refs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "tokens": [ + "coached-hint" + ] + }, + "result_id": "result-fact", + "rule_outcome": null, + "slot_id": "fact", + "status": "resolved" + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "other", + "invocation_digest": "sha256:36aaff3fa0fbf53ddf2cbfcbff3a36160204a9bdc7cd49d587d8b35ed78c76b5", + "next_provider_state": null, + "payload": { + "assessment": "negative", + "kind": "advisory", + "sample": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "assessment-sample", + "revision": "rev1" + }, + "score": 0.2 + }, + "result_id": "result-assessment", + "rule_outcome": null, + "slot_id": "assessment", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:c55bec711126640624b0115ec1e5eb58b9cad00c317a5ebced1bfb206ed2a73c", + "next_provider_state": null, + "payload": { + "disposition": "permit", + "kind": "decision", + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + }, + "result_id": "result-rule", + "rule_outcome": null, + "slot_id": "rule", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "optional-loss", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:7e21170d12333cb842412dc2ec08382006b707f9b1195129eed84a647b1a100b", + "next_provider_state": null, + "payload": null, + "result_id": "result-monitor", + "rule_outcome": null, + "slot_id": "monitor", + "status": "failed" + } + ], + "schema_version": "participant-control-evaluation/v2", + "state_proposals": [], + "support": [ + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-here", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-there", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "other", + "obligation_id": "input:assessment", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json b/contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json new file mode 100644 index 000000000..fd26ca9ae --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json @@ -0,0 +1,227 @@ +{ + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "other-sink", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [], + "instance_id": "other", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "other-fact" + } + ] +} diff --git a/contracts/schema-publication/entries/backend-manifest-v2.json b/contracts/schema-publication/entries/backend-manifest-v2.json index 0a6374b15..d12ad209a 100644 --- a/contracts/schema-publication/entries/backend-manifest-v2.json +++ b/contracts/schema-publication/entries/backend-manifest-v2.json @@ -1,9 +1,9 @@ { - "content_hash": "67408de0a854ab63582c29bc4b1dfaa2efea2e61dff598a648ff03b2ad74755d", + "content_hash": "a54f1d52cc08499c8ad50fc230c16f88f46da75a493c593c9faf8f463e4afd1f", "contract_id": "backend-manifest-v2", "last_change": { - "summary": "Publish optional backend operation and supervision contracts with contextual admission and bounded evidence (#1360).", - "content_hash": "67408de0a854ab63582c29bc4b1dfaa2efea2e61dff598a648ff03b2ad74755d" + "summary": "Allow exact declarations of participant-control v2 selection and evaluation contracts (#1365).", + "content_hash": "a54f1d52cc08499c8ad50fc230c16f88f46da75a493c593c9faf8f463e4afd1f" }, "schema_path": "contracts/schemas/backend-manifest/backend-manifest-v2.json", "stability": "draft" diff --git a/contracts/schema-publication/entries/backend-profile-v1.json b/contracts/schema-publication/entries/backend-profile-v1.json index 3cd54f8bd..a5acaf849 100644 --- a/contracts/schema-publication/entries/backend-profile-v1.json +++ b/contracts/schema-publication/entries/backend-profile-v1.json @@ -1,9 +1,9 @@ { - "content_hash": "4fd12684309b617fe4308c90efb2a4631af1fe5590cf91d5b62a0ea0adabd5a4", + "content_hash": "b51c39ee482a49608a96ce3bdc66f587a2cdff9daf5b8c872a079dd920d88f56", "contract_id": "backend-profile-v1", "last_change": { - "summary": "Publish optional backend operation and supervision contracts with contextual admission and bounded evidence (#1360).", - "content_hash": "4fd12684309b617fe4308c90efb2a4631af1fe5590cf91d5b62a0ea0adabd5a4" + "summary": "Allow exact declarations of participant-control v2 selection and evaluation contracts (#1365).", + "content_hash": "b51c39ee482a49608a96ce3bdc66f587a2cdff9daf5b8c872a079dd920d88f56" }, "schema_path": "contracts/schemas/profiles/backend-profile-v1.json", "stability": "draft" diff --git a/contracts/schema-publication/entries/participant-control-evaluation-v2.json b/contracts/schema-publication/entries/participant-control-evaluation-v2.json new file mode 100644 index 000000000..62db73cc1 --- /dev/null +++ b/contracts/schema-publication/entries/participant-control-evaluation-v2.json @@ -0,0 +1,10 @@ +{ + "contract_id": "participant-control-evaluation-v2", + "schema_path": "contracts/schemas/participant-runtime/participant-control-evaluation-v2.json", + "stability": "draft", + "content_hash": "8873652137087581573b20409b0389a63ae98dcc178b147fe846e6df06cdf242", + "last_change": { + "summary": "Publish exact-cut coverage, dependency inputs and composed decisions for participant control (#1365).", + "content_hash": "8873652137087581573b20409b0389a63ae98dcc178b147fe846e6df06cdf242" + } +} diff --git a/contracts/schema-publication/entries/participant-control-selection-v2.json b/contracts/schema-publication/entries/participant-control-selection-v2.json new file mode 100644 index 000000000..63fba7c48 --- /dev/null +++ b/contracts/schema-publication/entries/participant-control-selection-v2.json @@ -0,0 +1,10 @@ +{ + "contract_id": "participant-control-selection-v2", + "schema_path": "contracts/schemas/participant-runtime/participant-control-selection-v2.json", + "stability": "draft", + "content_hash": "f3a1cfc18b14a26413fa232c3baf351e2b48f2042253cc71a5dcea424652110b", + "last_change": { + "summary": "Publish admitted apparatus and profile obligations for applicable participant control (#1365).", + "content_hash": "f3a1cfc18b14a26413fa232c3baf351e2b48f2042253cc71a5dcea424652110b" + } +} diff --git a/contracts/schemas/backend-manifest/backend-manifest-v2.json b/contracts/schemas/backend-manifest/backend-manifest-v2.json index 169937c78..6cfc169aa 100644 --- a/contracts/schemas/backend-manifest/backend-manifest-v2.json +++ b/contracts/schemas/backend-manifest/backend-manifest-v2.json @@ -3628,6 +3628,8 @@ "participant-control-occurrence-v1", "participant-control-selection-v1", "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "participant-crossing-occurrence-v1", "participant-flow-control-relation-v1", "participant-lifecycle-event-v1", diff --git a/contracts/schemas/participant-runtime/participant-control-evaluation-v2.json b/contracts/schemas/participant-runtime/participant-control-evaluation-v2.json new file mode 100644 index 000000000..f7e532dca --- /dev/null +++ b/contracts/schemas/participant-runtime/participant-control-evaluation-v2.json @@ -0,0 +1,3157 @@ +{ + "$defs": { + "ControlAdvisoryModel": { + "additionalProperties": false, + "properties": { + "assessment": { + "enum": [ + "positive", + "negative", + "abstain" + ], + "title": "Assessment", + "type": "string" + }, + "kind": { + "const": "advisory", + "title": "Kind", + "type": "string" + }, + "sample": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "score": { + "maximum": 1, + "minimum": 0, + "title": "Score", + "type": "number" + } + }, + "required": [ + "kind", + "assessment", + "score", + "sample" + ], + "title": "ControlAdvisoryModel", + "type": "object" + }, + "ControlApplicabilityModel": { + "additionalProperties": false, + "properties": { + "direction": { + "enum": [ + "ingress", + "egress" + ], + "title": "Direction", + "type": "string" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "phase_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Phase Ref", + "type": "string" + }, + "sink_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Sink Ref", + "type": "string" + }, + "subject_kind": { + "$ref": "#/$defs/ParticipantCrossingSubjectKind" + } + }, + "required": [ + "participant_address", + "episode_id", + "direction", + "sink_ref", + "phase_ref", + "subject_kind" + ], + "title": "ControlApplicabilityModel", + "type": "object" + }, + "ControlApplicabilityV2Model": { + "additionalProperties": false, + "properties": { + "applicable_slot_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Applicable Slot Ids", + "type": "array" + }, + "context_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Context Digest", + "type": "string" + }, + "coverage": { + "items": { + "$ref": "#/$defs/ControlObligationCoverageV2Model" + }, + "maxItems": 256, + "title": "Coverage", + "type": "array" + }, + "derivation_evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Derivation Evidence", + "type": "array" + }, + "required_slot_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Required Slot Ids", + "type": "array" + }, + "selection_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Selection Digest", + "type": "string" + } + }, + "required": [ + "selection_digest", + "context_digest", + "applicable_slot_ids", + "required_slot_ids", + "coverage", + "derivation_evidence" + ], + "title": "ControlApplicabilityV2Model", + "type": "object" + }, + "ControlArtifactReferenceModel": { + "additionalProperties": false, + "description": "Exact out-of-line artifact coordinate, not an artifact/evidence payload.", + "properties": { + "digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Digest", + "type": "string" + }, + "kind": { + "enum": [ + "profile", + "mechanism", + "implementation", + "authority", + "evidence", + "limitation", + "memory", + "apparatus", + "run", + "crossing", + "history", + "provider-state", + "input", + "clock", + "trigger", + "rule", + "inject-delivery", + "disclosure", + "manifest", + "installation", + "control", + "transformation", + "action", + "lifecycle", + "audit", + "receipt", + "projection", + "policy", + "constraint", + "flow-relation" + ], + "title": "Kind", + "type": "string" + }, + "ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Ref", + "type": "string" + }, + "revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Revision", + "type": "string" + } + }, + "required": [ + "kind", + "ref", + "revision", + "digest" + ], + "title": "ControlArtifactReferenceModel", + "type": "object" + }, + "ControlAuditEffectModel": { + "additionalProperties": false, + "properties": { + "audience_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Audience Ref", + "type": "string" + }, + "audit_record": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "kind": { + "const": "audit", + "title": "Kind", + "type": "string" + }, + "retention_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "subject": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + } + }, + "required": [ + "kind", + "subject", + "audit_record", + "audience_ref", + "retention_policy" + ], + "title": "ControlAuditEffectModel", + "type": "object" + }, + "ControlCausalBoundsModel": { + "additionalProperties": false, + "properties": { + "expires_at_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expires At Order", + "type": "integer" + }, + "max_attempts": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Attempts", + "type": "integer" + }, + "max_depth": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Depth", + "type": "integer" + }, + "max_effects": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Effects", + "type": "integer" + }, + "max_fanout": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Fanout", + "type": "integer" + }, + "max_firings_per_rule": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Firings Per Rule", + "type": "integer" + } + }, + "required": [ + "max_depth", + "max_effects", + "max_fanout", + "max_firings_per_rule", + "max_attempts", + "expires_at_order" + ], + "title": "ControlCausalBoundsModel", + "type": "object" + }, + "ControlCausalCutModel": { + "additionalProperties": false, + "properties": { + "cut_kind": { + "const": "causal_frontier", + "title": "Cut Kind", + "type": "string" + }, + "cut_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Cut Ref", + "type": "string" + }, + "frontier_event_refs": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "minItems": 1, + "title": "Frontier Event Refs", + "type": "array" + }, + "history_domain": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "History Domain", + "type": "string" + }, + "order_model": { + "const": "causal_partial_order", + "title": "Order Model", + "type": "string" + }, + "predecessor_closure_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Predecessor Closure Ref", + "type": "string" + } + }, + "required": [ + "cut_kind", + "cut_ref", + "history_domain", + "order_model", + "frontier_event_refs", + "predecessor_closure_ref" + ], + "title": "ControlCausalCutModel", + "type": "object" + }, + "ControlCommitBoundaryV2Model": { + "additionalProperties": false, + "description": "One transaction envelope, not proof that an external effect occurred.", + "properties": { + "coverage_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Coverage Digest", + "type": "string" + }, + "decision_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Decision Digest", + "type": "string" + }, + "dispatchable_effect_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Dispatchable Effect Ids", + "type": "array" + }, + "effect_intent_digests": { + "items": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + "maxItems": 256, + "title": "Effect Intent Digests", + "type": "array" + }, + "effects_consumed": { + "maximum": 1000000, + "minimum": 0, + "title": "Effects Consumed", + "type": "integer" + }, + "expected_history_heads": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Expected History Heads", + "type": "array" + }, + "receipt": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "result_digests": { + "items": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + "maxItems": 256, + "title": "Result Digests", + "type": "array" + }, + "state_proposal_digests": { + "items": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + "maxItems": 256, + "title": "State Proposal Digests", + "type": "array" + }, + "status": { + "enum": [ + "prepared", + "committed", + "rejected" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "expected_history_heads", + "coverage_digest", + "result_digests", + "decision_digest", + "state_proposal_digests", + "effect_intent_digests", + "effects_consumed", + "status", + "receipt", + "dispatchable_effect_ids" + ], + "title": "ControlCommitBoundaryV2Model", + "type": "object" + }, + "ControlCompositionV2Model": { + "additionalProperties": false, + "properties": { + "blockers": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Blockers", + "type": "array" + }, + "contributing_result_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Contributing Result Ids", + "type": "array" + }, + "disposition": { + "enum": [ + "eligible", + "deny", + "withhold", + "unsupported", + "stale", + "weakened", + "failed", + "conflict" + ], + "title": "Disposition", + "type": "string" + }, + "lost_advice_slot_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Lost Advice Slot Ids", + "type": "array" + }, + "rule_revision": { + "const": "participant-control-applicability/rev1", + "title": "Rule Revision", + "type": "string" + } + }, + "required": [ + "rule_revision", + "disposition", + "blockers", + "contributing_result_ids", + "lost_advice_slot_ids" + ], + "title": "ControlCompositionV2Model", + "type": "object" + }, + "ControlDecisionModel": { + "additionalProperties": false, + "properties": { + "disposition": { + "enum": [ + "permit", + "deny", + "withhold", + "abstain" + ], + "title": "Disposition", + "type": "string" + }, + "kind": { + "const": "decision", + "title": "Kind", + "type": "string" + }, + "rule": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "disposition", + "rule" + ], + "title": "ControlDecisionModel", + "type": "object" + }, + "ControlDelayEffectModel": { + "additionalProperties": false, + "properties": { + "clock": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "earliest_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Earliest Order", + "type": "integer" + }, + "expiry_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expiry Order", + "type": "integer" + }, + "kind": { + "const": "delay", + "title": "Kind", + "type": "string" + }, + "latest_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Latest Order", + "type": "integer" + }, + "resumption_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "subject": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + } + }, + "required": [ + "kind", + "subject", + "clock", + "earliest_order", + "latest_order", + "expiry_order", + "resumption_policy" + ], + "title": "ControlDelayEffectModel", + "type": "object" + }, + "ControlEffectPlanV2Model": { + "additionalProperties": false, + "properties": { + "decisions": { + "items": { + "$ref": "#/$defs/ControlParentDecisionV2Model" + }, + "maxItems": 256, + "title": "Decisions", + "type": "array" + }, + "effect_outcomes": { + "default": [], + "items": { + "$ref": "#/$defs/ControlRealizationBindingModel" + }, + "maxItems": 256, + "title": "Effect Outcomes", + "type": "array" + }, + "effects": { + "items": { + "$ref": "#/$defs/ControlEffectRequestV2Model" + }, + "maxItems": 256, + "title": "Effects", + "type": "array" + }, + "incumbent_gate_disposition": { + "enum": [ + "permit", + "deny", + "withhold", + "unsupported", + "stale", + "failed" + ], + "title": "Incumbent Gate Disposition", + "type": "string" + }, + "parent_admission_receipt": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "parent_application_receipt": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "parent_applied": { + "title": "Parent Applied", + "type": "boolean" + }, + "parent_crossing": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "parent_disposition": { + "enum": [ + "permit", + "deny", + "withhold" + ], + "title": "Parent Disposition", + "type": "string" + }, + "realized_effect_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Realized Effect Ids", + "type": "array" + }, + "runnable_effect_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Runnable Effect Ids", + "type": "array" + } + }, + "required": [ + "parent_crossing", + "decisions", + "effects", + "incumbent_gate_disposition", + "realized_effect_ids", + "parent_applied", + "parent_disposition", + "runnable_effect_ids" + ], + "title": "ControlEffectPlanV2Model", + "type": "object" + }, + "ControlEffectRequestV2Model": { + "additionalProperties": false, + "description": "An exact owned effect with declared parent outcome prerequisites.", + "properties": { + "allowed_parent_dispositions": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Allowed Parent Dispositions", + "type": "array" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "effect_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Effect Id", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "key": { + "$ref": "#/$defs/ControlLogicalEffectKeyModel" + }, + "kind": { + "const": "effect-request", + "title": "Kind", + "type": "string" + }, + "originating_principal_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Originating Principal Ref", + "type": "string" + }, + "parent_outcome": { + "enum": [ + "none", + "admitted", + "applied" + ], + "title": "Parent Outcome", + "type": "string" + }, + "phase": { + "enum": [ + "required-predecessor", + "success-dependent", + "independent" + ], + "title": "Phase", + "type": "string" + }, + "predecessor_effect_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Predecessor Effect Ids", + "type": "array" + }, + "rule": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "target": { + "discriminator": { + "mapping": { + "audit": "#/$defs/ControlAuditEffectModel", + "delay": "#/$defs/ControlDelayEffectModel", + "handoff": "#/$defs/ControlHandoffEffectModel", + "inject": "#/$defs/ControlInjectEffectModel", + "interrupt": "#/$defs/ControlLifecycleEffectModel", + "mask": "#/$defs/ControlTransformationEffectModel", + "request-review": "#/$defs/ControlReviewEffectModel", + "route": "#/$defs/ControlTransformationEffectModel", + "shutdown": "#/$defs/ControlLifecycleEffectModel", + "transform": "#/$defs/ControlTransformationEffectModel" + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlTransformationEffectModel" + }, + { + "$ref": "#/$defs/ControlInjectEffectModel" + }, + { + "$ref": "#/$defs/ControlDelayEffectModel" + }, + { + "$ref": "#/$defs/ControlHandoffEffectModel" + }, + { + "$ref": "#/$defs/ControlReviewEffectModel" + }, + { + "$ref": "#/$defs/ControlLifecycleEffectModel" + }, + { + "$ref": "#/$defs/ControlAuditEffectModel" + } + ], + "title": "Target" + }, + "trigger": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "effect_id", + "key", + "rule", + "authority", + "phase", + "predecessor_effect_ids", + "target", + "evidence", + "parent_outcome", + "allowed_parent_dispositions", + "originating_principal_ref", + "trigger" + ], + "title": "ControlEffectRequestV2Model", + "type": "object" + }, + "ControlEffectiveSupportModel": { + "additionalProperties": false, + "properties": { + "constraints": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "context_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Context Digest", + "type": "string" + }, + "declaration_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "declared_level": { + "enum": [ + "exact", + "bounded", + "disclosed_weak", + "unsupported" + ], + "title": "Declared Level", + "type": "string" + }, + "downgrade_authority": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "effective_level": { + "enum": [ + "exact", + "bounded", + "disclosed_weak", + "unsupported" + ], + "title": "Effective Level", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "feature": { + "const": "participant_modular_control", + "title": "Feature", + "type": "string" + }, + "installation": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "limitations": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Limitations", + "type": "array" + }, + "status": { + "enum": [ + "resolved", + "missing", + "unknown", + "unsupported", + "stale", + "failed", + "weakened" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "instance_id", + "feature", + "declaration_ref", + "declared_level", + "effective_level", + "status", + "context_digest", + "installation", + "constraints", + "limitations", + "evidence", + "downgrade_authority" + ], + "title": "ControlEffectiveSupportModel", + "type": "object" + }, + "ControlHandoffEffectModel": { + "additionalProperties": false, + "properties": { + "completion_obligation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "expected_state_revision": { + "maximum": 1000000, + "minimum": 0, + "title": "Expected State Revision", + "type": "integer" + }, + "kind": { + "const": "handoff", + "title": "Kind", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "prior_controller_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Prior Controller Ref", + "type": "string" + }, + "resulting_controller_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Resulting Controller Ref", + "type": "string" + }, + "transition": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "transition", + "participant_address", + "episode_id", + "prior_controller_ref", + "resulting_controller_ref", + "expected_state_revision", + "completion_obligation" + ], + "title": "ControlHandoffEffectModel", + "type": "object" + }, + "ControlInjectEffectModel": { + "additionalProperties": false, + "properties": { + "delivery_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "disclosure_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "event_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Event Ref", + "type": "string" + }, + "inject_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Inject Ref", + "type": "string" + }, + "kind": { + "const": "inject", + "title": "Kind", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "result_item_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Result Item Ref", + "type": "string" + }, + "script_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Script Ref", + "type": "string" + }, + "source_item_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Source Item Ref", + "type": "string" + }, + "story_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Story Ref", + "type": "string" + } + }, + "required": [ + "kind", + "delivery_ref", + "inject_ref", + "event_ref", + "script_ref", + "story_ref", + "source_item_ref", + "result_item_ref", + "participant_address", + "episode_id", + "disclosure_ref" + ], + "title": "ControlInjectEffectModel", + "type": "object" + }, + "ControlInvocationV2Model": { + "additionalProperties": false, + "description": "One slot/stage call with immutable, disclosure-authorized inputs.", + "properties": { + "binding_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Binding Digest", + "type": "string" + }, + "context_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Context Digest", + "type": "string" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "invocation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Invocation Id", + "type": "string" + }, + "predecessor_inputs": { + "items": { + "$ref": "#/$defs/ControlPredecessorInputV2Model" + }, + "maxItems": 256, + "title": "Predecessor Inputs", + "type": "array" + }, + "projection": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "requested_slot_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "minItems": 1, + "title": "Requested Slot Ids", + "type": "array" + }, + "state_input": { + "$ref": "#/$defs/ControlStateInputV2Model" + } + }, + "required": [ + "invocation_id", + "instance_id", + "requested_slot_ids", + "binding_digest", + "context_digest", + "predecessor_inputs", + "state_input", + "projection" + ], + "title": "ControlInvocationV2Model", + "type": "object" + }, + "ControlLifecycleEffectModel": { + "additionalProperties": false, + "properties": { + "expected_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Expected Revision", + "type": "string" + }, + "kind": { + "enum": [ + "interrupt", + "shutdown" + ], + "title": "Kind", + "type": "string" + }, + "lifecycle_authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "operation": { + "enum": [ + "pause", + "cancel", + "interrupt", + "terminate" + ], + "title": "Operation", + "type": "string" + }, + "target_kind": { + "enum": [ + "participant", + "episode", + "execution", + "workflow", + "run" + ], + "title": "Target Kind", + "type": "string" + }, + "target_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Target Ref", + "type": "string" + } + }, + "required": [ + "kind", + "target_kind", + "target_ref", + "operation", + "lifecycle_authority", + "expected_revision" + ], + "title": "ControlLifecycleEffectModel", + "type": "object" + }, + "ControlLogicalEffectKeyModel": { + "additionalProperties": false, + "properties": { + "firing_epoch": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Firing Epoch", + "type": "string" + }, + "rule_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Id", + "type": "string" + }, + "rule_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Revision", + "type": "string" + }, + "run_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Run Ref", + "type": "string" + }, + "slot": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot", + "type": "string" + }, + "trigger_root": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Trigger Root", + "type": "string" + } + }, + "required": [ + "run_ref", + "trigger_root", + "rule_id", + "rule_revision", + "slot", + "firing_epoch" + ], + "title": "ControlLogicalEffectKeyModel", + "type": "object" + }, + "ControlLostAdviceV2Model": { + "additionalProperties": false, + "description": "Safe, bounded evidence for a discarded optional contribution.", + "properties": { + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "reason": { + "enum": [ + "malformed", + "missing", + "unknown", + "stale", + "failed", + "weakened", + "unsupported", + "conflict" + ], + "title": "Reason", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "reason", + "evidence" + ], + "title": "ControlLostAdviceV2Model", + "type": "object" + }, + "ControlMechanismBindingV2Model": { + "additionalProperties": false, + "properties": { + "applicability": { + "items": { + "$ref": "#/$defs/ControlApplicabilityModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Applicability", + "type": "array" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "configuration_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Configuration Digest", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "implementation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "limitations": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Limitations", + "type": "array" + }, + "mechanism": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "memory_scope": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "profiles": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Profiles", + "type": "array" + }, + "protocol_revision": { + "const": "participant-control-provider/v2", + "title": "Protocol Revision", + "type": "string" + }, + "state_scope": { + "$ref": "#/$defs/ControlStateScopeV2Model" + } + }, + "required": [ + "instance_id", + "profiles", + "mechanism", + "protocol_revision", + "implementation", + "configuration_digest", + "authority", + "memory_scope", + "applicability", + "evidence", + "limitations", + "state_scope" + ], + "title": "ControlMechanismBindingV2Model", + "type": "object" + }, + "ControlMechanismResultV2Model": { + "additionalProperties": false, + "description": "A typed output bound to the complete invocation input identity.", + "properties": { + "binding_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Binding Digest", + "type": "string" + }, + "context_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Context Digest", + "type": "string" + }, + "evaluated_basis": { + "default": [], + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Evaluated Basis", + "type": "array" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "invocation_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Invocation Digest", + "type": "string" + }, + "next_provider_state": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "payload": { + "anyOf": [ + { + "discriminator": { + "mapping": { + "advisory": "#/$defs/ControlAdvisoryModel", + "decision": "#/$defs/ControlDecisionModel", + "effect-request": "#/$defs/ControlEffectRequestV2Model", + "ifc-fact": { + "discriminator": { + "mapping": { + "sem-233/rev1": "#/$defs/ControlSecurityFactModel", + "teaching-influence-domain/rev1": "#/$defs/ControlTeachingFactModel" + }, + "propertyName": "domain" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlTeachingFactModel" + }, + { + "$ref": "#/$defs/ControlSecurityFactModel" + } + ] + } + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "discriminator": { + "mapping": { + "sem-233/rev1": "#/$defs/ControlSecurityFactModel", + "teaching-influence-domain/rev1": "#/$defs/ControlTeachingFactModel" + }, + "propertyName": "domain" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlTeachingFactModel" + }, + { + "$ref": "#/$defs/ControlSecurityFactModel" + } + ] + }, + { + "$ref": "#/$defs/ControlDecisionModel" + }, + { + "$ref": "#/$defs/ControlAdvisoryModel" + }, + { + "$ref": "#/$defs/ControlEffectRequestV2Model" + } + ] + }, + { + "type": "null" + } + ], + "title": "Payload" + }, + "result_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Result Id", + "type": "string" + }, + "rule_outcome": { + "anyOf": [ + { + "const": "not-triggered", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Rule Outcome" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + }, + "status": { + "enum": [ + "resolved", + "missing", + "unknown", + "unsupported", + "stale", + "failed", + "weakened" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "result_id", + "slot_id", + "instance_id", + "binding_digest", + "context_digest", + "status", + "payload", + "evidence", + "next_provider_state", + "invocation_digest" + ], + "title": "ControlMechanismResultV2Model", + "type": "object" + }, + "ControlObligationCoverageV2Model": { + "additionalProperties": false, + "properties": { + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "obligation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Id", + "type": "string" + }, + "slot_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Slot Ids", + "type": "array" + }, + "status": { + "enum": [ + "applies", + "proven-inapplicable", + "unresolved" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "obligation_id", + "status", + "slot_ids", + "evidence" + ], + "title": "ControlObligationCoverageV2Model", + "type": "object" + }, + "ControlParentDecisionV2Model": { + "additionalProperties": false, + "description": "A decision about the exact parent, never a scheduled operation.", + "properties": { + "decision_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Decision Id", + "type": "string" + }, + "disposition": { + "enum": [ + "permit", + "deny", + "withhold" + ], + "title": "Disposition", + "type": "string" + }, + "reasons": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Reasons", + "type": "array" + }, + "rule": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "decision_id", + "disposition", + "rule", + "reasons" + ], + "title": "ControlParentDecisionV2Model", + "type": "object" + }, + "ControlPolicyReferenceModel": { + "additionalProperties": false, + "properties": { + "decision_cut_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Decision Cut Ref", + "type": "string" + }, + "effective_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Effective Order", + "type": "integer" + }, + "policy_decision_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Policy Decision Ref", + "type": "string" + }, + "policy_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Policy Digest", + "type": "string" + }, + "policy_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Policy Id", + "type": "string" + }, + "policy_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Policy Revision", + "type": "string" + }, + "valid_from_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Valid From Order", + "type": "integer" + }, + "valid_until_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Valid Until Order", + "type": "integer" + } + }, + "required": [ + "policy_id", + "policy_revision", + "policy_digest", + "policy_decision_ref", + "decision_cut_ref", + "effective_order", + "valid_from_order", + "valid_until_order" + ], + "title": "ControlPolicyReferenceModel", + "type": "object" + }, + "ControlPredecessorInputV2Model": { + "additionalProperties": false, + "description": "Content-bound reference to one detached, typed predecessor result.", + "properties": { + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "result_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Result Digest", + "type": "string" + }, + "result_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Result Id", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "kind", + "result_id", + "result_digest" + ], + "title": "ControlPredecessorInputV2Model", + "type": "object" + }, + "ControlPriorEffectClaimModel": { + "additionalProperties": false, + "description": "A retained logical claim, authenticated through the admitted context.", + "properties": { + "content_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Content Digest", + "type": "string" + }, + "effect_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Effect Id", + "type": "string" + }, + "key": { + "$ref": "#/$defs/ControlLogicalEffectKeyModel" + } + }, + "required": [ + "effect_id", + "key", + "content_digest" + ], + "title": "ControlPriorEffectClaimModel", + "type": "object" + }, + "ControlProfileObligationV2Model": { + "additionalProperties": false, + "description": "Profile-owned obligation declared before any provider result exists.", + "properties": { + "constraints": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "obligation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Id", + "type": "string" + }, + "profile": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "required_kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Required Kind", + "type": "string" + }, + "required_strength": { + "enum": [ + "exact", + "bounded", + "disclosed_weak" + ], + "title": "Required Strength", + "type": "string" + } + }, + "required": [ + "obligation_id", + "profile", + "required_kind", + "required_strength", + "constraints" + ], + "title": "ControlProfileObligationV2Model", + "type": "object" + }, + "ControlProviderStateModel": { + "additionalProperties": false, + "properties": { + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "state": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "instance_id", + "state" + ], + "title": "ControlProviderStateModel", + "type": "object" + }, + "ControlRealizationBindingModel": { + "additionalProperties": false, + "description": "An out-of-line owner outcome, never a provider result or permission.", + "properties": { + "disposition": { + "enum": [ + "applied", + "failed", + "indeterminate", + "unsupported", + "withheld" + ], + "title": "Disposition", + "type": "string" + }, + "effect_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Effect Id", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "receipt": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "effect_id", + "disposition", + "receipt", + "evidence" + ], + "title": "ControlRealizationBindingModel", + "type": "object" + }, + "ControlResultSlotV2Model": { + "additionalProperties": false, + "description": "Admitted required-input support pin, independent of provider claims.", + "properties": { + "constraints": { + "default": [], + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "dependencies": { + "items": { + "$ref": "#/$defs/ControlSlotDependencyModel" + }, + "maxItems": 256, + "title": "Dependencies", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "required_strength": { + "default": "exact", + "enum": [ + "exact", + "bounded", + "disclosed_weak" + ], + "title": "Required Strength", + "type": "string" + }, + "role": { + "enum": [ + "mandatory", + "advisory" + ], + "title": "Role", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "instance_id", + "kind", + "role", + "dependencies" + ], + "title": "ControlResultSlotV2Model", + "type": "object" + }, + "ControlReviewEffectModel": { + "additionalProperties": false, + "properties": { + "approval_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "clock": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "denial_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "expiry_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expiry Order", + "type": "integer" + }, + "kind": { + "const": "request-review", + "title": "Kind", + "type": "string" + }, + "obligation_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Ref", + "type": "string" + }, + "parent": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "resumption_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "supervisor_authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "parent", + "obligation_ref", + "supervisor_authority", + "approval_ref", + "denial_ref", + "clock", + "expiry_order", + "resumption_policy" + ], + "title": "ControlReviewEffectModel", + "type": "object" + }, + "ControlRuleFiringsModel": { + "additionalProperties": false, + "properties": { + "firing_epochs": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Firing Epochs", + "type": "array" + }, + "rule_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Id", + "type": "string" + }, + "rule_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Revision", + "type": "string" + } + }, + "required": [ + "rule_id", + "rule_revision", + "firing_epochs" + ], + "title": "ControlRuleFiringsModel", + "type": "object" + }, + "ControlSecurityFactModel": { + "additionalProperties": false, + "description": "Reference the exact incumbent SEM-233 label and owning relation.", + "properties": { + "domain": { + "const": "sem-233/rev1", + "title": "Domain", + "type": "string" + }, + "kind": { + "const": "ifc-fact", + "title": "Kind", + "type": "string" + }, + "label_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Label Ref", + "type": "string" + }, + "relation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "domain", + "relation", + "label_ref" + ], + "title": "ControlSecurityFactModel", + "type": "object" + }, + "ControlSequenceCutModel": { + "additionalProperties": false, + "properties": { + "anchor_event_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Anchor Event Ref", + "type": "string" + }, + "anchor_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Anchor Order", + "type": "integer" + }, + "cut_kind": { + "const": "sequence_prefix", + "title": "Cut Kind", + "type": "string" + }, + "cut_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Cut Ref", + "type": "string" + }, + "history_domain": { + "enum": [ + "participant_episode_lifecycle", + "participant_behavior_history" + ], + "title": "History Domain", + "type": "string" + }, + "history_prefix_length": { + "maximum": 1000001, + "minimum": 1, + "title": "History Prefix Length", + "type": "integer" + }, + "order_model": { + "enum": [ + "control_plane_order", + "backend_serialized_order", + "behavior_history_order" + ], + "title": "Order Model", + "type": "string" + }, + "predecessor_event_refs": { + "default": [], + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Predecessor Event Refs", + "type": "array" + } + }, + "required": [ + "cut_kind", + "cut_ref", + "history_domain", + "order_model", + "anchor_event_ref", + "anchor_order", + "history_prefix_length" + ], + "title": "ControlSequenceCutModel", + "type": "object" + }, + "ControlSlotDependencyModel": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "kind" + ], + "title": "ControlSlotDependencyModel", + "type": "object" + }, + "ControlStateInputV2Model": { + "additionalProperties": false, + "properties": { + "predecessor_invocation_id": { + "anyOf": [ + { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Predecessor Invocation Id" + }, + "scope": { + "$ref": "#/$defs/ControlStateScopeV2Model" + }, + "source": { + "enum": [ + "committed", + "tentative" + ], + "title": "Source", + "type": "string" + }, + "state": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "scope", + "state", + "source", + "predecessor_invocation_id" + ], + "title": "ControlStateInputV2Model", + "type": "object" + }, + "ControlStateProposalV2Model": { + "additionalProperties": false, + "properties": { + "after": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "before": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "commit_on": { + "enum": [ + "evaluation", + "parent-release" + ], + "title": "Commit On", + "type": "string" + }, + "invocation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Invocation Id", + "type": "string" + }, + "predecessor_invocation_id": { + "anyOf": [ + { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Predecessor Invocation Id" + }, + "scope": { + "$ref": "#/$defs/ControlStateScopeV2Model" + } + }, + "required": [ + "invocation_id", + "scope", + "before", + "after", + "commit_on", + "predecessor_invocation_id" + ], + "title": "ControlStateProposalV2Model", + "type": "object" + }, + "ControlStateScopeV2Model": { + "additionalProperties": false, + "description": "Admitted sharing scope, distinct from participant memory and profile.", + "properties": { + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "configuration_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Configuration Digest", + "type": "string" + }, + "scope_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Scope Id", + "type": "string" + }, + "sharing": { + "enum": [ + "run", + "participant", + "episode" + ], + "title": "Sharing", + "type": "string" + } + }, + "required": [ + "scope_id", + "sharing", + "authority", + "configuration_digest" + ], + "title": "ControlStateScopeV2Model", + "type": "object" + }, + "ControlSubjectReferenceModel": { + "additionalProperties": false, + "anyOf": [ + { + "properties": { + "subject_revision": { + "type": "string" + } + }, + "required": [ + "subject_revision" + ] + }, + { + "properties": { + "subject_digest": { + "type": "string" + } + }, + "required": [ + "subject_digest" + ] + } + ], + "description": "Frozen specialization of the incumbent crossing subject coordinate.\n\nThe incumbent requires a revision *or* a digest, and a live runtime subject\nis frequently digest-only. Narrowing ``subject_revision`` to mandatory would\nmake the crossing occurrence this context names inexpressible, so the\nspecialization bounds the coordinate without adding a requirement the owner\ndoes not impose.", + "properties": { + "contract_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Contract Id", + "type": "string" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "subject_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Subject Digest" + }, + "subject_kind": { + "$ref": "#/$defs/ParticipantCrossingSubjectKind" + }, + "subject_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Subject Ref", + "type": "string" + }, + "subject_revision": { + "anyOf": [ + { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Subject Revision" + } + }, + "required": [ + "subject_kind", + "contract_id", + "subject_ref", + "participant_address", + "episode_id" + ], + "title": "ControlSubjectReferenceModel", + "type": "object" + }, + "ControlSupportAssessmentV2Model": { + "additionalProperties": false, + "description": "A recorded requirement-relative relation, subject to trusted resolution.", + "properties": { + "effective_support": { + "$ref": "#/$defs/ControlEffectiveSupportModel" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "obligation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Id", + "type": "string" + }, + "relation": { + "enum": [ + "exact", + "bounded-compatible", + "disclosed-weak-compatible", + "insufficient", + "unresolved" + ], + "title": "Relation", + "type": "string" + }, + "required_constraints": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Required Constraints", + "type": "array" + }, + "required_strength": { + "enum": [ + "exact", + "bounded", + "disclosed_weak" + ], + "title": "Required Strength", + "type": "string" + } + }, + "required": [ + "obligation_id", + "instance_id", + "required_strength", + "required_constraints", + "effective_support", + "relation", + "evidence" + ], + "title": "ControlSupportAssessmentV2Model", + "type": "object" + }, + "ControlTeachingFactModel": { + "additionalProperties": false, + "properties": { + "domain": { + "const": "teaching-influence-domain/rev1", + "title": "Domain", + "type": "string" + }, + "kind": { + "const": "ifc-fact", + "title": "Kind", + "type": "string" + }, + "source_refs": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Source Refs", + "type": "array" + }, + "tokens": { + "items": { + "enum": [ + "coached-hint", + "worked-example" + ], + "type": "string" + }, + "maxItems": 2, + "title": "Tokens", + "type": "array" + } + }, + "required": [ + "kind", + "domain", + "tokens", + "source_refs" + ], + "title": "ControlTeachingFactModel", + "type": "object" + }, + "ControlTransformationEffectModel": { + "additionalProperties": false, + "properties": { + "admission_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "destination_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Destination Ref", + "type": "string" + }, + "kind": { + "enum": [ + "transform", + "mask", + "route" + ], + "title": "Kind", + "type": "string" + }, + "result": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "source": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "transformation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "transformation", + "source", + "result", + "destination_ref", + "admission_policy" + ], + "title": "ControlTransformationEffectModel", + "type": "object" + }, + "ParticipantControlContextModel": { + "additionalProperties": false, + "description": "MPC-03 exact context. No private state, prompt, or native handle.", + "properties": { + "apparatus": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "attempt": { + "maximum": 1000000, + "minimum": 1, + "title": "Attempt", + "type": "integer" + }, + "audience_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Audience Ref", + "type": "string" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "clock": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "controller_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Controller Ref", + "type": "string" + }, + "crossing": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "depth": { + "maximum": 1000000, + "minimum": 0, + "title": "Depth", + "type": "integer" + }, + "destination_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Destination Ref", + "type": "string" + }, + "direction": { + "enum": [ + "ingress", + "egress" + ], + "title": "Direction", + "type": "string" + }, + "effects_consumed": { + "maximum": 1000000, + "minimum": 0, + "title": "Effects Consumed", + "type": "integer" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "expected_history_heads": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Expected History Heads", + "type": "array" + }, + "inputs": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Inputs", + "type": "array" + }, + "memory_scope": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "order": { + "maximum": 1000000, + "minimum": 0, + "title": "Order", + "type": "integer" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "phase_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Phase Ref", + "type": "string" + }, + "policy": { + "$ref": "#/$defs/ControlPolicyReferenceModel" + }, + "predecessors": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Predecessors", + "type": "array" + }, + "prior_effect_claims": { + "items": { + "$ref": "#/$defs/ControlPriorEffectClaimModel" + }, + "maxItems": 256, + "title": "Prior Effect Claims", + "type": "array" + }, + "provider_states": { + "items": { + "$ref": "#/$defs/ControlProviderStateModel" + }, + "maxItems": 64, + "title": "Provider States", + "type": "array" + }, + "rule_firings": { + "items": { + "$ref": "#/$defs/ControlRuleFiringsModel" + }, + "maxItems": 256, + "title": "Rule Firings", + "type": "array" + }, + "run": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "sink_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Sink Ref", + "type": "string" + }, + "state_cut": { + "discriminator": { + "mapping": { + "causal_frontier": "#/$defs/ControlCausalCutModel", + "sequence_prefix": "#/$defs/ControlSequenceCutModel" + }, + "propertyName": "cut_kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlSequenceCutModel" + }, + { + "$ref": "#/$defs/ControlCausalCutModel" + } + ], + "title": "State Cut" + }, + "subject": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "trigger": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "trigger_root": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Trigger Root", + "type": "string" + } + }, + "required": [ + "run", + "apparatus", + "participant_address", + "episode_id", + "subject", + "crossing", + "direction", + "sink_ref", + "audience_ref", + "destination_ref", + "controller_ref", + "authority", + "policy", + "state_cut", + "expected_history_heads", + "provider_states", + "inputs", + "memory_scope", + "clock", + "order", + "phase_ref", + "trigger_root", + "trigger", + "predecessors", + "depth", + "effects_consumed", + "prior_effect_claims", + "rule_firings", + "attempt" + ], + "title": "ParticipantControlContextModel", + "type": "object" + }, + "ParticipantControlRequestV2Model": { + "additionalProperties": false, + "description": "Exact K plus A(B,K), with B and total profile coverage retained.", + "properties": { + "applicability": { + "$ref": "#/$defs/ControlApplicabilityV2Model" + }, + "context": { + "$ref": "#/$defs/ParticipantControlContextModel" + }, + "selection": { + "$ref": "#/$defs/ParticipantControlSelectionV2Model" + } + }, + "required": [ + "selection", + "context", + "applicability" + ], + "title": "ParticipantControlRequestV2Model", + "type": "object" + }, + "ParticipantControlSelectionV2Model": { + "additionalProperties": false, + "description": "Complete B; no exact-cut filtering changes this identity.", + "properties": { + "apparatus": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "bindings": { + "items": { + "$ref": "#/$defs/ControlMechanismBindingV2Model" + }, + "maxItems": 64, + "title": "Bindings", + "type": "array" + }, + "bounds": { + "$ref": "#/$defs/ControlCausalBoundsModel" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "interpretation": { + "const": "participant-control-applicability/rev1", + "title": "Interpretation", + "type": "string" + }, + "obligations": { + "items": { + "$ref": "#/$defs/ControlProfileObligationV2Model" + }, + "maxItems": 256, + "title": "Obligations", + "type": "array" + }, + "required_profiles": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Required Profiles", + "type": "array" + }, + "schema_version": { + "const": "participant-control-selection/v2", + "title": "Schema Version", + "type": "string" + }, + "selection_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Selection Id", + "type": "string" + }, + "semantic_revision": { + "const": "sem-235/rev1", + "title": "Semantic Revision", + "type": "string" + }, + "slots": { + "items": { + "$ref": "#/$defs/ControlResultSlotV2Model" + }, + "maxItems": 256, + "title": "Slots", + "type": "array" + } + }, + "required": [ + "schema_version", + "selection_id", + "semantic_revision", + "apparatus", + "required_profiles", + "bindings", + "slots", + "bounds", + "evidence", + "interpretation", + "obligations" + ], + "title": "ParticipantControlSelectionV2Model", + "type": "object" + }, + "ParticipantCrossingSubjectKind": { + "description": "Closed typed references to incumbent participant and evidence carriers.", + "enum": [ + "participant-control-occurrence", + "participant-action-contract", + "participant-action-admission", + "participant-action-attempt", + "participant-action-result", + "participant-lifecycle-event", + "participant-observation", + "participant-decision-surface", + "participant-exposure", + "participant-inject-delivery", + "participant-context-view", + "participant-history-view", + "participant-status-view", + "experiment-evidence" + ], + "title": "ParticipantCrossingSubjectKind", + "type": "string" + } + }, + "$id": "https://openrae.github.io/rae/schemas/participant-control-evaluation-v2.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Complete v2 contract record; eligibility is not dispatch authority.", + "properties": { + "commit": { + "$ref": "#/$defs/ControlCommitBoundaryV2Model" + }, + "composition": { + "$ref": "#/$defs/ControlCompositionV2Model" + }, + "effect_plan": { + "$ref": "#/$defs/ControlEffectPlanV2Model" + }, + "evaluation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Evaluation Id", + "type": "string" + }, + "invocations": { + "items": { + "$ref": "#/$defs/ControlInvocationV2Model" + }, + "maxItems": 256, + "title": "Invocations", + "type": "array" + }, + "lost_advice": { + "items": { + "$ref": "#/$defs/ControlLostAdviceV2Model" + }, + "maxItems": 256, + "title": "Lost Advice", + "type": "array" + }, + "request": { + "$ref": "#/$defs/ParticipantControlRequestV2Model" + }, + "results": { + "items": { + "$ref": "#/$defs/ControlMechanismResultV2Model" + }, + "maxItems": 256, + "title": "Results", + "type": "array" + }, + "schema_version": { + "const": "participant-control-evaluation/v2", + "title": "Schema Version", + "type": "string" + }, + "state_proposals": { + "items": { + "$ref": "#/$defs/ControlStateProposalV2Model" + }, + "maxItems": 256, + "title": "State Proposals", + "type": "array" + }, + "support": { + "items": { + "$ref": "#/$defs/ControlSupportAssessmentV2Model" + }, + "maxItems": 256, + "title": "Support", + "type": "array" + } + }, + "required": [ + "schema_version", + "evaluation_id", + "request", + "invocations", + "results", + "support", + "lost_advice", + "effect_plan", + "state_proposals", + "composition", + "commit" + ], + "title": "ParticipantControlEvaluationV2Model", + "type": "object", + "x-raes-invariants": [ + { + "description": "Validate closed selection dependencies and exact trusted evaluation context; structural validity is not execution authority.", + "id": "participant-control-resolved-context", + "inputs": [ + { + "contract_id": "participant-control-evaluation-v2", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.validate_participant_control_resolved_context_v2" + } + ], + "x-raes-plane": "processor_backend_operational_observability", + "x-raes-semantic-profile": { + "contract_id": "participant-control-evaluation-v2", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/participant-runtime/participant-control-selection-v2.json b/contracts/schemas/participant-runtime/participant-control-selection-v2.json new file mode 100644 index 000000000..252c4d9fe --- /dev/null +++ b/contracts/schemas/participant-runtime/participant-control-selection-v2.json @@ -0,0 +1,600 @@ +{ + "$defs": { + "ControlApplicabilityModel": { + "additionalProperties": false, + "properties": { + "direction": { + "enum": [ + "ingress", + "egress" + ], + "title": "Direction", + "type": "string" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "phase_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Phase Ref", + "type": "string" + }, + "sink_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Sink Ref", + "type": "string" + }, + "subject_kind": { + "$ref": "#/$defs/ParticipantCrossingSubjectKind" + } + }, + "required": [ + "participant_address", + "episode_id", + "direction", + "sink_ref", + "phase_ref", + "subject_kind" + ], + "title": "ControlApplicabilityModel", + "type": "object" + }, + "ControlArtifactReferenceModel": { + "additionalProperties": false, + "description": "Exact out-of-line artifact coordinate, not an artifact/evidence payload.", + "properties": { + "digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Digest", + "type": "string" + }, + "kind": { + "enum": [ + "profile", + "mechanism", + "implementation", + "authority", + "evidence", + "limitation", + "memory", + "apparatus", + "run", + "crossing", + "history", + "provider-state", + "input", + "clock", + "trigger", + "rule", + "inject-delivery", + "disclosure", + "manifest", + "installation", + "control", + "transformation", + "action", + "lifecycle", + "audit", + "receipt", + "projection", + "policy", + "constraint", + "flow-relation" + ], + "title": "Kind", + "type": "string" + }, + "ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Ref", + "type": "string" + }, + "revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Revision", + "type": "string" + } + }, + "required": [ + "kind", + "ref", + "revision", + "digest" + ], + "title": "ControlArtifactReferenceModel", + "type": "object" + }, + "ControlCausalBoundsModel": { + "additionalProperties": false, + "properties": { + "expires_at_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expires At Order", + "type": "integer" + }, + "max_attempts": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Attempts", + "type": "integer" + }, + "max_depth": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Depth", + "type": "integer" + }, + "max_effects": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Effects", + "type": "integer" + }, + "max_fanout": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Fanout", + "type": "integer" + }, + "max_firings_per_rule": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Firings Per Rule", + "type": "integer" + } + }, + "required": [ + "max_depth", + "max_effects", + "max_fanout", + "max_firings_per_rule", + "max_attempts", + "expires_at_order" + ], + "title": "ControlCausalBoundsModel", + "type": "object" + }, + "ControlMechanismBindingV2Model": { + "additionalProperties": false, + "properties": { + "applicability": { + "items": { + "$ref": "#/$defs/ControlApplicabilityModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Applicability", + "type": "array" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "configuration_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Configuration Digest", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "implementation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "limitations": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Limitations", + "type": "array" + }, + "mechanism": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "memory_scope": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "profiles": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Profiles", + "type": "array" + }, + "protocol_revision": { + "const": "participant-control-provider/v2", + "title": "Protocol Revision", + "type": "string" + }, + "state_scope": { + "$ref": "#/$defs/ControlStateScopeV2Model" + } + }, + "required": [ + "instance_id", + "profiles", + "mechanism", + "protocol_revision", + "implementation", + "configuration_digest", + "authority", + "memory_scope", + "applicability", + "evidence", + "limitations", + "state_scope" + ], + "title": "ControlMechanismBindingV2Model", + "type": "object" + }, + "ControlProfileObligationV2Model": { + "additionalProperties": false, + "description": "Profile-owned obligation declared before any provider result exists.", + "properties": { + "constraints": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "obligation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Id", + "type": "string" + }, + "profile": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "required_kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Required Kind", + "type": "string" + }, + "required_strength": { + "enum": [ + "exact", + "bounded", + "disclosed_weak" + ], + "title": "Required Strength", + "type": "string" + } + }, + "required": [ + "obligation_id", + "profile", + "required_kind", + "required_strength", + "constraints" + ], + "title": "ControlProfileObligationV2Model", + "type": "object" + }, + "ControlResultSlotV2Model": { + "additionalProperties": false, + "description": "Admitted required-input support pin, independent of provider claims.", + "properties": { + "constraints": { + "default": [], + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "dependencies": { + "items": { + "$ref": "#/$defs/ControlSlotDependencyModel" + }, + "maxItems": 256, + "title": "Dependencies", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "required_strength": { + "default": "exact", + "enum": [ + "exact", + "bounded", + "disclosed_weak" + ], + "title": "Required Strength", + "type": "string" + }, + "role": { + "enum": [ + "mandatory", + "advisory" + ], + "title": "Role", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "instance_id", + "kind", + "role", + "dependencies" + ], + "title": "ControlResultSlotV2Model", + "type": "object" + }, + "ControlSlotDependencyModel": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "kind" + ], + "title": "ControlSlotDependencyModel", + "type": "object" + }, + "ControlStateScopeV2Model": { + "additionalProperties": false, + "description": "Admitted sharing scope, distinct from participant memory and profile.", + "properties": { + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "configuration_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Configuration Digest", + "type": "string" + }, + "scope_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Scope Id", + "type": "string" + }, + "sharing": { + "enum": [ + "run", + "participant", + "episode" + ], + "title": "Sharing", + "type": "string" + } + }, + "required": [ + "scope_id", + "sharing", + "authority", + "configuration_digest" + ], + "title": "ControlStateScopeV2Model", + "type": "object" + }, + "ParticipantCrossingSubjectKind": { + "description": "Closed typed references to incumbent participant and evidence carriers.", + "enum": [ + "participant-control-occurrence", + "participant-action-contract", + "participant-action-admission", + "participant-action-attempt", + "participant-action-result", + "participant-lifecycle-event", + "participant-observation", + "participant-decision-surface", + "participant-exposure", + "participant-inject-delivery", + "participant-context-view", + "participant-history-view", + "participant-status-view", + "experiment-evidence" + ], + "title": "ParticipantCrossingSubjectKind", + "type": "string" + } + }, + "$id": "https://openrae.github.io/rae/schemas/participant-control-selection-v2.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Complete B; no exact-cut filtering changes this identity.", + "properties": { + "apparatus": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "bindings": { + "items": { + "$ref": "#/$defs/ControlMechanismBindingV2Model" + }, + "maxItems": 64, + "title": "Bindings", + "type": "array" + }, + "bounds": { + "$ref": "#/$defs/ControlCausalBoundsModel" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "interpretation": { + "const": "participant-control-applicability/rev1", + "title": "Interpretation", + "type": "string" + }, + "obligations": { + "items": { + "$ref": "#/$defs/ControlProfileObligationV2Model" + }, + "maxItems": 256, + "title": "Obligations", + "type": "array" + }, + "required_profiles": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Required Profiles", + "type": "array" + }, + "schema_version": { + "const": "participant-control-selection/v2", + "title": "Schema Version", + "type": "string" + }, + "selection_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Selection Id", + "type": "string" + }, + "semantic_revision": { + "const": "sem-235/rev1", + "title": "Semantic Revision", + "type": "string" + }, + "slots": { + "items": { + "$ref": "#/$defs/ControlResultSlotV2Model" + }, + "maxItems": 256, + "title": "Slots", + "type": "array" + } + }, + "required": [ + "schema_version", + "selection_id", + "semantic_revision", + "apparatus", + "required_profiles", + "bindings", + "slots", + "bounds", + "evidence", + "interpretation", + "obligations" + ], + "title": "ParticipantControlSelectionV2Model", + "type": "object", + "x-raes-invariants": [ + { + "description": "Validate closed selection dependencies and exact trusted evaluation context; structural validity is not execution authority.", + "id": "participant-control-selection-graph", + "inputs": [ + { + "contract_id": "participant-control-selection-v2", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.ParticipantControlSelectionV2Model.model_validate" + } + ], + "x-raes-plane": "processor_backend_operational_observability", + "x-raes-semantic-profile": { + "contract_id": "participant-control-selection-v2", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/profiles/backend-profile-v1.json b/contracts/schemas/profiles/backend-profile-v1.json index bdacdd712..393dde9fd 100644 --- a/contracts/schemas/profiles/backend-profile-v1.json +++ b/contracts/schemas/profiles/backend-profile-v1.json @@ -48,6 +48,8 @@ "participant-control-occurrence-v1", "participant-control-selection-v1", "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "participant-crossing-occurrence-v1", "participant-flow-control-relation-v1", "participant-lifecycle-event-v1", diff --git a/docs/decisions/issue-1365-applicable-provider-contracts-preflight.md b/docs/decisions/issue-1365-applicable-provider-contracts-preflight.md new file mode 100644 index 000000000..0421794b7 --- /dev/null +++ b/docs/decisions/issue-1365-applicable-provider-contracts-preflight.md @@ -0,0 +1,110 @@ +# Issue #1365 — Applicable-provider contract preflight + +Date: 2026-09-27. Scope: guidance for API-424 contract publication, not an +implementation plan or a claim of runtime adoption. The issue is the delivery +contract; [ADR-111](adrs/adr-111-control-applicability-and-effect-decisions.md), +[CA-01–CA-09](../../specs/formal/participant-semantics/control-applicability-and-evaluation.md), +and the [migration contract](../migration/control-applicability-and-evaluation.md) +fix its meaning. The [original API-424 preflight](issue-1072-api-424-provider-contracts-preflight.md) +and [#1352 preflight](issue-1352-control-applicability-preflight.md) already +identify the broader boundaries; no new ADR or semantic algebra is needed. + +## Contract decisions and observed gaps + +Package paths below are relative to `implementations/python/packages/`. + +| Existing contract | Required revised boundary | +| --- | --- | +| `raes_contracts/contracts/participant_control_selection.py` and `participant_control_composition.py::ParticipantControlRequestModel` | Retain the complete admitted B, its digest, profile obligations, instance/configuration/authority/provenance and finite bounds. Represent exact-cut coverage separately: applies with complete slot and support mapping, profile-owned proven inapplicable with evidence, or unresolved. Derive A(B,K) as references into B, including required input closure. The current request requires every binding to match K; filtering B or treating profile presence as coverage changes the wrong identity. Valid empty A requires total obligation accounting and all incumbent gates. | +| `raes_backend_protocols/protocols.py::ParticipantControlProvider.resolve`, `raes_contracts/contracts/participant_control_results.py`, and `raes_runtime/participant_control_orchestration.py::_mechanism_results` | A provider/v1 call receives one common request per instance, so it cannot establish A.fact → B.assessment → A.rule. Publish a separately negotiated slot/stage-aware protocol input carrying requested slots, exact invocation identity, detached typed predecessor results or content-bound refs, authorized projection and pinned committed/tentative shared-state inputs. Bind each output to that invocation and its complete input identity. Missing or invalid mandatory inputs block; explicit evaluated false trigger is distinct from absent effect. No ambient result lookup or provider-supplied authority index. | +| `participant_control_results.py::ControlEffectiveSupportModel`, `raes_backend_protocols/participant_control_admission.py`, and `participant_control_composition.py::_support_blockers` | Admit required strength, coverage, constraints and evidence per obligation/input. Resolve against API-407's incumbent feature support and exact installed binding at K. Current all-exact, all-instance blocker is wrong for supported bounded requirements and optional loss. Required-input closure M fixes availability; advisory content does not itself veto. Incomplete/invalid mandatory results block with their reason. Normalize only a rejected optional contribution at its invocation boundary, discard its state/effects and retain safe lost-advice evidence; role comes from B/M, never the response. A malformed apparatus or unresolved required coverage cannot become optional failure. | +| `participant_control_effects.py`, `participant_control_effect_composition.py`, `participant_control_composition.py::admitted_effect_phase` | Publish unscheduled parent permit/deny/withhold separately from executable typed effects. Parent denial/withhold is invariant across legacy-shaped phases. Each effect retains its own target/owner, origin, phase, exact prerequisite event/outcome and allowed parent dispositions; independently admitted audit may follow denial. A required predecessor withholds until its exact owner outcome and fresh reevaluation. A later consequence waits for its declared parent admission/application event, never just an intent or accepted receipt. Check the combined parent/effect graph and target conflicts, not merely the slot DAG or one global phase. | +| `participant_control_coordinates.py::ControlProviderStateModel` and `participant_control_results.py::next_provider_state` | Pin state scope and version separately from participant memory and profile identity. Declare sharing/writers and whether a speculative update commits on evaluation or only on parent release. Multiple writers need an explicit tentative-state chain or conflict. State, complete decision/coverage/results, accepted effect intents/claims and consumed budgets join one expected-head transaction; a failed commit publishes none. Do not expose private state through predecessor inputs. | + +Keep declaration, installed capability, effective support, composition eligibility, +committed intent, owner outcome and observed realization distinct. `ControlDecisionModel`, +the closed `ControlEffectTarget` alternatives, API-409/API-423 crossing and +control carriers, and SEM-233's exact security relation remain their owners; +do not create a universal target, state, decision or operation DTO. The finite +[#1352 cases](../research/control-applicability/cases.md) are semantic witnesses, +not live protocol or store evidence. + +## Whole-repository gates + +| Layer and canonical incumbents | Contract publication guardrail | +| --- | --- | +| Portable ingress and shape: `raes_contracts/json_ingress.py::parse_bounded_json_object`, `contracts/base.py`, `participant_control_coordinates.py`, `parse_participant_control_evaluation` | Bound artifact bytes, JSON depth, graph nodes/edges, stages, predecessor material, result count and provider iteration. Reject duplicate members, non-finite numbers, coercion, unknown variants, duplicate IDs and cycles on JSON and direct Python ingress. Frozen `ContractModel` wrappers are not deeply immutable; detach nested inputs and revalidate returned portable projections. Structural validity never proves trusted coverage. | +| Artifact/configuration and authority: `raes_contracts/contracts/participant_control_profiles.py`, `raes_contracts/participant_flow_policy_profiles.py`, `raes_contracts/corpus.py`, `raes_contracts/participant_configuration.py`, `contracts/experiment_bindings.py`, `participant_control_resolution.py::validate_participant_control_resolved_context` | Resolve exact revision/digest and configuration owner; use trusted non-wire indexes for profile obligations, applicability, predecessor contents, support, disclosure, state scope, receipts and current K. Reuse `_canonical.py`/`control_digest` with defined hashed projections and canonical set-array order. No latest fallback, caller-selected search root or digest-as-permission rule. | +| Auth and effect policy: `raes_runtime/control_plane_api/_auth.py`, `ControlPlaneSecurityConfig.strict_defaults()`, `control_plane_api_guards.py::RequestSizeLimitMiddleware`, `participant_effect_authority.py`, `participant_control_receipts.py` | This publication adds no route. A consumer still verifies bearer/proxy identity, actor/role/target/audience, incumbent action/crossing/projection/final-sink gates and each effect owner's current policy. An effect acts for its originating principal; a drain caller is separately authorized before reading committed state. Provider references grant no authority. | +| Secrets, environment and host: `raes_contracts/secret_references.py`, `raes/runtime_environment.py::RuntimeEnvironmentVariable`, `runtime_values.py::enforce_observed_value_redaction` | Portable predecessor, state and evidence data contain only disclosure-authorized bounded values/refs, never credentials, raw prompts, private provider memory or rejected payloads. Any downstream env binding keeps name and `value`/`value_from` shape checks, source classification and redaction. The contract adds no launcher; backend-owned host/process/network protection remains separate. Never put sensitive material in argv, shell text, environment, temporary names or stdout/stderr. | +| Errors and observation: `raes_contracts/diagnostics.py`, `raes_runtime/backend_result_diagnostics.py`, `participant_control_diagnostics.py`, `control_plane_api/_responses.py`, `_operation_routes.py`, `control_plane_audit.py`, `raes/observability_plane_semantics.py` | Use value-independent bounded reasons and existing receipt/audit envelopes. Sanitize provider errors, validation paths and references before constructing diagnostics; `portable_diagnostic_payload` validates shape, not redaction. Apply plane classification and participant/audience projection to coverage, dependencies, denial, review existence and timing. No new exception hierarchy, logger or audit channel. | +| Persistence and recovery: `raes_contracts/participant_control_evaluation_history.py`, `raes_runtime/participant_control_causal_state.py`, `participant_control_records.py`, `participant_control_receipts.py`, `control_plane_store_record_migration.py`, memory/local `ControlPlaneStore` implementations | The new wire meaning must remain representable at the existing expected-head store boundary. Historical evaluation validation, folds and effect admission must select the recorded interpretation; a new reducer cannot rewrite old claims, budgets, receipts or pending work. No second state store, snapshot metadata shortcut or implied exactly-once external execution. Runtime/store adoption is a separate delivery gate. | + +`raes_backend_protocols` imports neutral `raes_contracts` DTOs only. Keep the +package boundary in `tools/policy/adr_policy.yaml` and +`tools/policy/repo_policy.py::_check_backend_protocol_contract_annotations`; +do not import runtime, concrete backend or conformance sanitizers into the +public protocol. Shared structural/composition rules belong in +`raes_contracts/contracts/participant_control_*`; independently trusted joins +belong in `participant_control_resolution.py`. Runtime routes, stores and +conformance must consume those decisions rather than replicate them. + +## Publication, evolution and evidence boundary + +The selection/evaluation schemas are draft in +`contracts/schemas/participant-runtime/`; ADR-061 permits a recorded draft edit, +but old and amended meanings still need explicit interpretation and exact +schema-content negotiation. A changed provider invocation cannot silently +remain `participant-control-provider/v1`. Keep its original reader/adapter only +for an independently verified dependency-free fragment; a `resolve` method +check does not establish revised support. Stable breaking changes require a +new contract ID. Historical records with no reliable interpretation pin keep +the legacy interpretation or an explicit unsupported/unverifiable disposition, +never inferred new coverage or executable effects. + +Publish each chosen revision coherently through normative schemas; matching +`raes_contracts/contracts/bundle_runtime.py` and exports; explicit +`tools/generate_contract_schemas.py` routing (otherwise it defaults to +`control-plane`); `contracts/schema-publication/entries/` hashes and +`last_change` under manifest v2; `manifest_authority.py` and API-407 +capability admission; `schema_invariants.py` and `x-raes-invariants` for +non-schema joins; `raes_conformance/conformance/validators.py`; packaged corpus +in `implementations/python/pyproject.toml`; and public/migration docs. Reuse +`tools/check_generated_schemas.py`, `check_schema_publication.py`, authority, +concept and lineage checks. A schema-only pass is not trusted-context validation. + +Keep `docs/explain/reference/modular-participant-control-contracts.md` and +`docs/public/participant-control.md` claim language scoped to the interpretation +their cited code actually implements. The retained v36/v37 formal-semantic and +specification-coverage bundles and the #1352 finite model do not establish +revised provider invocation, runtime adoption or backend realization. Follow +the existing `test_api_424_*` split for structural, trusted-context, +composition, publication and governance evidence, then use the RUN-320 +memory/local-store fixtures for versioned replay and zero-dispatch claims when +those consumers adopt the revision. Keep ownership and traceability aligned +with `tools/policy/requirement_order.yaml` and +`docs/governance/requirement-scopes/1072.json`. + +The essential paired examples are disjoint sinks under one B; an applicable +obligation with no slots versus proven inapplicability; A → B → A with exact +predecessor binding; optional malformed/unsupported advice versus required +input loss; false trigger versus missing result; bounded support against an +admitted requirement; denial/withhold in every phase; independently authorized +audit on denial; a later consequence awaiting actual parent application; +combined phase cycle; conflicting shared-state writers/failed commit; and +old/new history replay. Distinguish schema-invalid from structurally valid but +context-invalid fixtures, and do not promote finite-model evidence into a live +provider, backend or durability claim. + +The extension seam is the admitted obligation relation plus exact-cut +invocation/dependency, state-scope, support-constraint and typed prerequisite +bindings. Another sink or independently installed provider can use those +coordinates without editing the canonical algebra or adding backend-name +branches. New domain/effect meaning still requires governed closed publication. + +Non-goals for #1365: runtime scheduler, provider installation, effect dispatch, +store migration, HTTP endpoint, plugin host, policy interpreter, backend +instrumentation or parity, new credential/config framework, and historical +reinterpretation. `.ground-control.yaml`, `.gc/plan-rules.md`, +`tools/policy/requirement_order.yaml`, `noxfile.py`, `tools/nox_support/` and CI +remain workflow owners. Local verification stays targeted; CI owns full suites. diff --git a/docs/explain/reference/modular-participant-control-contracts.md b/docs/explain/reference/modular-participant-control-contracts.md index 5fb3ac86d..ed67ab608 100644 --- a/docs/explain/reference/modular-participant-control-contracts.md +++ b/docs/explain/reference/modular-participant-control-contracts.md @@ -12,21 +12,22 @@ nor effect dispatch. RUN-320 remains the orchestration owner. [ADR-111](../../decisions/adrs/adr-111-control-applicability-and-effect-decisions.md) and [CA-01–CA-09](../../../specs/formal/participant-semantics/control-applicability-and-evaluation.md) define revised coverage, predecessor-input, state and parent/effect meaning. -The v1 boundary documented below does not implement that amendment. In +The v1 runtime boundary documented below does not implement that amendment. In particular, its whole-selection applicability check cannot express disjoint sink subsets of one apparatus; profile presence alone does not prove required slot coverage; provider/v1 carries no fresh predecessor results; optional failures can block composition; and phase handling can mark a subsequent parent-targeted denial eligible. The existing verification map is bounded -legacy evidence, not proof that those gaps are repaired. +legacy evidence, not proof that those gaps are repaired in the runtime. The amendment keeps the full admitted apparatus, records coverage at each cut, evaluates typed slot/stage dependencies, isolates genuinely optional failure, and separates unscheduled parent decisions from independently authorized effects. Its [worked cases](../../research/control-applicability/cases.md) and [migration contract](../../migration/control-applicability-and-evaluation.md) -define the required interpretation and adoption checks. Existing schemas, -provider/v1 and retained history keep their original meaning. +define the required interpretation and adoption checks. Existing v1 schemas, +provider/v1 and retained history keep their original meaning. Issue #1365 +publishes a separate v2 representation; see below. ## IFC variability amendment (#1354) @@ -47,11 +48,13 @@ owner-aware interpretation or revised bounded-support adoption. | `participant-control-selection-v1` | Exact apparatus/profile/mechanism bindings, typed slots, applicability, acyclic dependencies and finite bounds | | `participant-control-evaluation-v1` | Immutable admitted context, all typed contributions, declared/effective support, composition and separate realization receipts | | `participant-control-teaching-profile-v1` | Closed wire projection of teaching-influence/rev1; two tokens, union propagation, retained memory and no release | +| `participant-control-selection-v2` | Complete admitted apparatus and profile obligations with per-slot support pins under `participant-control-applicability/rev1` | +| `participant-control-evaluation-v2` | Exact-cut coverage and applicable subset, slot/stage predecessor and state inputs, required support, unscheduled parent decisions, typed effect prerequisites and expected-head commit boundary | The normative schemas are in `contracts/schemas/participant-runtime/`, with per-contract publication records, fixtures and identical `schema_bundle()` -output. Installed distributions include all three schemas and the teaching -profile in the `raes_contracts` corpus. This is a participant-control profile, +output. Installed distributions include all five schemas, including the teaching +profile, in the `raes_contracts` corpus. This is a participant-control profile, not an SDL extension, interoperability profile or backend-allocation profile. `ParticipantControlProvider` lives in `raes_backend_protocols.protocols`. @@ -62,6 +65,35 @@ does not support runtime method-presence checks as evidence of capability. All nested request/result values are frozen, with detached immutable tuples. Proposed provider-state references are speculative, not committed state. +`ParticipantControlProviderV2.resolve_stage(invocation)` is separately +negotiated. Each invocation names one finite slot or stage, a content-bound +typed predecessor set, an exact context digest, admitted state scope/version +and disclosure projection. `ControlMechanismResultV2Model` binds the complete +invocation digest. The provider receives no portable credential, executable +selector, private state body or whole-apparatus authority through that DTO. +An operator backend resolves the bounded references under its installed +binding; a structural protocol match never establishes support. + +For v2, `ParticipantControlEvaluationV2Model` validates structural closure and +`validate_participant_control_resolved_context_v2` requires independently +supplied `ParticipantControlValidationContextV2`. Its resolver pins admitted +coverage, installed bindings, exact results, effective API-407 support, +safe-to-disclose references and authorized effects. Conformance reports +`structural-context-required` without that resolver. The v2 composition and +effect-plan models distinguish eligible evaluation from committed intent and +owner realization; a prepared or rejected commit dispatches nothing. +The resolver also binds each state proposal and its commit timing to an +independent state-write authorization. For every realized effect it requires a +retained full-payload claim, a prior committed and dispatchable evaluation at +the originating cut, effect authority for that cut, and the applied owner +receipt. Its origin plan proves the phase, parent and predecessor conditions. +Lifecycle operations must follow overlapping live-target effects. Published +fixtures show disjoint sinks, A → B → A, optional failure, missing coverage and +denial with an independent audit. Their bounded structural evidence does not +claim RUN-320 adoption or backend realization. The +[migration contract](../../migration/control-applicability-and-evaluation.md) +specifies exact reader/protocol negotiation and retained-history treatment. + ## Validation and authority ```python diff --git a/docs/governance/requirement-scopes/1365.json b/docs/governance/requirement-scopes/1365.json new file mode 100644 index 000000000..79bed3a30 --- /dev/null +++ b/docs/governance/requirement-scopes/1365.json @@ -0,0 +1,115 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1365, + "primary_requirement_uid": "API-424", + "requirement_uids": [ + "API-424" + ], + "bindings": { + "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json": [ + "API-424" + ], + "contracts/schema-publication/entries/backend-manifest-v2.json": [ + "API-424" + ], + "contracts/schema-publication/entries/backend-profile-v1.json": [ + "API-424" + ], + "contracts/schema-publication/entries/participant-control-evaluation-v2.json": [ + "API-424" + ], + "contracts/schema-publication/entries/participant-control-selection-v2.json": [ + "API-424" + ], + "contracts/schemas/backend-manifest/backend-manifest-v2.json": [ + "API-424" + ], + "contracts/schemas/participant-runtime/participant-control-evaluation-v2.json": [ + "API-424" + ], + "contracts/schemas/participant-runtime/participant-control-selection-v2.json": [ + "API-424" + ], + "contracts/schemas/profiles/backend-profile-v1.json": [ + "API-424" + ], + "docs/decisions/issue-1365-applicable-provider-contracts-preflight.md": [ + "API-424" + ], + "docs/explain/reference/modular-participant-control-contracts.md": [ + "API-424" + ], + "docs/governance/requirement-scopes/1365.json": [ + "API-424" + ], + "docs/migration/control-applicability-and-evaluation.md": [ + "API-424" + ], + "docs/public/participant-control.md": [ + "API-424" + ], + "docs/requirements/API-424/requirement.md": [ + "API-424" + ], + "implementations/python/packages/raes/observability_plane_semantics.py": [ + "API-424" + ], + "implementations/python/packages/raes_backend_protocols/protocols.py": [ + "API-424" + ], + "implementations/python/packages/raes_conformance/conformance/validators.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/bundle_runtime.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/manifest_authority.py": [ + "API-424" + ], + "implementations/python/tests/test_issue_1365_applicable_contracts.py": [ + "API-424" + ], + "tools/generate_contract_schemas.py": [ + "API-424" + ], + "tools/policy/requirement_order.yaml": [ + "API-424" + ] + } +} diff --git a/docs/migration/control-applicability-and-evaluation.md b/docs/migration/control-applicability-and-evaluation.md index 41221cc3d..f48b68cce 100644 --- a/docs/migration/control-applicability-and-evaluation.md +++ b/docs/migration/control-applicability-and-evaluation.md @@ -2,13 +2,18 @@ This is the adoption contract for [ADR-111](../decisions/adrs/adr-111-control-applicability-and-effect-decisions.md) and [CA-01–CA-09](../../specs/formal/participant-semantics/control-applicability-and-evaluation.md). -It defines producer/reader obligations, not a working migrator or new schema. +It defines producer/reader obligations, not a working migrator. Issue #1365 +publishes the v2 contract representation described below; runtime and history +adoption remain separate. ## Explicit interpretation boundary `participant-control-applicability/rev1` identifies the amendment. It is distinct from `sem-235/rev1`, profile revisions, wire `participant-control-selection/v1` and `participant-control-evaluation/v1`, and `participant-control-provider/v1`. +Issue #1365 publishes `participant-control-selection/v2`, +`participant-control-evaluation/v2` and `participant-control-provider/v2` as +separately negotiated contracts for the amendment. New invocation semantics must not be inferred from a `resolve` method or the presence/absence of optional fields. Retain separate semantic, profile, protocol, schema-content, implementation and storage pins. @@ -18,8 +23,68 @@ Their publication process may permit recorded changes in place; that does not make old and new meanings interchangeable. Adoption must introduce an explicit interpretation discriminator or new contract lineage, and negotiate exact schema content and protocol support at apparatus admission. Stable breaking -contracts require a new contract ID under ADR-061. No schema, protocol or -deprecation/removal window is published by this design delivery. +contracts require a new contract ID under ADR-061. The #1352 design delivery +published no schema or protocol; #1365 uses new contract IDs and does not open +a deprecation or removal window for v1. + +## Published v2 representation and negotiation (#1365) + +The v2 selection preserves the complete admitted apparatus, exact profile +obligations, binding configuration/authority/provenance and slot support pins. +An evaluation embeds that selection and records total obligation coverage and +the applicable slot subset at its exact context cut. Coverage is independently +resolved from the profile: an absent record, empty applicable discharge or +provider-returned `None` cannot prove inapplicability. Required-input closure +is explicit and a required advisory input must be available even though its +opinion does not decide the parent. V2 invocations bind requested slots, +content-bound predecessor results, an authorized projection and committed or +declared tentative state inputs; result digests bind back to the complete +invocation. Invalid optional output is reduced at that invocation boundary to +a value-independent lost-advice record. The accepted parent decision is an +unscheduled permit, deny or withhold. Effects have their own closed target, +origin, phase and outcome prerequisite, and only an independently authorized +consequence may follow denial or withhold. Decision-local runnable effects are +provisional: `admitted_control_effect_ids_v2` joins them with mandatory +composition before any intent or dispatch enters the commit envelope. Parent +release receipts and release-conditioned state cannot survive failed required +composition. The expected-head commit envelope binds +coverage, results, decision, state proposals, effect intents and consumption; +it is a contract for the incumbent store, not proof of a commit or dispatch. +An explicit required decision to deny or withhold is a complete parent outcome; +it does not count as a failed mandatory slot. Independent consequences may be +admitted for that exact outcome only when no unrelated mandatory blocker +remains. Duplicate effect IDs across provider results are rejected before +effect planning, so result order cannot suppress a request. + +The selection/evaluation schemas, public `ParticipantControlProviderV2` +protocol, corpus examples, publication entries and conformance registry are +published. The v2 conformance entry reports `structural-context-required` and +requires an operator supplied resolver for admitted coverage, installation, +effective API-407 support, exact results, incumbent crossing and IFC fact +owners, safe references and effect authority. +Coverage must discharge an obligation through a slot whose binding selects +that obligation's profile. Lifecycle effects require explicit ordering after +overlapping live-target operations. A realized effect must match a retained +full-payload claim and an operator-resolved earlier committed evaluation that +authorized its phase, parent outcome and effect predecessors, plus its exact +applied owner outcome. The resolver must also supply the authorized state +proposal for each write, including its commit timing; `commit_on` in a provider +record does not authorize itself. +An exact/bounded compatibility claim still needs independently resolved +constraint evidence; the wire relation alone is not proof. A backend's v1 +`participant_modular_control` declaration continues to require the original +v1 contracts. The v2 IDs are available for exact contract declarations, but +neither those declarations nor protocol method presence prove installed v2 +capability. Producers must negotiate the v2 IDs, their content hashes, the +semantic interpretation and provider/v2 before emitting v2 records. Readers +must route by the recorded contract ID; there is no global reducer switch. + +This publication does not change `RuntimeSnapshot`, the RUN-320 provider +scheduler, store/history folds or effect dispatcher. A live apparatus must +wait for coordinated consumer and backend evidence before adopting v2. No +legacy selection, result, claim, budget or pending intent is converted by +importing these models, and a v2 reader must not infer missing coverage from a +v1 record. | Artifact | Legacy reader | Amendment-aware reader | | --- | --- | --- | diff --git a/docs/public/participant-control.md b/docs/public/participant-control.md index d78ccdf0b..b5dea2d97 100644 --- a/docs/public/participant-control.md +++ b/docs/public/participant-control.md @@ -29,6 +29,17 @@ dispatches an admitted effect only through the RAES operation that already owns it. An effect acts for the principal whose operation admitted it, never for the caller that later requests its dispatch. +API-424 now also publishes v2 selection/evaluation contracts and +`ParticipantControlProviderV2` for per-crossing applicability and typed +dependency inputs. Those contracts keep the complete admitted apparatus +separate from the slots applicable at one state cut, require profile-owned +coverage and explicit input support, and separate parent decisions from +effect prerequisites. They require exact protocol/schema negotiation and a +trusted operator resolver. The current reference runtime and retained v1 +history continue to use provider/v1; v2 publication alone does not activate +v2 scheduling, state commit or effect dispatch. See the +[migration rules](../migration/control-applicability-and-evaluation.md). + | Plane | What it contains | What it does not imply | | --- | --- | --- | | World truth | Backend or environment state under its owning semantics | Participant visibility | diff --git a/docs/requirements/API-424/requirement.md b/docs/requirements/API-424/requirement.md index 394eb5268..4ca4ac739 100644 --- a/docs/requirements/API-424/requirement.md +++ b/docs/requirements/API-424/requirement.md @@ -67,6 +67,17 @@ requires explicit coordinated producer/protocol/reader adoption and preserves legacy history without inventing new coverage or realization evidence. ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `1365` (Publish applicable-provider and dependency-evaluation contracts) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py` (Complete admitted apparatus, profile obligations and exact-cut subset) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py` (Typed predecessor and scoped-state inputs with result binding) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py` (Requirement-relative support assessment) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py` (Unscheduled parent decisions and typed effect prerequisites) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py` (Composition, state and atomic commit boundary) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-control-selection-v2.json` (Published v2 apparatus selection) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-control-evaluation-v2.json` (Published v2 exact-cut evaluation) +- IMPLEMENTS → CONFIG `contracts/schema-publication/entries/participant-control-selection-v2.json` (Selection publication ledger) +- IMPLEMENTS → CONFIG `contracts/schema-publication/entries/participant-control-evaluation-v2.json` (Evaluation publication ledger) +- TESTS → TEST `implementations/python/tests/test_issue_1365_applicable_contracts.py` (Disjoint sinks, dependency chain, optional failure, support, effects, commit and publication) - IMPLEMENTS → GITHUB_ISSUE `1352` (Provider-input, applicability and effect contract decision) - IMPLEMENTS → SPEC `specs/formal/participant-semantics/control-applicability-and-evaluation.md` (Required revised contract meaning; not new schema publication) - IMPLEMENTS → DOCUMENTATION `docs/migration/control-applicability-and-evaluation.md` (Producer/reader and retained-history contract) diff --git a/implementations/python/packages/raes/observability_plane_semantics.py b/implementations/python/packages/raes/observability_plane_semantics.py index 7962bf782..763a1c509 100644 --- a/implementations/python/packages/raes/observability_plane_semantics.py +++ b/implementations/python/packages/raes/observability_plane_semantics.py @@ -40,6 +40,8 @@ class ObservabilityEvidencePlane(str, Enum): PLANE_BY_CONTRACT_ID: dict[str, ObservabilityEvidencePlane] = { "participant-control-selection-v1": ObservabilityEvidencePlane.PROCESSOR_BACKEND_OPERATIONAL, "participant-control-evaluation-v1": ObservabilityEvidencePlane.PROCESSOR_BACKEND_OPERATIONAL, + "participant-control-selection-v2": ObservabilityEvidencePlane.PROCESSOR_BACKEND_OPERATIONAL, + "participant-control-evaluation-v2": ObservabilityEvidencePlane.PROCESSOR_BACKEND_OPERATIONAL, "experiment-capture-spec-v1": ObservabilityEvidencePlane.AUTHORED_EVIDENCE_REQUIREMENT, "experiment-evidence-record-v1": ObservabilityEvidencePlane.CAPTURED_EVIDENCE, "experiment-derived-measure-v1": ObservabilityEvidencePlane.DERIVED_ANALYSIS, @@ -58,6 +60,8 @@ class ObservabilityEvidencePlane(str, Enum): PLANE_ANNOTATED_CONTRACT_IDS: tuple[str, ...] = ( "participant-control-selection-v1", "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "experiment-capture-spec-v1", "experiment-evidence-record-v1", "experiment-derived-measure-v1", diff --git a/implementations/python/packages/raes_backend_protocols/protocols.py b/implementations/python/packages/raes_backend_protocols/protocols.py index 7ee51c18e..239d21582 100644 --- a/implementations/python/packages/raes_backend_protocols/protocols.py +++ b/implementations/python/packages/raes_backend_protocols/protocols.py @@ -8,6 +8,10 @@ ParticipantTemporalRuntimeContextModel, ) from raes_contracts.contracts.participant_control_composition import ParticipantControlRequestModel +from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, +) from raes_contracts.contracts.participant_control_results import ControlMechanismResultModel from raes_contracts.contracts.participant_execution import ( ParticipantExecutionControlRequestModel, @@ -41,6 +45,17 @@ class ParticipantControlProvider(Protocol): def resolve(self, request: ParticipantControlRequestModel) -> tuple[ControlMechanismResultModel, ...]: ... +class ParticipantControlProviderV2(Protocol): + """Separately negotiated provider/v2 over one detached slot/stage input. + + The invocation supplies exact content-bound predecessor and scoped-state + references. An installed backend resolves only its admitted projection; + method presence alone proves no capability, authority or realization. + """ + + def resolve_stage(self, invocation: ControlInvocationV2Model) -> tuple[ControlMechanismResultV2Model, ...]: ... + + class Provisioner(Protocol): """Applies provisioning plans to the target environment.""" diff --git a/implementations/python/packages/raes_conformance/conformance/validators.py b/implementations/python/packages/raes_conformance/conformance/validators.py index 57a4364f5..f905675bd 100644 --- a/implementations/python/packages/raes_conformance/conformance/validators.py +++ b/implementations/python/packages/raes_conformance/conformance/validators.py @@ -39,9 +39,12 @@ ParticipantBoundaryFlowPolicyProfileModel, ParticipantConfigurationResultModel, ParticipantControlContextResolver, + ParticipantControlContextResolverV2, ParticipantControlEvaluationModel, + ParticipantControlEvaluationV2Model, ParticipantControlOccurrenceModel, ParticipantControlSelectionModel, + ParticipantControlSelectionV2Model, ParticipantControlTeachingProfileModel, ParticipantCrossingOccurrenceModel, ParticipantEpisodeHistoryEventModel, @@ -65,6 +68,7 @@ WorkflowExecutionStateModel, WorkflowHistoryEventModel, validate_participant_control_resolved_context, + validate_participant_control_resolved_context_v2, validate_participant_flow_control_resolved_context, validate_participant_information_state_resolved_context, ) @@ -155,6 +159,8 @@ "participant-control-teaching-profile-v1": ParticipantControlTeachingProfileModel.model_validate, "participant-control-selection-v1": ParticipantControlSelectionModel.model_validate, "participant-control-evaluation-v1": ParticipantControlEvaluationModel.model_validate, + "participant-control-selection-v2": ParticipantControlSelectionV2Model.model_validate, + "participant-control-evaluation-v2": ParticipantControlEvaluationV2Model.model_validate, "backend-augmentation-scope-v1": AugmentationPreparation.model_validate, "materialized-scenario-v1": MaterializedScenario.model_validate, "backend-materialization-attestation-v1": MaterializationSubmission.model_validate, @@ -195,6 +201,7 @@ { "participant-outcome-report-v2", "participant-control-evaluation-v1", + "participant-control-evaluation-v2", "associated-artifact-manifest-v1", "external-concept-bindings-v1", "semantic-projection-report-v1", @@ -383,9 +390,10 @@ def _mixed_composition_context_diagnostics( def _control_context_diagnostics( - payload: object, resolver: ParticipantControlContextResolver | None + contract_name: str, + payload: object, + resolver: ParticipantControlContextResolver | ParticipantControlContextResolverV2 | None, ) -> list[Diagnostic]: - contract_name = "participant-control-evaluation-v1" if resolver is None: return [ _diagnostic( @@ -395,8 +403,12 @@ def _control_context_diagnostics( ) ] try: - record = ParticipantControlEvaluationModel.model_validate(payload) - validate_participant_control_resolved_context(record, resolver) + if contract_name == "participant-control-evaluation-v2": + record = ParticipantControlEvaluationV2Model.model_validate(payload) + validate_participant_control_resolved_context_v2(record, resolver) + else: + record = ParticipantControlEvaluationModel.model_validate(payload) + validate_participant_control_resolved_context(record, resolver) except (TypeError, ValueError): return [ _diagnostic( @@ -413,13 +425,13 @@ def validate_contract_payload( information_state_context_resolver: ParticipantInformationStateContextResolver | None = None, flow_control_context_resolver: ParticipantFlowControlContextResolver | None = None, mixed_composition_context: MixedCompositionResolutionContext | None = None, - control_context_resolver: ParticipantControlContextResolver | None = None, + control_context_resolver: ParticipantControlContextResolver | ParticipantControlContextResolverV2 | None = None, ) -> tuple[Diagnostic, ...]: """Validate one payload through its structural and available contextual boundary.""" diagnostics = _validate_payload(contract_name, payload) - if not diagnostics and contract_name == "participant-control-evaluation-v1": - diagnostics.extend(_control_context_diagnostics(payload, control_context_resolver)) + if not diagnostics and contract_name in {"participant-control-evaluation-v1", "participant-control-evaluation-v2"}: + diagnostics.extend(_control_context_diagnostics(contract_name, payload, control_context_resolver)) if not diagnostics and contract_name == "participant-information-state-record-v1": diagnostics.extend(_information_state_context_diagnostics(payload, information_state_context_resolver)) if not diagnostics and contract_name == "participant-flow-control-relation-v1": diff --git a/implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py b/implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py index 3735ea957..21c1b285d 100644 --- a/implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py +++ b/implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py @@ -1,16 +1,25 @@ """Participant-control public facade and export manifest.""" +from .participant_control_applicability import ParticipantControlRequestV2Model, ParticipantControlSelectionV2Model from .participant_control_composition import ( ParticipantControlEvaluationModel, ParticipantControlRequestModel, parse_participant_control_evaluation, ) +from .participant_control_evaluation_v2 import ( + ParticipantControlEvaluationV2Model, + parse_participant_control_evaluation_v2, +) from .participant_control_profiles import ParticipantControlTeachingProfileModel, load_teaching_influence_profile from .participant_control_resolution import ( + ControlCommittedEffectIntentV2, ParticipantControlContextResolver, + ParticipantControlContextResolverV2, ParticipantControlValidationContext, + ParticipantControlValidationContextV2, validate_participant_control_context, validate_participant_control_resolved_context, + validate_participant_control_resolved_context_v2, ) from .participant_control_selection import ParticipantControlSelectionModel @@ -18,13 +27,21 @@ "ParticipantControlEvaluationModel", "ParticipantControlRequestModel", "ParticipantControlSelectionModel", + "ParticipantControlRequestV2Model", + "ParticipantControlSelectionV2Model", + "ParticipantControlEvaluationV2Model", "ParticipantControlTeachingProfileModel", "load_teaching_influence_profile", "ParticipantControlContextResolver", "ParticipantControlValidationContext", "validate_participant_control_context", "validate_participant_control_resolved_context", + "ParticipantControlValidationContextV2", + "ControlCommittedEffectIntentV2", + "ParticipantControlContextResolverV2", + "validate_participant_control_resolved_context_v2", "parse_participant_control_evaluation", + "parse_participant_control_evaluation_v2", ] PARTICIPANT_CONTROL_EXPORTS = __all__ diff --git a/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py b/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py index 5fe80e9d9..7461b4747 100644 --- a/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py +++ b/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py @@ -11,7 +11,9 @@ from .operation_carriers import OperationReceiptModel, OperationStatusModel from .participant_context import ParticipantContextViewModel from .participant_control import ParticipantControlOccurrenceModel +from .participant_control_applicability import ParticipantControlSelectionV2Model from .participant_control_composition import ParticipantControlEvaluationModel +from .participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model from .participant_control_profiles import ParticipantControlTeachingProfileModel from .participant_control_selection import ParticipantControlSelectionModel from .participant_crossing import ParticipantCrossingOccurrenceModel @@ -60,11 +62,22 @@ def _participant_control_schema_bundle() -> dict[str, dict[str, Any]]: roots = { "participant-control-selection-v1": ParticipantControlSelectionModel, "participant-control-evaluation-v1": ParticipantControlEvaluationModel, + "participant-control-selection-v2": ParticipantControlSelectionV2Model, + "participant-control-evaluation-v2": ParticipantControlEvaluationV2Model, } schemas = {} for contract_id, model in roots.items(): schema = model.model_json_schema() _add_raes_plane(schema, contract_id) + validator = ( + "raes_contracts.contracts.validate_participant_control_resolved_context_v2" + if contract_id == "participant-control-evaluation-v2" + else "raes_contracts.contracts.validate_participant_control_resolved_context" + if "evaluation" in contract_id + else "raes_contracts.contracts.ParticipantControlSelectionV2Model.model_validate" + if contract_id.endswith("selection-v2") + else "raes_contracts.contracts.ParticipantControlSelectionModel.model_validate" + ) _add_raes_invariant( schema, "participant-control-resolved-context" @@ -72,9 +85,7 @@ def _participant_control_schema_bundle() -> dict[str, dict[str, Any]]: else "participant-control-selection-graph", "Validate closed selection dependencies and exact trusted evaluation context; " "structural validity is not execution authority.", - validator="raes_contracts.contracts.validate_participant_control_resolved_context" - if "evaluation" in contract_id - else "raes_contracts.contracts.ParticipantControlSelectionModel.model_validate", + validator=validator, inputs=[{"contract_id": contract_id, "instance_path": "#"}], ) schemas[contract_id] = schema diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py b/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py new file mode 100644 index 000000000..0d3586e9a --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py @@ -0,0 +1,219 @@ +"""Versioned API-424 apparatus and exact-cut applicability records. + +These records describe an admitted evaluation. They do not install a provider +or make an untrusted applicability proof authoritative. +""" + +from __future__ import annotations + +from graphlib import TopologicalSorter +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from .base import ContractModel, PrefixedDigestString +from .participant_control_composition import control_digest +from .participant_control_coordinates import ( + Artifacts, + ControlArtifactReferenceModel, + ControlRef, + ControlRefs, + Evidence, + ParticipantControlContextModel, + ResultKind, + require_kind, + require_unique, +) +from .participant_control_selection import ( + ControlMechanismBindingModel, + ControlResultSlotModel, + ParticipantControlSelectionModel, +) + + +class ControlStateScopeV2Model(ContractModel): + """Admitted sharing scope, distinct from participant memory and profile.""" + + model_config = ConfigDict(frozen=True) + scope_id: ControlRef + sharing: Literal["run", "participant", "episode"] + authority: ControlArtifactReferenceModel + configuration_digest: PrefixedDigestString + + @model_validator(mode="after") + def _authority(self) -> Self: + require_kind(self.authority, "authority") + return self + + +class ControlMechanismBindingV2Model(ControlMechanismBindingModel): + protocol_revision: Literal["participant-control-provider/v2"] + state_scope: ControlStateScopeV2Model + + @model_validator(mode="after") + def _scope_binding(self) -> Self: + if ( + self.state_scope.authority != self.authority + or self.state_scope.configuration_digest != self.configuration_digest + ): + raise ValueError("participant control state scope differs from its binding") + return self + + +class ControlProfileObligationV2Model(ContractModel): + """Profile-owned obligation declared before any provider result exists.""" + + model_config = ConfigDict(frozen=True) + obligation_id: ControlRef + profile: ControlArtifactReferenceModel + required_kind: ResultKind + required_strength: Literal["exact", "bounded", "disclosed_weak"] + constraints: Artifacts + + @model_validator(mode="after") + def _owner(self) -> Self: + require_kind(self.profile, "profile") + for constraint in self.constraints: + require_kind(constraint, "constraint") + if self.required_strength == "bounded" and not self.constraints: + raise ValueError("bounded obligation requires admitted constraints") + return self + + +class ControlResultSlotV2Model(ControlResultSlotModel): + """Admitted required-input support pin, independent of provider claims.""" + + required_strength: Literal["exact", "bounded", "disclosed_weak"] = "exact" + constraints: Artifacts = () + + @model_validator(mode="after") + def _support_pin(self) -> Self: + for item in self.constraints: + require_kind(item, "constraint") + if self.required_strength == "bounded" and not self.constraints: + raise ValueError("bounded slot support requires admitted constraints") + return self + + +class ParticipantControlSelectionV2Model(ParticipantControlSelectionModel): + """Complete B; no exact-cut filtering changes this identity.""" + + schema_version: Literal["participant-control-selection/v2"] + interpretation: Literal["participant-control-applicability/rev1"] + bindings: Annotated[tuple[ControlMechanismBindingV2Model, ...], Field(max_length=64)] + slots: Annotated[tuple[ControlResultSlotV2Model, ...], Field(max_length=256)] + obligations: Annotated[tuple[ControlProfileObligationV2Model, ...], Field(max_length=256)] + + @model_validator(mode="after") + def _obligation_owners(self) -> Self: + require_unique(tuple(item.obligation_id for item in self.obligations)) + selected = {profile for binding in self.bindings for profile in binding.profiles} + declared = {item.profile for item in self.obligations} + if not {profile for profile in selected if profile.ref in self.required_profiles} <= declared: + raise ValueError("required participant control profile has no obligations") + if any(item.profile not in selected for item in self.obligations): + raise ValueError("participant control obligation profile is not admitted") + return self + + +class ControlObligationCoverageV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + obligation_id: ControlRef + status: Literal["applies", "proven-inapplicable", "unresolved"] + slot_ids: ControlRefs + evidence: Evidence + + @model_validator(mode="after") + def _discharge(self) -> Self: + require_unique(self.slot_ids) + if (self.status == "applies") != bool(self.slot_ids): + raise ValueError("participant control coverage has no valid slot discharge") + return self + + +class ControlApplicabilityV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + selection_digest: PrefixedDigestString + context_digest: PrefixedDigestString + applicable_slot_ids: ControlRefs + required_slot_ids: ControlRefs + coverage: Annotated[tuple[ControlObligationCoverageV2Model, ...], Field(max_length=256)] + derivation_evidence: Evidence + + +class ParticipantControlRequestV2Model(ContractModel): + """Exact K plus A(B,K), with B and total profile coverage retained.""" + + model_config = ConfigDict(frozen=True) + selection: ParticipantControlSelectionV2Model + context: ParticipantControlContextModel + applicability: ControlApplicabilityV2Model + + @model_validator(mode="after") + def _exact_cut(self) -> Self: + selection, context, applicability = self.selection, self.context, self.applicability + if selection.apparatus != context.apparatus: + raise ValueError("participant control apparatus binding differs") + if applicability.selection_digest != control_digest( + selection + ) or applicability.context_digest != control_digest(context): + raise ValueError("participant control applicability has a different admitted identity or cut") + slots = {slot.slot_id: slot for slot in selection.slots} + bindings = {binding.instance_id: binding for binding in selection.bindings} + obligations = {item.obligation_id: item for item in selection.obligations} + coverage = {item.obligation_id: item for item in applicability.coverage} + require_unique(tuple(item.obligation_id for item in applicability.coverage)) + if coverage.keys() != obligations.keys(): + raise ValueError("participant control coverage is incomplete") + applicable = set(applicability.applicable_slot_ids) + required = set(applicability.required_slot_ids) + require_unique(applicability.applicable_slot_ids) + require_unique(applicability.required_slot_ids) + if applicable - slots.keys() or required - applicable: + raise ValueError("participant control applicable slots are not admitted") + roots: set[str] = set() + for item in applicability.coverage: + obligation = obligations[item.obligation_id] + if item.status == "applies": + for identity in item.slot_ids: + if identity not in applicable or slots[identity].kind != obligation.required_kind: + raise ValueError("participant control coverage does not map to an applicable typed slot") + if obligation.profile not in bindings[slots[identity].instance_id].profiles: + raise ValueError("participant control coverage slot does not select its owning profile") + roots.update(item.slot_ids) + roots.update( + slot.slot_id for slot in selection.slots if slot.role == "mandatory" and slot.slot_id in applicable + ) + graph = {slot.slot_id: {dep.slot_id for dep in slot.dependencies} for slot in selection.slots} + for identity in reversed(tuple(TopologicalSorter(graph).static_order())): + if identity in roots: + roots.update(graph[identity]) + if roots != required or any(not graph[identity] <= applicable for identity in applicable): + raise ValueError("participant control required input closure or applicable subset is incomplete") + matching_instances = {slots[identity].instance_id for identity in applicable} + if {state.instance_id for state in context.provider_states} != matching_instances: + raise ValueError("participant control provider-state coverage is incomplete") + for instance in matching_instances: + binding = bindings[instance] + if binding.memory_scope != context.memory_scope or not any( + (a.participant_address, a.episode_id, a.direction, a.sink_ref, a.phase_ref, a.subject_kind) + == ( + context.participant_address, + context.episode_id, + context.direction, + context.sink_ref, + context.phase_ref, + context.subject.subject_kind, + ) + for a in binding.applicability + ): + raise ValueError("participant control applicable binding does not match the exact crossing") + bounds = selection.bounds + if ( + context.depth > bounds.max_depth + or context.effects_consumed > bounds.max_effects + or context.attempt > bounds.max_attempts + or context.order > bounds.expires_at_order + ): + raise ValueError("participant control causal bounds are exhausted") + return self diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py new file mode 100644 index 000000000..348d33c0c --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py @@ -0,0 +1,250 @@ +"""Unscheduled parent decisions and executable effect plans for API-424/v2.""" + +from __future__ import annotations + +from graphlib import CycleError, TopologicalSorter +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from .base import ContractModel +from .participant_control_coordinates import ( + ControlArtifactReferenceModel, + ControlRef, + ControlRefs, + Evidence, + require_kind, + require_unique, +) +from .participant_control_effect_composition import _effect_subject, _same_subject_conflicts +from .participant_control_effects import ( + ControlAuditEffectModel, + ControlDelayEffectModel, + ControlEffectRequestModel, + ControlHandoffEffectModel, + ControlInjectEffectModel, + ControlLifecycleEffectModel, + ControlReviewEffectModel, + ControlTransformationEffectModel, +) +from .participant_control_results import ControlRealizationBindingModel + +ControlExecutableEffectTargetV2 = Annotated[ + ControlTransformationEffectModel + | ControlInjectEffectModel + | ControlDelayEffectModel + | ControlHandoffEffectModel + | ControlReviewEffectModel + | ControlLifecycleEffectModel + | ControlAuditEffectModel, + Field(discriminator="kind"), +] + + +class ControlParentDecisionV2Model(ContractModel): + """A decision about the exact parent, never a scheduled operation.""" + + model_config = ConfigDict(frozen=True) + decision_id: ControlRef + disposition: Literal["permit", "deny", "withhold"] + rule: ControlArtifactReferenceModel + reasons: Evidence + + @model_validator(mode="after") + def _rule(self) -> Self: + require_kind(self.rule, "rule") + return self + + +class ControlEffectRequestV2Model(ControlEffectRequestModel): + """An exact owned effect with declared parent outcome prerequisites.""" + + phase: Literal["required-predecessor", "success-dependent", "independent"] + target: ControlExecutableEffectTargetV2 + parent_outcome: Literal["none", "admitted", "applied"] + allowed_parent_dispositions: ControlRefs + originating_principal_ref: ControlRef + trigger: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _phase(self) -> Self: + require_kind(self.trigger, "trigger") + require_unique(self.allowed_parent_dispositions) + if any(item not in {"permit", "deny", "withhold"} for item in self.allowed_parent_dispositions): + raise ValueError("independent consequence has an unknown parent disposition") + if self.phase == "required-predecessor" and (self.parent_outcome != "none" or self.allowed_parent_dispositions): + raise ValueError("required predecessor cannot await its own parent outcome") + if self.phase == "success-dependent" and (self.parent_outcome == "none" or self.allowed_parent_dispositions): + raise ValueError("success-dependent consequence needs an exact parent outcome") + if self.phase == "independent" and (self.parent_outcome != "none" or not self.allowed_parent_dispositions): + raise ValueError("independent consequence needs allowed parent dispositions") + return self + + +def base_parent_disposition_v2( + decisions: tuple[ControlParentDecisionV2Model, ...], incumbent_gate_disposition: str +) -> str: + """The actual parent decision before required effects defer release.""" + + dispositions = {item.disposition for item in decisions} + if "deny" in dispositions or incumbent_gate_disposition == "deny": + return "deny" + if "withhold" in dispositions or incumbent_gate_disposition != "permit": + return "withhold" + return "permit" + + +def _decision_and_runnable( + decisions: tuple[ControlParentDecisionV2Model, ...], + effects: tuple[ControlEffectRequestV2Model, ...], + incumbent_gate_disposition: str, + realized_effect_ids: tuple[str, ...], + parent_applied: bool, + parent_admission_receipt: ControlArtifactReferenceModel | None, + parent_application_receipt: ControlArtifactReferenceModel | None, +) -> tuple[str, tuple[str, ...]]: + require_unique(tuple(item.decision_id for item in decisions)) + require_unique(tuple(item.effect_id for item in effects)) + require_unique(tuple(item.key for item in effects)) + require_unique(realized_effect_ids) + if parent_applied and parent_application_receipt is None: + raise ValueError("applied parent requires an exact owner receipt") + if parent_applied and parent_admission_receipt is None: + raise ValueError("applied parent requires its admission receipt") + if not parent_applied and parent_application_receipt is not None: + raise ValueError("parent application receipt and disposition differ") + for receipt in (parent_admission_receipt, parent_application_receipt): + if receipt is not None: + require_kind(receipt, "receipt") + by_id = {item.effect_id: item for item in effects} + if not set(realized_effect_ids) <= by_id.keys(): + raise ValueError("realized effect is absent from the plan") + if any( + not set(by_id[effect_id].predecessor_effect_ids) <= set(realized_effect_ids) + for effect_id in realized_effect_ids + ): + raise ValueError("realized effect lacks an applied predecessor") + graph: dict[str, set[str]] = {item.effect_id: set(item.predecessor_effect_ids) for item in effects} + graph["@parent-admitted"] = {item.effect_id for item in effects if item.phase == "required-predecessor"} + graph["@parent-applied"] = {"@parent-admitted"} + for item in effects: + if item.phase == "success-dependent": + graph[item.effect_id].add("@parent-" + item.parent_outcome) + if any(dependencies - graph.keys() for dependencies in graph.values()): + raise ValueError("effect predecessor is unresolved") + try: + tuple(TopologicalSorter(graph).static_order()) + except CycleError: + raise ValueError("combined parent/effect prerequisite cycle") from None + for index, left in enumerate(effects): + for right in effects[index + 1 :]: + if _effect_subject(left.target) != _effect_subject(right.target): + continue + if _same_subject_conflicts(left.target, right.target): + raise ValueError("effect target conflict cannot be repaired by ordering") + base = base_parent_disposition_v2(decisions, incumbent_gate_disposition) + pending = graph["@parent-admitted"] - set(realized_effect_ids) + final = "withhold" if base == "permit" and pending else base + runnable = [] + for item in effects: + if item.effect_id in realized_effect_ids or not set(item.predecessor_effect_ids) <= set(realized_effect_ids): + continue + if item.phase == "required-predecessor" and base == "permit": + runnable.append(item.effect_id) + elif item.phase == "success-dependent" and base == "permit" and not pending: + if (item.parent_outcome == "admitted" and parent_admission_receipt is not None) or ( + item.parent_outcome == "applied" and parent_applied + ): + runnable.append(item.effect_id) + elif item.phase == "independent" and final in item.allowed_parent_dispositions: + runnable.append(item.effect_id) + return final, tuple(sorted(runnable)) + + +class ControlEffectPlanV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + parent_crossing: ControlArtifactReferenceModel + decisions: Annotated[tuple[ControlParentDecisionV2Model, ...], Field(max_length=256)] + effects: Annotated[tuple[ControlEffectRequestV2Model, ...], Field(max_length=256)] + incumbent_gate_disposition: Literal["permit", "deny", "withhold", "unsupported", "stale", "failed"] + realized_effect_ids: ControlRefs + parent_applied: bool + parent_admission_receipt: ControlArtifactReferenceModel | None = None + parent_application_receipt: ControlArtifactReferenceModel | None = None + effect_outcomes: Annotated[tuple[ControlRealizationBindingModel, ...], Field(max_length=256)] = () + parent_disposition: Literal["permit", "deny", "withhold"] + runnable_effect_ids: ControlRefs + + @model_validator(mode="after") + def _derived(self) -> Self: + require_kind(self.parent_crossing, "crossing") + require_unique(tuple(item.effect_id for item in self.effect_outcomes)) + if {item.effect_id for item in self.effect_outcomes} - {item.effect_id for item in self.effects}: + raise ValueError("effect outcome names an absent request") + if {item.effect_id for item in self.effect_outcomes if item.disposition == "applied"} != set( + self.realized_effect_ids + ): + raise ValueError("realized effect lacks an exact applied owner receipt") + expected = _decision_and_runnable( + self.decisions, + self.effects, + self.incumbent_gate_disposition, + self.realized_effect_ids, + self.parent_applied, + self.parent_admission_receipt, + self.parent_application_receipt, + ) + if (self.parent_disposition, self.runnable_effect_ids) != expected: + raise ValueError("parent/effect plan differs from the derived decision") + return self + + +def derive_control_effect_plan_v2( + *, + parent_crossing: ControlArtifactReferenceModel | dict, + decisions: tuple[ControlParentDecisionV2Model, ...] | list[ControlParentDecisionV2Model | dict], + effects: tuple[ControlEffectRequestV2Model, ...] | list[ControlEffectRequestV2Model | dict], + incumbent_gate_disposition: str, + realized_effect_ids: tuple[str, ...], + parent_applied: bool, + parent_admission_receipt: ControlArtifactReferenceModel | dict | None = None, + parent_application_receipt: ControlArtifactReferenceModel | dict | None = None, + effect_outcomes: tuple[ControlRealizationBindingModel, ...] | list[ControlRealizationBindingModel | dict] = (), +) -> ControlEffectPlanV2Model: + """Derive the recorded plan without dispatching or claiming realization.""" + + decisions = tuple(ControlParentDecisionV2Model.model_validate(item) for item in decisions) + effects = tuple(ControlEffectRequestV2Model.model_validate(item) for item in effects) + parent_admission_receipt = ( + ControlArtifactReferenceModel.model_validate(parent_admission_receipt) + if parent_admission_receipt is not None + else None + ) + parent_application_receipt = ( + ControlArtifactReferenceModel.model_validate(parent_application_receipt) + if parent_application_receipt is not None + else None + ) + effect_outcomes = tuple(ControlRealizationBindingModel.model_validate(item) for item in effect_outcomes) + final, runnable = _decision_and_runnable( + decisions, + effects, + incumbent_gate_disposition, + realized_effect_ids, + parent_applied, + parent_admission_receipt, + parent_application_receipt, + ) + return ControlEffectPlanV2Model( + parent_crossing=parent_crossing, + decisions=decisions, + effects=effects, + incumbent_gate_disposition=incumbent_gate_disposition, + realized_effect_ids=realized_effect_ids, + parent_applied=parent_applied, + parent_admission_receipt=parent_admission_receipt, + parent_application_receipt=parent_application_receipt, + effect_outcomes=effect_outcomes, + parent_disposition=final, + runnable_effect_ids=runnable, + ) diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py new file mode 100644 index 000000000..32f8abd28 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py @@ -0,0 +1,457 @@ +"""Scoped state and expected-head evaluation commit boundaries for API-424/v2.""" + +from __future__ import annotations + +from collections import defaultdict +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from ..json_ingress import parse_bounded_json_object +from .base import ContractModel, PrefixedDigestString +from .participant_control_applicability import ( + ControlStateScopeV2Model, + ParticipantControlRequestV2Model, + control_digest, +) +from .participant_control_coordinates import ( + ControlArtifactReferenceModel, + ControlCount, + ControlRef, + ControlRefs, + Evidence, + ParticipantControlContextModel, + require_kind, + require_unique, +) +from .participant_control_decisions_v2 import ControlEffectPlanV2Model, base_parent_disposition_v2 +from .participant_control_effect_composition import ( + _effect_ancestors, + _lifecycle_invalidates, + _retained_claims, + _validate_effect_bounds, +) +from .participant_control_invocation import ( + ControlInvocationV2Model, + ControlLostAdviceV2Model, + ControlMechanismResultV2Model, + _satisfied, + validate_control_invocations_v2, +) +from .participant_control_selection import ControlCausalBoundsModel +from .participant_control_support_v2 import ControlSupportAssessmentV2Model + +DigestsV2 = Annotated[tuple[PrefixedDigestString, ...], Field(max_length=256)] + + +class ControlStateProposalV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + invocation_id: ControlRef + scope: ControlStateScopeV2Model + before: ControlArtifactReferenceModel + after: ControlArtifactReferenceModel + commit_on: Literal["evaluation", "parent-release"] + predecessor_invocation_id: ControlRef | None + + @model_validator(mode="after") + def _states(self) -> Self: + require_kind(self.before, "provider-state") + require_kind(self.after, "provider-state") + if self.before == self.after or self.invocation_id == self.predecessor_invocation_id: + raise ValueError("state proposal does not advance a distinct invocation") + return self + + +def validate_state_proposals_v2(proposals: tuple[ControlStateProposalV2Model, ...] | list[dict]) -> None: + """Reject unordered same-scope writers and forks; no store mutation.""" + + records = tuple(ControlStateProposalV2Model.model_validate(item) for item in proposals) + require_unique(tuple(item.invocation_id for item in records)) + by_scope: dict[ControlStateScopeV2Model, list[ControlStateProposalV2Model]] = defaultdict(list) + for item in records: + by_scope[item.scope].append(item) + for writers in by_scope.values(): + if len(writers) <= 1: + if writers and writers[0].predecessor_invocation_id is not None: + raise ValueError("state conflict: tentative predecessor is absent") + continue + roots = [item for item in writers if item.predecessor_invocation_id is None] + if len(roots) != 1: + raise ValueError("state conflict: same-scope writers need one serial root") + by_parent = { + item.predecessor_invocation_id: item for item in writers if item.predecessor_invocation_id is not None + } + if len(by_parent) != len(writers) - 1: + raise ValueError("state conflict: tentative state writer fork") + current, seen = roots[0], {roots[0].invocation_id} + while current.invocation_id in by_parent: + successor = by_parent[current.invocation_id] + if successor.invocation_id in seen or successor.before != current.after: + raise ValueError("state conflict: tentative state chain differs") + seen.add(successor.invocation_id) + current = successor + if len(seen) != len(writers): + raise ValueError("state conflict: same-scope writers are unordered") + + +def validate_effect_budget_v2( + context: ParticipantControlContextModel, + bounds: ControlCausalBoundsModel, + plan: ControlEffectPlanV2Model, +) -> None: + """Reuse retained claim and causal bounds authority for v2 effect intents.""" + + effects = set(plan.effects) + by_id = {effect.effect_id: effect for effect in effects} + prior_claims = {claim.effect_id: claim for claim in context.prior_effect_claims} + for effect_id in plan.realized_effect_ids: + effect = by_id[effect_id] + claim = prior_claims.get(effect_id) + if claim is None or (claim.key, claim.content_digest) != (effect.key, control_digest(effect)): + raise ValueError("realized effect lacks its exact retained committed intent claim") + for effect in effects: + if (effect.key.run_ref, effect.key.trigger_root) != (context.run.ref, context.trigger_root): + raise ValueError("effect causal root differs from its exact request") + ancestors = _effect_ancestors({effect.effect_id: {effect} for effect in effects}) + ordered = tuple(effects) + for index, left in enumerate(ordered): + for right in ordered[index + 1 :]: + if ( + _lifecycle_invalidates(left.target, right.target, context) + and right.effect_id not in ancestors[left.effect_id] + ) or ( + _lifecycle_invalidates(right.target, left.target, context) + and left.effect_id not in ancestors[right.effect_id] + ): + raise ValueError("effect lifecycle order invalidates a live target") + blockers: set[str] = set() + new_keys = _retained_claims(effects, context, blockers) + if blockers: + raise ValueError("retained effect claim conflicts with the amended request") + _validate_effect_bounds(effects, new_keys, context, bounds) + + +class ControlCommitBoundaryV2Model(ContractModel): + """One transaction envelope, not proof that an external effect occurred.""" + + model_config = ConfigDict(frozen=True) + expected_history_heads: Evidence + coverage_digest: PrefixedDigestString + result_digests: DigestsV2 + decision_digest: PrefixedDigestString + state_proposal_digests: DigestsV2 + effect_intent_digests: DigestsV2 + effects_consumed: ControlCount + status: Literal["prepared", "committed", "rejected"] + receipt: ControlArtifactReferenceModel | None + dispatchable_effect_ids: ControlRefs + + @model_validator(mode="after") + def _boundary(self) -> Self: + for head in self.expected_history_heads: + require_kind(head, "history") + for values in (self.result_digests, self.state_proposal_digests, self.effect_intent_digests): + require_unique(values) + require_unique(self.dispatchable_effect_ids) + if self.status != "committed" and self.dispatchable_effect_ids: + raise ValueError("failed or prepared commit cannot authorize dispatch") + if (self.status == "committed") != (self.receipt is not None): + raise ValueError("committed transaction requires its exact receipt") + if self.receipt is not None: + require_kind(self.receipt, "receipt") + return self + + +class ControlCompositionV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + rule_revision: Literal["participant-control-applicability/rev1"] + disposition: Literal["eligible", "deny", "withhold", "unsupported", "stale", "weakened", "failed", "conflict"] + blockers: ControlRefs + contributing_result_ids: ControlRefs + lost_advice_slot_ids: ControlRefs + + @model_validator(mode="after") + def _canonical(self) -> Self: + for values in (self.blockers, self.contributing_result_ids, self.lost_advice_slot_ids): + if values != tuple(sorted(set(values))): + raise ValueError("composition evidence must be canonical and unique") + return self + + +def _required_support_keys(request: ParticipantControlRequestV2Model) -> set[tuple[str, str]]: + slots = {slot.slot_id: slot for slot in request.selection.slots} + roots: set[str] = set() + required: set[tuple[str, str]] = set() + for item in request.applicability.coverage: + if item.status != "applies": + continue + for slot_id in item.slot_ids: + roots.add(slot_id) + required.add((item.obligation_id, slots[slot_id].instance_id)) + for slot_id in request.applicability.required_slot_ids: + if slot_id not in roots: + required.add(("input:" + slot_id, slots[slot_id].instance_id)) + return required + + +def derive_control_composition_v2( + request: ParticipantControlRequestV2Model, + results: tuple[ControlMechanismResultV2Model, ...] | list[ControlMechanismResultV2Model | dict], + support: tuple[ControlSupportAssessmentV2Model, ...] | list[ControlSupportAssessmentV2Model | dict], + lost_advice: tuple[ControlLostAdviceV2Model, ...] | list[ControlLostAdviceV2Model | dict], + effect_plan: ControlEffectPlanV2Model, +) -> ControlCompositionV2Model: + """One canonical summary of mandatory availability and parent decisions.""" + + results = tuple(ControlMechanismResultV2Model.model_validate(item) for item in results) + support = tuple(ControlSupportAssessmentV2Model.model_validate(item) for item in support) + lost_advice = tuple(ControlLostAdviceV2Model.model_validate(item) for item in lost_advice) + by_slot = {item.slot_id: item for item in results} + require_unique(tuple(item.slot_id for item in results)) + require_unique(tuple((item.obligation_id, item.instance_id) for item in support)) + require_unique(tuple(item.slot_id for item in lost_advice)) + if {(item.obligation_id, item.instance_id) for item in support} != _required_support_keys(request): + raise ValueError("participant control required support coverage is incomplete") + cut_digest = control_digest(request.context) + obligations = {item.obligation_id: item for item in request.selection.obligations} + for item in support: + if item.effective_support.context_digest != cut_digest: + raise ValueError("participant control support uses a different state cut") + obligation = obligations.get(item.obligation_id) + if obligation is not None and ( + item.required_strength != obligation.required_strength + or item.required_constraints != obligation.constraints + ): + raise ValueError("participant control support differs from the admitted obligation") + if item.obligation_id.startswith("input:"): + slot = next( + (slot for slot in request.selection.slots if "input:" + slot.slot_id == item.obligation_id), None + ) + if slot is None or (item.required_strength, item.required_constraints) != ( + slot.required_strength, + slot.constraints, + ): + raise ValueError("participant control support differs from the admitted input pin") + required_slots = set(request.applicability.required_slot_ids) + failed_optional = {item.slot_id for item in results if item.slot_id not in required_slots and not _satisfied(item)} + if {item.slot_id for item in lost_advice} != failed_optional: + raise ValueError("optional failure must have exactly one safe lost-advice record") + for item in lost_advice: + result = by_slot[item.slot_id] + if result.next_provider_state is not None or result.payload is not None: + raise ValueError("discarded optional contribution cannot carry state or effect payload") + blockers = set() + causes: set[str] = set() + for coverage in request.applicability.coverage: + if coverage.status == "unresolved": + blockers.add("coverage:" + coverage.obligation_id) + causes.add("withhold") + for slot_id in required_slots: + if not _satisfied(by_slot[slot_id]): + result = by_slot[slot_id] + if ( + result.status == "resolved" + and result.payload is not None + and result.payload.kind == "decision" + and result.payload.disposition in {"deny", "withhold"} + ): + # A complete refusal is the parent outcome, not missing mandatory work. + continue + blockers.add("slot:" + slot_id) + if result.status in {"stale", "unsupported", "weakened"}: + causes.add(result.status) + elif result.payload is not None and result.payload.kind == "decision": + causes.add(result.payload.disposition if result.payload.disposition != "abstain" else "failed") + else: + causes.add("failed") + for item in support: + if not item.satisfied: + blockers.add("support:" + item.obligation_id + ":" + item.instance_id) + observed = item.effective_support + if observed.status in {"stale", "unsupported", "weakened"}: + causes.add(observed.status) + elif observed.effective_level == "unsupported": + causes.add("unsupported") + elif observed.status == "resolved": + causes.add("weakened") + else: + causes.add("failed") + if effect_plan.parent_disposition != "permit": + blockers.add("parent:" + effect_plan.parent_disposition) + causes.add(effect_plan.parent_disposition) + disposition = next( + ( + item + for item in ("conflict", "stale", "unsupported", "weakened", "deny", "withhold", "failed") + if item in causes + ), + "eligible", + ) + return ControlCompositionV2Model( + rule_revision="participant-control-applicability/rev1", + disposition=disposition, + blockers=tuple(sorted(blockers)), + contributing_result_ids=tuple(sorted(item.result_id for item in results)), + lost_advice_slot_ids=tuple(sorted(failed_optional)), + ) + + +def admitted_control_effect_ids_v2( + composition: ControlCompositionV2Model, plan: ControlEffectPlanV2Model +) -> frozenset[str]: + """Join decision-local effect readiness to mandatory composition.""" + + if composition.disposition == "eligible" and plan.parent_disposition == "permit": + return frozenset(plan.runnable_effect_ids) + if set(composition.blockers) != {"parent:" + plan.parent_disposition}: + return frozenset() + base = base_parent_disposition_v2(plan.decisions, plan.incumbent_gate_disposition) + admitted: set[str] = set() + if plan.parent_disposition == "withhold" and base == "permit": + admitted.update( + item.effect_id + for item in plan.effects + if item.phase == "required-predecessor" and item.effect_id in plan.runnable_effect_ids + ) + if base in {"deny", "withhold"} and plan.parent_disposition == base: + admitted.update( + item.effect_id + for item in plan.effects + if item.phase == "independent" + and base in item.allowed_parent_dispositions + and item.effect_id in plan.runnable_effect_ids + ) + return frozenset(admitted) + + +class ParticipantControlEvaluationV2Model(ContractModel): + """Complete v2 contract record; eligibility is not dispatch authority.""" + + model_config = ConfigDict(frozen=True) + schema_version: Literal["participant-control-evaluation/v2"] + evaluation_id: ControlRef + request: ParticipantControlRequestV2Model + invocations: Annotated[tuple[ControlInvocationV2Model, ...], Field(max_length=256)] + results: Annotated[tuple[ControlMechanismResultV2Model, ...], Field(max_length=256)] + support: Annotated[tuple[ControlSupportAssessmentV2Model, ...], Field(max_length=256)] + lost_advice: Annotated[tuple[ControlLostAdviceV2Model, ...], Field(max_length=256)] + effect_plan: ControlEffectPlanV2Model + state_proposals: Annotated[tuple[ControlStateProposalV2Model, ...], Field(max_length=256)] + composition: ControlCompositionV2Model + commit: ControlCommitBoundaryV2Model + + @model_validator(mode="after") + def _complete(self) -> Self: + validate_control_invocations_v2(self.request, self.invocations, self.results) + validate_state_proposals_v2(self.state_proposals) + validate_effect_budget_v2(self.request.context, self.request.selection.bounds, self.effect_plan) + if self.effect_plan.parent_crossing != self.request.context.crossing: + raise ValueError("parent decision uses a different crossing") + decisions = { + item.result_id: item.payload + for item in self.results + if item.status == "resolved" + and item.payload is not None + and item.payload.kind == "decision" + and item.payload.disposition != "abstain" + and item.slot_id in self.request.applicability.required_slot_ids + } + if {item.decision_id for item in self.effect_plan.decisions} != decisions.keys(): + raise ValueError("parent decisions do not preserve the typed result set") + for item in self.effect_plan.decisions: + payload = decisions[item.decision_id] + if item.disposition != payload.disposition or item.rule != payload.rule: + raise ValueError("parent decision differs from its provider result") + requested_effects = [ + item.payload + for item in self.results + if item.status == "resolved" and item.payload is not None and item.payload.kind == "effect-request" + ] + if len({item.effect_id for item in requested_effects}) != len(requested_effects): + raise ValueError("duplicate provider effect identity") + requested = {item.effect_id: item for item in requested_effects} + if {item.effect_id: item for item in self.effect_plan.effects} != requested: + raise ValueError("effect plan does not preserve the requested effects") + proposed = {item.invocation_id: item for item in self.state_proposals} + calls = {item.invocation_id: item for item in self.invocations} + if proposed.keys() - calls.keys(): + raise ValueError("state proposal has no invocation") + for call in self.invocations: + if call.state_input.source != "tentative": + continue + predecessor = proposed.get(call.state_input.predecessor_invocation_id) + if ( + predecessor is None + or predecessor.scope != call.state_input.scope + or predecessor.after != call.state_input.state + ): + raise ValueError("consumed tentative state has no matching transaction proposal") + result_by_slot = {item.slot_id: item for item in self.results} + for item in self.state_proposals: + call = calls[item.invocation_id] + if ( + call.state_input.scope != item.scope + or call.state_input.state != item.before + or not any( + result_by_slot[slot_id].next_provider_state == item.after for slot_id in call.requested_slot_ids + ) + ): + raise ValueError("state proposal differs from its exact invocation and result") + if ( + self.commit.status == "committed" + and self.effect_plan.parent_disposition != "permit" + and any(item.commit_on == "parent-release" for item in self.state_proposals) + ): + raise ValueError("parent-release state cannot commit for a refused parent") + if self.composition != derive_control_composition_v2( + self.request, self.results, self.support, self.lost_advice, self.effect_plan + ): + raise ValueError("composition differs from the canonical result") + parent_release_allowed = ( + self.composition.disposition == "eligible" and self.effect_plan.parent_disposition == "permit" + ) + if not parent_release_allowed: + if ( + self.effect_plan.parent_applied + or self.effect_plan.parent_admission_receipt is not None + or self.effect_plan.parent_application_receipt is not None + ): + raise ValueError("mandatory composition cannot authorize parent release receipts") + if self.commit.status == "committed" and any( + item.commit_on == "parent-release" for item in self.state_proposals + ): + raise ValueError("mandatory composition cannot commit parent release state") + eligible_effects = admitted_control_effect_ids_v2(self.composition, self.effect_plan) + expected_commit = ( + self.request.context.expected_history_heads, + control_digest(self.request.applicability), + tuple(control_digest(item) for item in self.results), + control_digest(self.effect_plan), + tuple(control_digest(item) for item in self.state_proposals), + tuple(control_digest(item) for item in self.effect_plan.effects if item.effect_id in eligible_effects), + self.request.context.effects_consumed, + ) + observed_commit = ( + self.commit.expected_history_heads, + self.commit.coverage_digest, + self.commit.result_digests, + self.commit.decision_digest, + self.commit.state_proposal_digests, + self.commit.effect_intent_digests, + self.commit.effects_consumed, + ) + if observed_commit != expected_commit: + raise ValueError("atomic commit boundary differs from its complete evaluation") + if set(self.commit.dispatchable_effect_ids) - eligible_effects: + raise ValueError("mandatory composition cannot dispatch an effect outside the admitted outcome") + return self + + +def parse_participant_control_evaluation_v2(source: str | bytes) -> ParticipantControlEvaluationV2Model: + """Bound v2 ingress and hide rejected provider values from parse errors.""" + + try: + payload = parse_bounded_json_object(source, max_bytes=1_048_576, max_depth=32) + return ParticipantControlEvaluationV2Model.model_validate(payload) + except (TypeError, ValueError): + raise ValueError("invalid participant control evaluation") from None diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py b/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py new file mode 100644 index 000000000..a887a940a --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py @@ -0,0 +1,256 @@ +"""Typed provider/v2 invocation inputs and result binding for API-424.""" + +from __future__ import annotations + +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from .base import ContractModel, PrefixedDigestString +from .participant_control_applicability import ( + ControlStateScopeV2Model, + ParticipantControlRequestV2Model, + control_digest, +) +from .participant_control_coordinates import ( + Artifacts, + ControlArtifactReferenceModel, + ControlRef, + Evidence, + ResultKind, + require_kind, + require_unique, +) +from .participant_control_decisions_v2 import ControlEffectRequestV2Model +from .participant_control_results import ( + ControlAdvisoryModel, + ControlDecisionModel, + ControlIFCFact, + ControlMechanismResultModel, +) + +ControlResultPayloadV2 = Annotated[ + ControlIFCFact | ControlDecisionModel | ControlAdvisoryModel | ControlEffectRequestV2Model, + Field(discriminator="kind"), +] + + +class ControlPredecessorInputV2Model(ContractModel): + """Content-bound reference to one detached, typed predecessor result.""" + + model_config = ConfigDict(frozen=True) + slot_id: ControlRef + kind: ResultKind + result_id: ControlRef + result_digest: PrefixedDigestString + + +class ControlStateInputV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + scope: ControlStateScopeV2Model + state: ControlArtifactReferenceModel + source: Literal["committed", "tentative"] + predecessor_invocation_id: ControlRef | None + + @model_validator(mode="after") + def _source(self) -> Self: + require_kind(self.state, "provider-state") + if (self.source == "tentative") != (self.predecessor_invocation_id is not None): + raise ValueError("tentative state requires its exact predecessor invocation") + return self + + +class ControlInvocationV2Model(ContractModel): + """One slot/stage call with immutable, disclosure-authorized inputs.""" + + model_config = ConfigDict(frozen=True) + invocation_id: ControlRef + instance_id: ControlRef + requested_slot_ids: Annotated[tuple[ControlRef, ...], Field(min_length=1, max_length=256)] + binding_digest: PrefixedDigestString + context_digest: PrefixedDigestString + predecessor_inputs: Annotated[tuple[ControlPredecessorInputV2Model, ...], Field(max_length=256)] + state_input: ControlStateInputV2Model + projection: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _identities(self) -> Self: + require_unique(self.requested_slot_ids) + require_unique(tuple(item.slot_id for item in self.predecessor_inputs)) + require_kind(self.projection, "projection") + return self + + +class ControlMechanismResultV2Model(ControlMechanismResultModel): + """A typed output bound to the complete invocation input identity.""" + + invocation_digest: PrefixedDigestString + payload: ControlResultPayloadV2 | None + rule_outcome: Literal["not-triggered"] | None = None + evaluated_basis: Artifacts = () + + @model_validator(mode="after") + def _resolved_payload(self) -> Self: + if self.rule_outcome == "not-triggered": + if self.status != "resolved" or self.payload is not None or not self.evaluated_basis: + raise ValueError("not-triggered rule must be resolved with an evaluated basis and no effect") + elif (self.status == "resolved") != (self.payload is not None): + raise ValueError("only a resolved mechanism result carries a typed payload") + if self.evaluated_basis and self.rule_outcome != "not-triggered": + raise ValueError("evaluated false-trigger basis needs the not-triggered outcome") + if self.next_provider_state is not None: + require_kind(self.next_provider_state, "provider-state") + if self.status != "resolved": + raise ValueError("unresolved results cannot propose provider state") + return self + + +class ControlLostAdviceV2Model(ContractModel): + """Safe, bounded evidence for a discarded optional contribution.""" + + model_config = ConfigDict(frozen=True) + slot_id: ControlRef + reason: Literal["malformed", "missing", "unknown", "stale", "failed", "weakened", "unsupported", "conflict"] + evidence: Evidence + + +def normalize_optional_result_failure_v2( + request: ParticipantControlRequestV2Model, + invocation: ControlInvocationV2Model, + raw_result: object, + *, + result_id: str, + evidence: ControlArtifactReferenceModel | dict, +) -> tuple[ControlMechanismResultV2Model, ControlLostAdviceV2Model]: + """Discard one rejected optional output without exposing provider text. + + The caller must already have isolated the invocation. This function never + catches an evaluation-wide failure or converts a required input to advice. + """ + + if len(invocation.requested_slot_ids) != 1: + raise ValueError("optional failure normalization requires one slot") + slot_id = invocation.requested_slot_ids[0] + if slot_id not in request.applicability.applicable_slot_ids: + raise ValueError("optional failure slot is not applicable") + if slot_id in request.applicability.required_slot_ids: + raise ValueError("mandatory participant control result cannot be normalized as optional") + try: + ControlMechanismResultV2Model.model_validate(raw_result) + except (TypeError, ValueError): + pass + else: + raise ValueError("optional result is already structurally valid") + evidence = ControlArtifactReferenceModel.model_validate(evidence) + require_kind(evidence, "evidence") + result = ControlMechanismResultV2Model( + result_id=result_id, + slot_id=slot_id, + instance_id=invocation.instance_id, + binding_digest=invocation.binding_digest, + context_digest=invocation.context_digest, + invocation_digest=control_digest(invocation), + status="failed", + payload=None, + evidence=(evidence,), + next_provider_state=None, + ) + loss = ControlLostAdviceV2Model(slot_id=slot_id, reason="malformed", evidence=(evidence,)) + return result, loss + + +def _satisfied(result: ControlMechanismResultV2Model) -> bool: + if result.status != "resolved": + return False + payload = result.payload + return result.rule_outcome == "not-triggered" or ( + payload is not None and (payload.kind != "decision" or payload.disposition == "permit") + ) + + +def validate_control_invocations_v2( + request: ParticipantControlRequestV2Model, + invocations: tuple[ControlInvocationV2Model, ...] | list[ControlInvocationV2Model | dict], + results: tuple[ControlMechanismResultV2Model, ...] | list[ControlMechanismResultV2Model | dict], +) -> None: + """Check the exact slot DAG and predecessor/state input bindings. + + This local check cannot attest that a provider actually consumed its input; + installation and trusted disclosure resolution remain separate gates. + """ + + calls = tuple(ControlInvocationV2Model.model_validate(item) for item in invocations) + outputs = tuple(ControlMechanismResultV2Model.model_validate(item) for item in results) + require_unique(tuple(call.invocation_id for call in calls)) + require_unique(tuple(result.result_id for result in outputs)) + require_unique(tuple(result.slot_id for result in outputs)) + slots = {slot.slot_id: slot for slot in request.selection.slots} + bindings = {binding.instance_id: binding for binding in request.selection.bindings} + by_slot: dict[str, ControlInvocationV2Model] = {} + for call in calls: + for slot_id in call.requested_slot_ids: + if slot_id in by_slot: + raise ValueError("participant control slot has duplicate invocations") + by_slot[slot_id] = call + expected = set(request.applicability.applicable_slot_ids) + if by_slot.keys() != expected or {item.slot_id for item in outputs} != expected: + raise ValueError("participant control invocation or result coverage is incomplete") + result_by_slot = {result.slot_id: result for result in outputs} + states = {state.instance_id: state.state for state in request.context.provider_states} + cut_digest = control_digest(request.context) + for call in calls: + binding = bindings.get(call.instance_id) + if binding is None or call.binding_digest != control_digest(binding) or call.context_digest != cut_digest: + raise ValueError("participant control invocation differs from the admitted binding or cut") + if call.state_input.scope != binding.state_scope: + raise ValueError("participant control invocation state scope differs") + if call.state_input.source == "committed": + if call.state_input.state != states.get(call.instance_id): + raise ValueError("participant control invocation state version differs") + else: + predecessor = next( + (item for item in calls if item.invocation_id == call.state_input.predecessor_invocation_id), None + ) + if predecessor is None or predecessor.instance_id != call.instance_id: + raise ValueError("participant control tentative state predecessor is absent") + proposals = { + result.next_provider_state + for result in outputs + if result.slot_id in predecessor.requested_slot_ids and result.next_provider_state is not None + } + if proposals != {call.state_input.state}: + raise ValueError("participant control tentative state does not bind a predecessor proposal") + declared = {dep.slot_id: dep.kind for slot_id in call.requested_slot_ids for dep in slots[slot_id].dependencies} + if declared.keys() & set(call.requested_slot_ids): + raise ValueError("participant control predecessor cannot be produced in the same invocation") + supplied = {item.slot_id: item for item in call.predecessor_inputs} + if supplied.keys() != declared.keys(): + raise ValueError("participant control predecessor input coverage is incomplete") + for slot_id, item in supplied.items(): + predecessor = result_by_slot.get(slot_id) + if ( + predecessor is None + or item.kind != declared[slot_id] + or predecessor.result_id != item.result_id + or control_digest(predecessor) != item.result_digest + ): + raise ValueError("participant control predecessor result identity or type differs") + if not _satisfied(predecessor) and any( + result_by_slot[slot_id].status == "resolved" for slot_id in call.requested_slot_ids + ): + raise ValueError("participant control unsatisfied predecessor cannot feed a resolved result") + for slot_id in call.requested_slot_ids: + slot = slots[slot_id] + result = result_by_slot[slot_id] + if ( + slot.instance_id != call.instance_id + or result.instance_id != call.instance_id + or result.binding_digest != call.binding_digest + or result.context_digest != call.context_digest + or result.invocation_digest != control_digest(call) + ): + raise ValueError("participant control result differs from its exact invocation") + if result.rule_outcome == "not-triggered" and slot.kind != "effect-request": + raise ValueError("not-triggered result requires a rule/effect slot") + if result.payload is not None and result.payload.kind != slot.kind: + raise ValueError("participant control result does not match its typed slot") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py b/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py index f5d1728d1..c5bc1ee6f 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py @@ -17,13 +17,26 @@ ParticipantControlOccurrenceModel, ParticipantControlTargetContextModel, ) +from .participant_control_applicability import ( + ControlMechanismBindingV2Model, + ControlObligationCoverageV2Model, + ParticipantControlRequestV2Model, +) from .participant_control_composition import ( ParticipantControlEvaluationModel, ParticipantControlRequestModel, + _validate_ifc_result, control_digest, ) from .participant_control_coordinates import ControlArtifactReferenceModel, ParticipantControlContextModel +from .participant_control_decisions_v2 import ControlEffectRequestV2Model from .participant_control_effects import ControlEffectRequestModel, ControlInjectEffectModel +from .participant_control_evaluation_v2 import ( + ControlStateProposalV2Model, + ParticipantControlEvaluationV2Model, + admitted_control_effect_ids_v2, +) +from .participant_control_invocation import ControlMechanismResultV2Model from .participant_control_results import ( ControlEffectiveSupportModel, ControlMechanismResultModel, @@ -31,6 +44,7 @@ ControlSecurityFactModel, ) from .participant_control_selection import ControlMechanismBindingModel +from .participant_control_support_v2 import ControlSupportAssessmentV2Model from .participant_control_validation import validate_participant_control_occurrence_context from .participant_crossing import ( ParticipantCrossingOccurrenceModel, @@ -290,3 +304,129 @@ def validate_participant_control_resolved_context( validate_participant_control_context(record, context) except Exception: raise ValueError("participant control trusted-context validation failed") from None + + +@dataclass(frozen=True) +class ControlCommittedEffectIntentV2: + """Owner-resolved prior transaction and realization for one retained effect.""" + + evaluation: ParticipantControlEvaluationV2Model + outcome: ControlRealizationBindingModel + + +@dataclass(frozen=True) +class ParticipantControlValidationContextV2: + """Operator-resolved v2 authorities; never supplied by a provider result.""" + + admitted_request: ParticipantControlRequestV2Model + admitted_crossing: ControlArtifactReferenceModel + installed_bindings: Mapping[str, ControlMechanismBindingV2Model] + resolved_coverage: Mapping[str, ControlObligationCoverageV2Model] + resolved_results: Mapping[str, ControlMechanismResultV2Model] + resolved_support: Mapping[tuple[str, str], ControlSupportAssessmentV2Model] + authorized_effects: Mapping[str, ParticipantControlContextModel] + safe_references: frozenset[ControlArtifactReferenceModel] + support_resolver: Callable[[ControlSupportAssessmentV2Model], bool] + incumbent_gate_disposition: str + crossing_records: tuple[ParticipantCrossingOccurrenceModel, ...] + crossing_subjects: tuple[ParticipantCrossingSubjectReferenceModel, ...] + crossing_policies: tuple[ParticipantCrossingPolicyReferenceModel, ...] + parent_admission_receipt: ControlArtifactReferenceModel | None = None + parent_application_receipt: ControlArtifactReferenceModel | None = None + realization_receipts: Mapping[str, ControlRealizationBindingModel] = field(default_factory=dict) + committed_effect_intents: Mapping[str, ControlCommittedEffectIntentV2] = field(default_factory=dict) + authorized_state_proposals: Mapping[str, ControlStateProposalV2Model] = field(default_factory=dict) + commit_receipt: ControlArtifactReferenceModel | None = None + control_records: tuple[ParticipantControlOccurrenceModel, ...] = () + control_declarations: tuple[ParticipantControlDeclarationModel, ...] = () + control_targets: tuple[ParticipantControlTargetContextModel, ...] = () + flow_relations: Mapping[str, ParticipantFlowControlRelationModel] = field(default_factory=dict) + flow_contexts: Mapping[str, ParticipantFlowControlValidationContext] = field(default_factory=dict) + inject_deliveries: Mapping[str, ParticipantInjectDelivery] = field(default_factory=dict) + + +ParticipantControlContextResolverV2 = Callable[ + [ParticipantControlEvaluationV2Model], ParticipantControlValidationContextV2 | None +] + + +def validate_participant_control_resolved_context_v2( + record: ParticipantControlEvaluationV2Model, + resolver: ParticipantControlContextResolverV2 | None, +) -> None: + """Check v2 wire claims against independent exact-cut owner resolutions.""" + + try: + record = ParticipantControlEvaluationV2Model.model_validate(record.model_dump(mode="python")) + context = resolver(record) if resolver is not None else None + if not isinstance(context, ParticipantControlValidationContextV2): + raise ValueError("missing trusted context") + if record.request != context.admitted_request or record.request.context.crossing != context.admitted_crossing: + raise ValueError("admitted request or crossing differs") + if not set(control_references(record)) <= context.safe_references: + raise ValueError("unsafe or unresolved portable reference") + _validate_incumbent_records(context) + _validate_crossing(record.request.context, context) + if {item.instance_id: item for item in record.request.selection.bindings} != context.installed_bindings: + raise ValueError("installed apparatus differs") + if {item.obligation_id: item for item in record.request.applicability.coverage} != context.resolved_coverage: + raise ValueError("profile-owned applicability differs") + if {item.slot_id: item for item in record.results} != context.resolved_results: + raise ValueError("provider results differ") + bindings = {item.instance_id: item for item in record.request.selection.bindings} + for result in record.results: + _validate_ifc_result(result, bindings[result.instance_id], record.request.context) + if isinstance(result.payload, ControlSecurityFactModel): + _validate_security_fact(result.payload, record.request.context, context) + if {(item.obligation_id, item.instance_id): item for item in record.support} != context.resolved_support: + raise ValueError("effective support differs") + for item in record.support: + if item.satisfied and not context.support_resolver(item): + raise ValueError("support relation is unsubstantiated") + if record.effect_plan.incumbent_gate_disposition != context.incumbent_gate_disposition: + raise ValueError("incumbent gate resolution differs") + effects = {item.effect_id: item for item in record.effect_plan.effects} + if {item.invocation_id: item for item in record.state_proposals} != context.authorized_state_proposals: + raise ValueError("state write and commit timing differ from owner authorization") + current_outcomes = {item.effect_id: item for item in record.effect_plan.effect_outcomes} + for effect_id in record.effect_plan.realized_effect_ids: + prior = context.committed_effect_intents.get(effect_id) + if not isinstance(prior, ControlCommittedEffectIntentV2): + raise ValueError("realized effect lacks an owner-resolved committed intent") + origin = ParticipantControlEvaluationV2Model.model_validate(prior.evaluation.model_dump(mode="python")) + origin_effects = {item.effect_id: item for item in origin.effect_plan.effects} + effect: ControlEffectRequestV2Model = effects[effect_id] + if ( + origin_effects.get(effect_id) != effect + or origin.request.context.run != record.request.context.run + or origin.request.context.trigger_root != record.request.context.trigger_root + or origin.request.context.crossing != record.request.context.crossing + or origin.request.context.order > record.request.context.order + or origin.request.context.effects_consumed >= record.request.context.effects_consumed + or origin.commit.status != "committed" + or origin.commit.receipt is None + or effect_id not in origin.commit.dispatchable_effect_ids + or effect_id not in origin.effect_plan.runnable_effect_ids + or effect_id not in admitted_control_effect_ids_v2(origin.composition, origin.effect_plan) + or context.authorized_effects.get(control_digest(effect)) != origin.request.context + or prior.outcome != current_outcomes.get(effect_id) + or prior.outcome.disposition != "applied" + ): + raise ValueError("realized effect differs from its committed full intent, cut, or prerequisites") + if isinstance(effect.target, ControlInjectEffectModel): + _validate_inject(effect.target, origin.request.context, context) + for effect_id in admitted_control_effect_ids_v2(record.composition, record.effect_plan): + if context.authorized_effects.get(control_digest(effects[effect_id])) != record.request.context: + raise ValueError("effect authority is unresolved for its full payload and cut") + if isinstance(effects[effect_id].target, ControlInjectEffectModel): + _validate_inject(effects[effect_id].target, record.request.context, context) + if ( + record.effect_plan.parent_admission_receipt != context.parent_admission_receipt + or record.effect_plan.parent_application_receipt != context.parent_application_receipt + or record.commit.receipt != context.commit_receipt + ): + raise ValueError("parent or transaction receipt differs from owner resolution") + if {item.effect_id: item for item in record.effect_plan.effect_outcomes} != context.realization_receipts: + raise ValueError("effect outcome differs from owner resolution") + except Exception: + raise ValueError("participant control trusted-context validation failed") from None diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py new file mode 100644 index 000000000..f6812ca9b --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py @@ -0,0 +1,54 @@ +"""Admitted support requirements and exact-cut API-407 assessment bindings.""" + +from __future__ import annotations + +from typing import Literal, Self + +from pydantic import ConfigDict, model_validator + +from .base import ContractModel +from .participant_control_coordinates import Artifacts, ControlRef, Evidence, require_kind +from .participant_control_results import ControlEffectiveSupportModel + + +class ControlSupportAssessmentV2Model(ContractModel): + """A recorded requirement-relative relation, subject to trusted resolution.""" + + model_config = ConfigDict(frozen=True) + obligation_id: ControlRef + instance_id: ControlRef + required_strength: Literal["exact", "bounded", "disclosed_weak"] + required_constraints: Artifacts + effective_support: ControlEffectiveSupportModel + relation: Literal["exact", "bounded-compatible", "disclosed-weak-compatible", "insufficient", "unresolved"] + evidence: Evidence + + @model_validator(mode="after") + def _relation(self) -> Self: + for item in self.required_constraints: + require_kind(item, "constraint") + if self.required_strength == "bounded" and not self.required_constraints: + raise ValueError("bounded support requirement needs admitted constraints") + observed = self.effective_support + if observed.instance_id != self.instance_id: + raise ValueError("support assessment has a different instance") + if self.relation in {"exact", "bounded-compatible", "disclosed-weak-compatible"}: + if observed.status != "resolved" or observed.installation is None: + raise ValueError("positive support relation needs resolved installed support") + if self.relation == "exact" and observed.effective_level != "exact": + raise ValueError("exact support relation needs exact effective support") + if self.relation == "bounded-compatible" and ( + self.required_strength == "exact" or observed.effective_level != "bounded" + ): + raise ValueError("bounded support does not satisfy exact or mismatched strength") + if self.relation == "disclosed-weak-compatible" and ( + self.required_strength != "disclosed_weak" or observed.effective_level != "disclosed_weak" + ): + raise ValueError("disclosed weak support relation is incompatible") + if self.required_strength == "exact" and self.relation not in {"exact", "insufficient", "unresolved"}: + raise ValueError("exact requirement cannot accept weaker support") + return self + + @property + def satisfied(self) -> bool: + return self.relation in {"exact", "bounded-compatible", "disclosed-weak-compatible"} diff --git a/implementations/python/packages/raes_contracts/manifest_authority.py b/implementations/python/packages/raes_contracts/manifest_authority.py index 32c53a528..680b83db3 100644 --- a/implementations/python/packages/raes_contracts/manifest_authority.py +++ b/implementations/python/packages/raes_contracts/manifest_authority.py @@ -72,6 +72,8 @@ "participant-control-occurrence-v1", "participant-control-selection-v1", "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "participant-crossing-occurrence-v1", "participant-flow-control-relation-v1", "participant-lifecycle-event-v1", diff --git a/implementations/python/tests/test_issue_1365_applicable_contracts.py b/implementations/python/tests/test_issue_1365_applicable_contracts.py new file mode 100644 index 000000000..2b9f5d327 --- /dev/null +++ b/implementations/python/tests/test_issue_1365_applicable_contracts.py @@ -0,0 +1,1776 @@ +"""API-424 applicability amendment contract witnesses; no runtime adoption claim.""" + +from copy import deepcopy + +import pytest +from participant_control_contract_fixtures import ( + context_payload, + effect_payload, + evaluation_payload, + ref, + selection_payload, +) + + +def _selection_v2(): + source = selection_payload() + source["schema_version"] = "participant-control-selection/v2" + source["interpretation"] = "participant-control-applicability/rev1" + source["bindings"][0]["protocol_revision"] = "participant-control-provider/v2" + source["bindings"][0]["state_scope"] = { + "scope_id": "state-influence", + "sharing": "participant", + "authority": source["bindings"][0]["authority"], + "configuration_digest": source["bindings"][0]["configuration_digest"], + } + source["slots"] = source["slots"][:1] + other = deepcopy(source["bindings"][0]) + other["instance_id"] = "other" + other["applicability"][0]["sink_ref"] = "other-sink" + other["state_scope"]["scope_id"] = "state-other" + source["bindings"].append(other) + source["slots"].append({**source["slots"][0], "slot_id": "other-fact", "instance_id": "other"}) + source["obligations"] = [ + { + "obligation_id": "teaching-here", + "profile": source["bindings"][0]["profiles"][0], + "required_kind": "ifc-fact", + "required_strength": "exact", + "constraints": [], + }, + { + "obligation_id": "teaching-there", + "profile": source["bindings"][0]["profiles"][0], + "required_kind": "ifc-fact", + "required_strength": "exact", + "constraints": [], + }, + ] + return source + + +def _request_v2(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlSelectionV2Model, + control_digest, + ) + + selection = ParticipantControlSelectionV2Model.model_validate(_selection_v2()) + context = context_payload() + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + + cut = ParticipantControlContextModel.model_validate(context) + request = { + "selection": selection.model_dump(mode="json"), + "context": context, + "applicability": { + "selection_digest": control_digest(selection), + "context_digest": control_digest(cut), + "applicable_slot_ids": ["fact"], + "required_slot_ids": ["fact"], + "coverage": [ + { + "obligation_id": "teaching-here", + "status": "applies", + "slot_ids": ["fact"], + "evidence": [ref("coverage-here")], + }, + { + "obligation_id": "teaching-there", + "status": "proven-inapplicable", + "slot_ids": [], + "evidence": [ref("coverage-there")], + }, + ], + "derivation_evidence": [ref("subset-derivation")], + }, + } + return request + + +def test_disjoint_sinks_retain_full_apparatus_and_coverage(): + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + + request = ParticipantControlRequestV2Model.model_validate(_request_v2()) + assert len(request.selection.bindings) == 2 + assert request.applicability.applicable_slot_ids == ("fact",) + + +def test_missing_required_obligation_cannot_become_empty_success(): + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + + request = _request_v2() + request["applicability"]["coverage"].pop() + with pytest.raises(ValueError, match="coverage"): + ParticipantControlRequestV2Model.model_validate(request) + + +def test_applicable_obligation_requires_admitted_slot(): + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + + request = _request_v2() + request["applicability"]["coverage"][0]["slot_ids"] = [] + with pytest.raises(ValueError, match="coverage"): + ParticipantControlRequestV2Model.model_validate(request) + + +def test_coverage_slot_must_belong_to_its_obligation_profile(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + from raes_contracts.contracts.participant_flow_control import PARTICIPANT_BOUNDARY_FLOW_POLICY_PROFILE_REV1_DIGEST + + payload = _request_v2() + other_profile = ref("participant-boundary-flow-policy-v1", "profile") + other_profile["digest"] = PARTICIPANT_BOUNDARY_FLOW_POLICY_PROFILE_REV1_DIGEST + payload["selection"]["bindings"][1]["profiles"] = [other_profile] + payload["selection"]["bindings"][1]["applicability"][0]["sink_ref"] = "teaching-observation" + payload["context"]["provider_states"].append( + {"instance_id": "other", "state": ref("state-other", "provider-state")} + ) + payload["applicability"]["applicable_slot_ids"] = ["fact", "other-fact"] + payload["applicability"]["required_slot_ids"] = ["fact", "other-fact"] + payload["applicability"]["coverage"][1].update(status="applies", slot_ids=["other-fact"]) + payload["applicability"]["selection_digest"] = control_digest( + ParticipantControlSelectionV2Model.model_validate(payload["selection"]) + ) + payload["applicability"]["context_digest"] = control_digest( + ParticipantControlContextModel.model_validate(payload["context"]) + ) + with pytest.raises(ValueError, match="profile"): + ParticipantControlRequestV2Model.model_validate(payload) + + +def test_total_proven_inapplicability_can_have_empty_applicable_subset(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + + request = _request_v2() + request["context"]["provider_states"] = [] + request["applicability"]["context_digest"] = control_digest( + ParticipantControlContextModel.model_validate(request["context"]) + ) + request["applicability"]["applicable_slot_ids"] = [] + request["applicability"]["required_slot_ids"] = [] + for coverage in request["applicability"]["coverage"]: + coverage["status"] = "proven-inapplicable" + coverage["slot_ids"] = [] + assert ParticipantControlRequestV2Model.model_validate(request).applicability.applicable_slot_ids == () + + +def _dependency_chain(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + + request = _request_v2() + selection = request["selection"] + selection["bindings"][1]["applicability"][0]["sink_ref"] = "teaching-observation" + selection["slots"] = [ + selection["slots"][0], + { + "slot_id": "assessment", + "instance_id": "other", + "kind": "advisory", + "role": "advisory", + "dependencies": [{"slot_id": "fact", "kind": "ifc-fact"}], + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "decision", + "role": "mandatory", + "dependencies": [{"slot_id": "assessment", "kind": "advisory"}], + }, + ] + selection["obligations"][1]["required_kind"] = "decision" + request["context"]["provider_states"].append( + {"instance_id": "other", "state": ref("state-other", "provider-state")} + ) + request["applicability"]["coverage"][1] = { + "obligation_id": "teaching-there", + "status": "applies", + "slot_ids": ["rule"], + "evidence": [ref("coverage-there")], + } + request["applicability"]["applicable_slot_ids"] = ["fact", "assessment", "rule"] + request["applicability"]["required_slot_ids"] = ["fact", "assessment", "rule"] + selected = ParticipantControlSelectionV2Model.model_validate(selection) + request["applicability"]["selection_digest"] = control_digest(selected) + request["applicability"]["context_digest"] = control_digest( + ParticipantControlContextModel.model_validate(request["context"]) + ) + return request + + +def _chain_records(request): + from raes_contracts.contracts.participant_control_applicability import control_digest + + selection = request.selection + context = request.context + binding = {item.instance_id: item for item in selection.bindings} + cut_digest = control_digest(context) + facts = { + "fact": { + "kind": "ifc-fact", + "domain": "teaching-influence-domain/rev1", + "tokens": ["coached-hint"], + "source_refs": [ref("observation-1", "input")], + }, + "assessment": { + "kind": "advisory", + "assessment": "negative", + "score": 0.2, + "sample": ref("assessment-sample"), + }, + "rule": {"kind": "decision", "disposition": "permit", "rule": ref("decision-rule", "rule")}, + } + instances = {"fact": "influence", "assessment": "other", "rule": "influence"} + kinds = {"fact": "ifc-fact", "assessment": "advisory", "rule": "decision"} + invocations, results = [], [] + for slot_id, predecessor in (("fact", None), ("assessment", "fact"), ("rule", "assessment")): + instance = instances[slot_id] + inputs = [] + if predecessor: + inputs.append( + { + "slot_id": predecessor, + "kind": kinds[predecessor], + "result_id": "result-" + predecessor, + "result_digest": control_digest(results[-1]), + } + ) + invocation = { + "invocation_id": "invoke-" + slot_id, + "instance_id": instance, + "requested_slot_ids": [slot_id], + "binding_digest": control_digest(binding[instance]), + "context_digest": cut_digest, + "predecessor_inputs": inputs, + "state_input": { + "scope": binding[instance].state_scope.model_dump(mode="json"), + "state": next( + s.state.model_dump(mode="json") for s in context.provider_states if s.instance_id == instance + ), + "source": "committed", + "predecessor_invocation_id": None, + }, + "projection": ref("input-projection", "projection"), + } + invocations.append(invocation) + from raes_contracts.contracts.participant_control_invocation import ControlInvocationV2Model + + result = { + "result_id": "result-" + slot_id, + "slot_id": slot_id, + "instance_id": instance, + "binding_digest": invocation["binding_digest"], + "context_digest": cut_digest, + "invocation_digest": control_digest(ControlInvocationV2Model.model_validate(invocation)), + "status": "resolved", + "payload": facts[slot_id], + "evidence": [ref("result-evidence")], + "next_provider_state": None, + } + from raes_contracts.contracts.participant_control_invocation import ControlMechanismResultV2Model + + results.append(ControlMechanismResultV2Model.model_validate(result)) + return invocations, results + + +def test_a_to_b_to_a_uses_exact_predecessor_results_and_pinned_state(): + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + from raes_contracts.contracts.participant_control_invocation import validate_control_invocations_v2 + + request = ParticipantControlRequestV2Model.model_validate(_dependency_chain()) + invocations, results = _chain_records(request) + validate_control_invocations_v2(request, invocations, results) + broken = deepcopy(invocations) + broken[2]["predecessor_inputs"][0]["result_digest"] = "sha256:" + "b" * 64 + with pytest.raises(ValueError, match="predecessor"): + validate_control_invocations_v2(request, broken, results) + wrong_state = deepcopy(invocations) + wrong_state[2]["state_input"]["state"] = ref("wrong-state", "provider-state") + with pytest.raises(ValueError, match="state version"): + validate_control_invocations_v2(request, wrong_state, results) + + +def test_dependency_cannot_be_satisfied_within_one_invocation(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_invocation import validate_control_invocations_v2 + + source = _dependency_chain() + source["selection"]["slots"][2]["dependencies"] = [{"slot_id": "fact", "kind": "ifc-fact"}] + source["applicability"]["required_slot_ids"] = ["fact", "rule"] + source["applicability"]["selection_digest"] = control_digest( + ParticipantControlSelectionV2Model.model_validate(source["selection"]) + ) + request = ParticipantControlRequestV2Model.model_validate(source) + invocations, results = _chain_records(request) + combined = deepcopy(invocations) + combined[0]["requested_slot_ids"] = ["fact", "rule"] + combined[0]["predecessor_inputs"] = deepcopy(combined[1]["predecessor_inputs"]) + combined.pop() + with pytest.raises(ValueError, match="same invocation"): + validate_control_invocations_v2(request, combined, results) + + +@pytest.mark.parametrize("phase", ["required-predecessor", "success-dependent", "independent"]) +def test_parent_denial_is_invariant_across_effect_phases(phase): + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + + effect = effect_payload() + effect.update( + phase=phase, + parent_outcome="applied" if phase == "success-dependent" else "none", + allowed_parent_dispositions=["deny", "withhold", "permit"] if phase == "independent" else [], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + result = derive_control_effect_plan_v2( + parent_crossing=ref("crossing-1", "crossing"), + decisions=[ + { + "decision_id": "deny-rule", + "disposition": "deny", + "rule": ref("deny-rule", "rule"), + "reasons": [ref("denial-reason")], + } + ], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + assert result.parent_disposition == "deny" + assert result.runnable_effect_ids == (("effect-1",) if phase == "independent" else ()) + + +def test_independent_audit_can_follow_denial_without_releasing_parent(): + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + + effect = effect_payload( + { + "kind": "audit", + "subject": context_payload()["subject"], + "audit_record": ref("audit-1", "audit"), + "audience_ref": "reviewers", + "retention_policy": ref("retention", "policy"), + } + ) + effect.update( + phase="independent", + parent_outcome="none", + allowed_parent_dispositions=["deny"], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + plan = derive_control_effect_plan_v2( + parent_crossing=ref("crossing-1", "crossing"), + decisions=[ + { + "decision_id": "deny-rule", + "disposition": "deny", + "rule": ref("deny-rule", "rule"), + "reasons": [ref("denial-reason")], + } + ], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + assert plan.parent_disposition == "deny" + assert plan.runnable_effect_ids == ("effect-1",) + + +def test_required_predecessor_cannot_wait_for_own_parent_application(): + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + + effect = effect_payload() + effect.update( + phase="required-predecessor", + parent_outcome="applied", + allowed_parent_dispositions=[], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + with pytest.raises(ValueError, match="cycle|predecessor"): + derive_control_effect_plan_v2( + parent_crossing=ref("crossing-1", "crossing"), + decisions=[], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + + +def test_later_effect_requires_exact_parent_owner_receipt(): + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + + effect = effect_payload() + effect.update( + phase="success-dependent", + parent_outcome="applied", + allowed_parent_dispositions=[], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + with pytest.raises(ValueError, match="receipt"): + derive_control_effect_plan_v2( + parent_crossing=ref("crossing-1", "crossing"), + decisions=[], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=True, + ) + + +def test_conflicting_routes_cannot_be_admitted_by_array_order(): + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + + source = context_payload()["subject"] + effects = [] + for index in (1, 2): + result = deepcopy(source) + result["subject_ref"] = "routed-" + str(index) + effect = effect_payload( + { + "kind": "route", + "transformation": ref("route-rule", "transformation"), + "source": source, + "result": result, + "destination_ref": "destination-" + str(index), + "admission_policy": ref("route-policy", "policy"), + } + ) + effect.update( + effect_id="effect-" + str(index), + phase="independent", + parent_outcome="none", + allowed_parent_dispositions=["permit"], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + effect["key"]["slot"] = "route-" + str(index) + effects.append(effect) + with pytest.raises(ValueError, match="conflict"): + derive_control_effect_plan_v2( + parent_crossing=ref("crossing-1", "crossing"), + decisions=[], + effects=effects, + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + + +def _optional_request(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlSelectionV2Model, + control_digest, + ) + + request = _request_v2() + request["selection"]["slots"].append( + {"slot_id": "monitor", "instance_id": "influence", "kind": "advisory", "role": "advisory", "dependencies": []} + ) + selected = ParticipantControlSelectionV2Model.model_validate(request["selection"]) + request["applicability"]["selection_digest"] = control_digest(selected) + request["applicability"]["applicable_slot_ids"] = ["fact", "monitor"] + return request + + +def test_optional_malformed_result_is_normalized_without_private_error_text(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + normalize_optional_result_failure_v2, + ) + + request = ParticipantControlRequestV2Model.model_validate(_optional_request()) + binding = request.selection.bindings[0] + invocation = ControlInvocationV2Model( + invocation_id="invoke-monitor", + instance_id="influence", + requested_slot_ids=("monitor",), + binding_digest=control_digest(binding), + context_digest=control_digest(request.context), + predecessor_inputs=(), + state_input={ + "scope": binding.state_scope, + "state": request.context.provider_states[0].state, + "source": "committed", + "predecessor_invocation_id": None, + }, + projection=ref("input-projection", "projection"), + ) + raw = {"slot_id": "monitor", "payload": {"secret": "private-token-do-not-disclose"}} + result, loss = normalize_optional_result_failure_v2( + request, invocation, raw, result_id="result-monitor", evidence=ref("optional-loss") + ) + assert result.status == "failed" and result.payload is None and result.next_provider_state is None + assert loss.slot_id == "monitor" + assert "private-token" not in repr(result) + repr(loss) + with pytest.raises(ValueError, match="mandatory"): + normalize_optional_result_failure_v2( + request, + invocation.model_copy(update={"requested_slot_ids": ("fact",)}), + raw, + result_id="result-fact", + evidence=ref("mandatory-loss"), + ) + + +def test_required_support_is_compared_to_admitted_strength(): + from raes_contracts.contracts.participant_control_support_v2 import ControlSupportAssessmentV2Model + + observed = evaluation_payload()["support"][0] + observed["declared_level"] = "bounded" + observed["effective_level"] = "bounded" + observed["constraints"] = [ref("one-unit", "constraint")] + with pytest.raises(ValueError, match="exact"): + ControlSupportAssessmentV2Model( + obligation_id="teaching-here", + instance_id="influence", + required_strength="exact", + required_constraints=(), + effective_support=observed, + relation="bounded-compatible", + evidence=(ref("support-relation"),), + ) + assessment = ControlSupportAssessmentV2Model( + obligation_id="teaching-here", + instance_id="influence", + required_strength="bounded", + required_constraints=(ref("two-units", "constraint"),), + effective_support=observed, + relation="bounded-compatible", + evidence=(ref("support-relation"),), + ) + assert assessment.satisfied + + +def test_required_input_support_cannot_downgrade_its_admitted_slot_pin(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_decisions_v2 import ControlEffectPlanV2Model + from raes_contracts.contracts.participant_control_evaluation_v2 import derive_control_composition_v2 + + record = _evaluation_v2() + selection = record["request"]["selection"] + selection["slots"][1]["required_strength"] = "bounded" + selection["slots"][1]["constraints"] = [ref("two-units", "constraint")] + record["request"]["applicability"]["selection_digest"] = control_digest( + ParticipantControlSelectionV2Model.model_validate(selection) + ) + request = ParticipantControlRequestV2Model.model_validate(record["request"]) + with pytest.raises(ValueError, match="support"): + derive_control_composition_v2( + request, + record["results"], + record["support"], + [], + ControlEffectPlanV2Model.model_validate(record["effect_plan"]), + ) + + +def test_false_trigger_is_distinct_from_missing_rule_result(): + from raes_contracts.contracts.participant_control_invocation import ControlMechanismResultV2Model + + record = evaluation_payload()["results"][1] + record["invocation_digest"] = "sha256:" + "b" * 64 + record["payload"] = None + record["rule_outcome"] = "not-triggered" + record["evaluated_basis"] = [ref("false-trigger-basis")] + result = ControlMechanismResultV2Model.model_validate(record) + assert result.status == "resolved" and result.rule_outcome == "not-triggered" + record["status"] = "missing" + with pytest.raises(ValueError, match="trigger|resolved"): + ControlMechanismResultV2Model.model_validate(record) + + +def test_shared_state_conflict_and_declared_tentative_chain(): + from raes_contracts.contracts.participant_control_evaluation_v2 import validate_state_proposals_v2 + + scope = _selection_v2()["bindings"][0]["state_scope"] + first = { + "invocation_id": "invoke-fact", + "scope": scope, + "before": ref("state-1", "provider-state"), + "after": ref("state-2", "provider-state"), + "commit_on": "evaluation", + "predecessor_invocation_id": None, + } + second = { + **first, + "invocation_id": "invoke-rule", + "after": ref("state-3", "provider-state"), + } + with pytest.raises(ValueError, match="state conflict"): + validate_state_proposals_v2([first, second]) + second["before"] = first["after"] + second["predecessor_invocation_id"] = "invoke-fact" + validate_state_proposals_v2([second, first]) + + +def test_rejected_commit_cannot_expose_dispatchable_effects(): + from raes_contracts.contracts.participant_control_evaluation_v2 import ControlCommitBoundaryV2Model + + record = { + "expected_history_heads": [ref("history-head", "history")], + "coverage_digest": "sha256:" + "a" * 64, + "result_digests": ["sha256:" + "b" * 64], + "decision_digest": "sha256:" + "c" * 64, + "state_proposal_digests": [], + "effect_intent_digests": ["sha256:" + "d" * 64], + "effects_consumed": 1, + "status": "rejected", + "receipt": None, + "dispatchable_effect_ids": ["effect-1"], + } + with pytest.raises(ValueError, match="dispatch"): + ControlCommitBoundaryV2Model.model_validate(record) + record["dispatchable_effect_ids"] = [] + ControlCommitBoundaryV2Model.model_validate(record) + + +def test_denied_parent_cannot_commit_release_conditioned_state(): + import json + + from raes_contracts.contracts.participant_control_applicability import control_digest + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + ControlStateProposalV2Model, + ParticipantControlEvaluationV2Model, + ) + from raes_contracts.corpus import corpus_family_root + + path = ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ) + record = json.loads(path.read_text()) + proposal = ControlStateProposalV2Model( + invocation_id="invoke-fact", + scope=record["request"]["selection"]["bindings"][0]["state_scope"], + before=record["invocations"][0]["state_input"]["state"], + after=ref("state-2", "provider-state"), + commit_on="parent-release", + predecessor_invocation_id=None, + ) + record["results"][0]["next_provider_state"] = proposal.after.model_dump(mode="json") + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + + by_slot = {item["slot_id"]: item for item in record["results"]} + for call in record["invocations"]: + for predecessor in call["predecessor_inputs"]: + predecessor["result_digest"] = control_digest( + ControlMechanismResultV2Model.model_validate(by_slot[predecessor["slot_id"]]) + ) + by_slot[call["requested_slot_ids"][0]]["invocation_digest"] = control_digest( + ControlInvocationV2Model.model_validate(call) + ) + record["commit"]["result_digests"] = [ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in record["results"] + ] + record["state_proposals"] = [proposal.model_dump(mode="json")] + record["commit"]["state_proposal_digests"] = [control_digest(proposal)] + record["commit"]["status"] = "committed" + record["commit"]["receipt"] = ref("commit-receipt", "receipt") + with pytest.raises(ValueError, match="parent-release state cannot commit for a refused parent"): + ParticipantControlEvaluationV2Model.model_validate(record) + proposal = proposal.model_copy(update={"commit_on": "evaluation"}) + record["state_proposals"] = [proposal.model_dump(mode="json")] + record["commit"]["state_proposal_digests"] = [control_digest(proposal)] + accepted = ParticipantControlEvaluationV2Model.model_validate(record) + from dataclasses import replace + + from participant_control_contract_fixtures import crossing_payload + from raes_contracts.contracts.participant_control_resolution import ( + ParticipantControlValidationContextV2, + control_references, + validate_participant_control_resolved_context_v2, + ) + from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel + + crossing = ParticipantCrossingOccurrenceModel.model_validate( + crossing_payload(accepted.request.context.model_dump(mode="json")) + ) + trusted = ParticipantControlValidationContextV2( + admitted_request=accepted.request, + admitted_crossing=accepted.request.context.crossing, + installed_bindings={item.instance_id: item for item in accepted.request.selection.bindings}, + resolved_coverage={item.obligation_id: item for item in accepted.request.applicability.coverage}, + resolved_results={item.slot_id: item for item in accepted.results}, + resolved_support={(item.obligation_id, item.instance_id): item for item in accepted.support}, + authorized_effects={control_digest(item): accepted.request.context for item in accepted.effect_plan.effects}, + safe_references=frozenset(control_references(accepted)), + support_resolver=lambda item: True, + incumbent_gate_disposition=accepted.effect_plan.incumbent_gate_disposition, + crossing_records=(crossing,), + crossing_subjects=(crossing.occurrence.subject,), + crossing_policies=(crossing.occurrence.policy,), + commit_receipt=accepted.commit.receipt, + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2(accepted, lambda _: trusted) + validate_participant_control_resolved_context_v2( + accepted, lambda _: replace(trusted, authorized_state_proposals={proposal.invocation_id: proposal}) + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + accepted, + lambda _: replace( + trusted, + authorized_state_proposals={ + proposal.invocation_id: proposal.model_copy(update={"commit_on": "parent-release"}) + }, + ), + ) + + +def test_new_effect_cannot_exceed_retained_causal_budget(): + import json + + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + from raes_contracts.contracts.participant_control_decisions_v2 import ControlEffectPlanV2Model + from raes_contracts.contracts.participant_control_evaluation_v2 import validate_effect_budget_v2 + from raes_contracts.contracts.participant_control_selection import ControlCausalBoundsModel + from raes_contracts.corpus import corpus_family_root + + record = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + context = ParticipantControlContextModel.model_validate(record["request"]["context"]) + bounds = ControlCausalBoundsModel.model_validate(record["request"]["selection"]["bounds"]) + plan = ControlEffectPlanV2Model.model_validate(record["effect_plan"]) + validate_effect_budget_v2(context, bounds, plan) + limited = context.model_copy(update={"effects_consumed": bounds.max_effects}) + with pytest.raises(ValueError, match="budget"): + validate_effect_budget_v2(limited, bounds, plan) + + +def test_v2_effect_admission_rejects_lifecycle_before_live_target_and_foreign_root(): + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import validate_effect_budget_v2 + from raes_contracts.contracts.participant_control_selection import ControlCausalBoundsModel + + context = ParticipantControlContextModel.model_validate(context_payload()) + bounds = ControlCausalBoundsModel.model_validate(selection_payload()["bounds"]) + lifecycle = effect_payload( + { + "kind": "shutdown", + "target_kind": "participant", + "target_ref": "participants.student", + "operation": "terminate", + "lifecycle_authority": ref("lifecycle", "authority"), + "expected_revision": "rev1", + } + ) + inject = effect_payload() + inject["effect_id"] = "effect-2" + inject["key"]["slot"] = "second" + inject["predecessor_effect_ids"] = ["effect-1"] + for effect in (lifecycle, inject): + effect.update( + phase="independent", + parent_outcome="none", + allowed_parent_dispositions=["permit"], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + plan = derive_control_effect_plan_v2( + parent_crossing=context.crossing, + decisions=[], + effects=[lifecycle, inject], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + with pytest.raises(ValueError, match="lifecycle"): + validate_effect_budget_v2(context, bounds, plan) + inject["predecessor_effect_ids"] = [] + unordered_plan = derive_control_effect_plan_v2( + parent_crossing=context.crossing, + decisions=[], + effects=[lifecycle, inject], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + with pytest.raises(ValueError, match="lifecycle"): + validate_effect_budget_v2(context, bounds, unordered_plan) + lifecycle["predecessor_effect_ids"] = ["effect-2"] + safe_plan = derive_control_effect_plan_v2( + parent_crossing=context.crossing, + decisions=[], + effects=[lifecycle, inject], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + validate_effect_budget_v2(context, bounds, safe_plan) + foreign = deepcopy(inject) + foreign["predecessor_effect_ids"] = [] + foreign["key"]["trigger_root"] = "different-root" + foreign_plan = derive_control_effect_plan_v2( + parent_crossing=context.crossing, + decisions=[], + effects=[foreign], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + with pytest.raises(ValueError, match="causal root"): + validate_effect_budget_v2(context, bounds, foreign_plan) + + +def test_consumed_tentative_state_requires_transaction_proposal(): + from raes_contracts.contracts.participant_control_applicability import control_digest + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + + payload = _evaluation_v2() + payload["results"][0]["next_provider_state"] = ref("state-next", "provider-state") + payload["invocations"][2]["state_input"].update( + source="tentative", + state=ref("state-next", "provider-state"), + predecessor_invocation_id="invoke-fact", + ) + by_slot = {item["slot_id"]: item for item in payload["results"]} + for call in payload["invocations"]: + for predecessor in call["predecessor_inputs"]: + predecessor["result_digest"] = control_digest( + ControlMechanismResultV2Model.model_validate(by_slot[predecessor["slot_id"]]) + ) + invocation = ControlInvocationV2Model.model_validate(call) + by_slot[call["requested_slot_ids"][0]]["invocation_digest"] = control_digest(invocation) + payload["commit"]["result_digests"] = [ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ] + with pytest.raises(ValueError, match="tentative state.*transaction proposal"): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +def _evaluation_v2(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import derive_control_composition_v2 + + request = ParticipantControlRequestV2Model.model_validate(_dependency_chain()) + invocations, results = _chain_records(request) + support = [] + for requirement_id, instance in ( + ("teaching-here", "influence"), + ("teaching-there", "influence"), + ("input:assessment", "other"), + ): + observed = evaluation_payload()["support"][0] + observed["instance_id"] = instance + observed["context_digest"] = control_digest(request.context) + support.append( + { + "obligation_id": requirement_id, + "instance_id": instance, + "required_strength": "exact", + "required_constraints": [], + "effective_support": observed, + "relation": "exact", + "evidence": [ref("support-relation")], + } + ) + plan = derive_control_effect_plan_v2( + parent_crossing=request.context.crossing, + decisions=[ + { + "decision_id": "result-rule", + "disposition": "permit", + "rule": ref("decision-rule", "rule"), + "reasons": [ref("decision-basis")], + } + ], + effects=[], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + composition = derive_control_composition_v2(request, results, support, [], plan) + return { + "schema_version": "participant-control-evaluation/v2", + "evaluation_id": "evaluation-v2", + "request": request.model_dump(mode="json"), + "invocations": invocations, + "results": [item.model_dump(mode="json") for item in results], + "support": support, + "lost_advice": [], + "effect_plan": plan.model_dump(mode="json"), + "state_proposals": [], + "composition": composition.model_dump(mode="json"), + "commit": { + "expected_history_heads": [item.model_dump(mode="json") for item in request.context.expected_history_heads], + "coverage_digest": control_digest(request.applicability), + "result_digests": [control_digest(item) for item in results], + "decision_digest": control_digest(plan), + "state_proposal_digests": [], + "effect_intent_digests": [], + "effects_consumed": request.context.effects_consumed, + "status": "prepared", + "receipt": None, + "dispatchable_effect_ids": [], + }, + } + + +def test_evaluation_v2_requires_complete_support_and_atomic_record_bindings(): + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + + payload = _evaluation_v2() + document = ParticipantControlEvaluationV2Model.model_validate(payload) + assert document.composition.disposition == "eligible" + payload["support"].pop() + with pytest.raises(ValueError, match="support"): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +@pytest.mark.parametrize("status,expected", [("stale", "stale"), ("weakened", "weakened")]) +def test_required_result_status_retains_its_composition_disposition(status, expected): + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + from raes_contracts.contracts.participant_control_decisions_v2 import ControlEffectPlanV2Model + from raes_contracts.contracts.participant_control_evaluation_v2 import derive_control_composition_v2 + + payload = _evaluation_v2() + payload["results"][-1]["status"] = status + payload["results"][-1]["payload"] = None + request = ParticipantControlRequestV2Model.model_validate(payload["request"]) + plan = ControlEffectPlanV2Model.model_validate(payload["effect_plan"]) + composition = derive_control_composition_v2(request, payload["results"], payload["support"], [], plan) + assert composition.disposition == expected + + +def test_v1_history_cannot_be_parsed_as_amended_evaluation(): + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + + with pytest.raises(ValueError): + ParticipantControlEvaluationV2Model.model_validate(evaluation_payload()) + + +def test_v2_bounded_json_ingress_preserves_abstention_and_rejects_duplicate_members(): + import json + + from raes_contracts.contracts.participant_control_applicability import control_digest + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + derive_control_composition_v2, + parse_participant_control_evaluation_v2, + ) + from raes_contracts.contracts.participant_control_invocation import ControlMechanismResultV2Model + + payload = _evaluation_v2() + payload["results"][-1]["payload"]["disposition"] = "abstain" + result = ControlMechanismResultV2Model.model_validate(payload["results"][-1]) + payload["commit"]["result_digests"][-1] = control_digest(result) + plan = derive_control_effect_plan_v2( + parent_crossing=payload["request"]["context"]["crossing"], + decisions=[], + effects=[], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + payload["effect_plan"] = plan.model_dump(mode="json") + payload["commit"]["decision_digest"] = control_digest(plan) + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + + request = ParticipantControlRequestV2Model.model_validate(payload["request"]) + payload["composition"] = derive_control_composition_v2( + request, payload["results"], payload["support"], [], plan + ).model_dump(mode="json") + parsed = parse_participant_control_evaluation_v2(json.dumps(payload)) + assert parsed.results[-1].payload.disposition == "abstain" + assert parsed.composition.disposition == "failed" + with pytest.raises(ValueError, match="invalid participant control evaluation"): + parse_participant_control_evaluation_v2('{"schema_version":"one","schema_version":"two"}') + + +def test_v2_effect_outcome_cannot_name_an_absent_effect(): + import json + + from raes_contracts.contracts.participant_control_decisions_v2 import ControlEffectPlanV2Model + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + )["effect_plan"] + payload["effect_outcomes"] = [ + { + "effect_id": "absent-effect", + "disposition": "failed", + "receipt": ref("absent-receipt", "receipt"), + "evidence": [ref("outcome-evidence")], + } + ] + with pytest.raises(ValueError, match="outcome"): + ControlEffectPlanV2Model.model_validate(payload) + + +def test_v2_publication_has_exact_schema_and_separate_provider_protocol(): + import json + + from jsonschema import Draft202012Validator + from raes_backend_protocols.protocols import ParticipantControlProviderV2 + from raes_conformance.conformance.validators import contract_validation_strength + from raes_contracts.contracts import schema_bundle + from raes_contracts.corpus import corpus_family_root + + bundle = schema_bundle() + for contract_id, payload in ( + ("participant-control-selection-v2", _selection_v2()), + ("participant-control-evaluation-v2", _evaluation_v2()), + ): + schema = bundle[contract_id] + Draft202012Validator.check_schema(schema) + Draft202012Validator(schema).validate(payload) + published = corpus_family_root("schemas") / "participant-runtime" / (contract_id + ".json") + assert json.loads(published.read_text()) == schema + assert contract_validation_strength("participant-control-evaluation-v2") == "structural-context-required" + assert "resolve_stage" in ParticipantControlProviderV2.__dict__ + + +def test_v2_trusted_context_rejects_unresolved_coverage_and_support(): + from participant_control_contract_fixtures import crossing_payload + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.contracts.participant_control_resolution import ( + ParticipantControlValidationContextV2, + control_references, + validate_participant_control_resolved_context_v2, + ) + from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel + + record = ParticipantControlEvaluationV2Model.model_validate(_evaluation_v2()) + crossing = ParticipantCrossingOccurrenceModel.model_validate( + crossing_payload(record.request.context.model_dump(mode="json")) + ) + trusted = ParticipantControlValidationContextV2( + admitted_request=record.request, + admitted_crossing=record.request.context.crossing, + installed_bindings={item.instance_id: item for item in record.request.selection.bindings}, + resolved_coverage={item.obligation_id: item for item in record.request.applicability.coverage}, + resolved_results={item.slot_id: item for item in record.results}, + resolved_support={(item.obligation_id, item.instance_id): item for item in record.support}, + authorized_effects={}, + safe_references=frozenset(control_references(record)), + support_resolver=lambda item: True, + incumbent_gate_disposition="permit", + crossing_records=(crossing,), + crossing_subjects=(crossing.occurrence.subject,), + crossing_policies=(crossing.occurrence.policy,), + ) + validate_participant_control_resolved_context_v2(record, lambda _: trusted) + from raes_conformance.conformance.validators import validate_contract_payload + + assert ( + validate_contract_payload("participant-control-evaluation-v2", record.model_dump(mode="json"))[0].code + == "conformance.semantic-context-required" + ) + assert ( + validate_contract_payload( + "participant-control-evaluation-v2", + record.model_dump(mode="json"), + control_context_resolver=lambda _: trusted, + ) + == () + ) + from dataclasses import replace + + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2(record, lambda _: replace(trusted, resolved_coverage={})) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + record, lambda _: replace(trusted, support_resolver=lambda item: False) + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + record, lambda _: replace(trusted, incumbent_gate_disposition="deny") + ) + committed_payload = _evaluation_v2() + committed_payload["commit"]["status"] = "committed" + committed_payload["commit"]["receipt"] = ref("atomic-receipt", "receipt") + committed = ParticipantControlEvaluationV2Model.model_validate(committed_payload) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2(committed, lambda _: trusted) + validate_participant_control_resolved_context_v2( + committed, + lambda _: replace( + trusted, + commit_receipt=committed.commit.receipt, + safe_references=frozenset(control_references(committed)), + ), + ) + + +def test_v2_effect_authorization_binds_full_payload_and_cut(): + import json + + from participant_control_contract_fixtures import crossing_payload + from raes_contracts.contracts.participant_control_applicability import control_digest + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.contracts.participant_control_resolution import ( + ParticipantControlValidationContextV2, + control_references, + validate_participant_control_resolved_context_v2, + ) + from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + record = ParticipantControlEvaluationV2Model.model_validate(payload) + crossing = ParticipantCrossingOccurrenceModel.model_validate( + crossing_payload(record.request.context.model_dump(mode="json")) + ) + assert record.effect_plan.runnable_effect_ids + from dataclasses import replace + + trusted = ParticipantControlValidationContextV2( + admitted_request=record.request, + admitted_crossing=record.request.context.crossing, + installed_bindings={item.instance_id: item for item in record.request.selection.bindings}, + resolved_coverage={item.obligation_id: item for item in record.request.applicability.coverage}, + resolved_results={item.slot_id: item for item in record.results}, + resolved_support={(item.obligation_id, item.instance_id): item for item in record.support}, + authorized_effects={control_digest(item): record.request.context for item in record.effect_plan.effects}, + safe_references=frozenset(control_references(record)), + support_resolver=lambda item: True, + incumbent_gate_disposition=record.effect_plan.incumbent_gate_disposition, + crossing_records=(crossing,), + crossing_subjects=(crossing.occurrence.subject,), + crossing_policies=(crossing.occurrence.policy,), + ) + validate_participant_control_resolved_context_v2(record, lambda _: trusted) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + record, + lambda _: replace( + trusted, + authorized_effects={item.effect_id: record.request.context for item in record.effect_plan.effects}, + ), + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + record, + lambda _: replace( + trusted, + authorized_effects={ + control_digest(item): record.request.context.model_copy(update={"sink_ref": "different-sink"}) + for item in record.effect_plan.effects + }, + ), + ) + + +def test_realized_effect_requires_prior_committed_full_intent_and_owner_outcome(): + import json + from dataclasses import replace + + from participant_control_contract_fixtures import crossing_payload + from raes_contracts.contracts.participant_control_applicability import control_digest + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + from raes_contracts.contracts.participant_control_decisions_v2 import ( + ControlEffectRequestV2Model, + derive_control_effect_plan_v2, + ) + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + ParticipantControlEvaluationV2Model, + validate_effect_budget_v2, + ) + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + from raes_contracts.contracts.participant_control_resolution import ( + ControlCommittedEffectIntentV2, + ParticipantControlValidationContextV2, + control_references, + validate_participant_control_resolved_context_v2, + ) + from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + effect = ControlEffectRequestV2Model.model_validate(payload["effect_plan"]["effects"][0]) + payload["commit"].update( + status="committed", + receipt=ref("origin-commit", "receipt"), + dispatchable_effect_ids=[effect.effect_id], + ) + origin = ParticipantControlEvaluationV2Model.model_validate(payload) + current = deepcopy(payload) + current["request"]["context"]["effects_consumed"] = 1 + current["request"]["context"]["prior_effect_claims"] = [ + { + "effect_id": effect.effect_id, + "key": effect.key.model_dump(mode="json"), + "content_digest": control_digest(effect), + } + ] + current["request"]["context"]["rule_firings"] = [ + { + "rule_id": effect.key.rule_id, + "rule_revision": effect.key.rule_revision, + "firing_epochs": [effect.key.firing_epoch], + } + ] + cut = ParticipantControlContextModel.model_validate(current["request"]["context"]) + old_digest, new_digest = control_digest(origin.request.context), control_digest(cut) + current = json.loads(json.dumps(current).replace(old_digest, new_digest)) + current["commit"].update( + coverage_digest=control_digest(origin.request.applicability), + effects_consumed=1, + status="prepared", + receipt=None, + dispatchable_effect_ids=[], + effect_intent_digests=[], + ) + current["request"]["applicability"]["context_digest"] = new_digest + by_slot = {item["slot_id"]: item for item in current["results"]} + for call in current["invocations"]: + for predecessor in call["predecessor_inputs"]: + predecessor["result_digest"] = control_digest( + ControlMechanismResultV2Model.model_validate(by_slot[predecessor["slot_id"]]) + ) + for slot_id in call["requested_slot_ids"]: + by_slot[slot_id]["invocation_digest"] = control_digest(ControlInvocationV2Model.model_validate(call)) + current["commit"]["result_digests"] = [ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in current["results"] + ] + outcome = { + "effect_id": effect.effect_id, + "disposition": "applied", + "receipt": ref("effect-applied", "receipt"), + "evidence": [ref("effect-owner-evidence")], + } + plan = derive_control_effect_plan_v2( + parent_crossing=origin.effect_plan.parent_crossing, + decisions=list(origin.effect_plan.decisions), + effects=list(origin.effect_plan.effects), + incumbent_gate_disposition=origin.effect_plan.incumbent_gate_disposition, + realized_effect_ids=(effect.effect_id,), + parent_applied=False, + effect_outcomes=[outcome], + ) + with pytest.raises(ValueError, match="retained committed intent claim"): + validate_effect_budget_v2(origin.request.context, origin.request.selection.bounds, plan) + current["effect_plan"] = plan.model_dump(mode="json") + current["commit"]["decision_digest"] = control_digest(plan) + current["commit"]["coverage_digest"] = control_digest( + origin.request.applicability.model_copy(update={"context_digest": new_digest}) + ) + record = ParticipantControlEvaluationV2Model.model_validate(current) + crossing = ParticipantCrossingOccurrenceModel.model_validate(crossing_payload(cut.model_dump(mode="json"))) + trusted = ParticipantControlValidationContextV2( + admitted_request=record.request, + admitted_crossing=cut.crossing, + installed_bindings={item.instance_id: item for item in record.request.selection.bindings}, + resolved_coverage={item.obligation_id: item for item in record.request.applicability.coverage}, + resolved_results={item.slot_id: item for item in record.results}, + resolved_support={(item.obligation_id, item.instance_id): item for item in record.support}, + authorized_effects={control_digest(effect): origin.request.context}, + safe_references=frozenset(control_references(record)), + support_resolver=lambda item: True, + incumbent_gate_disposition=record.effect_plan.incumbent_gate_disposition, + crossing_records=(crossing,), + crossing_subjects=(crossing.occurrence.subject,), + crossing_policies=(crossing.occurrence.policy,), + realization_receipts={effect.effect_id: plan.effect_outcomes[0]}, + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2(record, lambda _: trusted) + committed = ControlCommittedEffectIntentV2(evaluation=origin, outcome=plan.effect_outcomes[0]) + validate_participant_control_resolved_context_v2( + record, lambda _: replace(trusted, committed_effect_intents={effect.effect_id: committed}) + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + record, + lambda _: replace( + trusted, + committed_effect_intents={ + effect.effect_id: replace( + committed, evaluation=origin.model_copy(update={"request": record.request}) + ) + }, + ), + ) + + +def test_v2_security_fact_requires_incumbent_crossing_and_flow_relation_owner(): + from dataclasses import replace + + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + ParticipantControlEvaluationV2Model, + derive_control_composition_v2, + ) + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + from raes_contracts.contracts.participant_control_resolution import ( + ParticipantControlValidationContextV2, + control_references, + validate_participant_control_resolved_context_v2, + ) + from test_api_424_security_profile import security_record_and_context + + legacy, owner = security_record_and_context() + selection_data = legacy.request.selection.model_dump(mode="json") + selection_data["schema_version"] = "participant-control-selection/v2" + selection_data["interpretation"] = "participant-control-applicability/rev1" + binding_data = selection_data["bindings"][0] + binding_data["protocol_revision"] = "participant-control-provider/v2" + binding_data["state_scope"] = { + "scope_id": "security-state", + "sharing": "participant", + "authority": binding_data["authority"], + "configuration_digest": binding_data["configuration_digest"], + } + selection_data["obligations"] = [ + { + "obligation_id": "security-fact", + "profile": binding_data["profiles"][0], + "required_kind": "ifc-fact", + "required_strength": "exact", + "constraints": [], + } + ] + selection = ParticipantControlSelectionV2Model.model_validate(selection_data) + cut = legacy.request.context + request = ParticipantControlRequestV2Model.model_validate( + { + "selection": selection.model_dump(mode="json"), + "context": cut.model_dump(mode="json"), + "applicability": { + "selection_digest": control_digest(selection), + "context_digest": control_digest(cut), + "applicable_slot_ids": ["fact"], + "required_slot_ids": ["fact"], + "coverage": [ + { + "obligation_id": "security-fact", + "status": "applies", + "slot_ids": ["fact"], + "evidence": [ref("security-coverage")], + } + ], + "derivation_evidence": [ref("security-applicability")], + }, + } + ) + invocation = ControlInvocationV2Model.model_validate( + { + "invocation_id": "security-invocation", + "instance_id": "influence", + "requested_slot_ids": ["fact"], + "binding_digest": control_digest(selection.bindings[0]), + "context_digest": control_digest(cut), + "predecessor_inputs": [], + "state_input": { + "scope": selection.bindings[0].state_scope.model_dump(mode="json"), + "state": cut.provider_states[0].state.model_dump(mode="json"), + "source": "committed", + "predecessor_invocation_id": None, + }, + "projection": ref("security-projection", "projection"), + } + ) + result_data = legacy.results[0].model_dump(mode="json") + result_data.update( + binding_digest=control_digest(selection.bindings[0]), + context_digest=control_digest(cut), + invocation_digest=control_digest(invocation), + ) + result = ControlMechanismResultV2Model.model_validate(result_data) + support = { + "obligation_id": "security-fact", + "instance_id": "influence", + "required_strength": "exact", + "required_constraints": [], + "effective_support": legacy.support[0].model_dump(mode="json"), + "relation": "exact", + "evidence": [ref("security-support")], + } + plan = derive_control_effect_plan_v2( + parent_crossing=cut.crossing, + decisions=[], + effects=[], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + composition = derive_control_composition_v2(request, [result], [support], [], plan) + record = ParticipantControlEvaluationV2Model.model_validate( + { + "schema_version": "participant-control-evaluation/v2", + "evaluation_id": "security-evaluation", + "request": request.model_dump(mode="json"), + "invocations": [invocation.model_dump(mode="json")], + "results": [result.model_dump(mode="json")], + "support": [support], + "lost_advice": [], + "effect_plan": plan.model_dump(mode="json"), + "state_proposals": [], + "composition": composition.model_dump(mode="json"), + "commit": { + "expected_history_heads": [item.model_dump(mode="json") for item in cut.expected_history_heads], + "coverage_digest": control_digest(request.applicability), + "result_digests": [control_digest(result)], + "decision_digest": control_digest(plan), + "state_proposal_digests": [], + "effect_intent_digests": [], + "effects_consumed": cut.effects_consumed, + "status": "prepared", + "receipt": None, + "dispatchable_effect_ids": [], + }, + } + ) + trusted = ParticipantControlValidationContextV2( + admitted_request=request, + admitted_crossing=cut.crossing, + installed_bindings={"influence": selection.bindings[0]}, + resolved_coverage={item.obligation_id: item for item in request.applicability.coverage}, + resolved_results={"fact": result}, + resolved_support={(item.obligation_id, item.instance_id): item for item in record.support}, + authorized_effects={}, + safe_references=frozenset(set(owner.safe_references) | set(control_references(record))), + support_resolver=lambda item: True, + incumbent_gate_disposition="permit", + crossing_records=owner.crossing_records, + crossing_subjects=owner.crossing_subjects, + crossing_policies=owner.crossing_policies, + flow_relations=owner.flow_relations, + flow_contexts=owner.flow_contexts, + ) + validate_participant_control_resolved_context_v2(record, lambda _: trusted) + for wrong in (replace(trusted, crossing_records=()), replace(trusted, flow_contexts={})): + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2(record, lambda _, wrong=wrong: wrong) + + +def test_failed_required_composition_cannot_commit_parent_release_state(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + ControlStateProposalV2Model, + ParticipantControlEvaluationV2Model, + derive_control_composition_v2, + ) + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + + payload = _evaluation_v2() + request = ParticipantControlRequestV2Model.model_validate(payload["request"]) + payload["results"][-1].update(status="failed", payload=None) + proposal = ControlStateProposalV2Model( + invocation_id="invoke-fact", + scope=request.selection.bindings[0].state_scope, + before=request.context.provider_states[0].state, + after=ref("failed-parent-next-state", "provider-state"), + commit_on="parent-release", + predecessor_invocation_id=None, + ) + payload["results"][0]["next_provider_state"] = proposal.after.model_dump(mode="json") + by_slot = {item["slot_id"]: item for item in payload["results"]} + for call in payload["invocations"]: + for predecessor in call["predecessor_inputs"]: + predecessor["result_digest"] = control_digest( + ControlMechanismResultV2Model.model_validate(by_slot[predecessor["slot_id"]]) + ) + by_slot[call["requested_slot_ids"][0]]["invocation_digest"] = control_digest( + ControlInvocationV2Model.model_validate(call) + ) + plan = derive_control_effect_plan_v2( + parent_crossing=request.context.crossing, + decisions=[], + effects=[], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + payload["effect_plan"] = plan.model_dump(mode="json") + payload["composition"] = derive_control_composition_v2( + request, payload["results"], payload["support"], [], plan + ).model_dump(mode="json") + assert payload["composition"]["disposition"] == "failed" + payload["state_proposals"] = [proposal.model_dump(mode="json")] + payload["commit"].update( + result_digests=[ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ], + decision_digest=control_digest(plan), + state_proposal_digests=[control_digest(proposal)], + status="committed", + receipt=ref("failed-parent-commit", "receipt"), + ) + with pytest.raises(ValueError, match="mandatory composition.*parent release"): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +def test_failed_required_composition_cannot_dispatch_independent_effect(): + import json + + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + ParticipantControlEvaluationV2Model, + derive_control_composition_v2, + ) + from raes_contracts.contracts.participant_control_invocation import ControlMechanismResultV2Model + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + request = ParticipantControlRequestV2Model.model_validate(payload["request"]) + payload["results"][2].update(status="failed", payload=None) + effect = payload["results"][3]["payload"] + effect["allowed_parent_dispositions"] = ["permit"] + plan = derive_control_effect_plan_v2( + parent_crossing=request.context.crossing, + decisions=[], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + assert plan.parent_disposition == "permit" and plan.runnable_effect_ids == (effect["effect_id"],) + payload["effect_plan"] = plan.model_dump(mode="json") + payload["composition"] = derive_control_composition_v2( + request, payload["results"], payload["support"], payload["lost_advice"], plan + ).model_dump(mode="json") + assert payload["composition"]["disposition"] == "failed" + payload["commit"].update( + result_digests=[ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ], + decision_digest=control_digest(plan), + effect_intent_digests=[], + status="committed", + receipt=ref("failed-dispatch-commit", "receipt"), + dispatchable_effect_ids=[effect["effect_id"]], + ) + with pytest.raises(ValueError, match="mandatory composition.*dispatch"): + ParticipantControlEvaluationV2Model.model_validate(payload) + effect["allowed_parent_dispositions"] = ["deny"] + denied_plan = derive_control_effect_plan_v2( + parent_crossing=request.context.crossing, + decisions=[], + effects=[effect], + incumbent_gate_disposition="deny", + realized_effect_ids=(), + parent_applied=False, + ) + assert denied_plan.runnable_effect_ids == (effect["effect_id"],) + payload["effect_plan"] = denied_plan.model_dump(mode="json") + payload["composition"] = derive_control_composition_v2( + request, payload["results"], payload["support"], payload["lost_advice"], denied_plan + ).model_dump(mode="json") + payload["commit"].update( + result_digests=[ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ], + decision_digest=control_digest(denied_plan), + effect_intent_digests=[], + ) + with pytest.raises(ValueError, match="mandatory composition.*dispatch"): + ParticipantControlEvaluationV2Model.model_validate(payload) + effect["allowed_parent_dispositions"] = ["withhold"] + withheld_plan = derive_control_effect_plan_v2( + parent_crossing=request.context.crossing, + decisions=[], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + assert withheld_plan.runnable_effect_ids == () + payload["effect_plan"] = withheld_plan.model_dump(mode="json") + payload["composition"] = derive_control_composition_v2( + request, payload["results"], payload["support"], payload["lost_advice"], withheld_plan + ).model_dump(mode="json") + payload["commit"].update( + result_digests=[ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ], + decision_digest=control_digest(withheld_plan), + effect_intent_digests=[], + ) + with pytest.raises(ValueError, match="mandatory composition.*dispatch"): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +def test_duplicate_provider_effect_id_cannot_hide_an_earlier_request(): + import json + + from raes_contracts.contracts.participant_control_applicability import ( + ControlApplicabilityV2Model, + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + selection = payload["request"]["selection"] + assessment = next(item for item in selection["slots"] if item["slot_id"] == "assessment") + assessment["kind"] = "effect-request" + rule = next(item for item in selection["slots"] if item["slot_id"] == "rule") + rule["dependencies"][0]["kind"] = "effect-request" + rule_call = next(item for item in payload["invocations"] if item["requested_slot_ids"] == ["rule"]) + rule_call["predecessor_inputs"][0]["kind"] = "effect-request" + payload["request"]["applicability"]["selection_digest"] = control_digest( + ParticipantControlSelectionV2Model.model_validate(selection) + ) + results = {item["slot_id"]: item for item in payload["results"]} + collision = deepcopy(results["audit-slot"]["payload"]) + collision["key"]["slot"] = "earlier-provider" + results["assessment"]["payload"] = collision + for call in payload["invocations"]: + for predecessor in call["predecessor_inputs"]: + predecessor["result_digest"] = control_digest( + ControlMechanismResultV2Model.model_validate(results[predecessor["slot_id"]]) + ) + for slot_id in call["requested_slot_ids"]: + results[slot_id]["invocation_digest"] = control_digest(ControlInvocationV2Model.model_validate(call)) + payload["commit"].update( + coverage_digest=control_digest(ControlApplicabilityV2Model.model_validate(payload["request"]["applicability"])), + result_digests=[ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ], + ) + with pytest.raises(ValueError, match="duplicate provider effect identity"): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +def test_independent_audit_remains_dispatchable_after_denial(): + import json + + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + payload["commit"].update( + status="committed", + receipt=ref("denial-audit-commit", "receipt"), + dispatchable_effect_ids=payload["effect_plan"]["runnable_effect_ids"], + ) + record = ParticipantControlEvaluationV2Model.model_validate(payload) + assert record.composition.disposition == "deny" + assert record.commit.dispatchable_effect_ids + + +def test_published_v2_examples_cover_applicability_and_effect_boundaries(): + import json + + from jsonschema import Draft202012Validator + from raes_contracts.contracts import schema_bundle + from raes_contracts.contracts.participant_control_applicability import ParticipantControlSelectionV2Model + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.corpus import corpus_family_root + + examples = ( + ("participant-control-selection-v2", "valid", "disjoint-sinks", True, True), + ("participant-control-evaluation-v2", "valid", "dependency-chain", True, True), + ("participant-control-evaluation-v2", "valid", "optional-failure", True, True), + ("participant-control-evaluation-v2", "valid", "later-phase-denial", True, True), + ("participant-control-evaluation-v2", "invalid", "unknown-field", False, False), + ("participant-control-evaluation-v2", "context-invalid", "missing-obligation", True, False), + ) + for contract_id, category, name, schema_valid, model_valid in examples: + path = corpus_family_root("fixtures") / "participant-runtime" / contract_id / category / (name + ".json") + payload = json.loads(path.read_text()) + assert Draft202012Validator(schema_bundle()[contract_id]).is_valid(payload) is schema_valid + model = ( + ParticipantControlSelectionV2Model if "selection" in contract_id else ParticipantControlEvaluationV2Model + ) + try: + model.model_validate(payload) + except ValueError: + assert not model_valid + else: + assert model_valid diff --git a/tools/generate_contract_schemas.py b/tools/generate_contract_schemas.py index aa457b34c..80934659d 100644 --- a/tools/generate_contract_schemas.py +++ b/tools/generate_contract_schemas.py @@ -61,6 +61,8 @@ "participant-control-occurrence-v1", "participant-control-selection-v1", "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "participant-control-teaching-profile-v1", "participant-crossing-occurrence-v1", "participant-flow-control-relation-v1", @@ -173,7 +175,8 @@ def compact(value: object) -> str: for key, value in sorted(schema.items()): if key == "$defs": definitions = ",\n".join( - f" {json.dumps(label)}: {compact(body)}" for label, body in sorted(value.items()) + f" {json.dumps(label)}: {compact(body)}" + for label, body in sorted(value.items()) ) members.append(f' "$defs": {{\n{definitions}\n }}') else: diff --git a/tools/policy/requirement_order.yaml b/tools/policy/requirement_order.yaml index 434f1f15f..7604cf0ce 100644 --- a/tools/policy/requirement_order.yaml +++ b/tools/policy/requirement_order.yaml @@ -825,6 +825,25 @@ ownership: - implementations/python/tests/test_api_424_security_profile.py - implementations/python/tests/test_corpus_packaging.py - implementations/python/tests/test_json_ingress.py + # Issue #1365 publishes the applicability amendment as separately negotiated v2 contracts. + - contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json + - contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json + - contracts/schema-publication/entries/participant-control-evaluation-v2.json + - contracts/schema-publication/entries/participant-control-selection-v2.json + - contracts/schemas/participant-runtime/participant-control-evaluation-v2.json + - contracts/schemas/participant-runtime/participant-control-selection-v2.json + - docs/decisions/issue-1365-applicable-provider-contracts-preflight.md + - docs/governance/requirement-scopes/1365.json + - implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py + - implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py + - implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py + - implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py + - implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py + - implementations/python/tests/test_issue_1365_applicable_contracts.py - tools/generate_contract_schemas.py - tools/policy/requirement_order.yaml mixed-participant-composition: From fe620ab35f12e0430a8d0549e8e0b5fb8749d660 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 19:56:30 +0200 Subject: [PATCH 02/10] fix: format schema generator --- tools/generate_contract_schemas.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/generate_contract_schemas.py b/tools/generate_contract_schemas.py index 80934659d..34394d4b9 100644 --- a/tools/generate_contract_schemas.py +++ b/tools/generate_contract_schemas.py @@ -175,8 +175,7 @@ def compact(value: object) -> str: for key, value in sorted(schema.items()): if key == "$defs": definitions = ",\n".join( - f" {json.dumps(label)}: {compact(body)}" - for label, body in sorted(value.items()) + f" {json.dumps(label)}: {compact(body)}" for label, body in sorted(value.items()) ) members.append(f' "$defs": {{\n{definitions}\n }}') else: From c517c9a4d2ad0d4078a7768f566337923498e9a9 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 20:00:37 +0200 Subject: [PATCH 03/10] fix: resolve public migration guide link --- docs/public/participant-control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/public/participant-control.md b/docs/public/participant-control.md index 3fbc143c8..1405f4ee3 100644 --- a/docs/public/participant-control.md +++ b/docs/public/participant-control.md @@ -38,7 +38,7 @@ effect prerequisites. They require exact protocol/schema negotiation and a trusted operator resolver. The current reference runtime and retained v1 history continue to use provider/v1; v2 publication alone does not activate v2 scheduling, state commit or effect dispatch. See the -[migration rules](../migration/control-applicability-and-evaluation.md). +[migration rules](https://github.com/OpenRAE/rae/blob/dev/docs/migration/control-applicability-and-evaluation.md). | Plane | What it contains | What it does not imply | | --- | --- | --- | From b11e0fa34651db997733d29fdcc33f5bdf4eff8e Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 20:14:33 +0200 Subject: [PATCH 04/10] fix: refresh source-bound evidence for API-424 --- docs/governance/requirement-scopes/1365.json | 45 ++ .../analysis-v55.json | 156 ++++ .../bundles/retest-v55.json | 122 +++ .../execution-snapshot-v55.json | 652 ++++++++++++++++ .../formal-semantic-validation/index.md | 7 +- .../specification-coverage/analysis-v55.json | 105 +++ ...specification-coverage-issue-1365-v55.json | 10 + .../execution-snapshot-v55.json | 701 ++++++++++++++++++ docs/research/specification-coverage/index.md | 8 +- .../tests/test_specification_coverage.py | 2 +- tools/check_specification_coverage.py | 6 +- tools/formal_semantic_validation/_baseline.py | 2 + tools/formal_semantic_validation/_loading.py | 4 +- .../_release_revisions.py | 4 +- tools/formal_semantic_validation/_releases.py | 6 +- tools/formal_semantic_validation/_retest.py | 2 +- tools/policy/requirement_order.yaml | 7 + 17 files changed, 1826 insertions(+), 13 deletions(-) create mode 100644 docs/research/formal-semantic-validation/analysis-v55.json create mode 100644 docs/research/formal-semantic-validation/bundles/retest-v55.json create mode 100644 docs/research/formal-semantic-validation/execution-snapshot-v55.json create mode 100644 docs/research/specification-coverage/analysis-v55.json create mode 100644 docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v55.json create mode 100644 docs/research/specification-coverage/execution-snapshot-v55.json diff --git a/docs/governance/requirement-scopes/1365.json b/docs/governance/requirement-scopes/1365.json index 79bed3a30..d135af546 100644 --- a/docs/governance/requirement-scopes/1365.json +++ b/docs/governance/requirement-scopes/1365.json @@ -66,6 +66,30 @@ "docs/requirements/API-424/requirement.md": [ "API-424" ], + "docs/research/formal-semantic-validation/analysis-v55.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/bundles/retest-v55.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v55.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/index.md": [ + "API-424" + ], + "docs/research/specification-coverage/analysis-v55.json": [ + "API-424" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v55.json": [ + "API-424" + ], + "docs/research/specification-coverage/execution-snapshot-v55.json": [ + "API-424" + ], + "docs/research/specification-coverage/index.md": [ + "API-424" + ], "implementations/python/packages/raes/observability_plane_semantics.py": [ "API-424" ], @@ -105,6 +129,27 @@ "implementations/python/tests/test_issue_1365_applicable_contracts.py": [ "API-424" ], + "implementations/python/tests/test_specification_coverage.py": [ + "API-424" + ], + "tools/check_specification_coverage.py": [ + "API-424" + ], + "tools/formal_semantic_validation/_baseline.py": [ + "API-424" + ], + "tools/formal_semantic_validation/_loading.py": [ + "API-424" + ], + "tools/formal_semantic_validation/_release_revisions.py": [ + "API-424" + ], + "tools/formal_semantic_validation/_releases.py": [ + "API-424" + ], + "tools/formal_semantic_validation/_retest.py": [ + "API-424" + ], "tools/generate_contract_schemas.py": [ "API-424" ], diff --git a/docs/research/formal-semantic-validation/analysis-v55.json b/docs/research/formal-semantic-validation/analysis-v55.json new file mode 100644 index 000000000..38e38d5aa --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v55.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-1365-analysis-v55", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v55.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1365-execution-v55", + "generated_at": "2026-09-27", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained formal corpus does not install a provider or establish live effect realization." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes against the API-424 contract publication source. Bounded claims and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v55.json b/docs/research/formal-semantic-validation/bundles/retest-v55.json new file mode 100644 index 000000000..d5453bb76 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v55.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v55.json", + "analysis_sha256": "c326739dab19732e2070130be3dd2b49b6956a68c32729e6c06af653cb4ce3e5", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "56.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v55.json", + "snapshot_sha256": "4af2686ff2cf7924a9e7c76b8109e0006f92ee3385325adcf2898d45b8cd5a36" +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v55.json b/docs/research/formal-semantic-validation/execution-snapshot-v55.json new file mode 100644 index 000000000..3c83d4338 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v55.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1389-execution-v54", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v54.json", + "release_revision": "55.0.0", + "release_sha256": "89591fe7e90a57a5d6047eb442171a6c1b1fdac7e5cf13ef458a374dd0a54398" + }, + "captured_at": "2026-09-27T18:08:22.209825+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1365-execution-v55", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1365-execution-v55", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "c517c9a4d2ad0d4078a7768f566337923498e9a9", + "source_state": { + "base_revision": "c517c9a4d2ad0d4078a7768f566337923498e9a9", + "checkout_state": "modified", + "implementation_digest": "c52dfd8a90bc5f8f17da64348e72b2180be09b0dd67ac6acee32d1cc1a20ee3e", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/index.md b/docs/research/formal-semantic-validation/index.md index 74cfd2465..1cd2e19c3 100644 --- a/docs/research/formal-semantic-validation/index.md +++ b/docs/research/formal-semantic-validation/index.md @@ -336,7 +336,7 @@ outcomes and claim limits, recording the positive successor's changed result digest; the dangling-reference diagnostic remains identical. It establishes no autonomy threshold, authority grant, or realized attribution. -Current validation requires explicit release 55.0.0, rejects unsupported future +Current validation requires explicit release 56.0.0, rejects unsupported future or duplicate revisions, and never accepts an old/new output-digest pair as a substitute for replay. Historical releases (including the issue-826 supplement) undergo pin, shape, control, and internal-join checks without executing current @@ -506,3 +506,8 @@ Release 55.0.0 is recorded in [`analysis-v54.json`](analysis-v54.json). It replays the retained formal cases after issue #1389 admitted the exact participant inject delivery address as a temporal subject. Outcomes and bounded claim limits remain unchanged. + +Release 56.0.0 replays those formal cases against the API-424 v2 contract +publication source in [`execution-snapshot-v55.json`](execution-snapshot-v55.json) +and [`analysis-v55.json`](analysis-v55.json). Outcomes and bounded claims are +unchanged; provider installation and effect realization are outside this corpus. diff --git a/docs/research/specification-coverage/analysis-v55.json b/docs/research/specification-coverage/analysis-v55.json new file mode 100644 index 000000000..e41cdca5c --- /dev/null +++ b/docs/research/specification-coverage/analysis-v55.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1365-v55", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v55.json", + "docs/research/specification-coverage/analysis-v55.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-27", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "API-424 v2 participant-control publication is verified by its dedicated contract tests, not promoted to a live provider or backend claim by this retained matrix." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays unchanged classifications and claim limits against source that publishes API-424 v2 participant-control contracts. It does not demonstrate runtime adoption.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v55", + "snapshot_sha256": "d825109036e2c78bc2c09e7af43f9561cf2b7aacfcb205656f7d16feadf4ddcb" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v55.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v55.json new file mode 100644 index 000000000..070b7d364 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v55.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v55.json", + "analysis_sha256": "4399c76c151434f2f3d65411f7fb485b0f73a7b1f9d4e38cc5280af5afc975fd", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "55.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v55.json", + "snapshot_sha256": "0d69dc66f6ce1ca62e85107041cb92e39c44b491a482aa2a0599a964d2315211" +} diff --git a/docs/research/specification-coverage/execution-snapshot-v55.json b/docs/research/specification-coverage/execution-snapshot-v55.json new file mode 100644 index 000000000..3f269a09a --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v55.json @@ -0,0 +1,701 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27T18:08:22.209825+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "7587afe3c2a1d6e16b950cea018da396e90edee9c931361284a33b73ad110184", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained offline matrix does not establish provider installation, scheduling, or effect realization." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "c517c9a4d2ad0d4078a7768f566337923498e9a9", + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v55", + "snapshot_revision": "55.0.0", + "source_state": { + "base_revision": "c517c9a4d2ad0d4078a7768f566337923498e9a9", + "checkout_state": "modified", + "implementation_digest": "c52dfd8a90bc5f8f17da64348e72b2180be09b0dd67ac6acee32d1cc1a20ee3e", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/index.md b/docs/research/specification-coverage/index.md index af9bd738b..44863a4d6 100644 --- a/docs/research/specification-coverage/index.md +++ b/docs/research/specification-coverage/index.md @@ -292,7 +292,7 @@ the port scenario. Historical captures and archived example bytes are retained. The matrix classifications and untested concepts are unchanged; no execution authority, successful action, or live backend fidelity is inferred. -Current validation requires release 54.0.0 and rejects duplicate or unsupported +Current validation requires release 55.0.0 and rejects duplicate or unsupported future revisions. It executes current artifacts, requires exact source and package hashes, and checks all passing stage pointers. `source_state` discloses the base Git commit, modified checkout state, and exact implementation digest; @@ -443,3 +443,9 @@ and claim limits remain unchanged; participant delivery timing is verified by its dedicated compiler tests in [`execution-snapshot-v54.json`](execution-snapshot-v54.json) and [`analysis-v54.json`](analysis-v54.json). + +Release 55.0.0 replays the retained matrix against the API-424 v2 contract +publication source in [`execution-snapshot-v55.json`](execution-snapshot-v55.json) +and [`analysis-v55.json`](analysis-v55.json). Classifications and claim limits +are unchanged; provider installation and effect realization remain outside this +offline matrix. diff --git a/implementations/python/tests/test_specification_coverage.py b/implementations/python/tests/test_specification_coverage.py index cb3d4c9fc..85fb6425e 100644 --- a/implementations/python/tests/test_specification_coverage.py +++ b/implementations/python/tests/test_specification_coverage.py @@ -53,7 +53,7 @@ def test_immutable_bundle_index_preserves_concurrent_captures() -> None: bundles = copy_bundle(load_bundles, REPO_ROOT) assert {manifest["revision"] for manifest, *_rest in bundles} >= {"1.0.0", "1.1.0", "19.0.0"} manifest, *_rest = copy_bundle(load_bundle, REPO_ROOT) - assert manifest["revision"] == "54.0.0" + assert manifest["revision"] == "55.0.0" def test_historical_failures_name_the_revision_specific_documents() -> None: diff --git a/tools/check_specification_coverage.py b/tools/check_specification_coverage.py index 39cdc899c..f9370fe6a 100644 --- a/tools/check_specification_coverage.py +++ b/tools/check_specification_coverage.py @@ -157,12 +157,12 @@ def _load_bundle_index(repo_root: Path) -> list[tuple[str, dict[str, object]]]: "51.0.0", "52.0.0", "53.0.0", - } | {"54.0.0"} + } | {"54.0.0", "55.0.0"} if ( - dict(records)[current_path].get("revision") != "54.0.0" + dict(records)[current_path].get("revision") != "55.0.0" or {record.get("revision") for _, record in records} != supported_revisions ): - raise ValueError("coverage evidence requires the explicit current 54.0.0 release and supported history") + raise ValueError("coverage evidence requires the explicit current 55.0.0 release and supported history") return records diff --git a/tools/formal_semantic_validation/_baseline.py b/tools/formal_semantic_validation/_baseline.py index a7b4b7840..c243c73c6 100644 --- a/tools/formal_semantic_validation/_baseline.py +++ b/tools/formal_semantic_validation/_baseline.py @@ -190,6 +190,8 @@ def _selected_baseline_manifest( "52.0.0", "53.0.0", "54.0.0", + "55.0.0", + "56.0.0", }: expected_corpus_path = "docs/research/formal-semantic-validation/corpus/manifest-v4.json" elif baseline_revision in _V3_CORPUS_REVISIONS: diff --git a/tools/formal_semantic_validation/_loading.py b/tools/formal_semantic_validation/_loading.py index c7b8ba558..c56b5f44b 100644 --- a/tools/formal_semantic_validation/_loading.py +++ b/tools/formal_semantic_validation/_loading.py @@ -75,6 +75,6 @@ def load_retest_bundle( if not releases: raise ValueError("the formal semantic-validation index selects no v2 retest release") release = max(releases, key=lambda item: revision_key(item.manifest.get("revision"))) - if release.manifest.get("revision") != "55.0.0" or release.protocol.get("revision") != "2.0.0": - raise ValueError("the current formal evidence release must be the explicit 55.0.0 retest") + if release.manifest.get("revision") != "56.0.0" or release.protocol.get("revision") != "2.0.0": + raise ValueError("the current formal evidence release must be the explicit 56.0.0 retest") return release, release.protocol, release.corpus, release.snapshot, release.analysis diff --git a/tools/formal_semantic_validation/_release_revisions.py b/tools/formal_semantic_validation/_release_revisions.py index b818c373b..b1a838b0d 100644 --- a/tools/formal_semantic_validation/_release_revisions.py +++ b/tools/formal_semantic_validation/_release_revisions.py @@ -53,7 +53,7 @@ "51.0.0", "52.0.0", } -) | {"53.0.0", "54.0.0"} +) | {"53.0.0", "54.0.0", "55.0.0"} -_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"55.0.0"} +_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"56.0.0"} _SOURCE_BOUND_RETEST_REVISIONS = _SUPPORTED_RETEST_REVISIONS - {"3.0.0"} diff --git a/tools/formal_semantic_validation/_releases.py b/tools/formal_semantic_validation/_releases.py index aa4a05987..5e9f92e86 100644 --- a/tools/formal_semantic_validation/_releases.py +++ b/tools/formal_semantic_validation/_releases.py @@ -159,7 +159,7 @@ def validate_release_bundle(repo_root: Path, release: EvidenceRelease) -> list[P release.corpus, release.snapshot, release.analysis, - replay_current=manifest.get("revision") == "55.0.0", + replay_current=manifest.get("revision") == "56.0.0", ) ) else: @@ -272,6 +272,7 @@ def _expected_corpus_revision(release_revision: object) -> str: "53.0.0", "54.0.0", "55.0.0", + "56.0.0", }: expected_corpus_revision = "4.0.0" return expected_corpus_revision @@ -299,7 +300,7 @@ def validate_retest_bundle( return [ _failure( "formal-validation-current-replay-required", - "only releases 3.0.0 through 54.0.0 can use integrated historical validation", + "only releases 3.0.0 through 55.0.0 can use integrated historical validation", snapshot_path, ) ] @@ -427,6 +428,7 @@ def _current_retest_source_failures( "53.0.0": "52.0.0", "54.0.0": "53.0.0", "55.0.0": "54.0.0", + "56.0.0": "55.0.0", }[release_revision] if not isinstance(baseline, Mapping) or baseline.get("release_revision") != expected_baseline: failures.append( diff --git a/tools/formal_semantic_validation/_retest.py b/tools/formal_semantic_validation/_retest.py index cbe06846f..6e6c95161 100644 --- a/tools/formal_semantic_validation/_retest.py +++ b/tools/formal_semantic_validation/_retest.py @@ -37,7 +37,7 @@ ) from tools.policy.common import PolicyFailure -_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 56)) +_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 57)) @dataclasses.dataclass(frozen=True) diff --git a/tools/policy/requirement_order.yaml b/tools/policy/requirement_order.yaml index 80193a5ba..2d32c9907 100644 --- a/tools/policy/requirement_order.yaml +++ b/tools/policy/requirement_order.yaml @@ -838,12 +838,19 @@ ownership: - contracts/schemas/participant-runtime/participant-control-selection-v2.json - docs/decisions/issue-1365-applicable-provider-contracts-preflight.md - docs/governance/requirement-scopes/1365.json + - docs/research/formal-semantic-validation/analysis-v55.json + - docs/research/formal-semantic-validation/bundles/retest-v55.json + - docs/research/formal-semantic-validation/execution-snapshot-v55.json + - docs/research/specification-coverage/analysis-v55.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v55.json + - docs/research/specification-coverage/execution-snapshot-v55.json - implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py - implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py - implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py - implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py - implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py - implementations/python/tests/test_issue_1365_applicable_contracts.py + - tools/formal_semantic_validation/_release_revisions.py - tools/generate_contract_schemas.py - tools/policy/requirement_order.yaml mixed-participant-composition: From 8fef792a5cc3274427c8f851a359c0f54dc7f10b Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 20:22:15 +0200 Subject: [PATCH 05/10] fix: align evidence tests with current captures --- docs/governance/requirement-scopes/1365.json | 6 ++++++ .../python/tests/test_formal_semantic_validation.py | 5 +++-- .../python/tests/test_issue_989_versioned_evidence.py | 6 ++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/governance/requirement-scopes/1365.json b/docs/governance/requirement-scopes/1365.json index d135af546..ff68733c5 100644 --- a/docs/governance/requirement-scopes/1365.json +++ b/docs/governance/requirement-scopes/1365.json @@ -126,9 +126,15 @@ "implementations/python/packages/raes_contracts/manifest_authority.py": [ "API-424" ], + "implementations/python/tests/test_formal_semantic_validation.py": [ + "API-424" + ], "implementations/python/tests/test_issue_1365_applicable_contracts.py": [ "API-424" ], + "implementations/python/tests/test_issue_989_versioned_evidence.py": [ + "API-424" + ], "implementations/python/tests/test_specification_coverage.py": [ "API-424" ], diff --git a/implementations/python/tests/test_formal_semantic_validation.py b/implementations/python/tests/test_formal_semantic_validation.py index 812bf74be..9866d6dad 100644 --- a/implementations/python/tests/test_formal_semantic_validation.py +++ b/implementations/python/tests/test_formal_semantic_validation.py @@ -134,6 +134,7 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: "53.0.0", "54.0.0", "55.0.0", + "56.0.0", ] assert all(validate_release_bundle(REPO_ROOT, release) == [] for release in releases) @@ -142,10 +143,10 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: def test_current_retest_bundle_is_coherent_and_clean() -> None: release, protocol, corpus, snapshot, analysis = copy_bundle(load_retest_bundle, REPO_ROOT) - assert release.manifest["revision"] == "55.0.0" + assert release.manifest["revision"] == "56.0.0" assert protocol["revision"] == "2.0.0" assert corpus["revision"] == "4.0.0" - assert snapshot["baseline"]["release_revision"] == "54.0.0" + assert snapshot["baseline"]["release_revision"] == "55.0.0" assert snapshot["deviations"] == [] assert validate_retest_bundle(REPO_ROOT, release, protocol, corpus, snapshot, analysis) == [] diff --git a/implementations/python/tests/test_issue_989_versioned_evidence.py b/implementations/python/tests/test_issue_989_versioned_evidence.py index 707f08176..42f4f2753 100644 --- a/implementations/python/tests/test_issue_989_versioned_evidence.py +++ b/implementations/python/tests/test_issue_989_versioned_evidence.py @@ -230,7 +230,7 @@ def test_latest_current_release_is_versioned_and_strict(monkeypatch): from tools.formal_semantic_validation._releases import validate_retest_bundle release, protocol, corpus, snapshot, analysis = copy_bundle(load_retest_bundle, ROOT) - assert release.manifest["revision"] == "55.0.0" + assert release.manifest["revision"] == "56.0.0" original = _retest.replay_case def changed_result(root, case): @@ -283,7 +283,7 @@ def test_specification_current_capture_does_not_accept_old_artifact_digest(artif from tools.check_specification_coverage import load_bundle, validate_bundle manifest, protocol, snapshot, analysis = copy_bundle(load_bundle, ROOT) - assert manifest["revision"] == "54.0.0" + assert manifest["revision"] == "55.0.0" snapshot = deepcopy(snapshot) artifact = next(a for a in snapshot["artifacts"] if a["artifact_id"] == artifact_id) artifact["sha256"] = old_digest @@ -507,6 +507,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "53.0.0", "54.0.0", "55.0.0", + "56.0.0", ] if family == "formal" else [ @@ -565,6 +566,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "52.0.0", "53.0.0", "54.0.0", + "55.0.0", ] ) revisions.pop(-1 if removed == "current" else 0) From 55728f50c7e1e796650c13049e3bef8799240db4 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 20:30:10 +0200 Subject: [PATCH 06/10] Update stub manifest fixture for v2 controls --- .../backend-manifest/backend-manifest-v2/valid/stub.json | 2 ++ docs/governance/requirement-scopes/1365.json | 3 +++ 2 files changed, 5 insertions(+) diff --git a/contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json b/contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json index bd0784e14..3b9e19bd0 100644 --- a/contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json +++ b/contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json @@ -516,6 +516,8 @@ "participant-control-occurrence-v1", "participant-control-selection-v1", "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "participant-crossing-occurrence-v1", "participant-flow-control-relation-v1", "participant-lifecycle-event-v1", diff --git a/docs/governance/requirement-scopes/1365.json b/docs/governance/requirement-scopes/1365.json index ff68733c5..b801f797d 100644 --- a/docs/governance/requirement-scopes/1365.json +++ b/docs/governance/requirement-scopes/1365.json @@ -6,6 +6,9 @@ "API-424" ], "bindings": { + "contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json": [ + "API-424" + ], "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json": [ "API-424" ], From f08c22b3b8908b648072b660d85274a94b6c1b29 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 21:06:31 +0200 Subject: [PATCH 07/10] Refactor v2 control validation for quality gate --- docs/governance/requirement-scopes/1365.json | 21 + .../analysis-v56.json | 156 ++++ .../bundles/retest-v56.json | 122 +++ .../execution-snapshot-v56.json | 652 ++++++++++++++++ .../formal-semantic-validation/index.md | 7 +- .../specification-coverage/analysis-v56.json | 105 +++ ...specification-coverage-issue-1365-v56.json | 10 + .../execution-snapshot-v56.json | 701 ++++++++++++++++++ docs/research/specification-coverage/index.md | 8 +- .../contracts/bundle_runtime.py | 17 +- .../participant_control_applicability.py | 162 ++-- .../participant_control_composition_v2.py | 230 ++++++ .../participant_control_decisions_v2.py | 137 +++- .../participant_control_evaluation_v2.py | 456 +++++------- .../participant_control_invocation.py | 155 ++-- .../participant_control_resolution.py | 191 +++-- .../participant_control_support_v2.py | 37 +- .../tests/test_formal_semantic_validation.py | 5 +- .../test_issue_1365_applicable_contracts.py | 36 +- .../test_issue_989_versioned_evidence.py | 6 +- .../tests/test_specification_coverage.py | 2 +- tools/check_specification_coverage.py | 6 +- tools/formal_semantic_validation/_baseline.py | 1 + tools/formal_semantic_validation/_loading.py | 4 +- .../_release_revisions.py | 4 +- tools/formal_semantic_validation/_releases.py | 6 +- tools/formal_semantic_validation/_retest.py | 2 +- tools/policy/requirement_order.yaml | 7 + 28 files changed, 2683 insertions(+), 563 deletions(-) create mode 100644 docs/research/formal-semantic-validation/analysis-v56.json create mode 100644 docs/research/formal-semantic-validation/bundles/retest-v56.json create mode 100644 docs/research/formal-semantic-validation/execution-snapshot-v56.json create mode 100644 docs/research/specification-coverage/analysis-v56.json create mode 100644 docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json create mode 100644 docs/research/specification-coverage/execution-snapshot-v56.json create mode 100644 implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py diff --git a/docs/governance/requirement-scopes/1365.json b/docs/governance/requirement-scopes/1365.json index b801f797d..867acfefa 100644 --- a/docs/governance/requirement-scopes/1365.json +++ b/docs/governance/requirement-scopes/1365.json @@ -72,24 +72,42 @@ "docs/research/formal-semantic-validation/analysis-v55.json": [ "API-424" ], + "docs/research/formal-semantic-validation/analysis-v56.json": [ + "API-424" + ], "docs/research/formal-semantic-validation/bundles/retest-v55.json": [ "API-424" ], + "docs/research/formal-semantic-validation/bundles/retest-v56.json": [ + "API-424" + ], "docs/research/formal-semantic-validation/execution-snapshot-v55.json": [ "API-424" ], + "docs/research/formal-semantic-validation/execution-snapshot-v56.json": [ + "API-424" + ], "docs/research/formal-semantic-validation/index.md": [ "API-424" ], "docs/research/specification-coverage/analysis-v55.json": [ "API-424" ], + "docs/research/specification-coverage/analysis-v56.json": [ + "API-424" + ], "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v55.json": [ "API-424" ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json": [ + "API-424" + ], "docs/research/specification-coverage/execution-snapshot-v55.json": [ "API-424" ], + "docs/research/specification-coverage/execution-snapshot-v56.json": [ + "API-424" + ], "docs/research/specification-coverage/index.md": [ "API-424" ], @@ -111,6 +129,9 @@ "implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py": [ "API-424" ], + "implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py": [ + "API-424" + ], "implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py": [ "API-424" ], diff --git a/docs/research/formal-semantic-validation/analysis-v56.json b/docs/research/formal-semantic-validation/analysis-v56.json new file mode 100644 index 000000000..f99f9011d --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v56.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-1365-analysis-v56", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v56.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1365-execution-v56", + "generated_at": "2026-09-27", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained formal corpus does not install a provider or establish live effect realization." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes against the API-424 contract publication source. Bounded claims and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v56.json b/docs/research/formal-semantic-validation/bundles/retest-v56.json new file mode 100644 index 000000000..065b84fae --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v56.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v56.json", + "analysis_sha256": "2b71d39dc7e588df79b43d4407abd548e895ded01d34b63f97411f1f4b7b8f68", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "57.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v56.json", + "snapshot_sha256": "5deaf16c67b8c86a8aa7b11ad41046ec3a4231f1444d78b56f595eb5dd68f2f1" +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v56.json b/docs/research/formal-semantic-validation/execution-snapshot-v56.json new file mode 100644 index 000000000..ae3416d83 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v56.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1365-execution-v55", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v55.json", + "release_revision": "56.0.0", + "release_sha256": "8590fc247aed06864d934bb028ea33c0764c5ae2810fe4ce542af46ba284caac" + }, + "captured_at": "2026-09-27T19:01:34.868462+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1365-execution-v56", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "55728f50c7e1e796650c13049e3bef8799240db4", + "source_state": { + "base_revision": "55728f50c7e1e796650c13049e3bef8799240db4", + "checkout_state": "modified", + "implementation_digest": "511704c3267769662412f0142f83df5a6203383c4f6e1d5cad7a4da58a64cb25", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/index.md b/docs/research/formal-semantic-validation/index.md index 1cd2e19c3..32ad81936 100644 --- a/docs/research/formal-semantic-validation/index.md +++ b/docs/research/formal-semantic-validation/index.md @@ -336,7 +336,7 @@ outcomes and claim limits, recording the positive successor's changed result digest; the dangling-reference diagnostic remains identical. It establishes no autonomy threshold, authority grant, or realized attribution. -Current validation requires explicit release 56.0.0, rejects unsupported future +Current validation requires explicit release 57.0.0, rejects unsupported future or duplicate revisions, and never accepts an old/new output-digest pair as a substitute for replay. Historical releases (including the issue-826 supplement) undergo pin, shape, control, and internal-join checks without executing current @@ -511,3 +511,8 @@ Release 56.0.0 replays those formal cases against the API-424 v2 contract publication source in [`execution-snapshot-v55.json`](execution-snapshot-v55.json) and [`analysis-v55.json`](analysis-v55.json). Outcomes and bounded claims are unchanged; provider installation and effect realization are outside this corpus. + +Release 57.0.0 replays those formal cases against the API-424 v2 contract +refactor in [`execution-snapshot-v56.json`](execution-snapshot-v56.json) and +[`analysis-v56.json`](analysis-v56.json). Outcomes and bounded claims remain +unchanged; provider installation and effect realization are outside this corpus. diff --git a/docs/research/specification-coverage/analysis-v56.json b/docs/research/specification-coverage/analysis-v56.json new file mode 100644 index 000000000..715bdd124 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v56.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1365-v56", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v56.json", + "docs/research/specification-coverage/analysis-v56.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "directly-expressible": 10, + "profile-or-manifest-constraint": 2, + "deliberately-backend-specific": 1, + "missing": 3 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-27", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "API-424 v2 participant-control publication is verified by its dedicated contract tests, not promoted to a live provider or backend claim by this retained matrix." + ], + "load_bearing_results": { + "total": 10, + "passed": 10, + "failed": 0, + "missing": 0 + }, + "plain_language_outcome": "The retained specification matrix replays unchanged classifications and claim limits against source that publishes API-424 v2 participant-control contracts. It does not demonstrate runtime adoption.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "request_id": "survey-representative-range", + "status": "demonstrated", + "concept_count": 6, + "missing_count": 0, + "failed_stage_count": 0 + }, + { + "request_id": "cyborg-participant-evaluation", + "status": "partial", + "concept_count": 5, + "missing_count": 1, + "failed_stage_count": 1 + }, + { + "request_id": "vsdl-configurable-infrastructure", + "status": "partial", + "concept_count": 3, + "missing_count": 1, + "failed_stage_count": 1 + }, + { + "request_id": "cyber-dem-federation", + "status": "partial", + "concept_count": 2, + "missing_count": 1, + "failed_stage_count": 1 + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v56", + "snapshot_sha256": "8eae117dd7b506342ac927517aae8bba6fb7096d5280b5648bbfb07de8d89691" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json new file mode 100644 index 000000000..22c8141e6 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v56.json", + "analysis_sha256": "7a3d579dbae48c2b75f2aa60df7526814e61bd6c8335f71e8d629c47a4b167cb", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "56.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v56.json", + "snapshot_sha256": "6cd07a2e419e855116f159b5ec43e47a59d2db8936a55b8d32b76452c303671d" +} diff --git a/docs/research/specification-coverage/execution-snapshot-v56.json b/docs/research/specification-coverage/execution-snapshot-v56.json new file mode 100644 index 000000000..e429a9274 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v56.json @@ -0,0 +1,701 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27T19:01:34.868462+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "d4b7b27f1ce0e20902e13745d85d5326b641823a02002962b23e80eaadabab9a", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained offline matrix does not establish provider installation, scheduling, or effect realization." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "55728f50c7e1e796650c13049e3bef8799240db4", + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v56", + "snapshot_revision": "56.0.0", + "source_state": { + "base_revision": "55728f50c7e1e796650c13049e3bef8799240db4", + "checkout_state": "modified", + "implementation_digest": "511704c3267769662412f0142f83df5a6203383c4f6e1d5cad7a4da58a64cb25", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/index.md b/docs/research/specification-coverage/index.md index 44863a4d6..8a4817653 100644 --- a/docs/research/specification-coverage/index.md +++ b/docs/research/specification-coverage/index.md @@ -292,7 +292,7 @@ the port scenario. Historical captures and archived example bytes are retained. The matrix classifications and untested concepts are unchanged; no execution authority, successful action, or live backend fidelity is inferred. -Current validation requires release 55.0.0 and rejects duplicate or unsupported +Current validation requires release 56.0.0 and rejects duplicate or unsupported future revisions. It executes current artifacts, requires exact source and package hashes, and checks all passing stage pointers. `source_state` discloses the base Git commit, modified checkout state, and exact implementation digest; @@ -449,3 +449,9 @@ publication source in [`execution-snapshot-v55.json`](execution-snapshot-v55.jso and [`analysis-v55.json`](analysis-v55.json). Classifications and claim limits are unchanged; provider installation and effect realization remain outside this offline matrix. + +Release 56.0.0 replays that matrix against the API-424 v2 contract refactor in +[`execution-snapshot-v56.json`](execution-snapshot-v56.json) and +[`analysis-v56.json`](analysis-v56.json). Classifications and claim limits remain +unchanged; provider installation and effect realization remain outside this +offline matrix. diff --git a/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py b/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py index 7461b4747..c0cb8a142 100644 --- a/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py +++ b/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py @@ -69,15 +69,14 @@ def _participant_control_schema_bundle() -> dict[str, dict[str, Any]]: for contract_id, model in roots.items(): schema = model.model_json_schema() _add_raes_plane(schema, contract_id) - validator = ( - "raes_contracts.contracts.validate_participant_control_resolved_context_v2" - if contract_id == "participant-control-evaluation-v2" - else "raes_contracts.contracts.validate_participant_control_resolved_context" - if "evaluation" in contract_id - else "raes_contracts.contracts.ParticipantControlSelectionV2Model.model_validate" - if contract_id.endswith("selection-v2") - else "raes_contracts.contracts.ParticipantControlSelectionModel.model_validate" - ) + if contract_id == "participant-control-evaluation-v2": + validator = "raes_contracts.contracts.validate_participant_control_resolved_context_v2" + elif "evaluation" in contract_id: + validator = "raes_contracts.contracts.validate_participant_control_resolved_context" + elif contract_id.endswith("selection-v2"): + validator = "raes_contracts.contracts.ParticipantControlSelectionV2Model.model_validate" + else: + validator = "raes_contracts.contracts.ParticipantControlSelectionModel.model_validate" _add_raes_invariant( schema, "participant-control-resolved-context" diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py b/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py index 0d3586e9a..fe9737929 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py @@ -151,69 +151,103 @@ class ParticipantControlRequestV2Model(ContractModel): @model_validator(mode="after") def _exact_cut(self) -> Self: - selection, context, applicability = self.selection, self.context, self.applicability - if selection.apparatus != context.apparatus: - raise ValueError("participant control apparatus binding differs") - if applicability.selection_digest != control_digest( - selection - ) or applicability.context_digest != control_digest(context): - raise ValueError("participant control applicability has a different admitted identity or cut") - slots = {slot.slot_id: slot for slot in selection.slots} - bindings = {binding.instance_id: binding for binding in selection.bindings} - obligations = {item.obligation_id: item for item in selection.obligations} - coverage = {item.obligation_id: item for item in applicability.coverage} - require_unique(tuple(item.obligation_id for item in applicability.coverage)) - if coverage.keys() != obligations.keys(): - raise ValueError("participant control coverage is incomplete") - applicable = set(applicability.applicable_slot_ids) - required = set(applicability.required_slot_ids) - require_unique(applicability.applicable_slot_ids) - require_unique(applicability.required_slot_ids) - if applicable - slots.keys() or required - applicable: - raise ValueError("participant control applicable slots are not admitted") - roots: set[str] = set() - for item in applicability.coverage: + _validate_request_identity(self) + _validate_required_closure(self) + _validate_coverage(self) + _validate_context_bindings(self) + _validate_causal_bounds(self) + return self + + +def _validate_request_identity(request: ParticipantControlRequestV2Model) -> None: + selection, context, applicability = request.selection, request.context, request.applicability + if selection.apparatus != context.apparatus: + raise ValueError("participant control apparatus binding differs") + if applicability.selection_digest != control_digest(selection) or applicability.context_digest != control_digest( + context + ): + raise ValueError("participant control applicability has a different admitted identity or cut") + + +def _validate_coverage(request: ParticipantControlRequestV2Model) -> None: + selection, applicability = request.selection, request.applicability + slots = {slot.slot_id: slot for slot in selection.slots} + bindings = {binding.instance_id: binding for binding in selection.bindings} + obligations = {item.obligation_id: item for item in selection.obligations} + coverage = {item.obligation_id: item for item in applicability.coverage} + require_unique(tuple(item.obligation_id for item in applicability.coverage)) + if coverage.keys() != obligations.keys(): + raise ValueError("participant control coverage is incomplete") + applicable = set(applicability.applicable_slot_ids) + for item in applicability.coverage: + if item.status == "applies": obligation = obligations[item.obligation_id] - if item.status == "applies": - for identity in item.slot_ids: - if identity not in applicable or slots[identity].kind != obligation.required_kind: - raise ValueError("participant control coverage does not map to an applicable typed slot") - if obligation.profile not in bindings[slots[identity].instance_id].profiles: - raise ValueError("participant control coverage slot does not select its owning profile") - roots.update(item.slot_ids) - roots.update( - slot.slot_id for slot in selection.slots if slot.role == "mandatory" and slot.slot_id in applicable - ) - graph = {slot.slot_id: {dep.slot_id for dep in slot.dependencies} for slot in selection.slots} - for identity in reversed(tuple(TopologicalSorter(graph).static_order())): - if identity in roots: - roots.update(graph[identity]) - if roots != required or any(not graph[identity] <= applicable for identity in applicable): - raise ValueError("participant control required input closure or applicable subset is incomplete") - matching_instances = {slots[identity].instance_id for identity in applicable} - if {state.instance_id for state in context.provider_states} != matching_instances: - raise ValueError("participant control provider-state coverage is incomplete") - for instance in matching_instances: - binding = bindings[instance] - if binding.memory_scope != context.memory_scope or not any( - (a.participant_address, a.episode_id, a.direction, a.sink_ref, a.phase_ref, a.subject_kind) - == ( - context.participant_address, - context.episode_id, - context.direction, - context.sink_ref, - context.phase_ref, - context.subject.subject_kind, - ) - for a in binding.applicability - ): - raise ValueError("participant control applicable binding does not match the exact crossing") - bounds = selection.bounds - if ( - context.depth > bounds.max_depth - or context.effects_consumed > bounds.max_effects - or context.attempt > bounds.max_attempts - or context.order > bounds.expires_at_order + for identity in item.slot_ids: + if identity not in applicable or slots[identity].kind != obligation.required_kind: + raise ValueError("participant control coverage does not map to an applicable typed slot") + if obligation.profile not in bindings[slots[identity].instance_id].profiles: + raise ValueError("participant control coverage slot does not select its owning profile") + + +def _coverage_roots(request: ParticipantControlRequestV2Model) -> set[str]: + applicable = set(request.applicability.applicable_slot_ids) + roots = { + slot_id for item in request.applicability.coverage if item.status == "applies" for slot_id in item.slot_ids + } + roots.update( + slot.slot_id for slot in request.selection.slots if slot.role == "mandatory" and slot.slot_id in applicable + ) + return roots + + +def _validate_required_closure(request: ParticipantControlRequestV2Model) -> None: + applicability = request.applicability + slots = {slot.slot_id: slot for slot in request.selection.slots} + applicable = set(applicability.applicable_slot_ids) + required = set(applicability.required_slot_ids) + require_unique(applicability.applicable_slot_ids) + require_unique(applicability.required_slot_ids) + if applicable - slots.keys() or required - applicable: + raise ValueError("participant control applicable slots are not admitted") + roots = _coverage_roots(request) + graph = {slot.slot_id: {dep.slot_id for dep in slot.dependencies} for slot in request.selection.slots} + for identity in reversed(tuple(TopologicalSorter(graph).static_order())): + if identity in roots: + roots.update(graph[identity]) + if roots != required or any(not graph[identity] <= applicable for identity in applicable): + raise ValueError("participant control required input closure or applicable subset is incomplete") + + +def _validate_context_bindings(request: ParticipantControlRequestV2Model) -> None: + selection, context, applicability = request.selection, request.context, request.applicability + slots = {slot.slot_id: slot for slot in selection.slots} + bindings = {binding.instance_id: binding for binding in selection.bindings} + matching_instances = {slots[identity].instance_id for identity in applicability.applicable_slot_ids} + if {state.instance_id for state in context.provider_states} != matching_instances: + raise ValueError("participant control provider-state coverage is incomplete") + for instance in matching_instances: + binding = bindings[instance] + if binding.memory_scope != context.memory_scope or not any( + (a.participant_address, a.episode_id, a.direction, a.sink_ref, a.phase_ref, a.subject_kind) + == ( + context.participant_address, + context.episode_id, + context.direction, + context.sink_ref, + context.phase_ref, + context.subject.subject_kind, + ) + for a in binding.applicability ): - raise ValueError("participant control causal bounds are exhausted") - return self + raise ValueError("participant control applicable binding does not match the exact crossing") + + +def _validate_causal_bounds(request: ParticipantControlRequestV2Model) -> None: + context, bounds = request.context, request.selection.bounds + if ( + context.depth > bounds.max_depth + or context.effects_consumed > bounds.max_effects + or context.attempt > bounds.max_attempts + or context.order > bounds.expires_at_order + ): + raise ValueError("participant control causal bounds are exhausted") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py new file mode 100644 index 000000000..869082b5d --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py @@ -0,0 +1,230 @@ +"""Mandatory v2 participant-control composition and effect admission.""" + +from __future__ import annotations + +from typing import Literal, Self + +from pydantic import ConfigDict, model_validator + +from .base import ContractModel +from .participant_control_applicability import ParticipantControlRequestV2Model, control_digest +from .participant_control_coordinates import ControlRefs, require_unique +from .participant_control_decisions_v2 import ControlEffectPlanV2Model, base_parent_disposition_v2 +from .participant_control_invocation import ( + ControlLostAdviceV2Model, + ControlMechanismResultV2Model, + _satisfied, +) +from .participant_control_support_v2 import ControlSupportAssessmentV2Model + +_INPUT_PREFIX = "input:" + + +class ControlCompositionV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + rule_revision: Literal["participant-control-applicability/rev1"] + disposition: Literal["eligible", "deny", "withhold", "unsupported", "stale", "weakened", "failed", "conflict"] + blockers: ControlRefs + contributing_result_ids: ControlRefs + lost_advice_slot_ids: ControlRefs + + @model_validator(mode="after") + def _canonical(self) -> Self: + for values in (self.blockers, self.contributing_result_ids, self.lost_advice_slot_ids): + if values != tuple(sorted(set(values))): + raise ValueError("composition evidence must be canonical and unique") + return self + + +def _required_support_keys(request: ParticipantControlRequestV2Model) -> set[tuple[str, str]]: + slots = {slot.slot_id: slot for slot in request.selection.slots} + roots: set[str] = set() + required: set[tuple[str, str]] = set() + for item in request.applicability.coverage: + if item.status != "applies": + continue + for slot_id in item.slot_ids: + roots.add(slot_id) + required.add((item.obligation_id, slots[slot_id].instance_id)) + for slot_id in request.applicability.required_slot_ids: + if slot_id not in roots: + required.add((_INPUT_PREFIX + slot_id, slots[slot_id].instance_id)) + return required + + +def derive_control_composition_v2( + request: ParticipantControlRequestV2Model, + results: tuple[ControlMechanismResultV2Model, ...] | list[ControlMechanismResultV2Model | dict], + support: tuple[ControlSupportAssessmentV2Model, ...] | list[ControlSupportAssessmentV2Model | dict], + lost_advice: tuple[ControlLostAdviceV2Model, ...] | list[ControlLostAdviceV2Model | dict], + effect_plan: ControlEffectPlanV2Model, +) -> ControlCompositionV2Model: + """One canonical summary of mandatory availability and parent decisions.""" + + results = tuple(ControlMechanismResultV2Model.model_validate(item) for item in results) + support = tuple(ControlSupportAssessmentV2Model.model_validate(item) for item in support) + lost_advice = tuple(ControlLostAdviceV2Model.model_validate(item) for item in lost_advice) + require_unique(tuple(item.slot_id for item in results)) + require_unique(tuple((item.obligation_id, item.instance_id) for item in support)) + require_unique(tuple(item.slot_id for item in lost_advice)) + if {(item.obligation_id, item.instance_id) for item in support} != _required_support_keys(request): + raise ValueError("participant control required support coverage is incomplete") + _validate_support_bindings(request, support) + failed_optional = _validate_lost_advice(request, results, lost_advice) + blockers, causes = _composition_causes(request, results, support, effect_plan) + disposition = next( + ( + item + for item in ("conflict", "stale", "unsupported", "weakened", "deny", "withhold", "failed") + if item in causes + ), + "eligible", + ) + return ControlCompositionV2Model( + rule_revision="participant-control-applicability/rev1", + disposition=disposition, + blockers=tuple(sorted(blockers)), + contributing_result_ids=tuple(sorted(item.result_id for item in results)), + lost_advice_slot_ids=tuple(sorted(failed_optional)), + ) + + +def _validate_support_bindings( + request: ParticipantControlRequestV2Model, support: tuple[ControlSupportAssessmentV2Model, ...] +) -> None: + cut_digest = control_digest(request.context) + obligations = {item.obligation_id: item for item in request.selection.obligations} + for item in support: + if item.effective_support.context_digest != cut_digest: + raise ValueError("participant control support uses a different state cut") + obligation = obligations.get(item.obligation_id) + if obligation is not None and ( + item.required_strength != obligation.required_strength + or item.required_constraints != obligation.constraints + ): + raise ValueError("participant control support differs from the admitted obligation") + if item.obligation_id.startswith(_INPUT_PREFIX): + slot = next( + (slot for slot in request.selection.slots if _INPUT_PREFIX + slot.slot_id == item.obligation_id), None + ) + if slot is None or (item.required_strength, item.required_constraints) != ( + slot.required_strength, + slot.constraints, + ): + raise ValueError("participant control support differs from the admitted input pin") + + +def _validate_lost_advice( + request: ParticipantControlRequestV2Model, + results: tuple[ControlMechanismResultV2Model, ...], + lost_advice: tuple[ControlLostAdviceV2Model, ...], +) -> set[str]: + required_slots = set(request.applicability.required_slot_ids) + failed_optional = {item.slot_id for item in results if item.slot_id not in required_slots and not _satisfied(item)} + if {item.slot_id for item in lost_advice} != failed_optional: + raise ValueError("optional failure must have exactly one safe lost-advice record") + by_slot = {item.slot_id: item for item in results} + for item in lost_advice: + result = by_slot[item.slot_id] + if result.next_provider_state is not None or result.payload is not None: + raise ValueError("discarded optional contribution cannot carry state or effect payload") + return failed_optional + + +def _composition_causes( + request: ParticipantControlRequestV2Model, + results: tuple[ControlMechanismResultV2Model, ...], + support: tuple[ControlSupportAssessmentV2Model, ...], + effect_plan: ControlEffectPlanV2Model, +) -> tuple[set[str], set[str]]: + blockers: set[str] = set() + causes: set[str] = set() + for coverage in request.applicability.coverage: + if coverage.status == "unresolved": + blockers.add("coverage:" + coverage.obligation_id) + causes.add("withhold") + _required_result_causes(request, results, blockers, causes) + _support_causes(support, blockers, causes) + if effect_plan.parent_disposition != "permit": + blockers.add("parent:" + effect_plan.parent_disposition) + causes.add(effect_plan.parent_disposition) + return blockers, causes + + +def _complete_refusal(result: ControlMechanismResultV2Model) -> bool: + return ( + result.status == "resolved" + and result.payload is not None + and result.payload.kind == "decision" + and result.payload.disposition in {"deny", "withhold"} + ) + + +def _required_result_causes( + request: ParticipantControlRequestV2Model, + results: tuple[ControlMechanismResultV2Model, ...], + blockers: set[str], + causes: set[str], +) -> None: + by_slot = {item.slot_id: item for item in results} + required_slots = set(request.applicability.required_slot_ids) + for slot_id in required_slots: + result = by_slot[slot_id] + if _satisfied(result) or _complete_refusal(result): + continue + blockers.add("slot:" + slot_id) + causes.add(_required_result_cause(result)) + + +def _required_result_cause(result: ControlMechanismResultV2Model) -> str: + if result.status in {"stale", "unsupported", "weakened"}: + return result.status + if result.payload is not None and result.payload.kind == "decision": + return result.payload.disposition if result.payload.disposition != "abstain" else "failed" + return "failed" + + +def _support_causes(support: tuple[ControlSupportAssessmentV2Model, ...], blockers: set[str], causes: set[str]) -> None: + for item in support: + if not item.satisfied: + blockers.add("support:" + item.obligation_id + ":" + item.instance_id) + causes.add(_support_cause(item)) + + +def _support_cause(item: ControlSupportAssessmentV2Model) -> str: + observed = item.effective_support + if observed.status in {"stale", "unsupported", "weakened"}: + return observed.status + if observed.effective_level == "unsupported": + return "unsupported" + if observed.status == "resolved": + return "weakened" + return "failed" + + +def admitted_control_effect_ids_v2( + composition: ControlCompositionV2Model, plan: ControlEffectPlanV2Model +) -> frozenset[str]: + """Join decision-local effect readiness to mandatory composition.""" + + if composition.disposition == "eligible" and plan.parent_disposition == "permit": + return frozenset(plan.runnable_effect_ids) + if set(composition.blockers) != {"parent:" + plan.parent_disposition}: + return frozenset() + base = base_parent_disposition_v2(plan.decisions, plan.incumbent_gate_disposition) + admitted: set[str] = set() + if plan.parent_disposition == "withhold" and base == "permit": + admitted.update( + item.effect_id + for item in plan.effects + if item.phase == "required-predecessor" and item.effect_id in plan.runnable_effect_ids + ) + if base in {"deny", "withhold"} and plan.parent_disposition == base: + admitted.update( + item.effect_id + for item in plan.effects + if item.phase == "independent" + and base in item.allowed_parent_dispositions + and item.effect_id in plan.runnable_effect_ids + ) + return frozenset(admitted) diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py index 348d33c0c..ef067381f 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py @@ -3,7 +3,7 @@ from __future__ import annotations from graphlib import CycleError, TopologicalSorter -from typing import Annotated, Literal, Self +from typing import Annotated, Literal, NotRequired, Self, TypedDict, Unpack from pydantic import ConfigDict, Field, model_validator @@ -40,6 +40,16 @@ Field(discriminator="kind"), ] +_PARENT_ADMITTED = "@parent-admitted" + + +class _EffectPlanOptions(TypedDict): + parent_admission_receipt: NotRequired[ControlArtifactReferenceModel | dict | None] + parent_application_receipt: NotRequired[ControlArtifactReferenceModel | dict | None] + effect_outcomes: NotRequired[ + tuple[ControlRealizationBindingModel, ...] | list[ControlRealizationBindingModel | dict] + ] + class ControlParentDecisionV2Model(ContractModel): """A decision about the exact parent, never a scheduled operation.""" @@ -72,15 +82,21 @@ def _phase(self) -> Self: require_unique(self.allowed_parent_dispositions) if any(item not in {"permit", "deny", "withhold"} for item in self.allowed_parent_dispositions): raise ValueError("independent consequence has an unknown parent disposition") - if self.phase == "required-predecessor" and (self.parent_outcome != "none" or self.allowed_parent_dispositions): - raise ValueError("required predecessor cannot await its own parent outcome") - if self.phase == "success-dependent" and (self.parent_outcome == "none" or self.allowed_parent_dispositions): - raise ValueError("success-dependent consequence needs an exact parent outcome") - if self.phase == "independent" and (self.parent_outcome != "none" or not self.allowed_parent_dispositions): - raise ValueError("independent consequence needs allowed parent dispositions") + _validate_effect_phase(self) return self +def _validate_effect_phase(effect: ControlEffectRequestV2Model) -> None: + if effect.phase == "required-predecessor" and ( + effect.parent_outcome != "none" or effect.allowed_parent_dispositions + ): + raise ValueError("required predecessor cannot await its own parent outcome") + if effect.phase == "success-dependent" and (effect.parent_outcome == "none" or effect.allowed_parent_dispositions): + raise ValueError("success-dependent consequence needs an exact parent outcome") + if effect.phase == "independent" and (effect.parent_outcome != "none" or not effect.allowed_parent_dispositions): + raise ValueError("independent consequence needs allowed parent dispositions") + + def base_parent_disposition_v2( decisions: tuple[ControlParentDecisionV2Model, ...], incumbent_gate_disposition: str ) -> str: @@ -107,6 +123,30 @@ def _decision_and_runnable( require_unique(tuple(item.effect_id for item in effects)) require_unique(tuple(item.key for item in effects)) require_unique(realized_effect_ids) + _validate_parent_receipts(parent_applied, parent_admission_receipt, parent_application_receipt) + _validate_realized_effects(effects, realized_effect_ids) + graph = _effect_graph(effects) + _validate_effect_conflicts(effects) + base = base_parent_disposition_v2(decisions, incumbent_gate_disposition) + pending = graph[_PARENT_ADMITTED] - set(realized_effect_ids) + final = "withhold" if base == "permit" and pending else base + runnable = tuple( + sorted( + item.effect_id + for item in effects + if _effect_is_runnable( + item, realized_effect_ids, base, final, pending, parent_applied, parent_admission_receipt + ) + ) + ) + return final, runnable + + +def _validate_parent_receipts( + parent_applied: bool, + parent_admission_receipt: ControlArtifactReferenceModel | None, + parent_application_receipt: ControlArtifactReferenceModel | None, +) -> None: if parent_applied and parent_application_receipt is None: raise ValueError("applied parent requires an exact owner receipt") if parent_applied and parent_admission_receipt is None: @@ -116,6 +156,11 @@ def _decision_and_runnable( for receipt in (parent_admission_receipt, parent_application_receipt): if receipt is not None: require_kind(receipt, "receipt") + + +def _validate_realized_effects( + effects: tuple[ControlEffectRequestV2Model, ...], realized_effect_ids: tuple[str, ...] +) -> None: by_id = {item.effect_id: item for item in effects} if not set(realized_effect_ids) <= by_id.keys(): raise ValueError("realized effect is absent from the plan") @@ -124,9 +169,12 @@ def _decision_and_runnable( for effect_id in realized_effect_ids ): raise ValueError("realized effect lacks an applied predecessor") + + +def _effect_graph(effects: tuple[ControlEffectRequestV2Model, ...]) -> dict[str, set[str]]: graph: dict[str, set[str]] = {item.effect_id: set(item.predecessor_effect_ids) for item in effects} - graph["@parent-admitted"] = {item.effect_id for item in effects if item.phase == "required-predecessor"} - graph["@parent-applied"] = {"@parent-admitted"} + graph[_PARENT_ADMITTED] = {item.effect_id for item in effects if item.phase == "required-predecessor"} + graph["@parent-applied"] = {_PARENT_ADMITTED} for item in effects: if item.phase == "success-dependent": graph[item.effect_id].add("@parent-" + item.parent_outcome) @@ -136,29 +184,48 @@ def _decision_and_runnable( tuple(TopologicalSorter(graph).static_order()) except CycleError: raise ValueError("combined parent/effect prerequisite cycle") from None + return graph + + +def _validate_effect_conflicts(effects: tuple[ControlEffectRequestV2Model, ...]) -> None: for index, left in enumerate(effects): for right in effects[index + 1 :]: if _effect_subject(left.target) != _effect_subject(right.target): continue if _same_subject_conflicts(left.target, right.target): raise ValueError("effect target conflict cannot be repaired by ordering") - base = base_parent_disposition_v2(decisions, incumbent_gate_disposition) - pending = graph["@parent-admitted"] - set(realized_effect_ids) - final = "withhold" if base == "permit" and pending else base - runnable = [] - for item in effects: - if item.effect_id in realized_effect_ids or not set(item.predecessor_effect_ids) <= set(realized_effect_ids): - continue - if item.phase == "required-predecessor" and base == "permit": - runnable.append(item.effect_id) - elif item.phase == "success-dependent" and base == "permit" and not pending: - if (item.parent_outcome == "admitted" and parent_admission_receipt is not None) or ( - item.parent_outcome == "applied" and parent_applied - ): - runnable.append(item.effect_id) - elif item.phase == "independent" and final in item.allowed_parent_dispositions: - runnable.append(item.effect_id) - return final, tuple(sorted(runnable)) + + +def _success_dependent_ready( + item: ControlEffectRequestV2Model, + parent_applied: bool, + parent_admission_receipt: ControlArtifactReferenceModel | None, +) -> bool: + return (item.parent_outcome == "admitted" and parent_admission_receipt is not None) or ( + item.parent_outcome == "applied" and parent_applied + ) + + +def _effect_is_runnable( + item: ControlEffectRequestV2Model, + realized_effect_ids: tuple[str, ...], + base: str, + final: str, + pending: set[str], + parent_applied: bool, + parent_admission_receipt: ControlArtifactReferenceModel | None, +) -> bool: + if item.effect_id in realized_effect_ids or not set(item.predecessor_effect_ids) <= set(realized_effect_ids): + return False + if item.phase == "required-predecessor": + return base == "permit" + if item.phase == "success-dependent": + return ( + base == "permit" + and not pending + and _success_dependent_ready(item, parent_applied, parent_admission_receipt) + ) + return item.phase == "independent" and final in item.allowed_parent_dispositions class ControlEffectPlanV2Model(ContractModel): @@ -207,25 +274,23 @@ def derive_control_effect_plan_v2( incumbent_gate_disposition: str, realized_effect_ids: tuple[str, ...], parent_applied: bool, - parent_admission_receipt: ControlArtifactReferenceModel | dict | None = None, - parent_application_receipt: ControlArtifactReferenceModel | dict | None = None, - effect_outcomes: tuple[ControlRealizationBindingModel, ...] | list[ControlRealizationBindingModel | dict] = (), + **options: Unpack[_EffectPlanOptions], ) -> ControlEffectPlanV2Model: """Derive the recorded plan without dispatching or claiming realization.""" decisions = tuple(ControlParentDecisionV2Model.model_validate(item) for item in decisions) effects = tuple(ControlEffectRequestV2Model.model_validate(item) for item in effects) + admission_receipt = options.get("parent_admission_receipt") + application_receipt = options.get("parent_application_receipt") parent_admission_receipt = ( - ControlArtifactReferenceModel.model_validate(parent_admission_receipt) - if parent_admission_receipt is not None - else None + ControlArtifactReferenceModel.model_validate(admission_receipt) if admission_receipt is not None else None ) parent_application_receipt = ( - ControlArtifactReferenceModel.model_validate(parent_application_receipt) - if parent_application_receipt is not None - else None + ControlArtifactReferenceModel.model_validate(application_receipt) if application_receipt is not None else None + ) + effect_outcomes = tuple( + ControlRealizationBindingModel.model_validate(item) for item in options.get("effect_outcomes", ()) ) - effect_outcomes = tuple(ControlRealizationBindingModel.model_validate(item) for item in effect_outcomes) final, runnable = _decision_and_runnable( decisions, effects, diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py index 32f8abd28..278e31574 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py @@ -14,6 +14,11 @@ ParticipantControlRequestV2Model, control_digest, ) +from .participant_control_composition_v2 import ( + ControlCompositionV2Model, + admitted_control_effect_ids_v2, + derive_control_composition_v2, +) from .participant_control_coordinates import ( ControlArtifactReferenceModel, ControlCount, @@ -24,7 +29,7 @@ require_kind, require_unique, ) -from .participant_control_decisions_v2 import ControlEffectPlanV2Model, base_parent_disposition_v2 +from .participant_control_decisions_v2 import ControlEffectPlanV2Model from .participant_control_effect_composition import ( _effect_ancestors, _lifecycle_invalidates, @@ -35,7 +40,6 @@ ControlInvocationV2Model, ControlLostAdviceV2Model, ControlMechanismResultV2Model, - _satisfied, validate_control_invocations_v2, ) from .participant_control_selection import ControlCausalBoundsModel @@ -71,27 +75,35 @@ def validate_state_proposals_v2(proposals: tuple[ControlStateProposalV2Model, .. for item in records: by_scope[item.scope].append(item) for writers in by_scope.values(): - if len(writers) <= 1: - if writers and writers[0].predecessor_invocation_id is not None: - raise ValueError("state conflict: tentative predecessor is absent") - continue - roots = [item for item in writers if item.predecessor_invocation_id is None] - if len(roots) != 1: - raise ValueError("state conflict: same-scope writers need one serial root") - by_parent = { - item.predecessor_invocation_id: item for item in writers if item.predecessor_invocation_id is not None - } - if len(by_parent) != len(writers) - 1: - raise ValueError("state conflict: tentative state writer fork") - current, seen = roots[0], {roots[0].invocation_id} - while current.invocation_id in by_parent: - successor = by_parent[current.invocation_id] - if successor.invocation_id in seen or successor.before != current.after: - raise ValueError("state conflict: tentative state chain differs") - seen.add(successor.invocation_id) - current = successor - if len(seen) != len(writers): - raise ValueError("state conflict: same-scope writers are unordered") + _validate_scope_writers(writers) + + +def _validate_scope_writers(writers: list[ControlStateProposalV2Model]) -> None: + if len(writers) <= 1: + if writers and writers[0].predecessor_invocation_id is not None: + raise ValueError("state conflict: tentative predecessor is absent") + return + roots = [item for item in writers if item.predecessor_invocation_id is None] + if len(roots) != 1: + raise ValueError("state conflict: same-scope writers need one serial root") + by_parent = {item.predecessor_invocation_id: item for item in writers if item.predecessor_invocation_id is not None} + if len(by_parent) != len(writers) - 1: + raise ValueError("state conflict: tentative state writer fork") + _validate_scope_chain(roots[0], by_parent, len(writers)) + + +def _validate_scope_chain( + root: ControlStateProposalV2Model, by_parent: dict[str | None, ControlStateProposalV2Model], count: int +) -> None: + current, seen = root, {root.invocation_id} + while current.invocation_id in by_parent: + successor = by_parent[current.invocation_id] + if successor.invocation_id in seen or successor.before != current.after: + raise ValueError("state conflict: tentative state chain differs") + seen.add(successor.invocation_id) + current = successor + if len(seen) != count: + raise ValueError("state conflict: same-scope writers are unordered") def validate_effect_budget_v2( @@ -112,6 +124,15 @@ def validate_effect_budget_v2( for effect in effects: if (effect.key.run_ref, effect.key.trigger_root) != (context.run.ref, context.trigger_root): raise ValueError("effect causal root differs from its exact request") + _validate_lifecycle_order(effects, context) + blockers: set[str] = set() + new_keys = _retained_claims(effects, context, blockers) + if blockers: + raise ValueError("retained effect claim conflicts with the amended request") + _validate_effect_bounds(effects, new_keys, context, bounds) + + +def _validate_lifecycle_order(effects: set, context: ParticipantControlContextModel) -> None: ancestors = _effect_ancestors({effect.effect_id: {effect} for effect in effects}) ordered = tuple(effects) for index, left in enumerate(ordered): @@ -124,11 +145,6 @@ def validate_effect_budget_v2( and left.effect_id not in ancestors[right.effect_id] ): raise ValueError("effect lifecycle order invalidates a live target") - blockers: set[str] = set() - new_keys = _retained_claims(effects, context, blockers) - if blockers: - raise ValueError("retained effect claim conflicts with the amended request") - _validate_effect_bounds(effects, new_keys, context, bounds) class ControlCommitBoundaryV2Model(ContractModel): @@ -162,168 +178,6 @@ def _boundary(self) -> Self: return self -class ControlCompositionV2Model(ContractModel): - model_config = ConfigDict(frozen=True) - rule_revision: Literal["participant-control-applicability/rev1"] - disposition: Literal["eligible", "deny", "withhold", "unsupported", "stale", "weakened", "failed", "conflict"] - blockers: ControlRefs - contributing_result_ids: ControlRefs - lost_advice_slot_ids: ControlRefs - - @model_validator(mode="after") - def _canonical(self) -> Self: - for values in (self.blockers, self.contributing_result_ids, self.lost_advice_slot_ids): - if values != tuple(sorted(set(values))): - raise ValueError("composition evidence must be canonical and unique") - return self - - -def _required_support_keys(request: ParticipantControlRequestV2Model) -> set[tuple[str, str]]: - slots = {slot.slot_id: slot for slot in request.selection.slots} - roots: set[str] = set() - required: set[tuple[str, str]] = set() - for item in request.applicability.coverage: - if item.status != "applies": - continue - for slot_id in item.slot_ids: - roots.add(slot_id) - required.add((item.obligation_id, slots[slot_id].instance_id)) - for slot_id in request.applicability.required_slot_ids: - if slot_id not in roots: - required.add(("input:" + slot_id, slots[slot_id].instance_id)) - return required - - -def derive_control_composition_v2( - request: ParticipantControlRequestV2Model, - results: tuple[ControlMechanismResultV2Model, ...] | list[ControlMechanismResultV2Model | dict], - support: tuple[ControlSupportAssessmentV2Model, ...] | list[ControlSupportAssessmentV2Model | dict], - lost_advice: tuple[ControlLostAdviceV2Model, ...] | list[ControlLostAdviceV2Model | dict], - effect_plan: ControlEffectPlanV2Model, -) -> ControlCompositionV2Model: - """One canonical summary of mandatory availability and parent decisions.""" - - results = tuple(ControlMechanismResultV2Model.model_validate(item) for item in results) - support = tuple(ControlSupportAssessmentV2Model.model_validate(item) for item in support) - lost_advice = tuple(ControlLostAdviceV2Model.model_validate(item) for item in lost_advice) - by_slot = {item.slot_id: item for item in results} - require_unique(tuple(item.slot_id for item in results)) - require_unique(tuple((item.obligation_id, item.instance_id) for item in support)) - require_unique(tuple(item.slot_id for item in lost_advice)) - if {(item.obligation_id, item.instance_id) for item in support} != _required_support_keys(request): - raise ValueError("participant control required support coverage is incomplete") - cut_digest = control_digest(request.context) - obligations = {item.obligation_id: item for item in request.selection.obligations} - for item in support: - if item.effective_support.context_digest != cut_digest: - raise ValueError("participant control support uses a different state cut") - obligation = obligations.get(item.obligation_id) - if obligation is not None and ( - item.required_strength != obligation.required_strength - or item.required_constraints != obligation.constraints - ): - raise ValueError("participant control support differs from the admitted obligation") - if item.obligation_id.startswith("input:"): - slot = next( - (slot for slot in request.selection.slots if "input:" + slot.slot_id == item.obligation_id), None - ) - if slot is None or (item.required_strength, item.required_constraints) != ( - slot.required_strength, - slot.constraints, - ): - raise ValueError("participant control support differs from the admitted input pin") - required_slots = set(request.applicability.required_slot_ids) - failed_optional = {item.slot_id for item in results if item.slot_id not in required_slots and not _satisfied(item)} - if {item.slot_id for item in lost_advice} != failed_optional: - raise ValueError("optional failure must have exactly one safe lost-advice record") - for item in lost_advice: - result = by_slot[item.slot_id] - if result.next_provider_state is not None or result.payload is not None: - raise ValueError("discarded optional contribution cannot carry state or effect payload") - blockers = set() - causes: set[str] = set() - for coverage in request.applicability.coverage: - if coverage.status == "unresolved": - blockers.add("coverage:" + coverage.obligation_id) - causes.add("withhold") - for slot_id in required_slots: - if not _satisfied(by_slot[slot_id]): - result = by_slot[slot_id] - if ( - result.status == "resolved" - and result.payload is not None - and result.payload.kind == "decision" - and result.payload.disposition in {"deny", "withhold"} - ): - # A complete refusal is the parent outcome, not missing mandatory work. - continue - blockers.add("slot:" + slot_id) - if result.status in {"stale", "unsupported", "weakened"}: - causes.add(result.status) - elif result.payload is not None and result.payload.kind == "decision": - causes.add(result.payload.disposition if result.payload.disposition != "abstain" else "failed") - else: - causes.add("failed") - for item in support: - if not item.satisfied: - blockers.add("support:" + item.obligation_id + ":" + item.instance_id) - observed = item.effective_support - if observed.status in {"stale", "unsupported", "weakened"}: - causes.add(observed.status) - elif observed.effective_level == "unsupported": - causes.add("unsupported") - elif observed.status == "resolved": - causes.add("weakened") - else: - causes.add("failed") - if effect_plan.parent_disposition != "permit": - blockers.add("parent:" + effect_plan.parent_disposition) - causes.add(effect_plan.parent_disposition) - disposition = next( - ( - item - for item in ("conflict", "stale", "unsupported", "weakened", "deny", "withhold", "failed") - if item in causes - ), - "eligible", - ) - return ControlCompositionV2Model( - rule_revision="participant-control-applicability/rev1", - disposition=disposition, - blockers=tuple(sorted(blockers)), - contributing_result_ids=tuple(sorted(item.result_id for item in results)), - lost_advice_slot_ids=tuple(sorted(failed_optional)), - ) - - -def admitted_control_effect_ids_v2( - composition: ControlCompositionV2Model, plan: ControlEffectPlanV2Model -) -> frozenset[str]: - """Join decision-local effect readiness to mandatory composition.""" - - if composition.disposition == "eligible" and plan.parent_disposition == "permit": - return frozenset(plan.runnable_effect_ids) - if set(composition.blockers) != {"parent:" + plan.parent_disposition}: - return frozenset() - base = base_parent_disposition_v2(plan.decisions, plan.incumbent_gate_disposition) - admitted: set[str] = set() - if plan.parent_disposition == "withhold" and base == "permit": - admitted.update( - item.effect_id - for item in plan.effects - if item.phase == "required-predecessor" and item.effect_id in plan.runnable_effect_ids - ) - if base in {"deny", "withhold"} and plan.parent_disposition == base: - admitted.update( - item.effect_id - for item in plan.effects - if item.phase == "independent" - and base in item.allowed_parent_dispositions - and item.effect_id in plan.runnable_effect_ids - ) - return frozenset(admitted) - - class ParticipantControlEvaluationV2Model(ContractModel): """Complete v2 contract record; eligibility is not dispatch authority.""" @@ -345,106 +199,134 @@ def _complete(self) -> Self: validate_control_invocations_v2(self.request, self.invocations, self.results) validate_state_proposals_v2(self.state_proposals) validate_effect_budget_v2(self.request.context, self.request.selection.bounds, self.effect_plan) - if self.effect_plan.parent_crossing != self.request.context.crossing: - raise ValueError("parent decision uses a different crossing") - decisions = { - item.result_id: item.payload - for item in self.results - if item.status == "resolved" - and item.payload is not None - and item.payload.kind == "decision" - and item.payload.disposition != "abstain" - and item.slot_id in self.request.applicability.required_slot_ids - } - if {item.decision_id for item in self.effect_plan.decisions} != decisions.keys(): - raise ValueError("parent decisions do not preserve the typed result set") - for item in self.effect_plan.decisions: - payload = decisions[item.decision_id] - if item.disposition != payload.disposition or item.rule != payload.rule: - raise ValueError("parent decision differs from its provider result") - requested_effects = [ - item.payload - for item in self.results - if item.status == "resolved" and item.payload is not None and item.payload.kind == "effect-request" - ] - if len({item.effect_id for item in requested_effects}) != len(requested_effects): - raise ValueError("duplicate provider effect identity") - requested = {item.effect_id: item for item in requested_effects} - if {item.effect_id: item for item in self.effect_plan.effects} != requested: - raise ValueError("effect plan does not preserve the requested effects") - proposed = {item.invocation_id: item for item in self.state_proposals} - calls = {item.invocation_id: item for item in self.invocations} - if proposed.keys() - calls.keys(): - raise ValueError("state proposal has no invocation") - for call in self.invocations: - if call.state_input.source != "tentative": - continue - predecessor = proposed.get(call.state_input.predecessor_invocation_id) - if ( - predecessor is None - or predecessor.scope != call.state_input.scope - or predecessor.after != call.state_input.state - ): - raise ValueError("consumed tentative state has no matching transaction proposal") - result_by_slot = {item.slot_id: item for item in self.results} - for item in self.state_proposals: - call = calls[item.invocation_id] - if ( - call.state_input.scope != item.scope - or call.state_input.state != item.before - or not any( - result_by_slot[slot_id].next_provider_state == item.after for slot_id in call.requested_slot_ids - ) - ): - raise ValueError("state proposal differs from its exact invocation and result") + _validate_parent_decisions(self) + _validate_requested_effects(self) + _validate_state_bindings(self) + _validate_parent_release(self) + _validate_commit_snapshot(self) + return self + + +def _validate_parent_decisions(record: ParticipantControlEvaluationV2Model) -> None: + if record.effect_plan.parent_crossing != record.request.context.crossing: + raise ValueError("parent decision uses a different crossing") + decisions = { + item.result_id: item.payload + for item in record.results + if item.status == "resolved" + and item.payload is not None + and item.payload.kind == "decision" + and item.payload.disposition != "abstain" + and item.slot_id in record.request.applicability.required_slot_ids + } + if {item.decision_id for item in record.effect_plan.decisions} != decisions.keys(): + raise ValueError("parent decisions do not preserve the typed result set") + for item in record.effect_plan.decisions: + payload = decisions[item.decision_id] + if item.disposition != payload.disposition or item.rule != payload.rule: + raise ValueError("parent decision differs from its provider result") + + +def _validate_requested_effects(record: ParticipantControlEvaluationV2Model) -> None: + requested_effects = [ + item.payload + for item in record.results + if item.status == "resolved" and item.payload is not None and item.payload.kind == "effect-request" + ] + if len({item.effect_id for item in requested_effects}) != len(requested_effects): + raise ValueError("duplicate provider effect identity") + requested = {item.effect_id: item for item in requested_effects} + if {item.effect_id: item for item in record.effect_plan.effects} != requested: + raise ValueError("effect plan does not preserve the requested effects") + + +def _validate_state_bindings(record: ParticipantControlEvaluationV2Model) -> None: + proposed = {item.invocation_id: item for item in record.state_proposals} + calls = {item.invocation_id: item for item in record.invocations} + if proposed.keys() - calls.keys(): + raise ValueError("state proposal has no invocation") + _validate_tentative_inputs(record.invocations, proposed) + _validate_state_proposal_results(record, calls) + + +def _validate_tentative_inputs( + calls: tuple[ControlInvocationV2Model, ...], proposed: dict[str, ControlStateProposalV2Model] +) -> None: + for call in calls: + if call.state_input.source != "tentative": + continue + predecessor = proposed.get(call.state_input.predecessor_invocation_id) if ( - self.commit.status == "committed" - and self.effect_plan.parent_disposition != "permit" - and any(item.commit_on == "parent-release" for item in self.state_proposals) + predecessor is None + or predecessor.scope != call.state_input.scope + or predecessor.after != call.state_input.state ): - raise ValueError("parent-release state cannot commit for a refused parent") - if self.composition != derive_control_composition_v2( - self.request, self.results, self.support, self.lost_advice, self.effect_plan + raise ValueError("consumed tentative state has no matching transaction proposal") + + +def _validate_state_proposal_results( + record: ParticipantControlEvaluationV2Model, calls: dict[str, ControlInvocationV2Model] +) -> None: + result_by_slot = {item.slot_id: item for item in record.results} + for item in record.state_proposals: + call = calls[item.invocation_id] + if ( + call.state_input.scope != item.scope + or call.state_input.state != item.before + or not any(result_by_slot[slot_id].next_provider_state == item.after for slot_id in call.requested_slot_ids) ): - raise ValueError("composition differs from the canonical result") - parent_release_allowed = ( - self.composition.disposition == "eligible" and self.effect_plan.parent_disposition == "permit" - ) - if not parent_release_allowed: - if ( - self.effect_plan.parent_applied - or self.effect_plan.parent_admission_receipt is not None - or self.effect_plan.parent_application_receipt is not None - ): - raise ValueError("mandatory composition cannot authorize parent release receipts") - if self.commit.status == "committed" and any( - item.commit_on == "parent-release" for item in self.state_proposals - ): - raise ValueError("mandatory composition cannot commit parent release state") - eligible_effects = admitted_control_effect_ids_v2(self.composition, self.effect_plan) - expected_commit = ( - self.request.context.expected_history_heads, - control_digest(self.request.applicability), - tuple(control_digest(item) for item in self.results), - control_digest(self.effect_plan), - tuple(control_digest(item) for item in self.state_proposals), - tuple(control_digest(item) for item in self.effect_plan.effects if item.effect_id in eligible_effects), - self.request.context.effects_consumed, - ) - observed_commit = ( - self.commit.expected_history_heads, - self.commit.coverage_digest, - self.commit.result_digests, - self.commit.decision_digest, - self.commit.state_proposal_digests, - self.commit.effect_intent_digests, - self.commit.effects_consumed, - ) - if observed_commit != expected_commit: - raise ValueError("atomic commit boundary differs from its complete evaluation") - if set(self.commit.dispatchable_effect_ids) - eligible_effects: - raise ValueError("mandatory composition cannot dispatch an effect outside the admitted outcome") - return self + raise ValueError("state proposal differs from its exact invocation and result") + + +def _validate_parent_release(record: ParticipantControlEvaluationV2Model) -> None: + plan, commit = record.effect_plan, record.commit + if ( + commit.status == "committed" + and plan.parent_disposition != "permit" + and any(item.commit_on == "parent-release" for item in record.state_proposals) + ): + raise ValueError("parent-release state cannot commit for a refused parent") + if record.composition != derive_control_composition_v2( + record.request, record.results, record.support, record.lost_advice, plan + ): + raise ValueError("composition differs from the canonical result") + if record.composition.disposition == "eligible" and plan.parent_disposition == "permit": + return + _validate_refused_parent_release(record) + + +def _validate_refused_parent_release(record: ParticipantControlEvaluationV2Model) -> None: + plan, commit = record.effect_plan, record.commit + if plan.parent_applied or plan.parent_admission_receipt is not None or plan.parent_application_receipt is not None: + raise ValueError("mandatory composition cannot authorize parent release receipts") + if commit.status == "committed" and any(item.commit_on == "parent-release" for item in record.state_proposals): + raise ValueError("mandatory composition cannot commit parent release state") + + +def _validate_commit_snapshot(record: ParticipantControlEvaluationV2Model) -> None: + eligible_effects = admitted_control_effect_ids_v2(record.composition, record.effect_plan) + expected_commit = ( + record.request.context.expected_history_heads, + control_digest(record.request.applicability), + tuple(control_digest(item) for item in record.results), + control_digest(record.effect_plan), + tuple(control_digest(item) for item in record.state_proposals), + tuple(control_digest(item) for item in record.effect_plan.effects if item.effect_id in eligible_effects), + record.request.context.effects_consumed, + ) + observed_commit = ( + record.commit.expected_history_heads, + record.commit.coverage_digest, + record.commit.result_digests, + record.commit.decision_digest, + record.commit.state_proposal_digests, + record.commit.effect_intent_digests, + record.commit.effects_consumed, + ) + if observed_commit != expected_commit: + raise ValueError("atomic commit boundary differs from its complete evaluation") + if set(record.commit.dispatchable_effect_ids) - eligible_effects: + raise ValueError("mandatory composition cannot dispatch an effect outside the admitted outcome") def parse_participant_control_evaluation_v2(source: str | bytes) -> ParticipantControlEvaluationV2Model: diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py b/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py index a887a940a..3280d490c 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py @@ -8,6 +8,8 @@ from .base import ContractModel, PrefixedDigestString from .participant_control_applicability import ( + ControlMechanismBindingV2Model, + ControlResultSlotV2Model, ControlStateScopeV2Model, ParticipantControlRequestV2Model, control_digest, @@ -196,61 +198,102 @@ def validate_control_invocations_v2( if by_slot.keys() != expected or {item.slot_id for item in outputs} != expected: raise ValueError("participant control invocation or result coverage is incomplete") result_by_slot = {result.slot_id: result for result in outputs} - states = {state.instance_id: state.state for state in request.context.provider_states} cut_digest = control_digest(request.context) for call in calls: - binding = bindings.get(call.instance_id) - if binding is None or call.binding_digest != control_digest(binding) or call.context_digest != cut_digest: - raise ValueError("participant control invocation differs from the admitted binding or cut") - if call.state_input.scope != binding.state_scope: - raise ValueError("participant control invocation state scope differs") - if call.state_input.source == "committed": - if call.state_input.state != states.get(call.instance_id): - raise ValueError("participant control invocation state version differs") - else: - predecessor = next( - (item for item in calls if item.invocation_id == call.state_input.predecessor_invocation_id), None - ) - if predecessor is None or predecessor.instance_id != call.instance_id: - raise ValueError("participant control tentative state predecessor is absent") - proposals = { - result.next_provider_state - for result in outputs - if result.slot_id in predecessor.requested_slot_ids and result.next_provider_state is not None - } - if proposals != {call.state_input.state}: - raise ValueError("participant control tentative state does not bind a predecessor proposal") - declared = {dep.slot_id: dep.kind for slot_id in call.requested_slot_ids for dep in slots[slot_id].dependencies} - if declared.keys() & set(call.requested_slot_ids): - raise ValueError("participant control predecessor cannot be produced in the same invocation") - supplied = {item.slot_id: item for item in call.predecessor_inputs} - if supplied.keys() != declared.keys(): - raise ValueError("participant control predecessor input coverage is incomplete") - for slot_id, item in supplied.items(): - predecessor = result_by_slot.get(slot_id) - if ( - predecessor is None - or item.kind != declared[slot_id] - or predecessor.result_id != item.result_id - or control_digest(predecessor) != item.result_digest - ): - raise ValueError("participant control predecessor result identity or type differs") - if not _satisfied(predecessor) and any( - result_by_slot[slot_id].status == "resolved" for slot_id in call.requested_slot_ids - ): - raise ValueError("participant control unsatisfied predecessor cannot feed a resolved result") - for slot_id in call.requested_slot_ids: - slot = slots[slot_id] - result = result_by_slot[slot_id] - if ( - slot.instance_id != call.instance_id - or result.instance_id != call.instance_id - or result.binding_digest != call.binding_digest - or result.context_digest != call.context_digest - or result.invocation_digest != control_digest(call) - ): - raise ValueError("participant control result differs from its exact invocation") - if result.rule_outcome == "not-triggered" and slot.kind != "effect-request": - raise ValueError("not-triggered result requires a rule/effect slot") - if result.payload is not None and result.payload.kind != slot.kind: - raise ValueError("participant control result does not match its typed slot") + binding = _validate_call_identity(call, bindings.get(call.instance_id), cut_digest) + _validate_call_state(call, binding, request, calls, outputs) + _validate_call_predecessors(call, slots, result_by_slot) + _validate_call_results(call, slots, result_by_slot) + + +def _validate_call_identity( + call: ControlInvocationV2Model, binding: ControlMechanismBindingV2Model | None, cut_digest: str +) -> ControlMechanismBindingV2Model: + if binding is None or call.binding_digest != control_digest(binding) or call.context_digest != cut_digest: + raise ValueError("participant control invocation differs from the admitted binding or cut") + if call.state_input.scope != binding.state_scope: + raise ValueError("participant control invocation state scope differs") + return binding + + +def _validate_call_state( + call: ControlInvocationV2Model, + binding: ControlMechanismBindingV2Model, + request: ParticipantControlRequestV2Model, + calls: tuple[ControlInvocationV2Model, ...], + outputs: tuple[ControlMechanismResultV2Model, ...], +) -> None: + if call.state_input.source == "committed": + states = {state.instance_id: state.state for state in request.context.provider_states} + if call.state_input.state != states.get(call.instance_id): + raise ValueError("participant control invocation state version differs") + return + predecessor = next( + (item for item in calls if item.invocation_id == call.state_input.predecessor_invocation_id), None + ) + if predecessor is None or predecessor.instance_id != binding.instance_id: + raise ValueError("participant control tentative state predecessor is absent") + proposals = { + result.next_provider_state + for result in outputs + if result.slot_id in predecessor.requested_slot_ids and result.next_provider_state is not None + } + if proposals != {call.state_input.state}: + raise ValueError("participant control tentative state does not bind a predecessor proposal") + + +def _validate_call_predecessors( + call: ControlInvocationV2Model, + slots: dict[str, ControlResultSlotV2Model], + result_by_slot: dict[str, ControlMechanismResultV2Model], +) -> None: + declared = {dep.slot_id: dep.kind for slot_id in call.requested_slot_ids for dep in slots[slot_id].dependencies} + if declared.keys() & set(call.requested_slot_ids): + raise ValueError("participant control predecessor cannot be produced in the same invocation") + supplied = {item.slot_id: item for item in call.predecessor_inputs} + if supplied.keys() != declared.keys(): + raise ValueError("participant control predecessor input coverage is incomplete") + for slot_id, item in supplied.items(): + _validate_predecessor_input(call, item, declared[slot_id], result_by_slot) + + +def _validate_predecessor_input( + call: ControlInvocationV2Model, + item: ControlPredecessorInputV2Model, + declared_kind: str, + result_by_slot: dict[str, ControlMechanismResultV2Model], +) -> None: + predecessor = result_by_slot.get(item.slot_id) + if ( + predecessor is None + or item.kind != declared_kind + or predecessor.result_id != item.result_id + or control_digest(predecessor) != item.result_digest + ): + raise ValueError("participant control predecessor result identity or type differs") + if not _satisfied(predecessor) and any( + result_by_slot[slot_id].status == "resolved" for slot_id in call.requested_slot_ids + ): + raise ValueError("participant control unsatisfied predecessor cannot feed a resolved result") + + +def _validate_call_results( + call: ControlInvocationV2Model, + slots: dict[str, ControlResultSlotV2Model], + result_by_slot: dict[str, ControlMechanismResultV2Model], +) -> None: + for slot_id in call.requested_slot_ids: + slot = slots[slot_id] + result = result_by_slot[slot_id] + if ( + slot.instance_id != call.instance_id + or result.instance_id != call.instance_id + or result.binding_digest != call.binding_digest + or result.context_digest != call.context_digest + or result.invocation_digest != control_digest(call) + ): + raise ValueError("participant control result differs from its exact invocation") + if result.rule_outcome == "not-triggered" and slot.kind != "effect-request": + raise ValueError("not-triggered result requires a rule/effect slot") + if result.payload is not None and result.payload.kind != slot.kind: + raise ValueError("participant control result does not match its typed slot") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py b/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py index c5bc1ee6f..befd47511 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py @@ -361,72 +361,129 @@ def validate_participant_control_resolved_context_v2( context = resolver(record) if resolver is not None else None if not isinstance(context, ParticipantControlValidationContextV2): raise ValueError("missing trusted context") - if record.request != context.admitted_request or record.request.context.crossing != context.admitted_crossing: - raise ValueError("admitted request or crossing differs") - if not set(control_references(record)) <= context.safe_references: - raise ValueError("unsafe or unresolved portable reference") - _validate_incumbent_records(context) - _validate_crossing(record.request.context, context) - if {item.instance_id: item for item in record.request.selection.bindings} != context.installed_bindings: - raise ValueError("installed apparatus differs") - if {item.obligation_id: item for item in record.request.applicability.coverage} != context.resolved_coverage: - raise ValueError("profile-owned applicability differs") - if {item.slot_id: item for item in record.results} != context.resolved_results: - raise ValueError("provider results differ") - bindings = {item.instance_id: item for item in record.request.selection.bindings} - for result in record.results: - _validate_ifc_result(result, bindings[result.instance_id], record.request.context) - if isinstance(result.payload, ControlSecurityFactModel): - _validate_security_fact(result.payload, record.request.context, context) - if {(item.obligation_id, item.instance_id): item for item in record.support} != context.resolved_support: - raise ValueError("effective support differs") - for item in record.support: - if item.satisfied and not context.support_resolver(item): - raise ValueError("support relation is unsubstantiated") - if record.effect_plan.incumbent_gate_disposition != context.incumbent_gate_disposition: - raise ValueError("incumbent gate resolution differs") - effects = {item.effect_id: item for item in record.effect_plan.effects} - if {item.invocation_id: item for item in record.state_proposals} != context.authorized_state_proposals: - raise ValueError("state write and commit timing differ from owner authorization") - current_outcomes = {item.effect_id: item for item in record.effect_plan.effect_outcomes} - for effect_id in record.effect_plan.realized_effect_ids: - prior = context.committed_effect_intents.get(effect_id) - if not isinstance(prior, ControlCommittedEffectIntentV2): - raise ValueError("realized effect lacks an owner-resolved committed intent") - origin = ParticipantControlEvaluationV2Model.model_validate(prior.evaluation.model_dump(mode="python")) - origin_effects = {item.effect_id: item for item in origin.effect_plan.effects} - effect: ControlEffectRequestV2Model = effects[effect_id] - if ( - origin_effects.get(effect_id) != effect - or origin.request.context.run != record.request.context.run - or origin.request.context.trigger_root != record.request.context.trigger_root - or origin.request.context.crossing != record.request.context.crossing - or origin.request.context.order > record.request.context.order - or origin.request.context.effects_consumed >= record.request.context.effects_consumed - or origin.commit.status != "committed" - or origin.commit.receipt is None - or effect_id not in origin.commit.dispatchable_effect_ids - or effect_id not in origin.effect_plan.runnable_effect_ids - or effect_id not in admitted_control_effect_ids_v2(origin.composition, origin.effect_plan) - or context.authorized_effects.get(control_digest(effect)) != origin.request.context - or prior.outcome != current_outcomes.get(effect_id) - or prior.outcome.disposition != "applied" - ): - raise ValueError("realized effect differs from its committed full intent, cut, or prerequisites") - if isinstance(effect.target, ControlInjectEffectModel): - _validate_inject(effect.target, origin.request.context, context) - for effect_id in admitted_control_effect_ids_v2(record.composition, record.effect_plan): - if context.authorized_effects.get(control_digest(effects[effect_id])) != record.request.context: - raise ValueError("effect authority is unresolved for its full payload and cut") - if isinstance(effects[effect_id].target, ControlInjectEffectModel): - _validate_inject(effects[effect_id].target, record.request.context, context) - if ( - record.effect_plan.parent_admission_receipt != context.parent_admission_receipt - or record.effect_plan.parent_application_receipt != context.parent_application_receipt - or record.commit.receipt != context.commit_receipt - ): - raise ValueError("parent or transaction receipt differs from owner resolution") - if {item.effect_id: item for item in record.effect_plan.effect_outcomes} != context.realization_receipts: - raise ValueError("effect outcome differs from owner resolution") + _validate_v2_trusted_context(record, context) except Exception: raise ValueError("participant control trusted-context validation failed") from None + + +def _validate_v2_trusted_context( + record: ParticipantControlEvaluationV2Model, context: ParticipantControlValidationContextV2 +) -> None: + _validate_admitted_v2_identity(record, context) + _validate_v2_results_and_support(record, context) + _validate_v2_effect_authority(record, context) + _validate_v2_receipts(record, context) + + +def _validate_admitted_v2_identity( + record: ParticipantControlEvaluationV2Model, context: ParticipantControlValidationContextV2 +) -> None: + if record.request != context.admitted_request or record.request.context.crossing != context.admitted_crossing: + raise ValueError("admitted request or crossing differs") + if not set(control_references(record)) <= context.safe_references: + raise ValueError("unsafe or unresolved portable reference") + _validate_incumbent_records(context) + _validate_crossing(record.request.context, context) + if {item.instance_id: item for item in record.request.selection.bindings} != context.installed_bindings: + raise ValueError("installed apparatus differs") + if {item.obligation_id: item for item in record.request.applicability.coverage} != context.resolved_coverage: + raise ValueError("profile-owned applicability differs") + + +def _validate_v2_results_and_support( + record: ParticipantControlEvaluationV2Model, context: ParticipantControlValidationContextV2 +) -> None: + if {item.slot_id: item for item in record.results} != context.resolved_results: + raise ValueError("provider results differ") + bindings = {item.instance_id: item for item in record.request.selection.bindings} + for result in record.results: + _validate_ifc_result(result, bindings[result.instance_id], record.request.context) + if isinstance(result.payload, ControlSecurityFactModel): + _validate_security_fact(result.payload, record.request.context, context) + if {(item.obligation_id, item.instance_id): item for item in record.support} != context.resolved_support: + raise ValueError("effective support differs") + for item in record.support: + if item.satisfied and not context.support_resolver(item): + raise ValueError("support relation is unsubstantiated") + if record.effect_plan.incumbent_gate_disposition != context.incumbent_gate_disposition: + raise ValueError("incumbent gate resolution differs") + + +def _validate_v2_effect_authority( + record: ParticipantControlEvaluationV2Model, context: ParticipantControlValidationContextV2 +) -> None: + if {item.invocation_id: item for item in record.state_proposals} != context.authorized_state_proposals: + raise ValueError("state write and commit timing differ from owner authorization") + effects = {item.effect_id: item for item in record.effect_plan.effects} + current_outcomes = {item.effect_id: item for item in record.effect_plan.effect_outcomes} + for effect_id in record.effect_plan.realized_effect_ids: + _validate_prior_effect(record, context, effects[effect_id], current_outcomes.get(effect_id)) + for effect_id in admitted_control_effect_ids_v2(record.composition, record.effect_plan): + effect = effects[effect_id] + if context.authorized_effects.get(control_digest(effect)) != record.request.context: + raise ValueError("effect authority is unresolved for its full payload and cut") + if isinstance(effect.target, ControlInjectEffectModel): + _validate_inject(effect.target, record.request.context, context) + + +def _validate_prior_effect( + record: ParticipantControlEvaluationV2Model, + context: ParticipantControlValidationContextV2, + effect: ControlEffectRequestV2Model, + current_outcome: ControlRealizationBindingModel | None, +) -> None: + prior = context.committed_effect_intents.get(effect.effect_id) + if not isinstance(prior, ControlCommittedEffectIntentV2): + raise ValueError("realized effect lacks an owner-resolved committed intent") + origin = ParticipantControlEvaluationV2Model.model_validate(prior.evaluation.model_dump(mode="python")) + _validate_prior_identity(record, origin, effect) + _validate_prior_commit(origin, effect.effect_id) + if ( + context.authorized_effects.get(control_digest(effect)) != origin.request.context + or prior.outcome != current_outcome + or prior.outcome.disposition != "applied" + ): + raise ValueError("realized effect differs from its committed full intent, cut, or prerequisites") + if isinstance(effect.target, ControlInjectEffectModel): + _validate_inject(effect.target, origin.request.context, context) + + +def _validate_prior_identity( + record: ParticipantControlEvaluationV2Model, + origin: ParticipantControlEvaluationV2Model, + effect: ControlEffectRequestV2Model, +) -> None: + origin_effects = {item.effect_id: item for item in origin.effect_plan.effects} + if ( + origin_effects.get(effect.effect_id) != effect + or origin.request.context.run != record.request.context.run + or origin.request.context.trigger_root != record.request.context.trigger_root + or origin.request.context.crossing != record.request.context.crossing + or origin.request.context.order > record.request.context.order + or origin.request.context.effects_consumed >= record.request.context.effects_consumed + ): + raise ValueError("realized effect differs from its committed full intent, cut, or prerequisites") + + +def _validate_prior_commit(origin: ParticipantControlEvaluationV2Model, effect_id: str) -> None: + if ( + origin.commit.status != "committed" + or origin.commit.receipt is None + or effect_id not in origin.commit.dispatchable_effect_ids + or effect_id not in origin.effect_plan.runnable_effect_ids + or effect_id not in admitted_control_effect_ids_v2(origin.composition, origin.effect_plan) + ): + raise ValueError("realized effect differs from its committed full intent, cut, or prerequisites") + + +def _validate_v2_receipts( + record: ParticipantControlEvaluationV2Model, context: ParticipantControlValidationContextV2 +) -> None: + if ( + record.effect_plan.parent_admission_receipt != context.parent_admission_receipt + or record.effect_plan.parent_application_receipt != context.parent_application_receipt + or record.commit.receipt != context.commit_receipt + ): + raise ValueError("parent or transaction receipt differs from owner resolution") + if {item.effect_id: item for item in record.effect_plan.effect_outcomes} != context.realization_receipts: + raise ValueError("effect outcome differs from owner resolution") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py index f6812ca9b..c5a99610c 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py @@ -11,6 +11,26 @@ from .participant_control_results import ControlEffectiveSupportModel +def _validate_positive_support(relation: str, observed: ControlEffectiveSupportModel) -> None: + if relation in {"exact", "bounded-compatible", "disclosed-weak-compatible"} and ( + observed.status != "resolved" or observed.installation is None + ): + raise ValueError("positive support relation needs resolved installed support") + + +def _validate_strength_relation(relation: str, required_strength: str, observed_level: str) -> None: + if relation == "exact" and observed_level != "exact": + raise ValueError("exact support relation needs exact effective support") + if relation == "bounded-compatible" and (required_strength == "exact" or observed_level != "bounded"): + raise ValueError("bounded support does not satisfy exact or mismatched strength") + if relation == "disclosed-weak-compatible" and ( + required_strength != "disclosed_weak" or observed_level != "disclosed_weak" + ): + raise ValueError("disclosed weak support relation is incompatible") + if required_strength == "exact" and relation not in {"exact", "insufficient", "unresolved"}: + raise ValueError("exact requirement cannot accept weaker support") + + class ControlSupportAssessmentV2Model(ContractModel): """A recorded requirement-relative relation, subject to trusted resolution.""" @@ -32,21 +52,8 @@ def _relation(self) -> Self: observed = self.effective_support if observed.instance_id != self.instance_id: raise ValueError("support assessment has a different instance") - if self.relation in {"exact", "bounded-compatible", "disclosed-weak-compatible"}: - if observed.status != "resolved" or observed.installation is None: - raise ValueError("positive support relation needs resolved installed support") - if self.relation == "exact" and observed.effective_level != "exact": - raise ValueError("exact support relation needs exact effective support") - if self.relation == "bounded-compatible" and ( - self.required_strength == "exact" or observed.effective_level != "bounded" - ): - raise ValueError("bounded support does not satisfy exact or mismatched strength") - if self.relation == "disclosed-weak-compatible" and ( - self.required_strength != "disclosed_weak" or observed.effective_level != "disclosed_weak" - ): - raise ValueError("disclosed weak support relation is incompatible") - if self.required_strength == "exact" and self.relation not in {"exact", "insufficient", "unresolved"}: - raise ValueError("exact requirement cannot accept weaker support") + _validate_positive_support(self.relation, observed) + _validate_strength_relation(self.relation, self.required_strength, observed.effective_level) return self @property diff --git a/implementations/python/tests/test_formal_semantic_validation.py b/implementations/python/tests/test_formal_semantic_validation.py index 9866d6dad..2be76cffa 100644 --- a/implementations/python/tests/test_formal_semantic_validation.py +++ b/implementations/python/tests/test_formal_semantic_validation.py @@ -135,6 +135,7 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: "54.0.0", "55.0.0", "56.0.0", + "57.0.0", ] assert all(validate_release_bundle(REPO_ROOT, release) == [] for release in releases) @@ -143,10 +144,10 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: def test_current_retest_bundle_is_coherent_and_clean() -> None: release, protocol, corpus, snapshot, analysis = copy_bundle(load_retest_bundle, REPO_ROOT) - assert release.manifest["revision"] == "56.0.0" + assert release.manifest["revision"] == "57.0.0" assert protocol["revision"] == "2.0.0" assert corpus["revision"] == "4.0.0" - assert snapshot["baseline"]["release_revision"] == "55.0.0" + assert snapshot["baseline"]["release_revision"] == "56.0.0" assert snapshot["deviations"] == [] assert validate_retest_bundle(REPO_ROOT, release, protocol, corpus, snapshot, analysis) == [] diff --git a/implementations/python/tests/test_issue_1365_applicable_contracts.py b/implementations/python/tests/test_issue_1365_applicable_contracts.py index 2b9f5d327..700bd3c01 100644 --- a/implementations/python/tests/test_issue_1365_applicable_contracts.py +++ b/implementations/python/tests/test_issue_1365_applicable_contracts.py @@ -407,9 +407,10 @@ def test_required_predecessor_cannot_wait_for_own_parent_application(): originating_principal_ref="teacher", trigger=ref("trigger-1", "trigger"), ) + parent_crossing = ref("crossing-1", "crossing") with pytest.raises(ValueError, match="cycle|predecessor"): derive_control_effect_plan_v2( - parent_crossing=ref("crossing-1", "crossing"), + parent_crossing=parent_crossing, decisions=[], effects=[effect], incumbent_gate_disposition="permit", @@ -429,9 +430,10 @@ def test_later_effect_requires_exact_parent_owner_receipt(): originating_principal_ref="teacher", trigger=ref("trigger-1", "trigger"), ) + parent_crossing = ref("crossing-1", "crossing") with pytest.raises(ValueError, match="receipt"): derive_control_effect_plan_v2( - parent_crossing=ref("crossing-1", "crossing"), + parent_crossing=parent_crossing, decisions=[], effects=[effect], incumbent_gate_disposition="permit", @@ -468,9 +470,10 @@ def test_conflicting_routes_cannot_be_admitted_by_array_order(): ) effect["key"]["slot"] = "route-" + str(index) effects.append(effect) + parent_crossing = ref("crossing-1", "crossing") with pytest.raises(ValueError, match="conflict"): derive_control_effect_plan_v2( - parent_crossing=ref("crossing-1", "crossing"), + parent_crossing=parent_crossing, decisions=[], effects=effects, incumbent_gate_disposition="permit", @@ -526,16 +529,20 @@ def test_optional_malformed_result_is_normalized_without_private_error_text(): result, loss = normalize_optional_result_failure_v2( request, invocation, raw, result_id="result-monitor", evidence=ref("optional-loss") ) - assert result.status == "failed" and result.payload is None and result.next_provider_state is None + assert result.status == "failed" + assert result.payload is None + assert result.next_provider_state is None assert loss.slot_id == "monitor" assert "private-token" not in repr(result) + repr(loss) + mandatory_invocation = invocation.model_copy(update={"requested_slot_ids": ("fact",)}) + mandatory_evidence = ref("mandatory-loss") with pytest.raises(ValueError, match="mandatory"): normalize_optional_result_failure_v2( request, - invocation.model_copy(update={"requested_slot_ids": ("fact",)}), + mandatory_invocation, raw, result_id="result-fact", - evidence=ref("mandatory-loss"), + evidence=mandatory_evidence, ) @@ -546,6 +553,7 @@ def test_required_support_is_compared_to_admitted_strength(): observed["declared_level"] = "bounded" observed["effective_level"] = "bounded" observed["constraints"] = [ref("one-unit", "constraint")] + support_evidence = (ref("support-relation"),) with pytest.raises(ValueError, match="exact"): ControlSupportAssessmentV2Model( obligation_id="teaching-here", @@ -554,7 +562,7 @@ def test_required_support_is_compared_to_admitted_strength(): required_constraints=(), effective_support=observed, relation="bounded-compatible", - evidence=(ref("support-relation"),), + evidence=support_evidence, ) assessment = ControlSupportAssessmentV2Model( obligation_id="teaching-here", @@ -563,7 +571,7 @@ def test_required_support_is_compared_to_admitted_strength(): required_constraints=(ref("two-units", "constraint"),), effective_support=observed, relation="bounded-compatible", - evidence=(ref("support-relation"),), + evidence=support_evidence, ) assert assessment.satisfied @@ -585,13 +593,14 @@ def test_required_input_support_cannot_downgrade_its_admitted_slot_pin(): ParticipantControlSelectionV2Model.model_validate(selection) ) request = ParticipantControlRequestV2Model.model_validate(record["request"]) + plan = ControlEffectPlanV2Model.model_validate(record["effect_plan"]) with pytest.raises(ValueError, match="support"): derive_control_composition_v2( request, record["results"], record["support"], [], - ControlEffectPlanV2Model.model_validate(record["effect_plan"]), + plan, ) @@ -604,7 +613,8 @@ def test_false_trigger_is_distinct_from_missing_rule_result(): record["rule_outcome"] = "not-triggered" record["evaluated_basis"] = [ref("false-trigger-basis")] result = ControlMechanismResultV2Model.model_validate(record) - assert result.status == "resolved" and result.rule_outcome == "not-triggered" + assert result.status == "resolved" + assert result.rule_outcome == "not-triggered" record["status"] = "missing" with pytest.raises(ValueError, match="trigger|resolved"): ControlMechanismResultV2Model.model_validate(record) @@ -983,8 +993,9 @@ def test_required_result_status_retains_its_composition_disposition(status, expe def test_v1_history_cannot_be_parsed_as_amended_evaluation(): from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + payload = evaluation_payload() with pytest.raises(ValueError): - ParticipantControlEvaluationV2Model.model_validate(evaluation_payload()) + ParticipantControlEvaluationV2Model.model_validate(payload) def test_v2_bounded_json_ingress_preserves_abstention_and_rejects_duplicate_members(): @@ -1604,7 +1615,8 @@ def test_failed_required_composition_cannot_dispatch_independent_effect(): realized_effect_ids=(), parent_applied=False, ) - assert plan.parent_disposition == "permit" and plan.runnable_effect_ids == (effect["effect_id"],) + assert plan.parent_disposition == "permit" + assert plan.runnable_effect_ids == (effect["effect_id"],) payload["effect_plan"] = plan.model_dump(mode="json") payload["composition"] = derive_control_composition_v2( request, payload["results"], payload["support"], payload["lost_advice"], plan diff --git a/implementations/python/tests/test_issue_989_versioned_evidence.py b/implementations/python/tests/test_issue_989_versioned_evidence.py index 42f4f2753..2eebf3e75 100644 --- a/implementations/python/tests/test_issue_989_versioned_evidence.py +++ b/implementations/python/tests/test_issue_989_versioned_evidence.py @@ -230,7 +230,7 @@ def test_latest_current_release_is_versioned_and_strict(monkeypatch): from tools.formal_semantic_validation._releases import validate_retest_bundle release, protocol, corpus, snapshot, analysis = copy_bundle(load_retest_bundle, ROOT) - assert release.manifest["revision"] == "56.0.0" + assert release.manifest["revision"] == "57.0.0" original = _retest.replay_case def changed_result(root, case): @@ -283,7 +283,7 @@ def test_specification_current_capture_does_not_accept_old_artifact_digest(artif from tools.check_specification_coverage import load_bundle, validate_bundle manifest, protocol, snapshot, analysis = copy_bundle(load_bundle, ROOT) - assert manifest["revision"] == "55.0.0" + assert manifest["revision"] == "56.0.0" snapshot = deepcopy(snapshot) artifact = next(a for a in snapshot["artifacts"] if a["artifact_id"] == artifact_id) artifact["sha256"] = old_digest @@ -508,6 +508,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "54.0.0", "55.0.0", "56.0.0", + "57.0.0", ] if family == "formal" else [ @@ -567,6 +568,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "53.0.0", "54.0.0", "55.0.0", + "56.0.0", ] ) revisions.pop(-1 if removed == "current" else 0) diff --git a/implementations/python/tests/test_specification_coverage.py b/implementations/python/tests/test_specification_coverage.py index 85fb6425e..63823b7eb 100644 --- a/implementations/python/tests/test_specification_coverage.py +++ b/implementations/python/tests/test_specification_coverage.py @@ -53,7 +53,7 @@ def test_immutable_bundle_index_preserves_concurrent_captures() -> None: bundles = copy_bundle(load_bundles, REPO_ROOT) assert {manifest["revision"] for manifest, *_rest in bundles} >= {"1.0.0", "1.1.0", "19.0.0"} manifest, *_rest = copy_bundle(load_bundle, REPO_ROOT) - assert manifest["revision"] == "55.0.0" + assert manifest["revision"] == "56.0.0" def test_historical_failures_name_the_revision_specific_documents() -> None: diff --git a/tools/check_specification_coverage.py b/tools/check_specification_coverage.py index f9370fe6a..568669120 100644 --- a/tools/check_specification_coverage.py +++ b/tools/check_specification_coverage.py @@ -157,12 +157,12 @@ def _load_bundle_index(repo_root: Path) -> list[tuple[str, dict[str, object]]]: "51.0.0", "52.0.0", "53.0.0", - } | {"54.0.0", "55.0.0"} + } | {"54.0.0", "55.0.0", "56.0.0"} if ( - dict(records)[current_path].get("revision") != "55.0.0" + dict(records)[current_path].get("revision") != "56.0.0" or {record.get("revision") for _, record in records} != supported_revisions ): - raise ValueError("coverage evidence requires the explicit current 55.0.0 release and supported history") + raise ValueError("coverage evidence requires the explicit current 56.0.0 release and supported history") return records diff --git a/tools/formal_semantic_validation/_baseline.py b/tools/formal_semantic_validation/_baseline.py index c243c73c6..d578b3aa9 100644 --- a/tools/formal_semantic_validation/_baseline.py +++ b/tools/formal_semantic_validation/_baseline.py @@ -192,6 +192,7 @@ def _selected_baseline_manifest( "54.0.0", "55.0.0", "56.0.0", + "57.0.0", }: expected_corpus_path = "docs/research/formal-semantic-validation/corpus/manifest-v4.json" elif baseline_revision in _V3_CORPUS_REVISIONS: diff --git a/tools/formal_semantic_validation/_loading.py b/tools/formal_semantic_validation/_loading.py index c56b5f44b..caba98917 100644 --- a/tools/formal_semantic_validation/_loading.py +++ b/tools/formal_semantic_validation/_loading.py @@ -75,6 +75,6 @@ def load_retest_bundle( if not releases: raise ValueError("the formal semantic-validation index selects no v2 retest release") release = max(releases, key=lambda item: revision_key(item.manifest.get("revision"))) - if release.manifest.get("revision") != "56.0.0" or release.protocol.get("revision") != "2.0.0": - raise ValueError("the current formal evidence release must be the explicit 56.0.0 retest") + if release.manifest.get("revision") != "57.0.0" or release.protocol.get("revision") != "2.0.0": + raise ValueError("the current formal evidence release must be the explicit 57.0.0 retest") return release, release.protocol, release.corpus, release.snapshot, release.analysis diff --git a/tools/formal_semantic_validation/_release_revisions.py b/tools/formal_semantic_validation/_release_revisions.py index b1a838b0d..0c2d151ea 100644 --- a/tools/formal_semantic_validation/_release_revisions.py +++ b/tools/formal_semantic_validation/_release_revisions.py @@ -53,7 +53,7 @@ "51.0.0", "52.0.0", } -) | {"53.0.0", "54.0.0", "55.0.0"} +) | {"53.0.0", "54.0.0", "55.0.0", "56.0.0"} -_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"56.0.0"} +_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"57.0.0"} _SOURCE_BOUND_RETEST_REVISIONS = _SUPPORTED_RETEST_REVISIONS - {"3.0.0"} diff --git a/tools/formal_semantic_validation/_releases.py b/tools/formal_semantic_validation/_releases.py index 5e9f92e86..673aa2f5d 100644 --- a/tools/formal_semantic_validation/_releases.py +++ b/tools/formal_semantic_validation/_releases.py @@ -159,7 +159,7 @@ def validate_release_bundle(repo_root: Path, release: EvidenceRelease) -> list[P release.corpus, release.snapshot, release.analysis, - replay_current=manifest.get("revision") == "56.0.0", + replay_current=manifest.get("revision") == "57.0.0", ) ) else: @@ -273,6 +273,7 @@ def _expected_corpus_revision(release_revision: object) -> str: "54.0.0", "55.0.0", "56.0.0", + "57.0.0", }: expected_corpus_revision = "4.0.0" return expected_corpus_revision @@ -300,7 +301,7 @@ def validate_retest_bundle( return [ _failure( "formal-validation-current-replay-required", - "only releases 3.0.0 through 55.0.0 can use integrated historical validation", + "only releases 3.0.0 through 56.0.0 can use integrated historical validation", snapshot_path, ) ] @@ -429,6 +430,7 @@ def _current_retest_source_failures( "54.0.0": "53.0.0", "55.0.0": "54.0.0", "56.0.0": "55.0.0", + "57.0.0": "56.0.0", }[release_revision] if not isinstance(baseline, Mapping) or baseline.get("release_revision") != expected_baseline: failures.append( diff --git a/tools/formal_semantic_validation/_retest.py b/tools/formal_semantic_validation/_retest.py index 6e6c95161..ec1da39d7 100644 --- a/tools/formal_semantic_validation/_retest.py +++ b/tools/formal_semantic_validation/_retest.py @@ -37,7 +37,7 @@ ) from tools.policy.common import PolicyFailure -_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 57)) +_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 58)) @dataclasses.dataclass(frozen=True) diff --git a/tools/policy/requirement_order.yaml b/tools/policy/requirement_order.yaml index 2d32c9907..2b9b8deea 100644 --- a/tools/policy/requirement_order.yaml +++ b/tools/policy/requirement_order.yaml @@ -841,10 +841,17 @@ ownership: - docs/research/formal-semantic-validation/analysis-v55.json - docs/research/formal-semantic-validation/bundles/retest-v55.json - docs/research/formal-semantic-validation/execution-snapshot-v55.json + - docs/research/formal-semantic-validation/analysis-v56.json + - docs/research/formal-semantic-validation/bundles/retest-v56.json + - docs/research/formal-semantic-validation/execution-snapshot-v56.json - docs/research/specification-coverage/analysis-v55.json - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v55.json - docs/research/specification-coverage/execution-snapshot-v55.json + - docs/research/specification-coverage/analysis-v56.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json + - docs/research/specification-coverage/execution-snapshot-v56.json - implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py + - implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py - implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py - implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py - implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py From db372c0a3a1c0a0ed4700c9e74df2e76447b6bc2 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 21:08:18 +0200 Subject: [PATCH 08/10] Trace composition module to API-424 --- docs/requirements/API-424/requirement.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/requirements/API-424/requirement.md b/docs/requirements/API-424/requirement.md index 4ca4ac739..87b591a54 100644 --- a/docs/requirements/API-424/requirement.md +++ b/docs/requirements/API-424/requirement.md @@ -72,7 +72,8 @@ legacy history without inventing new coverage or realization evidence. - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py` (Typed predecessor and scoped-state inputs with result binding) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py` (Requirement-relative support assessment) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py` (Unscheduled parent decisions and typed effect prerequisites) -- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py` (Composition, state and atomic commit boundary) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py` (Mandatory composition and effect admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py` (Scoped state and atomic commit boundary) - IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-control-selection-v2.json` (Published v2 apparatus selection) - IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-control-evaluation-v2.json` (Published v2 exact-cut evaluation) - IMPLEMENTS → CONFIG `contracts/schema-publication/entries/participant-control-selection-v2.json` (Selection publication ledger) From f4138a50f8ab8f6362def473bfdcbe04b2810d12 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 21:46:26 +0200 Subject: [PATCH 09/10] Fix SonarCloud findings (cycle 2) --- docs/governance/requirement-scopes/1365.json | 18 + .../analysis-v57.json | 156 ++++ .../bundles/retest-v57.json | 122 +++ .../execution-snapshot-v57.json | 652 ++++++++++++++++ .../formal-semantic-validation/index.md | 5 + .../specification-coverage/analysis-v57.json | 105 +++ ...specification-coverage-issue-1365-v57.json | 10 + .../execution-snapshot-v57.json | 701 ++++++++++++++++++ docs/research/specification-coverage/index.md | 6 + .../participant_control_applicability.py | 21 +- .../participant_control_composition_v2.py | 35 +- .../participant_control_decisions_v2.py | 14 +- .../participant_control_evaluation_v2.py | 26 +- .../participant_control_invocation.py | 22 +- .../participant_control_resolution.py | 8 +- .../participant_control_support_v2.py | 8 +- .../tests/test_formal_semantic_validation.py | 5 +- .../test_issue_989_versioned_evidence.py | 6 +- .../tests/test_specification_coverage.py | 2 +- tools/check_specification_coverage.py | 6 +- tools/formal_semantic_validation/_baseline.py | 1 + tools/formal_semantic_validation/_loading.py | 4 +- .../_release_revisions.py | 4 +- tools/formal_semantic_validation/_releases.py | 6 +- tools/formal_semantic_validation/_retest.py | 2 +- tools/policy/requirement_order.yaml | 6 + 26 files changed, 1887 insertions(+), 64 deletions(-) create mode 100644 docs/research/formal-semantic-validation/analysis-v57.json create mode 100644 docs/research/formal-semantic-validation/bundles/retest-v57.json create mode 100644 docs/research/formal-semantic-validation/execution-snapshot-v57.json create mode 100644 docs/research/specification-coverage/analysis-v57.json create mode 100644 docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json create mode 100644 docs/research/specification-coverage/execution-snapshot-v57.json diff --git a/docs/governance/requirement-scopes/1365.json b/docs/governance/requirement-scopes/1365.json index 867acfefa..015bdff19 100644 --- a/docs/governance/requirement-scopes/1365.json +++ b/docs/governance/requirement-scopes/1365.json @@ -75,18 +75,27 @@ "docs/research/formal-semantic-validation/analysis-v56.json": [ "API-424" ], + "docs/research/formal-semantic-validation/analysis-v57.json": [ + "API-424" + ], "docs/research/formal-semantic-validation/bundles/retest-v55.json": [ "API-424" ], "docs/research/formal-semantic-validation/bundles/retest-v56.json": [ "API-424" ], + "docs/research/formal-semantic-validation/bundles/retest-v57.json": [ + "API-424" + ], "docs/research/formal-semantic-validation/execution-snapshot-v55.json": [ "API-424" ], "docs/research/formal-semantic-validation/execution-snapshot-v56.json": [ "API-424" ], + "docs/research/formal-semantic-validation/execution-snapshot-v57.json": [ + "API-424" + ], "docs/research/formal-semantic-validation/index.md": [ "API-424" ], @@ -96,18 +105,27 @@ "docs/research/specification-coverage/analysis-v56.json": [ "API-424" ], + "docs/research/specification-coverage/analysis-v57.json": [ + "API-424" + ], "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v55.json": [ "API-424" ], "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json": [ "API-424" ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json": [ + "API-424" + ], "docs/research/specification-coverage/execution-snapshot-v55.json": [ "API-424" ], "docs/research/specification-coverage/execution-snapshot-v56.json": [ "API-424" ], + "docs/research/specification-coverage/execution-snapshot-v57.json": [ + "API-424" + ], "docs/research/specification-coverage/index.md": [ "API-424" ], diff --git a/docs/research/formal-semantic-validation/analysis-v57.json b/docs/research/formal-semantic-validation/analysis-v57.json new file mode 100644 index 000000000..139a4c2c5 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v57.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-1365-analysis-v57", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v57.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1365-execution-v57", + "generated_at": "2026-09-27", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained formal corpus does not install a provider or establish live effect realization." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes against the API-424 contract publication source. Bounded claims and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v57.json b/docs/research/formal-semantic-validation/bundles/retest-v57.json new file mode 100644 index 000000000..a696a4fbb --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v57.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v57.json", + "analysis_sha256": "c9ed4aef4650ed562bae07776971ded51c3992fcb0fc07d1a7bb8a7712660126", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "58.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v57.json", + "snapshot_sha256": "9adece3ad86c39bd9ace8393e86c8d2711d3e878112e8e0ae6d34b75d8763b02" +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v57.json b/docs/research/formal-semantic-validation/execution-snapshot-v57.json new file mode 100644 index 000000000..5b4a145e6 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v57.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1365-execution-v56", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v56.json", + "release_revision": "57.0.0", + "release_sha256": "a773606179bb65f94c15a8a75080bb80206c9ad8b9516066accaf58f609ae89d" + }, + "captured_at": "2026-09-27T19:41:33.023883+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1365-execution-v57", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "db372c0a3a1c0a0ed4700c9e74df2e76447b6bc2", + "source_state": { + "base_revision": "db372c0a3a1c0a0ed4700c9e74df2e76447b6bc2", + "checkout_state": "modified", + "implementation_digest": "0c667bdcb81de158930a56cacab8530bd3bc82d996494c827187feab58f73d2a", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/index.md b/docs/research/formal-semantic-validation/index.md index 32ad81936..3c1347dc3 100644 --- a/docs/research/formal-semantic-validation/index.md +++ b/docs/research/formal-semantic-validation/index.md @@ -516,3 +516,8 @@ Release 57.0.0 replays those formal cases against the API-424 v2 contract refactor in [`execution-snapshot-v56.json`](execution-snapshot-v56.json) and [`analysis-v56.json`](analysis-v56.json). Outcomes and bounded claims remain unchanged; provider installation and effect realization are outside this corpus. + +Release 58.0.0 replays those formal cases against the API-424 contract +quality-gate refactor in [`execution-snapshot-v57.json`](execution-snapshot-v57.json) +and [`analysis-v57.json`](analysis-v57.json). Outcomes and bounded claims remain +unchanged; provider installation and effect realization are outside this corpus. diff --git a/docs/research/specification-coverage/analysis-v57.json b/docs/research/specification-coverage/analysis-v57.json new file mode 100644 index 000000000..1e3b2b256 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v57.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1365-v57", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v57.json", + "docs/research/specification-coverage/analysis-v57.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-27", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "API-424 v2 participant-control publication is verified by its dedicated contract tests, not promoted to a live provider or backend claim by this retained matrix." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays unchanged classifications and claim limits against source that publishes API-424 v2 participant-control contracts. It does not demonstrate runtime adoption.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v57", + "snapshot_sha256": "3fb1abe1f19140d3ef4de8e104a9666c09af11b4bb0e9e4257b2dca5c86d927f" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json new file mode 100644 index 000000000..1979df581 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v57.json", + "analysis_sha256": "ad43af82f528446b0cb033a05b79c1640e9c95fe101e3b25f537cc4d78ce92e1", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "57.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v57.json", + "snapshot_sha256": "5837c90f9ad42b0624b6bd787273c21c02dcd9598b5283afcdc72842f8ea170b" +} diff --git a/docs/research/specification-coverage/execution-snapshot-v57.json b/docs/research/specification-coverage/execution-snapshot-v57.json new file mode 100644 index 000000000..d7f884567 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v57.json @@ -0,0 +1,701 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27T19:41:33.023883+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "46039a013e3d4f2377396fa57d4838b2fa984eb44be1dc0e9a9a166db58203bd", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained offline matrix does not establish provider installation, scheduling, or effect realization." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "db372c0a3a1c0a0ed4700c9e74df2e76447b6bc2", + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v57", + "snapshot_revision": "57.0.0", + "source_state": { + "base_revision": "db372c0a3a1c0a0ed4700c9e74df2e76447b6bc2", + "checkout_state": "modified", + "implementation_digest": "0c667bdcb81de158930a56cacab8530bd3bc82d996494c827187feab58f73d2a", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/index.md b/docs/research/specification-coverage/index.md index 8a4817653..9772fdb38 100644 --- a/docs/research/specification-coverage/index.md +++ b/docs/research/specification-coverage/index.md @@ -455,3 +455,9 @@ Release 56.0.0 replays that matrix against the API-424 v2 contract refactor in [`analysis-v56.json`](analysis-v56.json). Classifications and claim limits remain unchanged; provider installation and effect realization remain outside this offline matrix. + +Release 57.0.0 replays that matrix against the API-424 contract quality-gate +refactor in [`execution-snapshot-v57.json`](execution-snapshot-v57.json) and +[`analysis-v57.json`](analysis-v57.json). Classifications and claim limits remain +unchanged; provider installation and effect realization remain outside this +offline matrix. diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py b/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py index fe9737929..a1c216e75 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py @@ -181,12 +181,21 @@ def _validate_coverage(request: ParticipantControlRequestV2Model) -> None: applicable = set(applicability.applicable_slot_ids) for item in applicability.coverage: if item.status == "applies": - obligation = obligations[item.obligation_id] - for identity in item.slot_ids: - if identity not in applicable or slots[identity].kind != obligation.required_kind: - raise ValueError("participant control coverage does not map to an applicable typed slot") - if obligation.profile not in bindings[slots[identity].instance_id].profiles: - raise ValueError("participant control coverage slot does not select its owning profile") + _validate_applied_coverage(item, obligations[item.obligation_id], slots, bindings, applicable) + + +def _validate_applied_coverage( + item: ControlObligationCoverageV2Model, + obligation: ControlProfileObligationV2Model, + slots: dict[str, ControlResultSlotV2Model], + bindings: dict[str, ControlMechanismBindingV2Model], + applicable: set[str], +) -> None: + for identity in item.slot_ids: + if identity not in applicable or slots[identity].kind != obligation.required_kind: + raise ValueError("participant control coverage does not map to an applicable typed slot") + if obligation.profile not in bindings[slots[identity].instance_id].profiles: + raise ValueError("participant control coverage slot does not select its owning profile") def _coverage_roots(request: ParticipantControlRequestV2Model) -> set[str]: diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py index 869082b5d..5e5ca70b4 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py @@ -194,12 +194,14 @@ def _support_causes(support: tuple[ControlSupportAssessmentV2Model, ...], blocke def _support_cause(item: ControlSupportAssessmentV2Model) -> str: observed = item.effective_support if observed.status in {"stale", "unsupported", "weakened"}: - return observed.status - if observed.effective_level == "unsupported": - return "unsupported" - if observed.status == "resolved": - return "weakened" - return "failed" + cause = observed.status + elif observed.effective_level == "unsupported": + cause = "unsupported" + elif observed.status == "resolved": + cause = "weakened" + else: + cause = "failed" + return cause def admitted_control_effect_ids_v2( @@ -212,19 +214,22 @@ def admitted_control_effect_ids_v2( if set(composition.blockers) != {"parent:" + plan.parent_disposition}: return frozenset() base = base_parent_disposition_v2(plan.decisions, plan.incumbent_gate_disposition) - admitted: set[str] = set() if plan.parent_disposition == "withhold" and base == "permit": - admitted.update( - item.effect_id - for item in plan.effects - if item.phase == "required-predecessor" and item.effect_id in plan.runnable_effect_ids - ) - if base in {"deny", "withhold"} and plan.parent_disposition == base: - admitted.update( + admitted = _runnable_effects_in_phase(plan, "required-predecessor") + elif base in {"deny", "withhold"} and plan.parent_disposition == base: + admitted = frozenset( item.effect_id for item in plan.effects if item.phase == "independent" and base in item.allowed_parent_dispositions and item.effect_id in plan.runnable_effect_ids ) - return frozenset(admitted) + else: + admitted = frozenset() + return admitted + + +def _runnable_effects_in_phase(plan: ControlEffectPlanV2Model, phase: str) -> frozenset[str]: + return frozenset( + item.effect_id for item in plan.effects if item.phase == phase and item.effect_id in plan.runnable_effect_ids + ) diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py index ef067381f..d2be88fdd 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py @@ -216,16 +216,18 @@ def _effect_is_runnable( parent_admission_receipt: ControlArtifactReferenceModel | None, ) -> bool: if item.effect_id in realized_effect_ids or not set(item.predecessor_effect_ids) <= set(realized_effect_ids): - return False - if item.phase == "required-predecessor": - return base == "permit" - if item.phase == "success-dependent": - return ( + runnable = False + elif item.phase == "required-predecessor": + runnable = base == "permit" + elif item.phase == "success-dependent": + runnable = ( base == "permit" and not pending and _success_dependent_ready(item, parent_applied, parent_admission_receipt) ) - return item.phase == "independent" and final in item.allowed_parent_dispositions + else: + runnable = item.phase == "independent" and final in item.allowed_parent_dispositions + return runnable class ControlEffectPlanV2Model(ContractModel): diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py index 278e31574..2b2cca081 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py @@ -29,7 +29,7 @@ require_kind, require_unique, ) -from .participant_control_decisions_v2 import ControlEffectPlanV2Model +from .participant_control_decisions_v2 import ControlEffectPlanV2Model, ControlEffectRequestV2Model from .participant_control_effect_composition import ( _effect_ancestors, _lifecycle_invalidates, @@ -132,7 +132,9 @@ def validate_effect_budget_v2( _validate_effect_bounds(effects, new_keys, context, bounds) -def _validate_lifecycle_order(effects: set, context: ParticipantControlContextModel) -> None: +def _validate_lifecycle_order( + effects: set[ControlEffectRequestV2Model], context: ParticipantControlContextModel +) -> None: ancestors = _effect_ancestors({effect.effect_id: {effect} for effect in effects}) ordered = tuple(effects) for index, left in enumerate(ordered): @@ -211,13 +213,7 @@ def _validate_parent_decisions(record: ParticipantControlEvaluationV2Model) -> N if record.effect_plan.parent_crossing != record.request.context.crossing: raise ValueError("parent decision uses a different crossing") decisions = { - item.result_id: item.payload - for item in record.results - if item.status == "resolved" - and item.payload is not None - and item.payload.kind == "decision" - and item.payload.disposition != "abstain" - and item.slot_id in record.request.applicability.required_slot_ids + item.result_id: item.payload for item in record.results if _is_required_parent_decision(item, record.request) } if {item.decision_id for item in record.effect_plan.decisions} != decisions.keys(): raise ValueError("parent decisions do not preserve the typed result set") @@ -227,6 +223,18 @@ def _validate_parent_decisions(record: ParticipantControlEvaluationV2Model) -> N raise ValueError("parent decision differs from its provider result") +def _is_required_parent_decision( + result: ControlMechanismResultV2Model, request: ParticipantControlRequestV2Model +) -> bool: + return ( + result.status == "resolved" + and result.payload is not None + and result.payload.kind == "decision" + and result.payload.disposition != "abstain" + and result.slot_id in request.applicability.required_slot_ids + ) + + def _validate_requested_effects(record: ParticipantControlEvaluationV2Model) -> None: requested_effects = [ item.payload diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py b/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py index 3280d490c..1caf1cc44 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py @@ -285,15 +285,21 @@ def _validate_call_results( for slot_id in call.requested_slot_ids: slot = slots[slot_id] result = result_by_slot[slot_id] - if ( - slot.instance_id != call.instance_id - or result.instance_id != call.instance_id - or result.binding_digest != call.binding_digest - or result.context_digest != call.context_digest - or result.invocation_digest != control_digest(call) - ): - raise ValueError("participant control result differs from its exact invocation") + _validate_call_result_identity(call, slot, result) if result.rule_outcome == "not-triggered" and slot.kind != "effect-request": raise ValueError("not-triggered result requires a rule/effect slot") if result.payload is not None and result.payload.kind != slot.kind: raise ValueError("participant control result does not match its typed slot") + + +def _validate_call_result_identity( + call: ControlInvocationV2Model, slot: ControlResultSlotV2Model, result: ControlMechanismResultV2Model +) -> None: + if ( + slot.instance_id != call.instance_id + or result.instance_id != call.instance_id + or result.binding_digest != call.binding_digest + or result.context_digest != call.context_digest + or result.invocation_digest != control_digest(call) + ): + raise ValueError("participant control result differs from its exact invocation") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py b/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py index befd47511..b942f083c 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py @@ -56,6 +56,8 @@ from .participant_flow_control_context import ParticipantFlowControlValidationContext from .participant_flow_control_validation import validate_participant_flow_control_context +_COMMITTED_EFFECT_MISMATCH = "realized effect differs from its committed full intent, cut, or prerequisites" + @dataclass(frozen=True) class ParticipantControlValidationContext: @@ -443,7 +445,7 @@ def _validate_prior_effect( or prior.outcome != current_outcome or prior.outcome.disposition != "applied" ): - raise ValueError("realized effect differs from its committed full intent, cut, or prerequisites") + raise ValueError(_COMMITTED_EFFECT_MISMATCH) if isinstance(effect.target, ControlInjectEffectModel): _validate_inject(effect.target, origin.request.context, context) @@ -462,7 +464,7 @@ def _validate_prior_identity( or origin.request.context.order > record.request.context.order or origin.request.context.effects_consumed >= record.request.context.effects_consumed ): - raise ValueError("realized effect differs from its committed full intent, cut, or prerequisites") + raise ValueError(_COMMITTED_EFFECT_MISMATCH) def _validate_prior_commit(origin: ParticipantControlEvaluationV2Model, effect_id: str) -> None: @@ -473,7 +475,7 @@ def _validate_prior_commit(origin: ParticipantControlEvaluationV2Model, effect_i or effect_id not in origin.effect_plan.runnable_effect_ids or effect_id not in admitted_control_effect_ids_v2(origin.composition, origin.effect_plan) ): - raise ValueError("realized effect differs from its committed full intent, cut, or prerequisites") + raise ValueError(_COMMITTED_EFFECT_MISMATCH) def _validate_v2_receipts( diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py index c5a99610c..d033bf137 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py @@ -19,6 +19,12 @@ def _validate_positive_support(relation: str, observed: ControlEffectiveSupportM def _validate_strength_relation(relation: str, required_strength: str, observed_level: str) -> None: + _validate_observed_strength(relation, required_strength, observed_level) + if required_strength == "exact" and relation not in {"exact", "insufficient", "unresolved"}: + raise ValueError("exact requirement cannot accept weaker support") + + +def _validate_observed_strength(relation: str, required_strength: str, observed_level: str) -> None: if relation == "exact" and observed_level != "exact": raise ValueError("exact support relation needs exact effective support") if relation == "bounded-compatible" and (required_strength == "exact" or observed_level != "bounded"): @@ -27,8 +33,6 @@ def _validate_strength_relation(relation: str, required_strength: str, observed_ required_strength != "disclosed_weak" or observed_level != "disclosed_weak" ): raise ValueError("disclosed weak support relation is incompatible") - if required_strength == "exact" and relation not in {"exact", "insufficient", "unresolved"}: - raise ValueError("exact requirement cannot accept weaker support") class ControlSupportAssessmentV2Model(ContractModel): diff --git a/implementations/python/tests/test_formal_semantic_validation.py b/implementations/python/tests/test_formal_semantic_validation.py index 2be76cffa..cd9c3d98b 100644 --- a/implementations/python/tests/test_formal_semantic_validation.py +++ b/implementations/python/tests/test_formal_semantic_validation.py @@ -136,6 +136,7 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: "55.0.0", "56.0.0", "57.0.0", + "58.0.0", ] assert all(validate_release_bundle(REPO_ROOT, release) == [] for release in releases) @@ -144,10 +145,10 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: def test_current_retest_bundle_is_coherent_and_clean() -> None: release, protocol, corpus, snapshot, analysis = copy_bundle(load_retest_bundle, REPO_ROOT) - assert release.manifest["revision"] == "57.0.0" + assert release.manifest["revision"] == "58.0.0" assert protocol["revision"] == "2.0.0" assert corpus["revision"] == "4.0.0" - assert snapshot["baseline"]["release_revision"] == "56.0.0" + assert snapshot["baseline"]["release_revision"] == "57.0.0" assert snapshot["deviations"] == [] assert validate_retest_bundle(REPO_ROOT, release, protocol, corpus, snapshot, analysis) == [] diff --git a/implementations/python/tests/test_issue_989_versioned_evidence.py b/implementations/python/tests/test_issue_989_versioned_evidence.py index 2eebf3e75..c4a4ed3fc 100644 --- a/implementations/python/tests/test_issue_989_versioned_evidence.py +++ b/implementations/python/tests/test_issue_989_versioned_evidence.py @@ -230,7 +230,7 @@ def test_latest_current_release_is_versioned_and_strict(monkeypatch): from tools.formal_semantic_validation._releases import validate_retest_bundle release, protocol, corpus, snapshot, analysis = copy_bundle(load_retest_bundle, ROOT) - assert release.manifest["revision"] == "57.0.0" + assert release.manifest["revision"] == "58.0.0" original = _retest.replay_case def changed_result(root, case): @@ -283,7 +283,7 @@ def test_specification_current_capture_does_not_accept_old_artifact_digest(artif from tools.check_specification_coverage import load_bundle, validate_bundle manifest, protocol, snapshot, analysis = copy_bundle(load_bundle, ROOT) - assert manifest["revision"] == "56.0.0" + assert manifest["revision"] == "57.0.0" snapshot = deepcopy(snapshot) artifact = next(a for a in snapshot["artifacts"] if a["artifact_id"] == artifact_id) artifact["sha256"] = old_digest @@ -509,6 +509,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "55.0.0", "56.0.0", "57.0.0", + "58.0.0", ] if family == "formal" else [ @@ -569,6 +570,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "54.0.0", "55.0.0", "56.0.0", + "57.0.0", ] ) revisions.pop(-1 if removed == "current" else 0) diff --git a/implementations/python/tests/test_specification_coverage.py b/implementations/python/tests/test_specification_coverage.py index 63823b7eb..3b205523d 100644 --- a/implementations/python/tests/test_specification_coverage.py +++ b/implementations/python/tests/test_specification_coverage.py @@ -53,7 +53,7 @@ def test_immutable_bundle_index_preserves_concurrent_captures() -> None: bundles = copy_bundle(load_bundles, REPO_ROOT) assert {manifest["revision"] for manifest, *_rest in bundles} >= {"1.0.0", "1.1.0", "19.0.0"} manifest, *_rest = copy_bundle(load_bundle, REPO_ROOT) - assert manifest["revision"] == "56.0.0" + assert manifest["revision"] == "57.0.0" def test_historical_failures_name_the_revision_specific_documents() -> None: diff --git a/tools/check_specification_coverage.py b/tools/check_specification_coverage.py index 568669120..f8cf9785e 100644 --- a/tools/check_specification_coverage.py +++ b/tools/check_specification_coverage.py @@ -157,12 +157,12 @@ def _load_bundle_index(repo_root: Path) -> list[tuple[str, dict[str, object]]]: "51.0.0", "52.0.0", "53.0.0", - } | {"54.0.0", "55.0.0", "56.0.0"} + } | {"54.0.0", "55.0.0", "56.0.0", "57.0.0"} if ( - dict(records)[current_path].get("revision") != "56.0.0" + dict(records)[current_path].get("revision") != "57.0.0" or {record.get("revision") for _, record in records} != supported_revisions ): - raise ValueError("coverage evidence requires the explicit current 56.0.0 release and supported history") + raise ValueError("coverage evidence requires the explicit current 57.0.0 release and supported history") return records diff --git a/tools/formal_semantic_validation/_baseline.py b/tools/formal_semantic_validation/_baseline.py index d578b3aa9..3c31c1e1b 100644 --- a/tools/formal_semantic_validation/_baseline.py +++ b/tools/formal_semantic_validation/_baseline.py @@ -193,6 +193,7 @@ def _selected_baseline_manifest( "55.0.0", "56.0.0", "57.0.0", + "58.0.0", }: expected_corpus_path = "docs/research/formal-semantic-validation/corpus/manifest-v4.json" elif baseline_revision in _V3_CORPUS_REVISIONS: diff --git a/tools/formal_semantic_validation/_loading.py b/tools/formal_semantic_validation/_loading.py index caba98917..95524a9f4 100644 --- a/tools/formal_semantic_validation/_loading.py +++ b/tools/formal_semantic_validation/_loading.py @@ -75,6 +75,6 @@ def load_retest_bundle( if not releases: raise ValueError("the formal semantic-validation index selects no v2 retest release") release = max(releases, key=lambda item: revision_key(item.manifest.get("revision"))) - if release.manifest.get("revision") != "57.0.0" or release.protocol.get("revision") != "2.0.0": - raise ValueError("the current formal evidence release must be the explicit 57.0.0 retest") + if release.manifest.get("revision") != "58.0.0" or release.protocol.get("revision") != "2.0.0": + raise ValueError("the current formal evidence release must be the explicit 58.0.0 retest") return release, release.protocol, release.corpus, release.snapshot, release.analysis diff --git a/tools/formal_semantic_validation/_release_revisions.py b/tools/formal_semantic_validation/_release_revisions.py index 0c2d151ea..a80df07f9 100644 --- a/tools/formal_semantic_validation/_release_revisions.py +++ b/tools/formal_semantic_validation/_release_revisions.py @@ -53,7 +53,7 @@ "51.0.0", "52.0.0", } -) | {"53.0.0", "54.0.0", "55.0.0", "56.0.0"} +) | {"53.0.0", "54.0.0", "55.0.0", "56.0.0", "57.0.0"} -_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"57.0.0"} +_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"58.0.0"} _SOURCE_BOUND_RETEST_REVISIONS = _SUPPORTED_RETEST_REVISIONS - {"3.0.0"} diff --git a/tools/formal_semantic_validation/_releases.py b/tools/formal_semantic_validation/_releases.py index 673aa2f5d..3e32b7473 100644 --- a/tools/formal_semantic_validation/_releases.py +++ b/tools/formal_semantic_validation/_releases.py @@ -159,7 +159,7 @@ def validate_release_bundle(repo_root: Path, release: EvidenceRelease) -> list[P release.corpus, release.snapshot, release.analysis, - replay_current=manifest.get("revision") == "57.0.0", + replay_current=manifest.get("revision") == "58.0.0", ) ) else: @@ -274,6 +274,7 @@ def _expected_corpus_revision(release_revision: object) -> str: "55.0.0", "56.0.0", "57.0.0", + "58.0.0", }: expected_corpus_revision = "4.0.0" return expected_corpus_revision @@ -301,7 +302,7 @@ def validate_retest_bundle( return [ _failure( "formal-validation-current-replay-required", - "only releases 3.0.0 through 56.0.0 can use integrated historical validation", + "only releases 3.0.0 through 57.0.0 can use integrated historical validation", snapshot_path, ) ] @@ -431,6 +432,7 @@ def _current_retest_source_failures( "55.0.0": "54.0.0", "56.0.0": "55.0.0", "57.0.0": "56.0.0", + "58.0.0": "57.0.0", }[release_revision] if not isinstance(baseline, Mapping) or baseline.get("release_revision") != expected_baseline: failures.append( diff --git a/tools/formal_semantic_validation/_retest.py b/tools/formal_semantic_validation/_retest.py index ec1da39d7..0b8a902d8 100644 --- a/tools/formal_semantic_validation/_retest.py +++ b/tools/formal_semantic_validation/_retest.py @@ -37,7 +37,7 @@ ) from tools.policy.common import PolicyFailure -_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 58)) +_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 59)) @dataclasses.dataclass(frozen=True) diff --git a/tools/policy/requirement_order.yaml b/tools/policy/requirement_order.yaml index 2b9b8deea..9f6596022 100644 --- a/tools/policy/requirement_order.yaml +++ b/tools/policy/requirement_order.yaml @@ -844,12 +844,18 @@ ownership: - docs/research/formal-semantic-validation/analysis-v56.json - docs/research/formal-semantic-validation/bundles/retest-v56.json - docs/research/formal-semantic-validation/execution-snapshot-v56.json + - docs/research/formal-semantic-validation/analysis-v57.json + - docs/research/formal-semantic-validation/bundles/retest-v57.json + - docs/research/formal-semantic-validation/execution-snapshot-v57.json - docs/research/specification-coverage/analysis-v55.json - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v55.json - docs/research/specification-coverage/execution-snapshot-v55.json - docs/research/specification-coverage/analysis-v56.json - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json - docs/research/specification-coverage/execution-snapshot-v56.json + - docs/research/specification-coverage/analysis-v57.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json + - docs/research/specification-coverage/execution-snapshot-v57.json - implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py - implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py - implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py From eca6b8bb8b0ba230d247ad0fb813ad26a80d9689 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 22:23:18 +0200 Subject: [PATCH 10/10] Fix SonarCloud findings (cycle 3) --- .../_release_revisions.py | 60 ++++++++++++++++++ tools/formal_semantic_validation/_releases.py | 61 +------------------ 2 files changed, 62 insertions(+), 59 deletions(-) diff --git a/tools/formal_semantic_validation/_release_revisions.py b/tools/formal_semantic_validation/_release_revisions.py index 708f976ea..a6754d0cd 100644 --- a/tools/formal_semantic_validation/_release_revisions.py +++ b/tools/formal_semantic_validation/_release_revisions.py @@ -57,3 +57,63 @@ _SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"59.0.0"} _SOURCE_BOUND_RETEST_REVISIONS = _SUPPORTED_RETEST_REVISIONS - {"3.0.0"} + +_RETEST_BASELINE_REVISIONS = { + "4.0.0": "3.0.0", + "5.0.0": "4.0.0", + "6.0.0": "5.0.0", + "7.0.0": "6.0.0", + "8.0.0": "7.0.0", + "9.0.0": "8.0.0", + "10.0.0": "9.0.0", + "11.0.0": "10.0.0", + "12.0.0": "11.0.0", + "13.0.0": "12.0.0", + "14.0.0": "13.0.0", + "15.0.0": "14.0.0", + "16.0.0": "15.0.0", + "17.0.0": "16.0.0", + "18.0.0": "17.0.0", + "19.0.0": "18.0.0", + "20.0.0": "19.0.0", + "21.0.0": "20.0.0", + "22.0.0": "21.0.0", + "23.0.0": "22.0.0", + "24.0.0": "23.0.0", + "25.0.0": "24.0.0", + "26.0.0": "25.0.0", + "27.0.0": "26.0.0", + "28.0.0": "27.0.0", + "29.0.0": "28.0.0", + "30.0.0": "29.0.0", + "31.0.0": "30.0.0", + "32.0.0": "31.0.0", + "33.0.0": "32.0.0", + "34.0.0": "33.0.0", + "35.0.0": "34.0.0", + "36.0.0": "35.0.0", + "37.0.0": "36.0.0", + "38.0.0": "37.0.0", + "39.0.0": "38.0.0", + "40.0.0": "39.0.0", + "41.0.0": "40.0.0", + "42.0.0": "41.0.0", + # Parallel issue-218 captures retain their original pre-identity baseline. + "43.0.0": "41.0.0", + "44.0.0": "43.0.0", + "45.0.0": "42.0.0", + "46.0.0": "45.0.0", + "47.0.0": "42.0.0", + "48.0.0": "42.0.0", + "49.0.0": "45.0.0", + "50.0.0": "49.0.0", + "51.0.0": "50.0.0", + "52.0.0": "51.0.0", + "53.0.0": "52.0.0", + "54.0.0": "53.0.0", + "55.0.0": "54.0.0", + "56.0.0": "55.0.0", + "57.0.0": "56.0.0", + "58.0.0": "57.0.0", + "59.0.0": "58.0.0", +} diff --git a/tools/formal_semantic_validation/_releases.py b/tools/formal_semantic_validation/_releases.py index 8b250833e..544885aa1 100644 --- a/tools/formal_semantic_validation/_releases.py +++ b/tools/formal_semantic_validation/_releases.py @@ -13,6 +13,7 @@ from tools.formal_semantic_validation._protocol import _validate_protocol from tools.formal_semantic_validation._release_revisions import ( _HISTORICAL_RETEST_REVISIONS, + _RETEST_BASELINE_REVISIONS, _SOURCE_BOUND_RETEST_REVISIONS, _SUPPORTED_RETEST_REVISIONS, ) @@ -377,65 +378,7 @@ def _current_retest_source_failures( ) ) baseline = snapshot.get("baseline") - expected_baseline = { - "4.0.0": "3.0.0", - "5.0.0": "4.0.0", - "6.0.0": "5.0.0", - "7.0.0": "6.0.0", - "8.0.0": "7.0.0", - "9.0.0": "8.0.0", - "10.0.0": "9.0.0", - "11.0.0": "10.0.0", - "12.0.0": "11.0.0", - "13.0.0": "12.0.0", - "14.0.0": "13.0.0", - "15.0.0": "14.0.0", - "16.0.0": "15.0.0", - "17.0.0": "16.0.0", - "18.0.0": "17.0.0", - "19.0.0": "18.0.0", - "20.0.0": "19.0.0", - "21.0.0": "20.0.0", - "22.0.0": "21.0.0", - "23.0.0": "22.0.0", - "24.0.0": "23.0.0", - "25.0.0": "24.0.0", - "26.0.0": "25.0.0", - "27.0.0": "26.0.0", - "28.0.0": "27.0.0", - "29.0.0": "28.0.0", - "30.0.0": "29.0.0", - "31.0.0": "30.0.0", - "32.0.0": "31.0.0", - "33.0.0": "32.0.0", - "34.0.0": "33.0.0", - "35.0.0": "34.0.0", - "36.0.0": "35.0.0", - "37.0.0": "36.0.0", - "38.0.0": "37.0.0", - "39.0.0": "38.0.0", - "40.0.0": "39.0.0", - "41.0.0": "40.0.0", - "42.0.0": "41.0.0", - # Parallel issue-218 captures retain their original pre-identity baseline. - "43.0.0": "41.0.0", - "44.0.0": "43.0.0", - "45.0.0": "42.0.0", - "46.0.0": "45.0.0", - "47.0.0": "42.0.0", - "48.0.0": "42.0.0", - "49.0.0": "45.0.0", - "50.0.0": "49.0.0", - "51.0.0": "50.0.0", - "52.0.0": "51.0.0", - "53.0.0": "52.0.0", - "54.0.0": "53.0.0", - "55.0.0": "54.0.0", - "56.0.0": "55.0.0", - "57.0.0": "56.0.0", - "58.0.0": "57.0.0", - "59.0.0": "58.0.0", - }[release_revision] + expected_baseline = _RETEST_BASELINE_REVISIONS[release_revision] if not isinstance(baseline, Mapping) or baseline.get("release_revision") != expected_baseline: failures.append( _failure(