Skip to content

[pre-v1.0] Verification depth + action_receipts: two-axis verification model #66

Closed

Description

@imran-siddique

Tracking issue for the verification-depth design discussed in agentrust-io/trace-tests#50. This is maintainer-driven editorial work for v1.0, not a contributor schema merge.

Decision

Two independent verification axes, rather than a single extended depth enum:

verification:
  depth: surface | builder_chain | dependency_chain   # supply-chain axis (nests)
  action_receipts: required | optional | none          # intra-run axis (does not nest)

Rationale (from agentrust-io/trace-tests#50):

  • The supply-chain axis nests: dependency_chain implies builder_chain implies surface.
  • Action receipts do not nest with supply-chain depth: a complete per-action receipt chain can exist for an agent whose builder is unknown, and full dependency_chain trust can exist with zero action receipts. Folding action into the depth enum would imply a false ordering.

Scope of the editorial change

  1. schema/trace-claim.json
    • build_provenance.provenance_depth: surface | builder | transitive (records omitting it MUST be treated as surface, backward compatible).
    • appraisal.provenance_depth_verified: depth the verifier actually executed. Downgrade allowed, upgrade forbidden; MUST be present when build_provenance is appraised.
    • verification.action_receipts: required | optional | none, with appraisal.action_receipts_verified mirroring the same downgrade-allowed / upgrade-forbidden rule.
  2. docs/verification.md
    • Per-depth verifier obligations (surface / builder_chain / dependency_chain) and the downgrade rule.
    • Profile floors (default = surface; SLSA L2+ = builder; FIPS / EU AI Act Annex IV high-risk = transitive; cMCP reference = builder).
    • Action-receipt verification section for the required case.
  3. Settle the open transitive-coverage-URI question (collapses into the same verification object).

Why depth matters

A SLSA attestation from a trusted builder remains signature-valid even when a maintainer CI token is stolen and used to publish a poisoned input. Surface verification accepts the record; transitive rejects it. Concrete cases: Mastra @mastra/core (2026-06-17) and the CI-token-theft pattern raised on postcss#2096.

Cross-references

  • trace-spec#50 (origin discussion)
  • cmcp#337 / cmcp#339 (embodied-action evidence profile, related evidence binding)
  • demos#10 and integrations#6 (per-action receipt action_ref / Ed25519 / hash-chain shape, the reference for action_receipts: required)

Out of scope

Accepting a contributor-authored normative schema diff directly. The schema and verification.md text will land through the maintainer editorial process.

Activity

  1. arian-gogani commented on Jun 26, 2026

    @arian-gogani

    if the action_receipts axis is landing in v1.0, nobulex can serve as a reference implementation for the receipt primitive.

    the shape that already works in production: each agent action produces a signed receipt with action_ref = SHA-256(JCS({agent_id, action_type, scope, timestamp_ms})). RFC 8785 canonicalization for cross-implementation determinism. Ed25519 signature verified against a pinned authorized key (not the embedded key). hash-chained for ordering.

    the important property for trace-spec: verification is offline and operator-independent. a verifier recomputes the action_ref from the receipt fields, checks the signature against the agent's registered key, and walks the hash chain. no callback to the operator's infrastructure. that's what makes it evidence rather than a log.

    LangChain and CrewAI callback integrations ship with the SDK, so the receipt emits at the framework hook layer without custom instrumentation.

    happy to align the receipt schema with whatever shape the action_receipts axis settles on. the primitive is small enough that conformance is a fixture, not an integration project.

    pip install nobulex / npm install @nobulex/core
    https://github.com/arian-gogani/nobulex

  2. carloshvp commented on Jun 27, 2026

    @carloshvp
    Member

    I think the two-axis model is the right shape, especially for embodied-agent workflows.

    One framing that may be worth making explicit in docs/verification.md:

    For embodied AI, verification.action_receipts: required should mean every externally consequential action has offline-verifiable receipt evidence bound to the session. It should not mean TRACE proves physical completion, controller safety, or that the real world changed as intended.

    That distinction matters because embodied systems have at least three separable layers:

    1. session-level evidence: the TRACE record and its policy/build/runtime appraisal;
    2. action-level evidence: per-action receipts for tool/controller handoff and authorization scope;
    3. physical-world outcome evidence: observations from controllers, monitors, humans, or safety systems.

    I’d suggest the verifier obligations for action_receipts: required stay focused on layer 2:

    • recompute action_ref from a canonical action preimage;
    • verify the receipt signature against a pinned or manifest-bound key, not only a self-asserted embedded key;
    • verify hash-chain ordering when receipts are chained;
    • verify the receipt binds back to the TRACE/cMCP session or call identifier;
    • distinguish missing, stale, mismatched, or unverifiable receipts from negative controller outcomes;
    • avoid treating a valid receipt as proof of physical completion or functional-safety certification.

    For embodied-action profiles, this gives a useful maintainer boundary: TRACE/cMCP can require offline-verifiable action evidence below the session level without over-claiming actuation safety or physical outcome proof.

    If helpful, I can follow this with fixture-style examples for a small embodied-action receipt chain: one passing case, one missing-receipt case, one signature/key mismatch, and one receipt that is valid but represents controller rejection rather than physical completion.

  3. arian-gogani commented on Jun 27, 2026

    @arian-gogani

    @carloshvp the three-layer split (session evidence, action evidence, physical-world outcome) is the right decomposition for embodied systems. the verifier obligations you listed for layer 2 are exactly the shape nobulex implements:

    • recompute action_ref from canonical preimage (SHA-256 over JCS)
    • verify signature against pinned/manifest-bound key, not embedded key
    • verify hash-chain ordering
    • bind receipt back to session/call identifier
    • distinguish missing/stale/mismatched receipts from negative outcomes

    the "not proof of physical completion" boundary is important. a signed receipt proves the agent issued a command to the controller with specific parameters. it does not prove the physical effect occurred. that distinction keeps the verification layer honest about what cryptography can and cannot prove.

    fixture examples for an embodied-action chain would be useful for the spec. the negative cases (missing receipt, key mismatch, valid receipt with controller rejection) are where the verification obligations become concrete rather than aspirational.

  4. 0xbrainkid commented on Jun 28, 2026

    @0xbrainkid

    Useful split. One small extension that would make this easier to compose with external agent identity systems: keep verification.action_receipts about the intra-run evidence, but add an explicit non-scoring slot for the subject identifier those receipts bind to.

    For example:

    subject:
      agent_did: did:...
      agent_card_uri: https://...
      external_evidence:
        - type: satp_attestation
          uri: ...
          digest: ...

    That keeps TRACE from becoming a directory or reputation layer, while still making the handoff clean for cross-org trust systems. AgentFolio/SATP can consume the resolved DID + receipt digest as external evidence; TRACE remains responsible for saying what was verified in this run and at what depth.

    The important boundary is the one already called out above: a receipt proves a specific consequential action was issued under a verifiable key/session. It should not be overloaded into a claim that the physical or business outcome succeeded.

  5. carloshvp commented on Jul 4, 2026

    @carloshvp
    Member

    I agree with this split. A non-scoring subject slot would make action_receipts easier to compose with external identity systems without turning TRACE into an identity directory or reputation layer.

    The boundary I would keep explicit:

    • verification.action_receipts stays about intra-run evidence: receipt presence, canonical preimage, signature, hash-chain/order, and binding to the TRACE session or call.
    • subject identifies what those receipts are intended to bind to: DID, Agent Card URI, manifest subject, external evidence digest, etc.
    • Base TRACE verification should not need to dereference external identity systems to compute its verification depth. It can check local syntax and digest binding, then expose the subject material for AgentFolio/SATP/registry layers to resolve.
    • Missing or unresolvable external evidence should be reported separately from a failed receipt verification unless a stricter profile explicitly requires that resolver.

    For embodied/action evidence this preserves the three-layer model: session evidence, action issuance evidence, and external-world or business outcome evidence. TRACE can say the action evidence is well-bound and offline-verifiable; it should not imply the physical/business outcome succeeded.

  6. lywinged commented on Aug 1, 2026

    @lywinged
    Collaborator

    Strong +1 on the two-axis split — receipt chains and supply-chain depth verify different properties, and the non-nesting call is right.

    One verifier-side question I'd like to see settled alongside the axis definition: how does a verifier distinguish "no action occurred" from "a receipt was dropped"? With action_receipts: required, completeness is the property that matters, and a chain can't prove it on its own.

    A position from operating a per-action receipt emitter in production: you can't make the emitter gap-proof — any high-throughput writer has a crash window where a tail of receipts is lost. What worked instead was making the emitter detect and disclose its own gaps as first-class signed records ("lost ≤N ms of tail at time T, chain resumes at entry X"), so the verifier can treat a disclosed gap (bounded, operator-attested) differently from an undisclosed one (silent, presumptively adversarial). A chain that can only be "perfect or broken" quietly pressures operators to backfill; one that can say exactly what it lost stays honest under real failure.

    If useful, I'm happy to draft this as an ADR for the verification.md obligations side: gap-disclosure semantics + the disclosed/undisclosed distinction as distinct verifier outcomes, with adversarial vectors (dropped-receipt vs. disclosed-gap) for trace-tests.

  7. Montygogetter commented on Aug 16, 2026

    @Montygogetter

    In thinking about action_receipts: required from a governance and audit perspective, I’m curious about the intended boundary of verification. Is the goal to establish that the referenced action receipt is valid, or should a verifier also be able to establish correspondence between the executed action and the policy decision or authorization state governing that action?

    For example, if a policy decision escalates an action for human approval, an auditor may need to establish that the approval preceded execution, applied to the specific action taken, and remained within the authorized scope—not simply that a valid action receipt exists.

    If that linkage is in scope, would you see the minimum semantics belonging in TRACE itself, an action-receipt profile or annex, or primarily in the producing runtime?

  8. tarikskalic33 commented on Aug 16, 2026

    @tarikskalic33

    One additional negative-control case may be worth separating from missing/mismatched receipts: a cryptographically valid but authority-stale receipt.

    If action_receipts: required only checks canonical preimage, signature, chain ordering, and session binding, a receipt could still verify even though the action was issued under an obsolete policy/capability/authority state. That seems distinct from both supply-chain depth and physical/business outcome evidence.

    A possible profile-level shape (without making TRACE an authorization engine) would be to let the receipt bind an immutable authority_context_digest or equivalent derived by the governing runtime from the policy/grant/state it actually evaluated. TRACE would verify the binding, not interpret the external policy semantics.

    Useful conformance vectors could then include:

    • current authority context + valid receipt -> pass;
    • valid signature/chain but altered policy or capability digest -> fail binding;
    • replay of an old valid receipt under a new authority epoch / fencing generation -> fail binding;
    • valid current receipt whose controller outcome is rejection -> receipt evidence passes, outcome remains negative (preserves the session/action/outcome split already discussed).

    This would also give appraisal.action_receipts_verified a cleaner fail-closed boundary: cryptographic integrity of the receipt is necessary but not sufficient when the applicable profile says authority-context binding is required.

    I'm implementing a similar separation in AEGIS Omega (authority evaluation, stale-state rejection, fencing/replay rejection, then receipt emission), and I'd be happy to turn this into fixture-style negative controls rather than propose a competing TRACE schema.

  9. tarikskalic33 commented on Aug 16, 2026

    @tarikskalic33

    I’m exploring a fellowship proposal around executable conformance for the action_receipts axis in this issue. One failure class that seems worth making explicit in the negative vectors is cryptographically valid but authority-stale evidence.

    A receipt can be well-formed, correctly signed, and hash-chain consistent while still being inadmissible for the action it purports to justify if the authority context moved between authorization and effect. Examples:

    • policy_digest / capability-set digest no longer matches the admitted state;
    • a lease/fencing generation advanced, so the signer still has a valid key but no longer has current write authority;
    • the same signed receipt is replayed under a different run/action root;
    • the receipt binds to the right tool call but the wrong pre-state / expected parent.

    I’d suggest keeping this orthogonal to supply-chain provenance_depth, consistent with the two-axis model here: build provenance may verify transitively while the action receipt is stale, and a complete action-receipt chain may verify while build provenance remains surface.

    For action_receipts: required, a useful conformance rule could be: the verifier MUST prove receipt completeness and context binding for the exact admitted action/state tuple; signature validity alone is insufficient. The appraisal should report the receipt axis actually verified and fail closed on an unresolvable authority epoch rather than upgrading it.

    I’m working on an open governed-agent control plane that uses deterministic execution identity, expected-parent checks, fencing/replay rejection, and receipt chains, so I can contribute negative vectors/reference-verifier work here if this direction matches the maintainers’ intended semantics. I’m not proposing a competing schema or trust score—just executable falsification cases for the decision already made in agentrust-io/trace-tests#66.

  10. tarikskalic33 commented on Aug 16, 2026

    @tarikskalic33

    Following up with a concrete non-normative fixture set for the authority-stale class. I’m deliberately not proposing a competing TRACE schema; the point is to make the existing action_receipts decision falsifiable.

    Assume the verifier has already established canonical preimage + pinned-key signature + hash-chain integrity. Then evaluate an independent context-binding predicate over the admitted tuple:

    B(receipt, admitted) :=
      receipt.session_ref     == admitted.session_ref
      ∧ receipt.action_ref    == admitted.action_ref
      ∧ receipt.policy_digest == admitted.policy_digest
      ∧ receipt.state_digest  == admitted.state_digest
      ∧ receipt.authority_epoch == admitted.authority_epoch
      ∧ receipt.fence_token   == admitted.fence_token
    

    Four useful vectors:

    - id: receipt_exact_context_pass
      crypto_valid: true
      chain_valid: true
      context_binding: true
      replay_seen: false
      expected: PASS
    
    - id: receipt_valid_signature_stale_policy
      crypto_valid: true
      chain_valid: true
      receipt.policy_digest: sha256:policy-v2
      admitted.policy_digest: sha256:policy-v3
      expected: FAIL_CONTEXT_BINDING
    
    - id: receipt_valid_signature_stale_authority_epoch
      crypto_valid: true
      chain_valid: true
      receipt.authority_epoch: 41
      admitted.authority_epoch: 42
      receipt.fence_token: fence-41
      admitted.fence_token: fence-42
      expected: FAIL_STALE_AUTHORITY
    
    - id: receipt_replayed_into_new_session
      crypto_valid: true
      chain_valid: true
      receipt.session_ref: session-A
      admitted.session_ref: session-B
      replay_seen: true
      expected: FAIL_REPLAY_OR_SESSION_BINDING

    The important appraisal boundary is that crypto_valid=true is necessary but not sufficient for a profile that requires authority-context binding. A verifier should never upgrade action_receipts_verified merely because the signature/hash chain passes when the admitted authority tuple cannot be resolved or no longer matches.

    Separately, I’d keep real-world/business outcome outside this predicate. A receipt can pass all of the above while outcome = UNKNOWN or while a controller rejects the requested action. That preserves the session-evidence / action-evidence / outcome-evidence separation already discussed in this thread.

    If this matches the maintainer intent, I can turn these into the repo’s preferred fixture format once that format is settled.

  11. lywinged commented on Aug 16, 2026

    @lywinged
    Collaborator

    @tarikskalic33 — the concrete set makes this answerable. Intent for v1.0 belongs to the editorial work this issue tracks; what can be answered today is how your set sits against what is already merged, and against the discipline this corpus already enforces.
    On the boundary first: the fail-closed you want falls out of what just landed in #173, with #177’s separation folded in. On the depth axis: evidence that resolves and contradicts fails the appraisal, and a verifier does not downgrade to escape it; evidence that does not resolve downgrades honestly — the verifier records the depth it actually achieved; and a configured floor not met by the verified value sets appraisal.status to contraindicated. action_receipts isn’t in the schema yet, but once the axis lands, required plays exactly the role a depth floor plays. So the net behaviour you’re asking for — never upgrade on signature alone when the admitted tuple is stale or unresolvable — is the decided semantics carried over, not a new rule: the stale tuple fails outright, and the unresolvable one downgrades into a floor that refuses it.
    On the four vectors, held to the corpus’s own discipline — each vector triggers exactly one rule, and each rule gets two independent vectors split by a named defect:
    Three of your six bound fields already have vectors. session_ref: acta 06-session-binding-mismatch plus conformance 12/21. action_ref: conformance 05/10/18/19. policy_digest: acta 05-stale-policy-digest (signature: pass, policy_freshness: fail — your vector 2 is this defect). The genuinely uncovered surface is state_digest, authority_epoch, fence_token, and the replay ledger — build there, and point at the existing vectors for the rest rather than folding them into one predicate; a single B() with one failure code would coarsen a separation the corpus already tests.
    Vector 3 carries two defects at once (epoch behind and fence behind) — split it, or define fence_token as derived from the epoch so it’s one defect. Vector 4’s expected code contains “OR”, which is the tell: a vector whose expectation is a disjunction can’t split defects. As sketched (mismatched session_ref) it’s caught by the existing session-binding rule with no new machinery; the uncovered replay case is the one where every binding matches and replay_seen is true.
    And the set is missing its fifth member, which your own sentence names: “cannot be resolved or no longer matches” are two outcomes, not one. In the merged semantics they even produce different appraisal shapes — contradicted fails the appraisal outright; unresolvable records the honestly-downgraded value and the floor then sets contraindicated. Same net refusal under required, distinguishable on the record, and the depth corpus already encodes exactly this split vector by vector: 05-…-dependency-publisher-untrusted is the contradicted case, 06-…-resolved-dependencies-absent is the unresolvable one. Your epoch pair should mirror those two.
    Your outcome split is already conformance-tested and survives all of this: acta 02-valid-denied.json (decision deny, every check pass) and cA2A’s ACTION-007 both pin “receipt evidence passes, outcome remains negative.”
    On format, nothing needs to settle before you draft: expected outcomes declared beside the vectors (acta’s expected.json; agent-manifest embeds the expected VerificationResult per vector — either beats encoding them in tests), deterministic keys with committed private halves, and a generator — the guard merged in #171 auto-discovers any gen_*.py under examples/ and holds it to byte-reproduction; #178 is open on making that the corpus-wide invariant. Since the axis is editorial, attach the drafts to this issue as candidate fixtures — they can’t merge ahead of the schema decision, but they’ll test it the day it lands and inform it before then. Happy to review

  12. 33 remaining items

  13. burakoktenli commented on Aug 27, 2026

    @burakoktenli

    I am preparing an AgenTrust Fellowship proposal and want to check whether this direction is useful before I write against it, rather than duplicating work already in the thread.

    The invariant this thread has converged on, that attempt identity is not logical-operation identity and neither is external-outcome identity, is currently defended by curated fixture corpora and hand-constructed finite products. altrudev's Calibration-Studio artifact is the most developed of these, and its own history shows the residual risk: an unresolved provenance state with no verified depth was absent from the first model, a mutant survived because of it, and the product was expanded from 40 to 48 states. The fix was correct. What a hand-enumerated product cannot establish about itself is whether another such state is still missing.

    I would propose a complementary artifact: a TLA+ specification of the evidence-layer lattice with non-promotion stated as a safety invariant, model-checked with TLC over a declared bound, with the bound, reachable state count, and vacuity results published. Counterexamples would convert into conformance fixtures rather than replacing the existing corpus.

    Two questions. Is that useful given the maintainer-directed placement of the normative work here? And where should the resulting fixtures live, given that the original ca2a#36 scope has been closed?

    My prior work is a TLA+ specification model-checked across 18,892 reachable states published with the JHSS paper. The main threat to this approach is abstraction: bounding delegation depth or collapsing cryptographic verification to a boolean could elide the failure modes the corpus is designed to catch, and I would rather hear that from maintainers now than discover it in month three.

  14. Yatsuiii commented on Aug 28, 2026

    @Yatsuiii

    @altrudev's point on #574, that correlation alone should not establish retry identity and that reuse needs agreement with an immutable action or intent binding, is the rule in the merged version: a conflicting binding is refused before upstream invocation.

    What it does not settle is which bytes the binding is over, and in cmcp today there is no single answer. The canonicalization call sites disagree:

    • catalog/approval.py:81, canonical_json, RFC 8785: ensure_ascii=False, members ordered by UTF-16 code unit
    • audit/trace_claim.py:299, also canonical_json, but json.dumps(sort_keys=True, ensure_ascii=True)
    • cmcp_verify/embodied_action.py:63, canonical_json_bytes, the same non-JCS form
    • audit/chain.py:90, catalog/loader.py:133, catalog/scanner.py:20, startup.py:98, each its own

    Three of those carry a canonical-JSON name and one of them is JCS. On ASCII records every one agrees byte for byte, which is why none of it has surfaced.

    The consequence for this issue is narrow. If an action binding is going to be cited across implementations, the canonicalization belongs where the binding is defined, rather than inherited from whichever helper happens to be in scope.

  15. safal207 commented on Aug 28, 2026

    @safal207
    Contributor

    Agreed. If an action binding is meant to be cited across implementations, the binding contract has to define the preimage and canonicalization profile; a field name such as canonical_json is not enough.

    The smallest useful regression looks like a paired non-ASCII vector whose semantic JSON value is the same but whose bytes diverge between RFC 8785/JCS and the current sort_keys=True, ensure_ascii=True form. The fixture should publish:

    • the declared canonicalization profile;
    • the exact canonical UTF-8 bytes;
    • the resulting commitment;
    • a positive case for the declared profile; and
    • a negative case rejecting the alternative serialization even though it decodes to an equivalent JSON value.

    I would also keep the identity boundary explicit: agreement on canonical content bytes can establish the same normalized action or intent binding, but it does not establish that two receipts describe the same real-world occurrence. Retry/occurrence identity still needs an immutable operation or occurrence identifier plus consumer-side reuse control.

    That turns the current implementation disagreement into a bounded interoperability fixture without overstating what the content commitment proves.

  16. aeoess commented on Aug 28, 2026

    @aeoess
    Contributor

    One thing I can't quite resolve yet with action_receipts: required: what is the verifier checking completeness against?

    The conformance verifier already has a clear result for a missing required receipt, but in the conformance fixtures the fact that a receipt was required comes from context.require_receipt, outside the record. On main I don't see the equivalent source of truth yet. tool_transcript gives the hash, call count and URI, and §3.3.2 tells the verifier how to validate and bind a receipt that exists, but not which calls were required to have one.

    If required means every call, I think that just needs to be explicit, with the transcript entries forming the coverage set. If it means every consequential call, then the consequential set needs to be admitted as data somehow, for example by the profile or per entry, so the verifier can join required calls to receipts without interpreting policy.

    Otherwise all receipts that are present can verify while a required one is silently absent, and action_receipts_verified: required can't distinguish the two cases.

    I think this is also the missing prerequisite for the disclosed-gap case discussed above: before a verifier can reason about a gap, it needs to know what was expected to be there.

  17. altrudev commented on Aug 29, 2026

    @altrudev
    Contributor
  18. Yatsuiii commented on Aug 29, 2026

    @Yatsuiii

    One boundary may need to be explicit before adopting the proposed expected-call_id → receipt-linked_call_id equality.

    Current cMCP/TRACE fixtures model one action receipt per call, while agentrust-io/trace-tests#95’s original candidate matrix distinguished a request receipt from a controller-outcome receipt; agentrust-io/trace-tests#101 ultimately implemented one generic missing-receipt case.

    Is v1.0 intentionally defining at most one required action receipt per call? If so, stating that invariant would make call-set equality sufficient and clarify how duplicate receipts are handled. If profiles may instead require multiple evidence roles for one call, completeness would need to range over profile-defined obligations such as (call_id, evidence_role), not call_id alone.

    I’m raising this as a cardinality clarification, not claiming the current single-receipt profile is bypassable.

  19. altrudev commented on Aug 29, 2026

    @altrudev
    Contributor
  20. imran-siddique commented on Sep 3, 2026

    @imran-siddique
    MemberAuthor

    Linking #279. The two-axis proposal here is the oldest statement of something five newer threads have re-derived independently: verification depth where a layer was not reached is not the same as a layer that failed, and neither is a pass.

    #279 collects the instances (#190, #191, #192, #206, #249) and extracts the rule from #260, the one place it has already shipped. Worth reading them together before this one is settled, since a two-axis model that does not carry a not-reached value on the depth axis would need the same fix immediately.

    #279

  21. aeoess commented on Sep 4, 2026

    @aeoess
    Contributor

    I dug into the depth case from #279 on current main. The runner already knows the difference between "stopped at builder" and "tried transitive but couldn't resolve the evidence": empty vs populated unresolved.

    That distinction gets lost when you serialize the Appraisal. Same fields, same bytes, and there's nowhere to carry why the deeper level wasn't reached.

    I put together a small candidate fixture set on top of the existing depth vectors. No schema change, no public API, and the naming is intentionally provisional. It just makes the two cases impossible to collapse into one.

    Branch: main...aeoess:trace-spec:examples/build-provenance-depth-not-reached

  22. ioanavalea commented on Sep 6, 2026

    @ioanavalea
    Contributor

    Scoping this to one line in the docs/verification.md bullet, and to the EU AI Act half of it only. I have not checked the FIPS half against NIST and am not speaking to it.

    Profile floors (default = surface; SLSA L2+ = builder; FIPS / EU AI Act Annex IV high-risk = transitive; cMCP reference = builder).

    The citation that carries the intent

    If the floor is meant to rest on an EU instrument, the one that actually mandates a component inventory is the Cyber Resilience Act, Regulation (EU) 2024/2847, Annex I Part II point 1. It requires manufacturers to identify and document components, including by drawing up a software bill of materials in a commonly used and machine-readable format covering at the very least the top-level dependencies.

    That is a real, dated, machine-readable obligation and it is the closest thing in EU law to what this axis measures. It also sets its floor at top-level dependencies rather than transitive. "At the very least" permits going deeper; it does not require it. CRA reporting duties begin 11 September 2026 and the Annex I obligations including the SBOM apply from 11 December 2027.

    So the defensible version of the line is closer to: CRA Annex I Part II = builder chain with a top-level component inventory, with full dependency-chain treated as a floor this specification sets above any current regulatory requirement.

    Why the Annex IV citation does not hold

    Three separate problems.

    Annex IV is not a classification annex. Its title is "Technical documentation referred to in Article 11(1)" and it is a content schedule for the technical file. Classification is Annex III for standalone high-risk and Article 6(1) together with Annex I for product-embedded high-risk. "Annex IV high-risk" does not name a category that exists, so a reader trying to work out whether the floor applies to them has nowhere to look it up.

    Annex IV imposes no verification obligation at any depth. The terms supply chain, dependency, bill of materials and build provenance do not appear in it. The nearest hook is real and worth naming rather than dismissing: point 2(a) requires description of recourse to pre-trained systems or tools provided by third parties and how those were used, integrated or modified by the provider. That is a narrative disclosure duty, qualified by "where relevant", covering the provider's own direct inputs. It is one hop, it specifies no attestation and no format, and a paragraph of prose discharges it. Point 2(d) is the only place the word provenance appears in the Annex, and it governs training data sets rather than build artifacts.

    The obligation is not in application. Article 11 and Annex IV sit in Chapter III Section 2. Following Regulation (EU) 2026/1744 those obligations apply from 2 December 2027 for Annex III systems and 2 August 2028 for Annex I. A reader taking this table as guidance today would conclude that a requirement binds them which does not yet bind anyone.

    The provision people usually reach for

    Article 25(4) is the value-chain provision, and it is worth knowing why it does not support a transitive floor either. It requires the high-risk provider and a third party supplying an AI system, tools, services, components or processes to specify by written agreement the information, capabilities and technical access needed for compliance. It is contractual rather than technical, it reaches one hop, and it expressly excludes third parties making tools, services, processes or components publicly available under a free and open-source licence. In any real dependency graph the transitive tail is overwhelmingly open source, which is precisely the population that carve-out removes.

    Article 15(5) requires resilience against attempts by unauthorised third parties to alter use, outputs or performance, and enumerates data poisoning, model poisoning, adversarial examples and confidentiality attacks. That threat model is inference-time and AI-specific. Build pipeline compromise is not in it.

    What I am not arguing

    The security reasoning behind the floor is correct, and the stolen-CI-token case in the issue is a real attack class that surface verification does accept. Nothing above touches that.

    A specification is also entitled to set a floor above the law, and requiring full dependency-chain verification for high-risk deployments is a legitimate editorial choice. The defect is presenting an editorial choice as a regulatory derivation. Relabelling the table as recommended profiles rather than regulatory floors would dissolve most of this.

    The direction of travel supports the intent rather than undercutting it. prEN 18282, the JTC 21 cybersecurity work item under M/593, is explicitly CRA-aligned, so a future harmonised standard could import component expectations into Article 15(5) conformity. None of the M/593 items is cited in the OJEU yet and none currently addresses supply-chain depth. The honest position is that the AI Act does not reach build provenance today, and that when it does it will most likely arrive through Article 15(5) and the CRA rather than through Annex IV.

    Two editorial notes

    The issue gives the same three-rung ladder two vocabularies: surface | builder_chain | dependency_chain in the verification block, and surface | builder | transitive for build_provenance.provenance_depth. If both land as written, docs/verification.md and schema/trace-claim.json will name the same depth differently. Worth settling before v1.0.

    On SLSA, v1.0 defines a Build track at L1 to L3 and no dependency track, so "transitive" has no SLSA referent. "SLSA L2+ = builder" reads correctly against the Build track. I mention it only because the two vocabularies share a line.

    I am happy to draft replacement prose for the profile-floors paragraph with the citations checked, if that is useful. The issue is clear that contributor schema diffs are out of scope, so I would keep it to the docs/verification.md text for a maintainer to take or leave.

    Drafting and source verification were AI-assisted. Annex IV, Article 11, Article 15, Article 25 and Article 113 as amended were read against Regulation (EU) 2024/1689, and Annex I Part II against Regulation (EU) 2024/2847, before being cited.

  23. safal207 commented on Sep 6, 2026

    @safal207
    Contributor

    Thanks @ioanavalea — I agree with the correction to the Annex IV attribution. A transitive floor can remain a specification-defined security requirement for a named profile, but should not be presented as derived from Annex IV.

    One refinement for the replacement prose: CRA Annex I Part II(1) establishes a machine-readable component inventory covering at least top-level dependencies; that alone does not establish TRACE "builder" verification or transitive provenance verification.

    The security rationale remains useful: signature-valid provenance can coexist with compromised inputs. Deeper verification can expose discrepancies within its checked coverage and trust policy; it does not guarantee rejection of every poisoned input.

    Your offered documentation-only replacement would be welcome for maintainer consideration: separate regulatory context from specification-defined profile requirements, and reconcile "surface | builder | transitive" with the longer depth labels. This can preserve the two-axis model and achieved-depth reporting without implying legal compliance.

  24. imran-siddique commented on Sep 7, 2026

    @imran-siddique
    MemberAuthor

    This remains the umbrella for the two-axis model, not a claim that every linked deliverable is unfinished. The offered profile-floor documentation correction should remain a separate docs-only contribution for @ioanavalea, with source verification and no new conformance or legal-compliance claim.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

cosai-ws4Named in the CoSAI WS4 Phase 1 review (ws4 #149) as a review targetenhancementNew feature or requesthelp wantedExtra attention is neededpre-v1.0Editorial work targeted before v1.0

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions