# In what scenarios must an instance refuse delegated credential issuance under AAIF when scope attenuation is structurally unverifiable **Date of Record:** 2026 November 13 **Scope:** Applies to AAIF-compliant issuers performing delegated credential minting (agent-to-agent delegation) where the requested scope is a strict subset of the principal's granted scope. Does not apply to root credential issuance by a human principal, nor to re-delegation where the full scope is forwarded unchanged (a null attenuation). ## Core Distinction The critical distinction is between *semantic* attenuation and *structural* attenuation. Semantic attenuation is the claim that the delegated scope is a subset of the parent scope — a property of the permission *language*. Structural attenuation is a verifiable, machine-checkable proof that the subset relation holds under the issuer's own policy engine, independent of the requester's assertion. Most AAIF implementations conflate the two. An agent presents a delegation request containing a `scope` claim and a `parent_credential` reference. The issuer checks that the parent credential is valid and unrevoked, then trusts the requester's assertion that the child scope is narrower. This is semantic attenuation without structural verification. It fails silently the moment the scope language admits constructs whose subset relation is not decidable by the issuer's checker — wildcard expansion, negative permissions, conditional/attribute-based predicates, time-bounded intersections, or resource-hierarchy inheritance. The non-obvious invariant: **a delegated credential is only as attenuated as the weakest scope construct the issuer can actually decide.** If any element of the requested scope falls outside the decidable fragment of the issuer's policy engine, the issuer cannot prove attenuation, and issuance must be refused — not downgraded, not "best-effort narrowed," not deferred to the requester's good faith. ## What Was Observed In a live multi-agent pipeline, an orchestrator agent held a parent credential with scope `read:datasets/*` plus a conditional grant `write:datasets/{owner==self}`. It requested a child credential for a worker agent with scope `write:datasets/{owner==orchestrator}` — semantically a narrowing, since the worker could only write datasets owned by the orchestrator, which is a strict subset of the parent's conditional write grant. The issuer's policy engine evaluated the conditional predicate by *substituting the requesting agent's identity* into the attribute expression rather than by proving a subset over the attribute domain. Because the orchestrator satisfied `owner==self`, the substitution succeeded, and the child credential was issued with the predicate re-bound to the worker. The worker then wrote to a dataset whose `owner` attribute was attacker-controlled and set to the worker's own identity — a resource the orchestrator never had write access to. The attenuation was semantic but not structural: the issuer had verified *satisfiability for the parent*, not *containment of the child's attribute space within the parent's*. A second observation: wildcard scopes such as `read:datasets/*` were treated as narrowing to `read:datasets/prod/*` without checking whether the resource hierarchy defined `prod` as a sibling of `*` rather than a descendant. In one deployment, `*` excluded a quarantine namespace by policy; the "narrowed" child scope re-included it, because the wildcard's exclusion was carried in a separate negative-permission list that the attenuation checker did not consult. ## Condition of Validity Refusal is mandated when **all** of the following hold: 1. The parent credential carries at least one scope element drawn from a construct outside the issuer's decidable subset-checker (conditionals, negatives, hierarchy-inherited grants, or wildcards with policy-level exclusions). 2. The requested child scope touches any resource or attribute reachable by that undecidable element. 3. The issuer cannot produce a structural proof — a decision procedure output, not a satisfiability check — that `child_scope ⊆ parent_scope` under the same policy snapshot that governed the parent's issuance. Refusal is *not* mandated when the parent scope is entirely within the decidable fragment (flat, positive, enumerable resource identifiers), or when the child scope is byte-identical to the parent (null attenuation, which requires only parent validity), or when the issuer can fall back to a decidable over-approximation and the caller explicitly accepts the coarser scope in the credential itself. ## Decision Boundary The boundary is drawn at *provability*, not at *plausibility*. An issuer that can only check satisfiability must treat conditional and negative constructs as opaque atoms; an opaque atom in the parent scope means the parent's true extent is unknown, and any child scope claiming to be a subset of an unknown set is unverifiable by construction. The correct action is refusal with a machine-readable reason (`attenuation_unverifiable`), not a narrowing to the decidable portion — because narrowing to the decidable portion silently drops the negative constraints that made the parent safe. The asymmetry matters: over-issuance is a security failure that may go undetected for the credential's entire lifetime; refusal is a liveness failure that surfaces immediately at the call site and is recoverable by re-requesting with an explicit, decidable scope. The system must prefer the loud, recoverable failure. ## Operational Effect After enforcing provability-gated issuance, the pipeline observed a burst of `attenuation_unverifiable` refusals concentrated on conditional and wildcard-with-exclusion scopes. Each refusal forced the requesting agent to re-express its delegation in flat, enumerable terms or to request a policy-engine-assisted scope derivation from the issuer itself. The refusals were correct: two of them, when traced, corresponded to attempted delegations that would have granted cross-tenant write access via attribute rebinding. The cost was added round-trips at delegation time; the benefit was that the credential graph became auditable — every edge in the delegation chain now carries a structural subset proof, so revocation and blast-radius analysis can be computed rather than estimated. ```python class AttenuationUnverifiableError(RuntimeError): """Raised when an issuer cannot structurally prove child_scope ⊆ parent_scope.""" pass DECIDABLE_CONSTRUCTS = frozenset({"flat_resource", "enumerated_action"}) def issue_delegated_credential(parent_credential, requested_scope, policy_engine): # Reject if any parent scope element is outside the decidable fragment. for element in parent_credential.scope: if element.construct not in DECIDABLE_CONSTRUCTS: if requested_scope.touches(element.resource_domain): raise AttenuationUnverifiableError( f"parent scope element {element!r} uses undecidable construct " f"{element.construct!r}; cannot prove attenuation for " f"requested scope {requested_scope!r}" ) # Structural proof, not satisfiability check. proof = policy_engine.prove_subset( child=requested_scope, parent=parent_credential.scope, snapshot=parent_credential.policy_snapshot, ) if proof is None: raise AttenuationUnverifiableError( f"policy engine produced no subset proof for {requested_scope!r} " f"against parent {parent_credential.id!r}" ) if not proof.is_structural: raise AttenuationUnverifiableError( f"proof for {requested_scope!r} is satisfiability-only; " f"refusing to issue on non-structural evidence" ) return policy_engine.mint( parent=parent_credential, scope=requested_scope, attenuation_proof=proof, ) ```