From 0e2240ece5ff89561784667b5ccfe7d35227e4c7 Mon Sep 17 00:00:00 2001 From: Bashir Loyan Date: Tue, 8 Sep 2026 14:07:41 -0400 Subject: [PATCH 1/4] Add files via upload test(conformance): separate ACTION provenance, authorization, and controller reporting Adds provenance_status, authorization_decision, and controller_outcome to the private ACTION conformance helper's result, so a correct denial or a correct controller rejection is never confused with a broken or absent delegation chain. Fields are set in prerequisite order, per the agreed scope: authorization is reported only once provenance verifies, and controller_outcome only once authorization allows the request. provenance_status distinguishes invalid (chain/records present but a check found a defect) from missing (the record set needed to complete the check was never supplied), per the five agreed reporting cases. Cases 1-4 map onto existing ACTION-003/005/006/007/008 cases, now asserting all three new fields. Case 5 (outside helper scope) is documented in tests/conformance/README.md but not exercised by a new case in this change. Preserves the existing classification and code fields, and every existing reason code, for compatibility. --- tests/conformance/test_profile_conformance.py | 133 +++++++++++++++--- 1 file changed, 111 insertions(+), 22 deletions(-) diff --git a/tests/conformance/test_profile_conformance.py b/tests/conformance/test_profile_conformance.py index 47847e8..48923fa 100644 --- a/tests/conformance/test_profile_conformance.py +++ b/tests/conformance/test_profile_conformance.py @@ -68,6 +68,19 @@ class _ActionEvidence: @dataclass(frozen=True) class _ActionEvidenceResult: + """The ACTION helper's verdict. + + ``provenance_status``, ``authorization_decision``, and ``controller_outcome`` + report three separate questions (see tests/conformance/README.md, Group 7) + so a correct denial or a correct rejection is never confused with a + provenance defect. ``classification`` and ``code`` are the pre-existing + broad category and exact reason, kept for compatibility with callers that + only look at those two fields. + """ + + provenance_status: str + authorization_decision: str + controller_outcome: str classification: str code: str @@ -149,18 +162,54 @@ def _verify_action_evidence( evidence: _ActionEvidence, policy: LocalPolicy, ) -> _ActionEvidenceResult: + """Check delegation-linked ACTION evidence and report three axes. + + Security order: provenance is checked first. authorization_decision is + only meaningful once provenance is verified, so a provenance failure + reports authorization and controller reporting as not_evaluated rather + than guessing at either. controller_outcome is reported only once + authorization allows the request, for the same reason. + """ + # A provenance check needs a full record for every hop the chain claims. + # A short or empty record set is not evidence that failed verification; + # it is evidence that was never supplied, so it is reported as missing + # rather than invalid. + hops = [record for record in records if not record.denied] + if not records or len(hops) != len(chain): + return _ActionEvidenceResult( + "missing", + "not_evaluated", + "not_evaluated", + "provenance_invalid", + ProvenanceLinkBroken.code, + ) + try: verify_delegation_chain(chain, trusted_root_issuers={chain[0].issuer}) verify_dag(records) cross_check_chain(records, chain) except CA2AError as exc: - return _ActionEvidenceResult("provenance_invalid", exc.code) + return _ActionEvidenceResult( + "invalid", "not_evaluated", "not_evaluated", "provenance_invalid", exc.code + ) leaf = records[-1] if evidence.trace_record_hash != leaf.record_hash(): - return _ActionEvidenceResult("provenance_invalid", ProvenanceLinkBroken.code) + return _ActionEvidenceResult( + "invalid", + "not_evaluated", + "not_evaluated", + "provenance_invalid", + ProvenanceLinkBroken.code, + ) if evidence.credential_id != leaf.credential_id: - return _ActionEvidenceResult("provenance_invalid", ProvenanceLinkBroken.code) + return _ActionEvidenceResult( + "invalid", + "not_evaluated", + "not_evaluated", + "provenance_invalid", + ProvenanceLinkBroken.code, + ) try: # Offline replay of recorded evidence: the auditor is re-deciding an @@ -179,11 +228,15 @@ def _verify_action_evidence( require_holder_proof=False, ) except ScopeNotPermitted as exc: - return _ActionEvidenceResult("authorization_invalid", exc.code) + return _ActionEvidenceResult( + "verified", "denied", "not_evaluated", "authorization_invalid", exc.code + ) if evidence.controller_decision == "rejected": - return _ActionEvidenceResult("valid_negative_outcome", "CONTROLLER_REJECTED") - return _ActionEvidenceResult("verified", "ACCEPTED") + return _ActionEvidenceResult( + "verified", "allowed", "rejected", "valid_negative_outcome", "CONTROLLER_REJECTED" + ) + return _ActionEvidenceResult("verified", "allowed", "accepted", "verified", "ACCEPTED") # --- Group 1: Delegation --- @@ -444,7 +497,9 @@ def test_action_001_valid_delegated_action_evidence() -> None: _action_evidence(records), LocalPolicy.of(["robot.move", "robot.inspect"]), ) - assert result == _ActionEvidenceResult("verified", "ACCEPTED") + assert result == _ActionEvidenceResult( + "verified", "allowed", "accepted", "verified", "ACCEPTED" + ) def test_action_002_parent_trace_hash_mismatch_is_provenance_invalid() -> None: @@ -463,7 +518,9 @@ def test_action_002_parent_trace_hash_mismatch_is_provenance_invalid() -> None: _action_evidence(records), LocalPolicy.of(["robot.move"]), ) - assert result == _ActionEvidenceResult("provenance_invalid", "PROVENANCE_LINK_BROKEN") + assert result == _ActionEvidenceResult( + "invalid", "not_evaluated", "not_evaluated", "provenance_invalid", "PROVENANCE_LINK_BROKEN" + ) def test_action_003_missing_parent_trace_record_is_provenance_invalid() -> None: @@ -475,7 +532,9 @@ def test_action_003_missing_parent_trace_record_is_provenance_invalid() -> None: _action_evidence(records), LocalPolicy.of(["robot.move"]), ) - assert result == _ActionEvidenceResult("provenance_invalid", "PROVENANCE_LINK_BROKEN") + assert result == _ActionEvidenceResult( + "missing", "not_evaluated", "not_evaluated", "provenance_invalid", "PROVENANCE_LINK_BROKEN" + ) def test_action_004_unknown_delegation_credential_id_is_provenance_invalid() -> None: @@ -487,7 +546,9 @@ def test_action_004_unknown_delegation_credential_id_is_provenance_invalid() -> _action_evidence(records, credential_id="unknown-credential"), LocalPolicy.of(["robot.move"]), ) - assert result == _ActionEvidenceResult("provenance_invalid", "PROVENANCE_LINK_BROKEN") + assert result == _ActionEvidenceResult( + "invalid", "not_evaluated", "not_evaluated", "provenance_invalid", "PROVENANCE_LINK_BROKEN" + ) def test_action_005_action_outside_delegated_scope_is_authorization_invalid() -> None: @@ -499,7 +560,9 @@ def test_action_005_action_outside_delegated_scope_is_authorization_invalid() -> _action_evidence(records, requested_capability="robot.stop"), LocalPolicy.of(["robot.move", "robot.stop"]), ) - assert result == _ActionEvidenceResult("authorization_invalid", "SCOPE_NOT_PERMITTED") + assert result == _ActionEvidenceResult( + "verified", "denied", "not_evaluated", "authorization_invalid", "SCOPE_NOT_PERMITTED" + ) def test_action_006_local_policy_denial_is_authorization_invalid() -> None: @@ -511,7 +574,9 @@ def test_action_006_local_policy_denial_is_authorization_invalid() -> None: _action_evidence(records, requested_capability="robot.inspect"), LocalPolicy.of(["robot.move"]), ) - assert result == _ActionEvidenceResult("authorization_invalid", "SCOPE_NOT_PERMITTED") + assert result == _ActionEvidenceResult( + "verified", "denied", "not_evaluated", "authorization_invalid", "SCOPE_NOT_PERMITTED" + ) def test_action_007_controller_rejection_is_valid_negative_outcome() -> None: @@ -523,7 +588,9 @@ def test_action_007_controller_rejection_is_valid_negative_outcome() -> None: _action_evidence(records, controller_decision="rejected"), LocalPolicy.of(["robot.move"]), ) - assert result == _ActionEvidenceResult("valid_negative_outcome", "CONTROLLER_REJECTED") + assert result == _ActionEvidenceResult( + "verified", "allowed", "rejected", "valid_negative_outcome", "CONTROLLER_REJECTED" + ) def test_action_008_invalid_delegation_signature_is_provenance_invalid() -> None: @@ -542,18 +609,26 @@ def test_action_008_invalid_delegation_signature_is_provenance_invalid() -> None # The controls establish both downstream classifications that invalid provenance must preempt. assert _verify_action_evidence(chain, records, evidence, restrictive_policy) == ( - _ActionEvidenceResult("authorization_invalid", "SCOPE_NOT_PERMITTED") + _ActionEvidenceResult( + "verified", "denied", "not_evaluated", "authorization_invalid", "SCOPE_NOT_PERMITTED" + ) ) assert _verify_action_evidence(chain, records, evidence, permissive_policy) == ( - _ActionEvidenceResult("valid_negative_outcome", "CONTROLLER_REJECTED") + _ActionEvidenceResult( + "verified", "allowed", "rejected", "valid_negative_outcome", "CONTROLLER_REJECTED" + ) ) # ACTION-008 verifies provenance validation precedes authorization and controller outcome classification. assert _verify_action_evidence(bad_chain, records, evidence, restrictive_policy) == ( - _ActionEvidenceResult("provenance_invalid", "INVALID_CREDENTIAL") + _ActionEvidenceResult( + "invalid", "not_evaluated", "not_evaluated", "provenance_invalid", "INVALID_CREDENTIAL" + ) ) assert _verify_action_evidence(bad_chain, records, evidence, permissive_policy) == ( - _ActionEvidenceResult("provenance_invalid", "INVALID_CREDENTIAL") + _ActionEvidenceResult( + "invalid", "not_evaluated", "not_evaluated", "provenance_invalid", "INVALID_CREDENTIAL" + ) ) @@ -572,7 +647,9 @@ def test_action_009_multi_hop_attenuation_verifies() -> None: _action_evidence(records), LocalPolicy.of(["robot.move", "robot.inspect"]), ) - assert result == _ActionEvidenceResult("verified", "ACCEPTED") + assert result == _ActionEvidenceResult( + "verified", "allowed", "accepted", "verified", "ACCEPTED" + ) def test_action_010_intermediate_scope_widening_is_provenance_invalid() -> None: @@ -590,7 +667,9 @@ def test_action_010_intermediate_scope_widening_is_provenance_invalid() -> None: _action_evidence(records), LocalPolicy.of(["robot.move", "robot.inspect"]), ) - assert result == _ActionEvidenceResult("provenance_invalid", "SCOPE_ESCALATION") + assert result == _ActionEvidenceResult( + "invalid", "not_evaluated", "not_evaluated", "provenance_invalid", "SCOPE_ESCALATION" + ) def test_action_011_delegatee_mismatch_is_provenance_invalid() -> None: @@ -610,7 +689,9 @@ def test_action_011_delegatee_mismatch_is_provenance_invalid() -> None: _action_evidence(records), LocalPolicy.of(["robot.move"]), ) - assert result == _ActionEvidenceResult("provenance_invalid", "PROVENANCE_LINK_BROKEN") + assert result == _ActionEvidenceResult( + "invalid", "not_evaluated", "not_evaluated", "provenance_invalid", "PROVENANCE_LINK_BROKEN" + ) # 2001-09-09 and 2100-01-01. The action-evidence helper replays through the @@ -629,7 +710,9 @@ def test_action_012_expired_delegation_credential_is_provenance_invalid() -> Non _action_evidence(records), LocalPolicy.of(["robot.move", "robot.inspect"]), ) - assert result == _ActionEvidenceResult("provenance_invalid", "CREDENTIAL_EXPIRED") + assert result == _ActionEvidenceResult( + "invalid", "not_evaluated", "not_evaluated", "provenance_invalid", "CREDENTIAL_EXPIRED" + ) def test_action_013_not_yet_valid_delegation_credential_is_provenance_invalid() -> None: @@ -641,7 +724,13 @@ def test_action_013_not_yet_valid_delegation_credential_is_provenance_invalid() _action_evidence(records), LocalPolicy.of(["robot.move", "robot.inspect"]), ) - assert result == _ActionEvidenceResult("provenance_invalid", "CREDENTIAL_NOT_YET_VALID") + assert result == _ActionEvidenceResult( + "invalid", + "not_evaluated", + "not_evaluated", + "provenance_invalid", + "CREDENTIAL_NOT_YET_VALID", + ) # --- Group 8: Holder binding --- From 60dccf3b52768d9764c4ddd64225a4e3fc3a932b Mon Sep 17 00:00:00 2001 From: Bashir Loyan Date: Tue, 8 Sep 2026 14:12:59 -0400 Subject: [PATCH 2/4] Update READMe Updates tests/conformance/README.md to document the three fields, the security order, the provenance/controller boundaries (controller_outcome remains a claimed test-helper input, not cryptographically proven execution evidence), and the five minimum reporting cases. --- tests/conformance/README.md | 77 ++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/tests/conformance/README.md b/tests/conformance/README.md index 1eeaf11..765211c 100644 --- a/tests/conformance/README.md +++ b/tests/conformance/README.md @@ -95,21 +95,72 @@ Spec: [call-graph.md](../../docs/spec/call-graph.md) Spec: [trace-a2a-profile.md](../../docs/spec/trace-a2a-profile.md), [provenance-dag.md](../../docs/spec/provenance-dag.md), [call-graph.md](../../docs/spec/call-graph.md) +The ACTION helper checks delegation-linked action evidence and reports its +verdict on three separate axes, so a correct denial or a correct rejection +is never confused with a broken or absent provenance chain: + +| Field | Question it answers | Values | +|---|---|---| +| `provenance_status` | Did the delegation chain and its linked TRACE/provenance records check out? | `verified`, `invalid`, `missing`, `not_evaluated` | +| `authorization_decision` | Was the requested capability actually allowed by the delegated scope and local policy? | `allowed`, `denied`, `not_evaluated` | +| `controller_outcome` | What did the controller report for the action? | `accepted`, `rejected`, `not_evaluated` | + +**Security order.** `provenance_status` is checked first. `authorization_decision` +is only reported once provenance is verified; a provenance failure reports +authorization and controller as `not_evaluated` rather than guessing at +either. `controller_outcome` is reported only once authorization allows the +request, for the same reason. A local-policy or scope denial (ACTION-005, +ACTION-006) is `verified` / `denied` / `not_evaluated`: the denial shows the +control worked correctly, and is not evidence of bad provenance. + +**`provenance_status` boundary.** `invalid` means the chain or its records +were present and a check found a defect in them (a tampered signature, a +broken or mismatched link, scope escalation, or a credential outside its +validity window). `missing` means the record set needed to complete the +check was never supplied — for example a non-root record without its +parent — which is a different fact from a check that ran and failed. +`verified` means every check this helper performs on the chain and its +records passed. `provenance_status` does not cover holder-proof binding: +this helper replays recorded evidence offline, so it does not check whether +the original caller proved it held the leaf key, and it never claims that +absence as either `verified` or `invalid`. + +**`controller_outcome` boundary.** `controller_outcome` is currently a +claimed test-helper input (`_ActionEvidence.controller_decision`), not +evidence cryptographically proven by a signed ACTION or TRACE record. + +`classification` and `code` are the pre-existing broad category and exact +reason, kept on the result for compatibility with callers that only look at +those two fields. + +The five minimum reporting cases agreed for this issue: + +1. Policy denial: `verified` / `denied` / `not_evaluated` (ACTION-005, ACTION-006). +2. Controller rejection: `verified` / `allowed` / `rejected` (ACTION-007). +3. Invalid signature: `invalid` / `not_evaluated` / `not_evaluated` (ACTION-008). +4. Missing evidence: `missing` / `not_evaluated` / `not_evaluated` (ACTION-003). +5. Outside helper scope: `not_evaluated`, not `verified` or `invalid`. Not covered + by a case in this change; existing ACTION cases do not exercise a check this + helper is structurally unable to perform. + | ID | Level | Requirement | Expected outcome | |---|---|---|---| -| ACTION-001 | MUST | A delegated action with a parent-linked TRACE/provenance record, matching credential id, and permitted capability verifies. | `verified`. | -| ACTION-002 | MUST | A child action record whose parent record hash does not match the canonical parent record hash is rejected as provenance-invalid. | `PROVENANCE_LINK_BROKEN`. | -| ACTION-003 | MUST | A non-root delegated action record without its parent record is rejected as provenance-invalid. | `PROVENANCE_LINK_BROKEN`. | -| ACTION-004 | MUST | Action evidence naming a delegation credential id that is not the verified leaf credential is rejected as provenance-invalid. | `PROVENANCE_LINK_BROKEN`. | -| ACTION-005 | MUST | A requested action outside the effective delegated scope is classified as authorization-invalid, not malformed provenance. | `SCOPE_NOT_PERMITTED`. | -| ACTION-006 | MUST | A valid delegated action denied by local policy is classified as authorization-invalid, not malformed provenance. | `SCOPE_NOT_PERMITTED`. | -| ACTION-007 | MUST | A valid delegated action whose controller outcome is negative remains valid evidence of a negative outcome. | `valid_negative_outcome`. | -| ACTION-008 | MUST | An action whose delegation chain contains a credential with an invalid signature is rejected as provenance-invalid before authorization or outcome handling. | `INVALID_CREDENTIAL`. | -| ACTION-009 | MUST | A delegated action with a strictly attenuating multi-hop credential chain verifies. | `verified`. | -| ACTION-010 | MUST | An action evidence chain with scope widening at an intermediate hop is rejected as provenance-invalid. | `SCOPE_ESCALATION`. | -| ACTION-011 | MUST | Action evidence whose delegatee differs from the subject of the referenced credential is rejected as provenance-invalid. | `PROVENANCE_LINK_BROKEN`. | -| ACTION-012 | MUST | Action evidence whose delegation chain contains an expired credential is rejected as provenance-invalid. | `CREDENTIAL_EXPIRED`. | -| ACTION-013 | MUST | Action evidence whose delegation chain contains a not-yet-valid credential is rejected as provenance-invalid. | `CREDENTIAL_NOT_YET_VALID`. | +| ACTION-001 | MUST | A delegated action with a parent-linked TRACE/provenance record, matching credential id, and permitted capability verifies. | `verified` / `allowed` / `accepted` (`verified`, `ACCEPTED`). | +| ACTION-002 | MUST | A child action record whose parent record hash does not match the canonical parent record hash is rejected as provenance-invalid. | `invalid` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `PROVENANCE_LINK_BROKEN`). | +| ACTION-003 | MUST | A non-root delegated action record without its parent record is rejected as provenance-missing. | `missing` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `PROVENANCE_LINK_BROKEN`). | +| ACTION-004 | MUST | Action evidence naming a delegation credential id that is not the verified leaf credential is rejected as provenance-invalid. | `invalid` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `PROVENANCE_LINK_BROKEN`). | +| ACTION-005 | MUST | A requested action outside the effective delegated scope is classified as authorization-invalid, not malformed provenance. | `verified` / `denied` / `not_evaluated` (`authorization_invalid`, `SCOPE_NOT_PERMITTED`). | +| ACTION-006 | MUST | A valid delegated action denied by local policy is classified as authorization-invalid, not malformed provenance. | `verified` / `denied` / `not_evaluated` (`authorization_invalid`, `SCOPE_NOT_PERMITTED`). | +| ACTION-007 | MUST | A valid delegated action whose controller outcome is negative remains valid evidence of a negative outcome. | `verified` / `allowed` / `rejected` (`valid_negative_outcome`, `CONTROLLER_REJECTED`). | +| ACTION-008 | MUST | An action whose delegation chain contains a credential with an invalid signature is rejected as provenance-invalid before authorization or outcome handling. | `invalid` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `INVALID_CREDENTIAL`). | +| ACTION-009 | MUST | A delegated action with a strictly attenuating multi-hop credential chain verifies. | `verified` / `allowed` / `accepted` (`verified`, `ACCEPTED`). | +| ACTION-010 | MUST | An action evidence chain with scope widening at an intermediate hop is rejected as provenance-invalid. | `invalid` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `SCOPE_ESCALATION`). | +| ACTION-011 | MUST | Action evidence whose delegatee differs from the subject of the referenced credential is rejected as provenance-invalid. | `invalid` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `PROVENANCE_LINK_BROKEN`). | +| ACTION-012 | MUST | Action evidence whose delegation chain contains an expired credential is rejected as provenance-invalid. | `invalid` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `CREDENTIAL_EXPIRED`). | +| ACTION-013 | MUST | Action evidence whose delegation chain contains a not-yet-valid credential is rejected as provenance-invalid. | `invalid` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `CREDENTIAL_NOT_YET_VALID`). | + +ACTION-014 and ACTION-015 are reserved for the historical-replay work +tracked separately; this issue does not use them. ## Group 8: Holder binding From 813002a09eac81eeb5fdd634dd70430507536b00 Mon Sep 17 00:00:00 2001 From: Bashir Loyan Date: Tue, 8 Sep 2026 15:20:37 -0400 Subject: [PATCH 3/4] update test_profile_conformance.py test(conformance): add provenance/authorization/controller reporting to ACTION helper Adds provenance_status, authorization_decision, and controller_outcome to _ActionEvidenceResult, set by _verify_action_evidence() in prerequisite order: authorization only once provenance verifies, controller_outcome only once authorization allows the request. provenance_status distinguishes invalid (chain/records present but a check failed), missing (records incomplete for the claimed chain), and not_evaluated (no chain claimed at all, ACTION-016: outside this helper's scope). Updates all 13 existing ACTION assertions and adds ACTION-016 for the fifth agreed reporting case. Preserves existing classification, code, and reason codes. --- tests/conformance/test_profile_conformance.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/conformance/test_profile_conformance.py b/tests/conformance/test_profile_conformance.py index 48923fa..58da9b0 100644 --- a/tests/conformance/test_profile_conformance.py +++ b/tests/conformance/test_profile_conformance.py @@ -170,6 +170,19 @@ def _verify_action_evidence( than guessing at either. controller_outcome is reported only once authorization allows the request, for the same reason. """ + # No delegation chain is claimed at all: there is nothing here for a + # delegation-provenance check to evaluate. This is a check deliberately + # outside this helper's scope, not a failed or incomplete one, so every + # axis is not_evaluated rather than invalid or missing. + if not chain: + return _ActionEvidenceResult( + "not_evaluated", + "not_evaluated", + "not_evaluated", + "outside_helper_scope", + "NO_DELEGATION_CHAIN", + ) + # A provenance check needs a full record for every hop the chain claims. # A short or empty record set is not evidence that failed verification; # it is evidence that was never supplied, so it is reported as missing @@ -733,6 +746,29 @@ def test_action_013_not_yet_valid_delegation_credential_is_provenance_invalid() ) +def test_action_016_no_delegation_chain_is_outside_helper_scope() -> None: + """ACTION-016: a check deliberately outside this helper's scope. + + No delegation chain is claimed, so there is nothing for a + delegation-provenance check to evaluate -- this is not the same as + evidence being invalid or incomplete (ACTION-002/003). All three axes + are reported as not_evaluated rather than guessed at. + """ + result = _verify_action_evidence( + [], + [], + _ActionEvidence("x", "y", "robot.move"), + LocalPolicy.of(["robot.move"]), + ) + assert result == _ActionEvidenceResult( + "not_evaluated", + "not_evaluated", + "not_evaluated", + "outside_helper_scope", + "NO_DELEGATION_CHAIN", + ) + + # --- Group 8: Holder binding --- From 23930803cda1581220a94e29f0186181995ae22a Mon Sep 17 00:00:00 2001 From: Bashir Loyan Date: Tue, 8 Sep 2026 15:22:28 -0400 Subject: [PATCH 4/4] update readme docs(conformance): document ACTION provenance/authorization/controller fields Documents provenance_status, authorization_decision, and controller_outcome on the ACTION helper's result for Issue #144: what each field covers, the security order, the invalid/missing/not_evaluated distinction, and that controller_outcome is a claimed test-helper input, not cryptographically proven evidence. Lists the five minimum reporting cases and updates the ACTION-001 through ACTION-013 and ACTION-016 outcome column. Closes #144 --- tests/conformance/README.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/conformance/README.md b/tests/conformance/README.md index 765211c..cd76f2e 100644 --- a/tests/conformance/README.md +++ b/tests/conformance/README.md @@ -116,14 +116,17 @@ control worked correctly, and is not evidence of bad provenance. **`provenance_status` boundary.** `invalid` means the chain or its records were present and a check found a defect in them (a tampered signature, a broken or mismatched link, scope escalation, or a credential outside its -validity window). `missing` means the record set needed to complete the -check was never supplied — for example a non-root record without its -parent — which is a different fact from a check that ran and failed. -`verified` means every check this helper performs on the chain and its -records passed. `provenance_status` does not cover holder-proof binding: -this helper replays recorded evidence offline, so it does not check whether -the original caller proved it held the leaf key, and it never claims that -absence as either `verified` or `invalid`. +validity window). `missing` means a chain is claimed but the record set +needed to complete the check was never fully supplied — for example a +non-root record without its parent — which is a different fact from a +check that ran and failed. `not_evaluated` means no delegation chain was +claimed at all (ACTION-016): there is nothing here for a delegation- +provenance check to evaluate, which is a different fact again from evidence +that is missing or invalid. `verified` means every check this helper +performs on the chain and its records passed. `provenance_status` does not +cover holder-proof binding: this helper replays recorded evidence offline, +so it does not check whether the original caller proved it held the leaf +key, and it never claims that absence as either `verified` or `invalid`. **`controller_outcome` boundary.** `controller_outcome` is currently a claimed test-helper input (`_ActionEvidence.controller_decision`), not @@ -139,9 +142,9 @@ The five minimum reporting cases agreed for this issue: 2. Controller rejection: `verified` / `allowed` / `rejected` (ACTION-007). 3. Invalid signature: `invalid` / `not_evaluated` / `not_evaluated` (ACTION-008). 4. Missing evidence: `missing` / `not_evaluated` / `not_evaluated` (ACTION-003). -5. Outside helper scope: `not_evaluated`, not `verified` or `invalid`. Not covered - by a case in this change; existing ACTION cases do not exercise a check this - helper is structurally unable to perform. +5. Outside helper scope: `not_evaluated` / `not_evaluated` / `not_evaluated` + (ACTION-016) — a check this helper is deliberately not evaluating, never + reported as `verified` or `invalid`. | ID | Level | Requirement | Expected outcome | |---|---|---|---| @@ -158,6 +161,7 @@ The five minimum reporting cases agreed for this issue: | ACTION-011 | MUST | Action evidence whose delegatee differs from the subject of the referenced credential is rejected as provenance-invalid. | `invalid` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `PROVENANCE_LINK_BROKEN`). | | ACTION-012 | MUST | Action evidence whose delegation chain contains an expired credential is rejected as provenance-invalid. | `invalid` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `CREDENTIAL_EXPIRED`). | | ACTION-013 | MUST | Action evidence whose delegation chain contains a not-yet-valid credential is rejected as provenance-invalid. | `invalid` / `not_evaluated` / `not_evaluated` (`provenance_invalid`, `CREDENTIAL_NOT_YET_VALID`). | +| ACTION-016 | MUST | Action evidence naming no delegation chain at all is outside this helper's scope, not a failed or incomplete check. | `not_evaluated` / `not_evaluated` / `not_evaluated` (`outside_helper_scope`, `NO_DELEGATION_CHAIN`). | ACTION-014 and ACTION-015 are reserved for the historical-replay work tracked separately; this issue does not use them.