Skip to main content

Capability Language Core
draft-wei-capability-language-core-00

Document Type Active Internet-Draft (individual)
Author Jijie Wei
Last updated 2026-09-27
RFC stream (None)
Intended RFC status (None)
Formats
Stream Stream state (No stream defined)
Consensus boilerplate Unknown
RFC Editor Note (None)
IESG IESG state I-D Exists
Telechat date (None)
Responsible AD (None)
Send notices to (None)
draft-wei-capability-language-core-00
Network Working Group                                             J. Wei
Internet-Draft                                                Individual
Intended status: Experimental                          27 September 2026
Expires: 31 March 2027

                        Capability Language Core
                 draft-wei-capability-language-core-00

Abstract

   This document defines the Capability Language Core (CLC), a minimal,
   executable language for describing what an agent is authorized to do.
   It defines the capability identifier grammar, the entailment relation
   between a grant and an operation, intersection of grants from
   multiple sources, the constraint model, and a deterministic decision
   function with stable reason codes and a three-valued verdict (allow,
   deny, allow_unresolved).

   The language is carrier-neutral: it defines what is evaluated, not
   how it is carried or trusted.  Trust models, native verification,
   execution lifecycle, and receipt or token formats are out of scope
   (Section 11).  Conformance is exercised by a published corpus of 123
   vectors and 1184 property cases; three implementations (Go, Python,
   TypeScript) that share an author pass both. *Implementation
   conformance and this document's claim of a conformance class are
   separate.* An implementation conforms to CLC-A when it meets the
   obligations Section 12 lists for that class, and it may claim that
   conformance on its own, whatever other implementations exist.
   Section 12 additionally sets a maturity bar for _this document's_
   claim — two independent implementations agreeing on verdict and
   reason.  That bar is *not* met here: as Section 12 states under
   "Independence of implementations", the three implementations named in
   the README share an author, so their agreement is a regression test
   for the text, not independent validation.  CLC-A is still claimed by
   this revision as the baseline authorization class, whose obligations
   are implementable and exercised by a published corpus; the
   independent-implementation threshold is recorded as unmet.  The
   evidence-side class CLC-E is *not* claimed: its relations, value
   grammar, reference implementation and corpus ship here.

Wei                       Expires 31 March 2027                 [Page 1]
Internet-Draft                   CLC-v1                   September 2026

   This revision also folds the delegation *containment* relation into
   the document (Section 13): Contains(parent, child) decides whether a
   child grant stays inside a parent's declared boundary — the single
   question a delegation chain asks at every hop that the core's
   entailment and intersection do not answer.  It ships with the
   conformance class *CLC-D* and its own corpus, is strictly additive,
   and changes no CLC-A verdict, reason code, or vector.  It absorbs the
   previously separate experimental containment extension, which is
   retired.

Status of This Memo

   This Internet-Draft is submitted in full conformance with the
   provisions of BCP 78 and BCP 79.

   Internet-Drafts are working documents of the Internet Engineering
   Task Force (IETF).  Note that other groups may also distribute
   working documents as Internet-Drafts.  The list of current Internet-
   Drafts is at https://datatracker.ietf.org/drafts/current/.

   Internet-Drafts are draft documents valid for a maximum of six months
   and may be updated, replaced, or obsoleted by other documents at any
   time.  It is inappropriate to use Internet-Drafts as reference
   material or to cite them other than as "work in progress."

   This Internet-Draft will expire on 31 March 2027.

Copyright Notice

   Copyright (c) 2026 IETF Trust and the persons identified as the
   document authors.  All rights reserved.

   This document is subject to BCP 78 and the IETF Trust's Legal
   Provisions Relating to IETF Documents (https://trustee.ietf.org/
   license-info) in effect on the date of publication of this document.
   Please review these documents carefully, as they describe your rights
   and restrictions with respect to this document.

Table of Contents

   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   4
   2.  Terminology . . . . . . . . . . . . . . . . . . . . . . . . .   6
   3.  Grammar . . . . . . . . . . . . . . . . . . . . . . . . . . .   7
   4.  Action  . . . . . . . . . . . . . . . . . . . . . . . . . . .   8
     4.1.  Operation (authorization side)  . . . . . . . . . . . . .   8
     4.2.  ObservedAction (evidence side)  . . . . . . . . . . . . .   8
     4.3.  Identity  . . . . . . . . . . . . . . . . . . . . . . . .   9
   5.  Grant . . . . . . . . . . . . . . . . . . . . . . . . . . . .  10

Wei                       Expires 31 March 2027                 [Page 2]
Internet-Draft                   CLC-v1                   September 2026

   6.  Binding . . . . . . . . . . . . . . . . . . . . . . . . . . .  10
     6.1.  Entailment (authorization binding)  . . . . . . . . . . .  11
     6.2.  Parameters  . . . . . . . . . . . . . . . . . . . . . . .  11
     6.3.  Algorithm . . . . . . . . . . . . . . . . . . . . . . . .  15
     6.4.  Match (evidence binding)  . . . . . . . . . . . . . . . .  16
     6.5.  Extended parameter bounds (param_bounds)  . . . . . . . .  16
     6.6.  Intersection of bounds (BoundMeet)  . . . . . . . . . . .  20
   7.  Intersection (∩)  . . . . . . . . . . . . . . . . . . . . . .  22
     7.1.  ConstraintUnion (derived projection)  . . . . . . . . . .  25
   8.  Constraint  . . . . . . . . . . . . . . . . . . . . . . . . .  26
     8.1.  Authorization-side constraints  . . . . . . . . . . . . .  26
     8.2.  Evidence-side constraints . . . . . . . . . . . . . . . .  29
     8.3.  Unified constraint grammar  . . . . . . . . . . . . . . .  29
     8.4.  Recognized but not evaluated (residual-obligation
           channel)  . . . . . . . . . . . . . . . . . . . . . . . .  29
     8.5.  Resolving residual obligations (Resolve)  . . . . . . . .  30
   9.  Decision Function (Authorization Side)  . . . . . . . . . . .  32
     9.1.  Resolved Reason Ordering (normative)  . . . . . . . . . .  34
     9.2.  Reason Codes (normative)  . . . . . . . . . . . . . . . .  38
   10. Satisfaction Function (Evidence Side) . . . . . . . . . . . .  42
   11. Semantic Boundary . . . . . . . . . . . . . . . . . . . . . .  43
   12. Conformance . . . . . . . . . . . . . . . . . . . . . . . . .  44
     12.1.  Language Revision  . . . . . . . . . . . . . . . . . . .  47
   13. Delegation Containment  . . . . . . . . . . . . . . . . . . .  51
     13.1.  Motivation . . . . . . . . . . . . . . . . . . . . . . .  51
     13.2.  Terminology and Notation . . . . . . . . . . . . . . . .  52
     13.3.  Relation Signature . . . . . . . . . . . . . . . . . . .  52
     13.4.  Containment Algorithm  . . . . . . . . . . . . . . . . .  53
       13.4.1.  Layer 1: Grant validity  . . . . . . . . . . . . . .  53
       13.4.2.  Layer 2: Identifier coverage . . . . . . . . . . . .  53
       13.4.3.  Layer 3: Parameter narrowing . . . . . . . . . . . .  54
       13.4.4.  Constraints are outside the relation . . . . . . . .  55
       13.4.5.  Delegation-mode lattice (binding-profile
               pre-check)  . . . . . . . . . . . . . . . . . . . . .  56
     13.5.  Reason Codes . . . . . . . . . . . . . . . . . . . . . .  56
     13.6.  Conformance Class CLC-D  . . . . . . . . . . . . . . . .  57
     13.7.  Corpus . . . . . . . . . . . . . . . . . . . . . . . . .  58
     13.8.  AIC-JWT / AIC Certificate Binding  . . . . . . . . . . .  59
       13.8.1.  Profile contract (for any binding profile) . . . . .  60
     13.9.  Related Work and Positioning . . . . . . . . . . . . . .  61
       13.9.1.  The gap CLC fills  . . . . . . . . . . . . . . . . .  62
       13.9.2.  What containment is for  . . . . . . . . . . . . . .  62
       13.9.3.  How CLC relates to adjacent work . . . . . . . . . .  62
       13.9.4.  Where the demand actually is . . . . . . . . . . . .  71
       13.9.5.  Adoption risks . . . . . . . . . . . . . . . . . . .  71
       13.9.6.  Positioning statement  . . . . . . . . . . . . . . .  72
     13.10. Open Issues  . . . . . . . . . . . . . . . . . . . . . .  72
       13.10.1.  Parameter model rigidity (closed in CLC-1.10) . . .  72

Wei                       Expires 31 March 2027                 [Page 3]
Internet-Draft                   CLC-v1                   September 2026

       13.10.2.  Consumer obligations for allow_unresolved (closed in
               CLC-1.11) . . . . . . . . . . . . . . . . . . . . . .  73
       13.10.3.  Constraints as a union axis (closed in CLC-1.12)  .  73
       13.10.4.  Containment as evidence, not authorization (closed in
               CLC-1.13) . . . . . . . . . . . . . . . . . . . . . .  74
     13.11. AuthorizeWithChain (fused chain authorization) . . . . .  74
     13.12. Revision and Governance  . . . . . . . . . . . . . . . .  75
   14. Security Considerations . . . . . . . . . . . . . . . . . . .  75
   15. IANA Considerations . . . . . . . . . . . . . . . . . . . . .  77
   16. Privacy Considerations  . . . . . . . . . . . . . . . . . . .  78
   17. References  . . . . . . . . . . . . . . . . . . . . . . . . .  78
     17.1.  Normative References . . . . . . . . . . . . . . . . . .  78
     17.2.  Informative References . . . . . . . . . . . . . . . . .  79
   Appendix A.  Consumption Mapping  . . . . . . . . . . . . . . . .  81
   Appendix B.  Reference Vectors  . . . . . . . . . . . . . . . . .  83
     B.1.  Syntax (9 vectors)  . . . . . . . . . . . . . . . . . . .  83
     B.2.  Entailment (8 vectors)  . . . . . . . . . . . . . . . . .  84
     B.3.  Params (39 vectors) . . . . . . . . . . . . . . . . . . .  85
     B.4.  Intersection (10 vectors) . . . . . . . . . . . . . . . .  90
     B.5.  Decision (34 vectors) . . . . . . . . . . . . . . . . . .  91
     B.6.  Combined (11 vectors) . . . . . . . . . . . . . . . . . .  95
     B.7.  Containment (external corpus) . . . . . . . . . . . . . .  97
     B.8.  Extended parameter bounds (external corpus) . . . . . . .  97
     B.9.  Resolve (external corpus) . . . . . . . . . . . . . . . .  98
     B.10. ConstraintUnion (external corpus) . . . . . . . . . . . .  98
     B.11. AuthorizeWithChain (external corpus)  . . . . . . . . . .  98
     B.12. BoundMeet (external corpus) . . . . . . . . . . . . . . .  98
   Appendix C.  Containment Carrier Vocabulary and Reason-Code
           Mapping . . . . . . . . . . . . . . . . . . . . . . . . .  99
     C.1.  Vocabulary  . . . . . . . . . . . . . . . . . . . . . . .  99
     C.2.  Failure-code mapping  . . . . . . . . . . . . . . . . . . 100
     C.3.  Profile obligations exercised by the corpus . . . . . . . 102
   Acknowledgements  . . . . . . . . . . . . . . . . . . . . . . . . 102
   Author's Address  . . . . . . . . . . . . . . . . . . . . . . . . 102

1.  Introduction

   CLC-v1 is a *universal minimal language* that can be consistently
   presented on either the authorization side or the evidence side.

   The two sides share five foundational abstractions:

Wei                       Expires 31 March 2027                 [Page 4]
Internet-Draft                   CLC-v1                   September 2026

       +==============+====================+=======================+
       | Foundation   | Authorization Side | Evidence Side         |
       +==============+====================+=======================+
       | *Action*     | Operation          | ObservedAction        |
       |              | (concrete request) | (asserted effect)     |
       +--------------+--------------------+-----------------------+
       | *Identity*   | CapabilityId       | ActionId (instance-   |
       |              | (class-level)      | level)                |
       +--------------+--------------------+-----------------------+
       | *Binding*    | Entailment (grant  | Match (evidence ↔     |
       |              | ⊆ operation)       | action)               |
       +--------------+--------------------+-----------------------+
       | *Constraint* | Grant params /     | Evidence requirements |
       |              | limits             | / freshness           |
       +--------------+--------------------+-----------------------+
       | *Verdict*    | allow / deny /     | SATISFIED /           |
       |              | allow_unresolved   | UNSATISFIED           |
       +--------------+--------------------+-----------------------+

                                  Table 1

   *Conventions.* The key words "MUST", "MUST NOT", "REQUIRED", "SHALL",
   "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT
   RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
   interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only
   when, they appear in all capitals, as shown here.  "UTC instant" is
   an [RFC3339] timestamp.  "JCS" is the JSON Canonicalization Scheme
   [RFC8785]; "I-JSON" is [RFC7493].

   The language structure is identical on both sides; only the direction
   differs:

   *  Authorization: "I grant you permission to do X"

   *  Evidence: "I have evidence that X was done"

   The design principles behind this core — including what the language
   *deliberately refuses* (no control flow, no mutable state, no
   general-purpose policy language) — are stated in capability-language-
   core-principles-v1.md (in [CLC-CORPUS]):

      P1 minimal core · P2 no control flow · P3 immutable values · P4
      domains, not types · P5 deterministic and terminating · P6 fail-
      closed · P7 define once, consume everywhere · P8 carriers separate
      from semantics · P9 local decidability · P10 bounded work · P11
      composition narrows only · P12 ≥2 independent implementations

Wei                       Expires 31 March 2027                 [Page 5]
Internet-Draft                   CLC-v1                   September 2026

2.  Terminology

       +================+==========================================+
       | Term           | Definition                               |
       +================+==========================================+
       | *Action*       | The thing being referenced — an abstract |
       |                | operation class (auth) or a concrete     |
       |                | asserted effect (evidence).              |
       +----------------+------------------------------------------+
       | *CapabilityId* | Structured name identifying a class of   |
       |                | actions within a scheme.                 |
       +----------------+------------------------------------------+
       | *Operation*    | Concrete action request: a CapabilityId  |
       |                | plus parameters.                         |
       +----------------+------------------------------------------+
       | *Grant*        | Principal's authorization of a           |
       |                | CapabilityId with optional params and    |
       |                | constraints.                             |
       +----------------+------------------------------------------+
       | *Binding*      | Abstract concept connecting an Identity  |
       |                | to an Action.  Entailment (auth) and     |
       |                | Match (evidence) are concrete instances. |
       +----------------+------------------------------------------+
       | *Entailment*   | Authorization-side binding: "Grant G     |
       |                | covers operation O" (⊆).                 |
       +----------------+------------------------------------------+
       | *Match*        | Evidence-side binding: "Evidence E is    |
       |                | bound to exact action A".                |
       +----------------+------------------------------------------+
       | *Constraint*   | Bound on how an action may be used       |
       |                | (auth) or what evidence is required      |
       |                | (evidence).                              |
       +----------------+------------------------------------------+
       | *Intersection* | Combining multiple grant sources into an |
       |                | effective set (∩).                       |
       +----------------+------------------------------------------+
       | *Verdict*      | Outcome of evaluation: allow/deny/       |
       |                | allow_unresolved (auth) or SATISFIED/    |
       |                | UNSATISFIED (evidence).                  |
       +----------------+------------------------------------------+
       | *Decision*     | Authorization-side verdict: allow,       |
       |                | allow_unresolved, or deny + reason (+    |
       |                | additive unresolved).                    |
       +----------------+------------------------------------------+
       | *Satisfaction* | Evidence-side verdict: SATISFIED or      |
       |                | UNSATISFIED.                             |
       +----------------+------------------------------------------+

Wei                       Expires 31 March 2027                 [Page 6]
Internet-Draft                   CLC-v1                   September 2026

                                  Table 2

   Note: *Binding* in CLC-v1 denotes the identity↔action relation
   (coverage on the authorization side, match on the evidence side).  It
   is *not* key binding (cnf / DPoP / mTLS sender constraint), which
   belongs to the native artifact's specification (see Section 11).

   Verdicts are written lowercase on the authorization side (allow/deny/
   allow_unresolved) and uppercase on the evidence side (SATISFIED/
   UNSATISFIED), following the EMILIA/AEB convention.

3.  Grammar

   capability-id = scheme ":" action [ ":" wildcard ]
   wildcard      = "*"
   scheme        = vendor "/" product "-v" major
   vendor        = 1*( ALPHA / DIGIT / "-" )
   product       = 1*( ALPHA / DIGIT / "-" )
   major         = 1*DIGIT
   action        = segment *( ":" segment )
   segment       = 1*( ALPHA / DIGIT / "-" / "_" / "." )

   The trailing wildcard is part of the identifier grammar (the optional
   final ":" "*" above), so std/database-v1:query:* is a well-formed
   capability-id; only that shape is a wildcard (Section 3, below).

   *Unambiguous scheme.* Because product may contain -, the -v<major>
   suffix is the *last* occurrence of -v followed by digits.  A product
   that itself contains the two-character sequence -v is invalid
   (invalid_capability_id), so a/b-v1-v2 is rejected rather than parsed
   two ways; b-v1 as a product name is not expressible.  A scheme in use
   before this rule that relies on such a product is out of grammar in
   v1.

   A v1 identifier is scheme:action (e.g. std/database-v1:query:SELECT).
   Trailing * as a complete segment matches *one or more* remaining
   segments (it does not match the empty remainder: std/database-
   v1:query:* does not cover std/database-v1:query).

   *Wildcard scope for v1*: CLC-v1 core defines exactly one wildcard
   shape: the complete trailing segment *.  Partial (std/crm-v1:re*),
   bare *, **, {a,b}, [a-z] are *reserved for v2*; a v1-conforming
   implementation MUST reject them (unsupported_wildcard).  If a
   capability scheme declares its own extended grammar, that scheme's
   conforming implementation may accept it; but CLC-v1 core conformance
   neither requires nor authorizes those forms (see Section 12).

Wei                       Expires 31 March 2027                 [Page 7]
Internet-Draft                   CLC-v1                   September 2026

   *Wildcard detection precedes grammar conformance.* A forbidden
   wildcard shape is reported as unsupported_wildcard even when the same
   string also violates the base grammar (Section 3 segment): the
   wildcard-shape checks run before the generic invalid_capability_id
   test.

           +==========================+========================+
           | Examples: std/database-  | database:query invalid |
           | v1:query:SELECT valid    |                        |
           +==========================+========================+
           | std/database-v1:query:*  | std/database-          |
           | valid                    | v1:query:SEL* invalid  |
           +--------------------------+------------------------+

                                  Table 3

4.  Action

   An action is the thing being referenced.  CLC-v1 defines two concrete
   forms:

4.1.  Operation (authorization side)

   A concrete action request: CapabilityId + parameters.

   { "id": "std/database-v1:query:SELECT",
     "params": {"tables":["customers"], "limit":{"max":50}} }

   Missing id → deny("missing_capability_id").

4.2.  ObservedAction (evidence side)

   The material action constructed by the effect boundary from executor-
   controlled facts.  CLC-v1 defines the interface, not the construction
   algorithm.

   An ObservedAction carries:

   *  action_type: the action type name declared by the relying-party-
      pinned type definition (the class this projection belongs to)

   *  material_fields: every field the type definition declares
      *material*

   *  digest: computed over the canonical *material projection* (below)

   The material projection is deterministic and normative:

Wei                       Expires 31 March 2027                 [Page 8]
Internet-Draft                   CLC-v1                   September 2026

   *  The action type declares a *material field set* (required and
      optional-but-included).  Only that set enters the digest.

   *  Canonical serialization is the JSON Canonicalization Scheme (JCS)
      [RFC8785]; the one suite defined in v1 is jcs-sha256
      (Section 4.3).

   *  The projection identity is the language's own, not a CAID: clc-
      action:1:<type>:<suite>:<b64url>.  A CAID [CAID] covers the
      *complete* Action Object under its own suite registry and
      identifies the action object, not an occurrence; this projection
      covers only the declared material set.  The two are related by a
      relying-party-pinned Action-Mapping Profile (Section 6.4), never
      by treating the strings as interchangeable.

   *  A field the type does not declare as material MUST be excluded
      from the digest and MUST NOT affect Match: an ObservedAction
      carrying undeclared fields is not invalidated, but those fields
      carry no action identity.

   *  A type-declared material field that is *missing* makes the
      ObservedAction non-matchable: coverage MUST NOT be inferred,
      defaulted, or repaired (UNSATISFIED, Section 10).  This is the
      evidence-side mirror of key closure (Section 6.2): the absence of
      a governing field is fail-closed, never fail-open.

   The effect boundary MUST construct the ObservedAction from facts it
   controls.  It MUST NOT copy a requester-supplied action digest
   without deriving or checking the corresponding fact.

4.3.  Identity

   Two identity levels, corresponding to the two Action forms:

Wei                       Expires 31 March 2027                 [Page 9]
Internet-Draft                   CLC-v1                   September 2026

   +========+============+===========+=================================+
   |Level   |Identity    |Scope      | Example                         |
   +========+============+===========+=================================+
   |Class   |CapabilityId|Covers a   | std/database-v1:query:*         |
   |        |            |class of   |                                 |
   |        |            |actions    |                                 |
   +--------+------------+-----------+---------------------------------+
   |Instance|ActionId    |Identifies | clc-                            |
   |        |(projection |the        | action:1:payment.release.1:jcs- |
   |        |digest)     |material   | sha256:...                      |
   |        |            |content of |                                 |
   |        |            |one        |                                 |
   |        |            |action,    |                                 |
   |        |            |*not* an   |                                 |
   |        |            |occurrence |                                 |
   +--------+------------+-----------+---------------------------------+

                                  Table 4

   A CapabilityId covers a class; an ActionId identifies the material
   content of one action.  It does not identify an *occurrence*:
   ActionId binds the declared material content, and correlation to a
   particular occurrence additionally requires an occurrence
   discriminator defined and checked by the consuming profile.  CAID-03
   Section 4.6 carries such a discriminator as the optional
   occurrence_id of an action object; when a profile uses one, it MUST
   appear among the declared material fields for it to affect the
   digest.  Allocating unique occurrences and proving one-time
   consumption or execution stay outside both documents (CAID-03
   Section 7).  Entailment checks class coverage; Match checks content
   binding.

5.  Grant

   A grant = CapabilityId + optional params + optional constraints.

   grant       = capability-id [ params ] [ constraints ]
   constraint  = scheme ":" type [ ":" params ]

   Constraints are deny-when-declared: explicitly empty bound (e.g. zero
   max, empty allowlist) denies the class; omitted bound uses scheme
   default.

6.  Binding

   Binding is the abstract concept of connecting an Identity to an
   Action.  CLC-v1 defines two concrete instances:

Wei                       Expires 31 March 2027                [Page 10]
Internet-Draft                   CLC-v1                   September 2026

6.1.  Entailment (authorization binding)

   Grant G covers operation O if:

   1.  *Literal*: G = O (byte-for-byte after normalization).

   2.  *Trailing wildcard*: G = scheme:prefix:* and O has *at least one*
       segment after scheme:prefix: (segment-boundary comparison, not
       lexical prefix).

    +=========================+======================+===============+
    | Grant                   | Operation            | Result        |
    +=========================+======================+===============+
    | std/database-v1:query:* | std/database-        | yes: wildcard |
    |                         | v1:query:SELECT      | matches       |
    +-------------------------+----------------------+---------------+
    | std/database-v1:query:* | std/database-        | yes: matches  |
    |                         | v1:query:SELECT:deep | multi-segment |
    +-------------------------+----------------------+---------------+
    | std/database-v1:query:* | std/database-        | no: different |
    |                         | v1:admin:DDL         | namespace     |
    +-------------------------+----------------------+---------------+
    | std/database-           | std/database-        | no: literal   |
    | v1:query:SELECT         | v1:query:INSERT      | mismatch      |
    +-------------------------+----------------------+---------------+

                                 Table 5

6.2.  Parameters

     +=========+===============================+=====================+
     | Type    | Rule                          | Example             |
     +=========+===============================+=====================+
     | number  | op ≤ grant                    | 50 ≤ 100 (holds)    |
     +---------+-------------------------------+---------------------+
     | string  | exact                         | "a" = "a" (holds)   |
     +---------+-------------------------------+---------------------+
     | boolean | exact                         | true = true (holds) |
     +---------+-------------------------------+---------------------+
     | array   | set of allowed values (enum): | {"station":[1,2,3]} |
     |         | request scalar must equal a   | ⊇ 2 (holds); ⊇ 9    |
     |         | member; request array — every | (does not hold)     |
     |         | element must equal a member   |                     |
     +---------+-------------------------------+---------------------+
     | object  | every grant key in op, values | {"t":["id"]} ⊆      |
     |         | recurse                       | {"t":["id","name"]} |
     |         |                               | (holds)             |
     +---------+-------------------------------+---------------------+

Wei                       Expires 31 March 2027                [Page 11]
Internet-Draft                   CLC-v1                   September 2026

     | other   | exact equality                | —                   |
     +---------+-------------------------------+---------------------+

                                  Table 6

   *Boolean parameters are exact only.* A boolean grant value is matched
   by exact equality (true covers true only).  Booleans are *not*
   numbers: an implementation MUST NOT interpret true/false as 1/0 and
   MUST NOT run them through the numeric-bound rule (op ≤ grant).  They
   also play no role in numeric comparisons or bounds.  (Boolean GRANT-
   side bounds are rare in practice; the setting that matters — an
   operation-side boolean value under key closure — is pinned by
   undeclared-001.)

   Table rows map to vectors: number → params-001/params-002; array
   scalar member → params-009 (holds) / params-010 (fails); array
   element-wise → params-011 (holds) / params-012 (fails); object
   recursion allow → params-015, deny-direction → params-005;
   categorical guard → params-014.  Under the old (pre-v1.1) array-as-
   bound rule the object example read (does not hold); with the v1.1
   enum rule the request-side element id is a member of the granted set,
   so the row is (holds) (see clc-v1-ambiguities.md Section 2).

   *Enum semantics of arrays (v1.1).* An array-valued grant parameter is
   the *set of allowed values*, not an order.  The request MAY supply a
   single scalar (must be a member of the set) or an array (every
   element must be a member).  Members compare by *exact equality*: a
   number inside a granted array denotes that exact value, not a bound.
   This is what keeps categorical identifiers safe — grant
   {"station":[3]} does *not* cover station 2 (failure →
   deny("not_in_enum")).  Scheme authors SHOULD encode categorical
   parameters (station, cell, batch, tool id) as arrays; a scalar number
   in the grant keeps upper-bound (bound) semantics for ordered
   quantities (max_rows, speed, angle).

   An explicitly empty grant array [] denies the class
   (empty_bound_denies_class).

   Grant with no params — or with an *empty {} params object* — covers
   any operation params (unconstrained).  An absent params and
   "params":{} are semantically equivalent — consistent across
   entailment (Section 6.3) and intersection (Section 7 rule 6;
   Section 9.1).

   A null parameter value is invalid in v1 → reject
   (invalid_params_null).  Absent ≠ explicitly empty (see Section 8).

Wei                       Expires 31 March 2027                [Page 12]
Internet-Draft                   CLC-v1                   September 2026

   *Key closure (Plan A).* A bounded grant governs its declared keys: an
   operation carrying a parameter key the grant does not declare is
   rejected → deny("undeclared_param") (fail-closed, Section 9.1 layer 7
   request side).  An unconstrained grant (no params, or params:{})
   accepts any operation params, so no key closure applies there.
   Within layer 7 the missing-key check (params_missing) resolves
   *before* the undeclared-key check (undeclared_param); both are key-
   level and run before the enum/bound value checks (layers 8–9).

   *Params representation and input normalization (v1.1).* Before any
   Section 9.1 layer runs, the params object is normalized at the input
   boundary:

   1.  *Canonical serialization.* Params are normalized to the JSON
       Canonicalization Scheme form (JCS, RFC 8785): object members
       sorted by code unit, numbers in the ECMAScript Number::toString
       form, no insignificant whitespace.  The original key order,
       number spelling and spacing are *not* preserved —
       canonicalization replaces them with the deterministic form (this
       is what the size cap in step 4 and the material digest are
       measured on).  Evaluation never guesses; a lossy re-serialization
       (e.g. a map that drops duplicate keys) is not used for decisions.

   2.  *Duplicate keys.* A params object with a duplicate JSON key is
       rejected: deny("invalid_params_duplicate_key").

   3.  *Number shape.* A numeric param is rejected with
       deny("invalid_params_number") when it is *non-finite or out of
       IEEE-754 range* (e.g. 1e400, an exponent beyond the representable
       maximum — it is an overflow, not an over-precision case) or
       *over-precision* (more than 17 significant decimal digits, e.g.
       1.0000000000000001).  The digit count operates on the *JSON token
       as received* — the raw digit-character sequence at the input
       boundary, before it enters any storage / float / decimal
       representation — so float64 and decimal/bignum implementations
       MUST NOT diverge: the judged input is always the raw token text
       (params-* number probes in the corpus; near-limit forms such as
       1.0000000000000001 extend the probes without changing the rule).
       Malformed params text that cannot be parsed as an object is
       refused with the same code (see step 7).

   4.  *Size and depth.* Params whose canonical length exceeds 512
       *UTF-8 octets* — measured in octets, never in code points or
       UTF-16 code units (rev CLC-1.4) — or whose nesting depth exceeds
       32, are rejected: deny("invalid_params_size").  Nesting depth
       counts *both* objects and arrays, with the outermost object as
       level 1 (31 nested arrays inside the top-level object therefore =
       depth 32).

Wei                       Expires 31 March 2027                [Page 13]
Internet-Draft                   CLC-v1                   September 2026

   5.  *Order of checks.* Size/depth (4) precede duplicate keys (2),
       which precedes number shape (3); the first failing check wins.
       All five run before Section 9.1 layer 1, so normalized params are
       the only view the layers see.  The size in step 4 is measured
       with every token in its JCS spelling (numbers re-spelled per
       ECMAScript Number::toString, strings per JCS Section 3.2.2.2) but
       *without* requiring key uniqueness, so it is defined even when a
       duplicate key makes full JCS undefined; this is why an over-limit
       input that also has a duplicate key reports invalid_params_size
       (params-025/params-026).

   6.  *Decoded-object path.* A caller that supplies params already
       decoded (no raw_params text) cannot reproduce the original byte
       stream; in that case the size check (4) applies to a *canonical
       serialization* (the JCS form of the decoded object: sorted keys,
       compact), and the depth check (4) applies to the decoded
       structure directly.  Byte-exactness against a specific original
       text is guaranteed only for the raw path; but *both* entry points
       MUST reject the caps — an oversized/deep params object is denied
       whichever way it arrives.

   7.  *Malformed Unicode is a property of the received text.* A lone
       surrogate escape, or an octet that is not valid UTF-8, has no JCS
       form (Section 3.2.2.2): step 1 cannot normalize it, so the input
       is refused.  The refusal reuses invalid_params_number
       (Section 9.2), whose scope is *"the params input is not
       representable in canonical form"* — a numeric param that is non-
       finite / over-precision _or_ text that is not well-formed Unicode
       / not parseable as a JSON object.  (One stable code, not one per
       malformation: introducing a second code would change the reason
       for inputs already pinned to invalid_params_number, which the
       additive rule forbids.)  That check is defined over the octets as
       received and MUST run *before* any decoding step, because a
       general-purpose JSON decoder does not preserve the distinction.
       Go's encoding/json, for example, replaces both a lone surrogate
       escape and an invalid octet with U+FFFD, so {"s":"\ud800"},
       {"s":"\ufffd"} and a literal invalid octet arrive at the decision
       function as the same value — {"s":"\ufffd"} — and the refusal
       cannot be recovered afterwards.  Therefore:

       *  the party that receives the input MUST run steps 1-5 on the
          received text, not on a re-serialization of a decoded value;

Wei                       Expires 31 March 2027                [Page 14]
Internet-Draft                   CLC-v1                   September 2026

       *  an implementation that exposes *only* a decoded-value entry
          point MUST NOT be described as refusing malformed Unicode: its
          verdict is defined over the value it was handed, which may
          already be a repaired one.  The entry point that takes the
          text is the normative one, and an implementation SHOULD name
          the two so a caller cannot mistake one for the other;

       *  two parties that must agree on the verdict MUST agree on the
          received text, or on a digest of it.

6.3.  Algorithm

Entails(G, O) → bool:
  1. G.namespace ≠ O.namespace → false      (namespace = scheme + action Class, Section 9.1 layer 3)
  2. G.id doesn't cover O.id → false        (path coverage, Section 9.1 layer 4)
  3. G declares no key → true               (both `params` and `param_bounds` absent/empty
                                             → grant unconstrained, absent ≡ empty object)
  4. O.params absent → false                (bounded grant, request omits it → fail-closed,
                                             except a declared key marked `optional`, Section 6.5)
  5. params_subset(O.params, keys(params) ∪ keys(param_bounds), param_bounds)
                                            (compare declared set incl. Section 6.5 bounds, Section 9.1 layers 5–9)

   Step 5 is the Section 9.1 layers 5–9 comparison over the grant's
   *declared key set* (keys(params) ∪ keys(param_bounds), Section 6.5):
   params values follow Section 6.2 value subset, and a param_bounds key
   additionally enforces its Bound (inclusive min/max, step, enum
   cardinality, optional, nested).  A grant with no param_bounds reduces
   step 5 to the params-only comparison of earlier revisions.  Steps 3
   and 4 read the declared key set, so a grant whose only declarations
   are param_bounds is still "bounded" for presence purposes.

   When more than one check fails, the reported reason follows the fixed
   resolved-reason ordering of Section 9.1.  The remaining rules of this
   section are unchanged.

   *Rule for missing operation params*: If the grant has a params bound
   (step 3 does not apply) and the operation has *no params field at
   all* (not merely a key missing, but the entire field absent), step 4
   applies: Entails → false → deny("params_missing").  This covers both
   "operation omits the entire params object" and "operation omits a
   single bounded key" — both fail closed.

   If a capability scheme declares a default value for a parameter, the
   implementation MUST apply that scheme default to O before step 4;
   absent such a declared default, step 4 denies.

Wei                       Expires 31 March 2027                [Page 15]
Internet-Draft                   CLC-v1                   September 2026

   *Layer 6 (null) resolves before presence.* If the grant's params
   carry a null value (or the operation's params do), the failure is
   invalid_params_null (Section 9.1 layer 6) and is reported even when
   the operation omits the params field entirely — i.e. layer 6 resolves
   *before* step 4's params_missing.  This is the fixed Section 9.1
   ordering; it shadows the literal step order of the algorithm above,
   which lists presence before the null checks.

6.4.  Match (evidence binding)

   Evidence E is bound to exact action A if:

   1.  E carries a valid ActionId in the language's projection form
       (clc-action:1:…, Section 4.3).  A CAID is a different object and
       relates to it only through a pinned Action-Mapping Profile.

   2.  E's ActionId equals the recomputed ActionId of the
       ObservedAction.

   3.  The ActionId was computed under the relying-party-pinned suite
       and definition source.

   Match is content correlation only.  It does not validate a native
   artifact and does not authorize execution.  It also does not identify
   an occurrence: correlation to a particular occurrence additionally
   requires an occurrence discriminator defined and checked by the
   consuming profile (CAID-03 Section 4.6), and a profile that uses one
   MUST pin how it is obtained and that the language sees it among the
   declared material fields.

   Cross-format mapping (E's native format ≠ A's canonical form) uses an
   Action-Mapping Profile: a hash-identified projection pinned by the
   relying party, with results EQUIVALENT_UNDER_PROFILE, NOT_EQUIVALENT,
   or INDETERMINATE.

6.5.  Extended parameter bounds (param_bounds)

   params (Section 6.2) stays exactly as defined: a scalar number is an
   upper bound, an array is a membership set, an object recurses per
   key, and there is no lower bound, no step, no cardinality bound and
   no optional-key marker.  This subsection adds those as a *separate,
   optional grant field*, param_bounds, so that *no existing params
   input changes meaning*: a grant without param_bounds behaves exactly
   as before, and every params verdict is unchanged.

   *Binding rule (one authoritative representation per key).* A
   parameter key MUST be declared in *at most one* of params and
   param_bounds.  Declaring the same key in both is rejected:

Wei                       Expires 31 March 2027                [Page 16]
Internet-Draft                   CLC-v1                   September 2026

   deny("invalid_params_binding").  The *declared key set* of a grant is
   keys(params) ∪ keys(param_bounds); key closure (Section 9.1 layer 7)
   and the params:{}≡absent rule are read over that set.

   *Four distinct layers — do not collapse them in a decoder.* A bare {}
   has different meaning at each layer, and an implementation must keep
   them separate before normalizing:

   1.  *Container presence* — "params" absent vs "params":{}.  Both are
       *unconstrained* (no declared keys); {} is not "declares nothing
       and denies".

   2.  *Declaration site* — a key is declared via params *or*
       param_bounds (never both).  The site decides which value algebra
       applies to the key.

   3.  *Value constraint* — inside params, an empty value *is* a
       restriction ({"tables":[]} denies the class; deny-when-declared,
       Section 6.2); inside param_bounds, an empty Bound {} declares the
       key with *no* value constraint (it only participates in key
       closure).  Same {}, opposite reading, because the layer differs.

   4.  *Request value* — what O supplies (or a materialized default),
       evaluated per the key's family at layers 7–9.

   A decoder that maps {}→"absent" or {}→"empty set" uniformly across
   these layers will disagree with the language on at least one of them.

   *Bound grammar (closed).* param_bounds maps a key to a Bound object:

Bound = {                          // a closed JSON object, at most one family
  // numeric family
  "min":       <number>,           // inclusive lower bound
  "max":       <number>,           // inclusive upper bound
  "step":      <number > 0>,       // value must be an integer multiple of step
  // enum family
  "enum":      [ <scalar> ],       // allowed values (membership set)
  "min_items": <integer >= 0>,     // request cardinality lower bound
  "max_items": <integer >= 0>,     // request cardinality upper bound
  // nested family
  "nested":    { <key>: Bound },   // recursion for an object-valued key
  // orthogonal to all families
  "optional":  <boolean>           // request MAY omit the key (default false)
}

   (Bound is JSON, not ASN.1: the members above are JSON keys and the
   comments are notation only.)

Wei                       Expires 31 March 2027                [Page 17]
Internet-Draft                   CLC-v1                   September 2026

   Every member is optional and the object is *closed* — a member
   outside this set is rejected (invalid_params_binding).  A Bound
   carries *at most one value family*, plus optional which is
   orthogonal:

   *  *numeric family* — any of min, max, step;

   *  *enum family* — enum, and/or min_items/max_items;

   *  *nested family* — nested.

   Mixing families (e.g. enum with max, or nested with min) is rejected
   (invalid_params_binding).  An *empty* Bound {} declares the key with
   no value constraint (it still participates in key closure).  min >
   max, step ≤ 0, min_items > max_items, or a negative min_items are
   rejected (invalid_params_binding).  The whole param_bounds object is
   subject to the same input normalization as params (Section 6.2):
   canonical serialization, duplicate keys, number shape, size (512
   octets) and depth (32), checked at layer 2.

   *Entailment semantics (grant G vs operation O).*

   *  *Presence (layer 7).* A declared key with optional: true MAY be
      absent from O; a declared key with optional absent or false MUST
      be present (params_missing otherwise).  An operation key not in
      the declared key set is undeclared_param.  (Unchanged behavior for
      grants that declare no param_bounds, where every declared key is
      required.)

   *  *Enum family (layer 8).* If enum is declared, the request value
      must be a member (not_in_enum, unchanged rule): a scalar request
      must equal a member; an array request must have every element
      equal to a member.  Equal here is *JSON type-sensitive equality*:
      two values are equal only when they have the same JSON type _and_
      the same value — true equals neither 1 nor 0 (the Section 6.2 rule
      that booleans are never numbers applies to set membership as
      well), and the string "1" equals neither the number 1 nor true.
      Numbers are compared after the Section 6.2 canonicalization of the
      input boundary (one IEEE-754 binary64 value, rendered per
      ECMAScript Number::toString under JCS [RFC8785]), so 1 and 1.0 —
      the same value in two spellings — *are* equal.  The same equality
      is used by Section 6.2 array membership, by the Section 6.6 enum
      intersection, and by Section 13.4.3 enum narrowing;
      implementations MUST NOT substitute a host-language equality that
      coerces across JSON types (e.g. a language where true == 1).  If
      min_items / max_items are declared, the *request cardinality* (an
      array's length; a scalar counts as 1) must satisfy min_items ≤ n ≤
      max_items, else params_cardinality.

Wei                       Expires 31 March 2027                [Page 18]
Internet-Draft                   CLC-v1                   September 2026

   *  *Numeric family (layer 9).* min/max are *inclusive*: a numeric
      request value must satisfy min ≤ v ≤ max (each bound, when
      declared), else params_out_of_range.  step requires the request
      value to be an integer multiple of step, evaluated in IEEE-754
      binary64 as let q = v / step in q == floor(q) && q * step == v
      (deterministic across implementations, since both operands arrive
      at the boundary as binary64), else params_not_multiple.  A
      numeric-family bound applied to a non-number request value is
      fail-closed (params_exceed_grant).

   *  *Nested family (layer 8/9).* nested recurses the value comparison
      on an object-valued request key with the same rules, including
      symmetric key closure and optional at each depth.  A nested bound
      applied to a non-object request value is fail-closed
      (params_exceed_grant).

   *Scheme defaults.* A capability scheme (Section 3 scheme grammar;
   data/std/...) MAY declare param_defaults, a map from a parameter key
   to its *default value*. The consumer obligation of Section 6.3 is
   thereby made concrete: before evaluating Entails(G, O), an
   implementation MUST materialize, for every key O omits that the
   scheme declares a default for, that default into O's params;
   precedence is *explicit operation value > scheme default > absent*.
   A default is applied only when it is needed to satisfy a grant-
   declared key; it never adds a key the grant does not declare (the
   key-closure check is unchanged).

   *optional closes the default interaction (no recursion).* The
   presence of the key is decided *first*, by the grant's optional
   marker, and only then is a default consulted — so there is no "is the
   default needed?" loop:

   *  a *required* declared key (no optional/optional:false) that O
      omits: materialize param_defaults[key] if the scheme declares one
      (the key is then present with that value), else
      deny("params_missing");

   *  an *optional:true* declared key that O omits: the default is *not*
      materialized — optional is the explicit "may be absent" marker and
      wins over the default, so the key is simply absent (and satisfies
      presence);

   *  either way, an explicitly supplied O value wins over the default.

   Defaults are *scheme configuration, not language semantics*: the
   relation itself takes no scheme argument, and two consumers that load
   the same scheme reach the same verdict (a deployment that omits the
   scheme's defaults is a different input, and the difference is

Wei                       Expires 31 March 2027                [Page 19]
Internet-Draft                   CLC-v1                   September 2026

   attributable to the input, not to Entails).  In the containment
   relation a default is resolved *before* the grants are compared
   (Section 13.8.1 obligation 1), so containment compares resolved
   declared sets.  A param_defaults entry is *not* part of a grant's
   declared set and does *not* enter the containment lattice: Contains
   narrows over keys(params) ∪ keys(param_bounds) and the optional
   markers alone, so a parent's default never widens or inverts the
   optional→required narrowing direction (Section 13.4.3).

   *Containment (Section 13).* Section 13.4.3 narrows a child grant
   against its parent using this grammar: a param_bounds bound of the
   child must be within the parent's bound for the same key (numeric min
   raised or equal, max lowered or equal, step refined to an integer
   multiple, enum a subset, cardinality bounds tightened, optional not
   widened from required to optional, nested recursed).  A parameter
   declared in one of params/param_bounds and not the other is a key-set
   difference and fails key closure (params_not_narrower).

6.6.  Intersection of bounds (BoundMeet)

   Intersect (Section 7) combines the param_bounds of its sources with a
   *meet*, over the same "value → authorization set" denotation
   Section 7 uses for params.  The families are those of Section 6.5;
   the meet is defined per key declared in param_bounds by two or more
   sources.

   *  *optional* is orthogonal and combines by *conjunction*: the result
      key is optional only if every source marks it optional (optional
      := AND); a key required by any source stays required.

   *  *numeric family* (min/max/step): min is the *greatest* declared
      minimum, max the *least* declared maximum (undeclared means
      unbounded). step: if both declare one and one is an exact multiple
      of the other (the Section 6.5 multiple predicate), the meet's step
      is the *coarser* (larger) of the two — the coarser grid is a
      subset of the finer, so it is the meet.  If neither declared step
      is an exact integer multiple of the other, the meet *fails closed*
      (invalid_params_binding).  The two grids may still share values —
      the common grid of step:5 and step:7 is the set of multiples of
      35, so the intersection is not empty — but the meet's step must be
      one of the two *declared* steps: neither 5 nor 7 is an integer
      multiple of the other, so neither declared grid is a subset of the
      other, and the language does not synthesize an undeclared grid
      (such as the least common multiple) — no single step member of the
      two Bounds denotes both grids.  One declared step is carried
      through.  min > max after combining → the meet is *empty* →
      no_overlap.

Wei                       Expires 31 March 2027                [Page 20]
Internet-Draft                   CLC-v1                   September 2026

   *  *enum family* (enum/min_items/max_items): the enum member sets
      intersect (exact equality — the JSON type-sensitive equality
      Section 6.5 layer 8 defines, so 1, true and "1" are three distinct
      members); if both sources declare enum and the intersection is
      empty → no_overlap; min_items is the *greatest* declared value,
      max_items the *least*; max_items < min_items → no_overlap.

   *  *nested family* (nested): the two objects MUST have the *same key
      set*, else no_overlap (exactly as object-valued params,
      Section 7); the result recurses BoundMeet per key, with optional
      combining as above at every depth.

   *  *numeric ∩ enum (either source order)*: *refused — fails closed*
      (invalid_params_binding).  A sound meet would have to carry both
      the numeric side's shape constraint (min/max/step, which
      Section 6.5 layer 9 applies to numeric scalar request values only)
      and the enum side's member set (which Section 6.5 layer 8 applies
      to scalars *and* to arrays whose elements are all members) in one
      Bound — two families, which the closed Section 6.5 grammar
      deliberately does not express.  Filtering the member list by the
      numeric bound (the rev CLC-1.14 rule, removed here) was *broader
      than either source*: for numeric {min:2,max:4} ∩ enum
      {enum:[1,3,5]} the filtered result enum{3} accepts the array [3]
      (Section 6.5 layer 8: every element is a member), while the
      numeric source fail-closes that same request (Section 6.5 layer 9:
      a numeric-family bound applied to a non-number request value is
      params_exceed_grant).  Refusing the whole combination agrees with
      the rest of the language: Section 6.5 rejects _declaring_ two
      families in one Bound ("Mixing families … is rejected
      (invalid_params_binding)"), and Section 13.4.3 rejects _narrowing_
      into a family the other side does not declare ("a child Bound that
      *adds a family the parent does not declare* … is
      params_not_narrower").  The refusal covers every numeric × enum
      pair — with or without a member list on the enum side (a
      cardinality-only enum is the same cross-family clash), and
      regardless of whether any member happens to fall inside the
      numeric range: the family clash is decided *before* any member or
      range math, and it is symmetric in the two sources.

Wei                       Expires 31 March 2027                [Page 21]
Internet-Draft                   CLC-v1                   September 2026

   *  *scalar ∩ nested* (numeric or enum vs. nested), in either order: a
      cross-family meet, refused with invalid_params_binding *before*
      any value math — no single-family Bound can carry both a scalar
      shape (a value presence) and the object recursion; the refusal is
      symmetric in the sources and holds regardless of the nested side's
      contents, exactly like the numeric × enum case (design-notes D12).
      A CLC-1.14 draft read this pair as an empty meet (no_overlap,
      since no request value is both a scalar and an object); rev CLC-
      1.15 adjudicates it to the unrepresentable-meet code so that the
      empty-meet code stays reserved for genuinely empty meets _within
      one value family_.

   *  *empty Bound* {} is the identity for the value families ({} ∩ X =
      X); its optional still participates.

   The result always carries *at most one* value family, so it is a
   valid Section 6.5 Bound.  All failure modes are the Section 9.2 codes
   already associated with Intersect (no_overlap for an empty meet
   _within one value family_, such as min > max or disjoint enums;
   invalid_params_binding for an unrepresentable one — every cross-
   family pair in either order, including scalar ∩ nested, and
   incommensurable step grids); no new reason code is introduced.

   *Key site.* A key's *declaration site* (params vs param_bounds) must
   agree across the sources of one Intersect.  A key declared in params
   by one source and in param_bounds by another is refused
   (invalid_params_binding).  This can only arise for independent
   authority sources, never for a delegation chain: Section 13.4.3
   already requires the sites to match at every hop (a site difference
   is a key-set difference).  A key that no source declares in
   param_bounds stays in params and follows the Section 7 params meet
   unchanged.

7.  Intersection (∩)

   P_effective = P_principal ∩ C_agent ∩ P_gateway.

   Rules:

   1.  Each source provides a grant set.

   2.  Effective grant MUST be covered by at least one grant from every
       source.

Wei                       Expires 31 March 2027                [Page 22]
Internet-Draft                   CLC-v1                   September 2026

   3.  Same-capability constraints merged by *union*: every source's
       constraint strings are kept (constraints are conjunctive — each
       is independently checked, so a tighter bound of the same type
       binds by construction).  The merge is a set union of normalized
       constraint strings, *not* a meet that discards a looser one;
       dropping a source's constraint would drop its restriction.

   4.  Any source missing a capability → capability absent from
       effective set.

   5.  *Zero or absent sources fail closed.* An intersection over *no*
       sources at all — an empty source list, or every source absent —
       has no effective set: deny("absent_source").  (A source that
       _exists_ but carries no grant for the capability is rule 4, not
       rule 5.)

   6.  *Empty params declares no constraint.* A source whose grant has a
       present-but-empty params object contributes no restriction.  The
       accumulated bound is preserved: bounded-then-empty and empty-
       then-bounded must give the same result — source order MUST NOT
       change the outcome (consistent with direct-grant params:{} ≡
       absent, Section 6.3 step 3).

   *Identifier comparison is params-free (rule 2).* The effective
   identifier is the *narrowest* one covered by every source, chosen by
   the Section 6.1 identifier rules alone.  It MUST NOT be resolved by
   routing grants through Entails with params — a bounded grant faced
   with a params-less sibling would trigger presence handling
   (Section 6.3 step 4) and wrongly fail this open; the identifier is
   compared, params merge separately (rule 3).

   *Property: composition narrows only.* If Intersect succeeds, the
   result MUST be covered by every source and MUST NOT depend on the
   order of the sources; otherwise it MUST deny with a normative reason
   code and MUST never raise.  This meet-law is what the Section 6.2
   param-subset algebra preserves across sources; it is pinned by
   property-cases.json (1184 cases, Section 12).

   *Notation in this section*: params are JSON objects (e.g.
   {"tables":["a"]}); constraints use colon-notation triples (e.g.
   varwof/constraint-v1:time:window:[{"start":"00:00","end":"01:00"}]).
   The example tables below use compact shorthand for readability; each
   row shows the relevant params or constraints only.

   *Deny-when-declared*: {"tables":[]} (explicitly empty) = deny the
   class.  Omitted = scheme default.  A null value is invalid in v1 →
   reject (invalid_params_null).  Canonicalization MUST NOT broaden
   (segment-boundary comparison, not lexical prefix).

Wei                       Expires 31 March 2027                [Page 23]
Internet-Draft                   CLC-v1                   September 2026

   +====================+=============================================+================+
   |Source A            |Source B                                     |Result          |
   +====================+=============================================+================+
   |{"tables":["a","b"]}|{"tables":["a"]}                             |{"tables":["a"]}|
   |                    |                                             |(holds)         |
   +--------------------+---------------------------------------------+----------------+
   |{"tables":["a"]}    |{"tables":[]}                                |deny            |
   +--------------------+---------------------------------------------+----------------+
   |(unconstrained)     |(no grant)                                   |deny            |
   +--------------------+---------------------------------------------+----------------+
   |{"limit":100}       |{"limit":50}                                 |{"limit":50}    |
   |                    |                                             |(holds)         |
   +--------------------+---------------------------------------------+----------------+
   |(unconstrained)     |constraint                                   |recognized, not |
   |                    |time:window:[{"start":"00:00","end":"01:00"}]|evaluated by    |
   |                    |                                             |core (→         |
   |                    |                                             |allow_unresolved|
   |                    |                                             |+ unresolved)   |
   +--------------------+---------------------------------------------+----------------+
   |{"tables":["a"]}    |{"tables":["b"]}                             |deny            |
   +--------------------+---------------------------------------------+----------------+

                                  Table 7

   *The meet is over authorization sets, not JSON values.* Neither
   Intersect nor Section 6.2 compares JSON values directly; each value
   denotes an *authorization set* and the meet is set intersection on
   that denotation.  A number denotes (-∞, v] (an upper bound), an array
   denotes a membership set, a string/boolean denotes the singleton {v},
   and an object denotes the product of its keys' denotations.  So
   {"a":1} is not the set {1} but (-∞,1], and {"a":1} ∩ {"a":2} = (-∞,1]
   = {"a":1} — a minimum, not an empty set.  A meet is *empty* (→
   deny("no_overlap")) only when the denotations are genuinely disjoint:
   two enum sets with no common member (a numeric params meet is always
   a minimum, since params has no lower bound), or divergent key sets
   (next paragraph).

   *Object-value intersection requires identical key sets.* Two object
   values intersect key-by-key only when their key sets are the same;
   object values with different key sets → no_overlap deny.  Merging
   "shared keys" would drop the keys the other source constrains, so the
   result would not be covered by that source (P11 composition narrows
   only): {"a":1} ∩ {"b":1} → deny(no_overlap).  When the key sets *are*
   the same the values recurse under the Section 6.2 rules, so a numeric
   leaf intersects to its *minimum* ({"a":1} ∩ {"a":2} → {"a":1}, not a
   deny — matching the {"limit":100} ∩ {"limit":50} row above), and
   {"a":1} ∩ {"a":1} → {"a":1}.

Wei                       Expires 31 March 2027                [Page 24]
Internet-Draft                   CLC-v1                   September 2026

   *param_bounds merges by a defined meet.* Intersect combines each key
   declared in param_bounds with the meet of Section 6.6: numeric
   min/max/step, enum member intersection and cardinality, and nested
   recursion, with optional combined by conjunction.  An empty meet is
   no_overlap; an unrepresentable one is invalid_params_binding — any
   cross-family pair in either order (numeric × enum, including a
   cardinality-only enum and regardless of whether any member falls
   inside the numeric range; and scalar × nested), or two step grids
   where neither declared step is an exact integer multiple of the other
   (Section 6.6).  A key declared in params by one source and
   param_bounds by another is refused (invalid_params_binding,
   Section 6.6 "Key site").  The Section 7.1 ConstraintUnion projection
   is separate and unaffected.

7.1.  ConstraintUnion (derived projection)

   A consumer that has established a delegation chain often needs the
   chain's *whole constraint burden* without computing an effective
   grant.  That is exactly the constraint projection of chained
   Intersect (rule 3), exposed as a named function:

   ConstraintUnion(chain) → string[]        // chain = ordered Grant[]

   It returns the *normalized union* of every constraint string carried
   by the grants in chain — duplicates folded, result deterministically
   ordered.  "Lexically sorted" is pinned to *UTF-8 byte order*: the
   normalized strings are compared octet by octet over their UTF-8
   encodings.  For well-formed Unicode text this equals code-point
   order, and it is identical in every implementation whatever the host
   language's native string representation is — notably it is *not*
   UTF-16 code-unit order, which places supplementary-plane characters
   (encoded as surrogate pairs, first unit U+D800–U+DBFF) before
   characters in U+E000–U+FFFF, so an emoji would sort before a private-
   use-area character although its code point is higher.  This collation
   sits _after_ Section 6.2 canonicalization, not instead of it: JCS
   [RFC8785] Section 3.2.3's UTF-16 code-unit order governs object
   *member* order inside a canonical serialization, while this section's
   UTF-8 byte order governs the emitted constraint-string *list*; the
   two artefacts keep their own pinned collations and neither re-orders
   the other.  The union is the same normalization Intersect applies, so
   the two never disagree.  The residual-obligation list of Section 8.4
   (and the ordered Resolve output of Section 8.5) re-uses this same
   collation; the collation is defined here once, not restated there.

   ConstraintUnion is a *projection, not a meet*: it does not compare
   identifiers or parameters, does not read or validate constraint
   values, and does not check containment.  It asserts nothing about
   authority; the chain's per-hop Contains check (Section 13) and the

Wei                       Expires 31 March 2027                [Page 25]
Internet-Draft                   CLC-v1                   September 2026

   operation's Authorize/Resolve (Section 9, Section 8.5) remain
   separate steps.  Because it is not an effective-set computation, it
   carries no membership semantics of its own — an *empty chain fails
   closed* with absent_source (the same refusal Intersect gives over
   zero sources, Section 7 rule 5), so a caller that lost the chain
   cannot mistake "nothing to union" for "no constraints".

   This function is why Contains stays pure (Section 13.4.4):
   containment answers "is the child's declared boundary inside the
   parent's?", and the union answers "what does the chain collectively
   require?".  Folding the union into Contains would make the relation
   no longer a subset on the declared tuple and would let a null
   constraint check hide a broken boundary.

8.  Constraint

   Constraints are shared between authorization and evidence sides.

8.1.  Authorization-side constraints

   Limit how a capability may be used.  Constraints use colon-notation
   triples: scheme:type[:params], where *type is the second :-delimited
   segment* (scheme : type, optionally followed by : and params):

   *  varwof/constraint-v1:max_rows:100 — type max_rows, param 100

   *  varwof/constraint-v1:time:window:[{"start":"00:00","end":"01:00"}]
      — type time, param window: array of UTC window segments (a single
      window = a one-element array).  A scalar form (time:window:3600,
      the sliding-duration/freshness concept) is not a legal time:window
      value in v1 → invalid_constraint.

   *  varwof/constraint-v1:network:cidr:["10.0.0.0/8"] — type network,
      param cidr: JSON array (≤ 32 elements, each a legal IPv4/IPv6 CIDR
      string)

   v1 core *recognizes constraints by (scheme, type) pair*, not by type
   name alone: the identity of a constraint is *two* things — the
   declaring scheme and the type.  The core recognizes exactly these
   pairs:

   varwof/constraint-v1 : max_rows
   varwof/constraint-v1 : time
   varwof/constraint-v1 : network

   Any other (scheme, type) — including foo/database-v1:max_rows — is
   *not* core-recognized: it is rejected as unknown_constraint (fail-
   closed), never handed to a core evaluator.  This kills cross-scheme

Wei                       Expires 31 March 2027                [Page 26]
Internet-Draft                   CLC-v1                   September 2026

   semantic pollution: the type name alone never selects an evaluator,
   so a scheme that defines its own max_rows cannot have its semantics
   hijacked by Core's max_rows evaluator (§P7 define once, consume
   everywhere — scoped per declaring scheme).  Scheme-defined constraint
   types belong to the v2 / profile layer (evaluated by the declaring
   scheme); core's non-recognition of them is fail-closed
   (unknown_constraint).

   A recognized constraint MUST also conform to that type's *value
   grammar* (table below); a recognized type with a non-conforming value
   is rejected as invalid_constraint — never silently skipped, never
   passed through.  Intersection itself neither evaluates nor validates
   constraint values (it only merges constraint strings; the Section 8.1
   value grammar is enforced at the decision boundary, Section 9, and in
   the constraint merge rules below).

   The core defines an evaluator for max_rows only; evaluation of time/
   network bounds is the declaring capability scheme's responsibility
   (the scheme evaluates, the core owns only what the Section 8.1 value
   grammar _is_, not whether a moment/address currently hits it;
   Section 11).  A recognized-but-unevaluated constraint is carried on
   the decision's additive unresolved field — never silently dropped
   (Section 8.4).  A non-recognized (scheme,type) →
   deny("unknown_constraint") (fail-closed).

   +========+==========================================+==================+
   |type    |value grammar (v1)                        |core behavior     |
   +========+==========================================+==================+
   |max_rows|strict non-negative integer (JSON number  |evaluated against |
   |        |grammar: no leading +, no 0x, no trailing |op params; the    |
   |        |characters)                               |*operation-side   |
   |        |                                          |value domain* is a|
   |        |                                          |finite non-       |
   |        |                                          |negative integer —|
   |        |                                          |an absent param, a|
   |        |                                          |string, a boolean,|
   |        |                                          |a negative, a     |
   |        |                                          |fractional or a   |
   |        |                                          |non-finite value, |
   |        |                                          |or a value above  |
   |        |                                          |the bound →       |
   |        |                                          |max_rows:violated |
   |        |                                          |(cannot be shown  |
   |        |                                          |conforming = fail-|
   |        |                                          |closed; rev CLC-  |
   |        |                                          |1.4); constraint  |
   |        |                                          |value out of      |
   |        |                                          |grammar →         |

Wei                       Expires 31 March 2027                [Page 27]
Internet-Draft                   CLC-v1                   September 2026

   |        |                                          |invalid_constraint|
   +--------+------------------------------------------+------------------+
   |time    |non-empty JSON array (≤ 32 elements),     |recognized only → |
   |(window)|elements                                  |allow_unresolved +|
   |        |{"start":"HH:MM[:SS]","end":"HH:MM[:SS]"},|unresolved        |
   |        |UTC, repeated daily, single window = one- |(Section 8.4)     |
   |        |element array; each endpoint is a         |                  |
   |        |*seconds-of-day* value (parsed from       |                  |
   |        |HH:MM[:SS]), with the reserved endpoint   |                  |
   |        |"00:00" in the end position read as       |                  |
   |        |*86400* (next-day midnight, 24:00) — so a |                  |
   |        |segment is the half-open [startSec,       |                  |
   |        |endSec) and MUST satisfy startSec < endSec|                  |
   |        |(*not* lexical string order, which would  |                  |
   |        |wrongly reject the canonical 22:00→00:00  |                  |
   |        |segment, since "22:00" > "00:00" as text);|                  |
   |        |a single segment *must not cross          |                  |
   |        |midnight*, so a crossing window is split  |                  |
   |        |into two same-day segments (22:00→00:00 + |                  |
   |        |00:00→06:00); the segment list MUST be    |                  |
   |        |ascending by (startSec,endSec) and *non-  |                  |
   |        |overlapping*                              |                  |
   +--------+------------------------------------------+------------------+
   |network |legal IPv4/IPv6 CIDR strings (addr/mask), |recognized only → |
   |(cidr)  |syntax-level check only; JSON array ≤ 32  |allow_unresolved +|
   |        |elements                                  |unresolved        |
   |        |                                          |(Section 8.4)     |
   +--------+------------------------------------------+------------------+

                                  Table 8

   The expressible window set has no empty window and no full-day
   window; a declaring scheme that needs such windows extends the
   grammar (Section 11).

   Merge rules in v1: numeric → minimum wins; allowlist → intersection;
   unknown → deny.  The allowlist is the array-enum set (Section 6.2);
   intersecting it takes the shared members.  v1 core defines *no
   denylist constraint* — merging applies only to the varwof/
   constraint-v1 types the core itself evaluates; it does not define or
   merge scheme-defined types (Section 11).  Constraint-set merging is
   always *normalized and deterministically ordered* (duplicate strings
   collapsed, result ordered) — the same input yields the same
   constraint sequence in any implementation.  v1 does not tighten time/
   network bounds at intersection: intersection keeps only the
   constraint strings it encounters (no evaluation, no tightening,
   Section 8.4).

Wei                       Expires 31 March 2027                [Page 28]
Internet-Draft                   CLC-v1                   September 2026

8.2.  Evidence-side constraints

   Require specific evidence properties: freshness, consumption, quorum,
   initiator-exclusion, etc.

   These are structurally identical to authorization constraints — a
   type plus optional params — but evaluated against evidence artifacts
   rather than operation parameters.

8.3.  Unified constraint grammar

   constraint = scheme ":" type [ ":" params ]

   Both sides use the same grammar.  The validator (authorization) or
   evidence evaluator (evidence) interprets the type-specific params.

8.4.  Recognized but not evaluated (residual-obligation channel)

   A recognized constraint whose value conforms to its Section 8.1 value
   grammar but for which the v1 core defines *no evaluator* — v1: time,
   network; evaluation belongs to the declaring scheme (Section 11) —
   MUST NOT be silently dropped: it must appear in the decision's
   additive unresolved list (Section 9), and the verdict must be
   *allow_unresolved* (not allow):

   Decision = { verdict: "allow"|"deny"|"allow_unresolved",
                reason, unresolved: string[] }

   *Fail-closed boundary*: allow_unresolved is an *independent enum
   value*, never equal to allow.  A consumer (PEP / profile / declaring
   scheme) MUST evaluate or confirm every unresolved constraint before
   allowing; *if it cannot execute or confirm, it MUST deny* (AAC
   Section 6.6: "when it cannot perform or confirm, it should be treated
   as deny").  A consumer that only writes if decision.verdict ==
   "allow" cannot, on the literal enum, release a residual obligation as
   an already-satisfied allow — a path that leaves obligations
   unconfirmed must explicitly handle allow_unresolved to pass. *"the
   caller is supposed to check unresolved" is not sufficient defense*:
   the verdict itself must refuse the two-value short-circuit.

Wei                       Expires 31 March 2027                [Page 29]
Internet-Draft                   CLC-v1                   September 2026

   unresolved semantics and ordering: [] for deny and for fully
   evaluated allow; for allow_unresolved the normalized constraint
   strings with duplicates folded — *the conditional collation order of
   Section 7.1 (#-separated, then by UTF-8 byte sequence), the same
   comparison the ConstraintUnion payload uses* (rev CLC-1.15).  The
   order is deterministic: the same input yields the same byte sequence
   in any implementation — in particular NOT the ECMAScript default
   string order (UTF-16 code-unit order); join the residual obligations
   across all sources and sort exactly once, before emitting the
   decision.

   *Combined obligations (consumer side)*: multiple unresolved
   constraints of the same (scheme,type) form a conjunction (AND) —
   satisfying A and B satisfies all; OR, any-one, first-wins, and
   ignoring some entries are all *forbidden*.  Constraints of different
   (scheme,type) do not interact; each evaluates under its own declaring
   scheme (P11 composition narrows only: conjunction only narrows).

   The core owns only the *value grammar* ("what it is"); "whether this
   window/CIDR currently forms a boundary" ("how to evaluate") belongs
   to the declaring scheme (Section 11) — but the *boundary-moment
   semantics are part of the grammar* (Section 8.1: half-open [start,
   end), single segment within one day, crossing split into segments),
   and a scheme MUST NOT change that interpretation, only evaluate on
   top of it.

8.5.  Resolving residual obligations (Resolve)

   Section 8.4 delivers residual obligations but leaves the consumer's
   feedback loop undefined.  Resolve closes it: a consumer reports, per
   obligation, whether it is satisfied, violated, or still unknown, and
   the core collapses the result to a fresh decision.

   Resolution = { constraint: string,
                  status: "satisfied" | "violated" | "unknown" }
   Resolve(decision, resolutions, now?) → Decision

   decision is a Section 9 Decision; resolutions is an ordered list
   (possibly empty) of Resolution; now is an optional UTC instant
   (RFC3339, Z). now is the only input that lets the core tick a
   residual itself.

   Algorithm, in order:

   1.  *Terminal verdicts are fixed.* If decision.verdict is deny or
       allow, Resolve returns decision unchanged and ignores
       resolutions: a deny is never revived and an allow carries nothing
       to discharge.

Wei                       Expires 31 March 2027                [Page 30]
Internet-Draft                   CLC-v1                   September 2026

   2.  *Malformed input fails closed.* An entry that violates the
       grammar above (unrecognized status, empty/non-string constraint)
       → deny with invalid_resolution; a now that is not a valid instant
       → deny with invalid_timestamp.

   3.  *Discharge on allow_unresolved.* Let O = decision.unresolved
       (already normalized and sorted, Section 8.4).  For each o ∈ O, a
       status in {satisfied, violated, unknown} is computed by combining
       the sources below under the order *violated ≻ satisfied ≻
       unknown* (the most restrictive source wins — a consumer that
       reports violated is never overridden by a lenient one, and the
       core clock is never overridden by a lenient assertion):

       *  *Core clock* — if o is a core-recognized varwof/constraint-
          v1:time obligation whose value is a Section 8.1 window array,
          *and now is supplied*, the core evaluates it: now inside a
          segment → satisfied; outside every segment → violated.  This
          is the obligation's *TTL*: the discharge horizon is the end of
          the segment containing now, so re-invoking Resolve with a
          later now re-evaluates to violated once the window has passed
          — a cached allow does not outlive its window.

       *  *Consumer resolution* — a Resolution for o contributes its
          status; the most restrictive of the two wins.

       *  Absent everywhere → unknown.

   4.  *Repeated entries* for the same constraint combine under the same
       violated ≻ satisfied ≻ unknown order.

   5.  *Unrelated resolutions are ignored.* A Resolution whose
       constraint is not in O cannot widen the outcome; O is
       authoritative.

   6.  *Result*:

       *  any o has status violated → deny, reason = that constraint's
          {type}:violated (time:violated for a core-clock violation;
          otherwise the type segment of the constraint string),
          unresolved = [];

       *  every o is satisfied → allow, reason null, unresolved = [];

       *  otherwise → allow_unresolved, reason null, unresolved = the
          still- unknown subset (normalized + sorted, Section 8.4).

Wei                       Expires 31 March 2027                [Page 31]
Internet-Draft                   CLC-v1                   September 2026

   Without now, the core attaches no TTL to a time:window obligation: a
   consumer may report it satisfied (its declaring scheme owns its
   clock), but that discharge is only as fresh as the call, and a
   consumer that needs a core-enforced horizon MUST pass now.

   *Acting on a result (caching rule).* Resolve returns a plain
   decision; it does *not* carry an expiry field, so a core-clock
   discharge is only valid for the instant of the call.  A consumer that
   acts on a Resolve result involving a core-evaluated time:window
   obligation MUST either re-invoke Resolve with a current now
   immediately before acting, or not cache the result beyond the current
   segment's end (the discharge horizon, Section 8.5 step 3) — it MUST
   NOT hold a cached allow past that horizon.  Equivalently: a suite
   that needs a mechanically enforceable "valid until" value derives it
   from the segment end itself, not from the returned Decision.  (The
   obligation carried on an allow_unresolved decision remains the
   Section 8.4 object a consumer evaluates; this rule is only about not
   outliving a clock-based discharge.)

   Resolve is deterministic and fail-closed; it is idempotent
   (Resolve(Resolve(d, r, now), r, now) = Resolve(d, r, now)) and
   monotone (adding a violated never turns a deny into an allow;
   removing one never turns an allow into a deny).  It neither invents
   nor drops obligations.  It subsumes the coarser identity-level
   consumer gate (the reference implementations' Discharge, which only
   answers "do I understand and commit to every obligation?"):
   confirming an obligation is the satisfied case.  It defines no policy
   about _how_ the consumer evaluates an obligation (P2) — that is the
   declaring scheme's (Section 11).

9.  Decision Function (Authorization Side)

   Authorize(grants, operation) → Decision
   Decision = { verdict: "allow"|"deny"|"allow_unresolved",
                reason: string|null,
                unresolved: string[] }   // additive, Section 8.4

   Algorithm.  One precedence rule governs the whole function: *the
   grant-side pre-check resolves before operation validation.* An absent
   or empty grant set denies with capability_not_authorized even when
   the operation is absent as well (Section 9.1 layer 10; Appendix B.5,
   row D15, pins it), so a caller that passes neither input gets that
   reason and not missing_capability_id.  The algorithm below therefore
   runs the grant-side pre-check *first*, ahead of the numbered
   operation steps — the numbering mirrors Section 9.1's layer order for
   what follows, not the precedence of the pre-check.

Wei                       Expires 31 March 2027                [Page 32]
Internet-Draft                   CLC-v1                   September 2026

   1.  (Pre-check) An absent or empty grant set →
       deny("capability_not_authorized") (Section 9.1 layer 10), before
       any operation check; this is why the absent-grant / absent-
       operation pair resolves here and not to a layer-1 code.

   2.  Validate operation: missing/invalid id → deny with stable reason.
       The operation's *specific layer-1 code* is reported —
       missing_capability_id (no id), unsupported_wildcard (v1-forbidden
       wildcard shape) or invalid_capability_id (other grammar
       violation) — and is *not* collapsed to a generic code.

   3.  Find covering grants via Entailment (Section 6.1).  No covering
       grant → deny("capability_not_authorized") (Section 9.1 layer 10);
       the absent/empty case was already resolved by step 0.

   4.  For each covering grant: evaluate constraints (Section 8.1): non-
       recognized (scheme,type) → unknown_constraint; recognized but
       non-conforming value → invalid_constraint; recognized with a core
       evaluator (varwof/constraint-v1:max_rows) and violated →
       {type}:violated; recognized without a core evaluator (time,
       network) → residual obligation (Section 8.4).

   5.  *Aggregate* (multi-grant set, Section 9.1):

       *  Any covering grant that "allows" (no params/constraint
          rejection) → overall allow;

       *  Residual obligations = the unresolved *union across the
          covering grants that also allow* (normalized + sorted).  A
          covering grant that is rejected at the params/constraint layer
          contributes *no* obligations: it does not authorize, so its
          residuals are not carried.  The union is nevertheless order-
          independent, because every covering-and-allowing grant is
          visited;

       *  When no covering grant allows: if at least one covering grant
          rejects at the params/constraint layer → use the rejection
          reason of the *first covering grant in canonical order*
          (deterministic, Section 9.1); if no grant covers at all →
          capability_not_authorized.

   6.  Allow with non-empty residual obligations → verdict =
       allow_unresolved; allow with empty obligations → verdict = allow.

Wei                       Expires 31 March 2027                [Page 33]
Internet-Draft                   CLC-v1                   September 2026

   The grant set may be *absent or empty* — e.g. an integrator calls
   Authorize with no grant value or with an empty capability record.  A
   safe evaluator MUST NOT raise; it MUST resolve such input to
   deny("capability_not_authorized") (falling out of layer 10/step 2).
   An absent operation resolves to deny("missing_capability_id") (layer
   1); the evaluator MUST NOT raise there either.

   Properties: deterministic (same input → same output), fail-closed,
   stable reason codes.

9.1.  Resolved Reason Ordering (normative)

   When more than one condition fails for a grant/operation pair, the
   *resolved reason* (the single code reported) is the first applicable
   layer in this fixed order.  It applies to Entails (Section 6.3),
   Intersect (Section 7) and Authorize (this section).

   +==+=============+===============+==================================+
   |# |Layer        |Checks         |Reason code(s)                    |
   +==+=============+===============+==================================+
   |1 |CapabilityId |Section 3      |invalid_capability_id,            |
   |  |validity     |grammar,       |missing_capability_id,            |
   |  |             |wildcard shape |unsupported_wildcard              |
   +--+-------------+---------------+----------------------------------+
   |2 |Params       |Duplicate JSON |invalid_params_duplicate_key,     |
   |  |normalization|keys; non-     |invalid_params_number,            |
   |  |             |normalizable   |invalid_params_size,              |
   |  |             |numbers (non-  |invalid_params_binding            |
   |  |             |finite / out-  |                                  |
   |  |             |of-range /     |                                  |
   |  |             |over-          |                                  |
   |  |             |precision) and |                                  |
   |  |             |malformed-     |                                  |
   |  |             |Unicode text;  |                                  |
   |  |             |size/depth     |                                  |
   |  |             |limits         |                                  |
   |  |             |(Section 6.2); |                                  |
   |  |             |param_bounds   |                                  |
   |  |             |well-          |                                  |
   |  |             |formedness and |                                  |
   |  |             |the one-       |                                  |
   |  |             |representation |                                  |
   |  |             |binding rule   |                                  |
   |  |             |(Section 6.5)  |                                  |
   +--+-------------+---------------+----------------------------------+
   |3 |Namespace =  |Grant vs       |different_namespace               |
   |  |scheme +     |operation      |                                  |
   |  |action Class |scheme and     |                                  |

Wei                       Expires 31 March 2027                [Page 34]
Internet-Draft                   CLC-v1                   September 2026

   |  |             |Class          |                                  |
   +--+-------------+---------------+----------------------------------+
   |4 |Path coverage|Literal path   |literal_mismatch,                 |
   |  |(same        |segments,      |wildcard_requires_trailing_segment|
   |  |namespace)   |trailing-      |                                  |
   |  |             |wildcard depth |                                  |
   +--+-------------+---------------+----------------------------------+
   |5 |Explicit     |Any grant/     |empty_bound_denies_class          |
   |  |empty bound  |intersection   |                                  |
   |  |             |source         |                                  |
   |  |             |declares a     |                                  |
   |  |             |*parameter     |                                  |
   |  |             |value* that is |                                  |
   |  |             |[]/{} (a       |                                  |
   |  |             |present-but-   |                                  |
   |  |             |empty params   |                                  |
   |  |             |object is _no_ |                                  |
   |  |             |constraint,    |                                  |
   |  |             |Section 7 rule |                                  |
   |  |             |6)             |                                  |
   +--+-------------+---------------+----------------------------------+
   |6 |Null values  |Any null       |invalid_params_null               |
   |  |             |parameter      |                                  |
   |  |             |value          |                                  |
   +--+-------------+---------------+----------------------------------+
   |7 |Param        |Grant bounds a |params_missing, undeclared_param  |
   |  |presence     |param the      |                                  |
   |  |             |operation      |                                  |
   |  |             |omits (or      |                                  |
   |  |             |operation has  |                                  |
   |  |             |no params);    |                                  |
   |  |             |operation      |                                  |
   |  |             |carries a key  |                                  |
   |  |             |the grant does |                                  |
   |  |             |not declare.   |                                  |
   |  |             |A param_bounds |                                  |
   |  |             |key with       |                                  |
   |  |             |optional:true  |                                  |
   |  |             |is exempt from |                                  |
   |  |             |the omission   |                                  |
   |  |             |half           |                                  |
   |  |             |(Section 6.5)  |                                  |
   +--+-------------+---------------+----------------------------------+
   |8 |Enum         |Request value  |not_in_enum, params_cardinality   |
   |  |membership   |not a member   |                                  |
   |  |and          |of a granted   |                                  |
   |  |cardinality  |array set      |                                  |
   |  |             |(Section 6.2); |                                  |

Wei                       Expires 31 March 2027                [Page 35]
Internet-Draft                   CLC-v1                   September 2026

   |  |             |request        |                                  |
   |  |             |cardinality    |                                  |
   |  |             |outside a      |                                  |
   |  |             |param_bounds   |                                  |
   |  |             |min_items/     |                                  |
   |  |             |max_items      |                                  |
   |  |             |(Section 6.5)  |                                  |
   +--+-------------+---------------+----------------------------------+
   |9 |Bound        |Numeric/bound  |params_exceed_grant,              |
   |  |comparison   |exceeded;      |params_out_of_range,              |
   |  |             |param_bounds   |params_not_multiple               |
   |  |             |min/max out of |                                  |
   |  |             |range; step    |                                  |
   |  |             |not an integer |                                  |
   |  |             |multiple       |                                  |
   |  |             |(Section 6.5)  |                                  |
   +--+-------------+---------------+----------------------------------+
   |10|Coverage     |Intersection   |no_overlap, absent_source,        |
   |  |emptiness    |result empty;  |capability_not_authorized         |
   |  |             |zero sources;  |                                  |
   |  |             |no grant       |                                  |
   |  |             |covers the     |                                  |
   |  |             |operation      |                                  |
   +--+-------------+---------------+----------------------------------+
   |11|Constraint   |Non-recognized |unknown_constraint,               |
   |  |evaluation   |(scheme,type); |invalid_constraint,               |
   |  |             |recognized     |{type}:violated                   |
   |  |             |type value out |                                  |
   |  |             |of Section 8.1 |                                  |
   |  |             |grammar;       |                                  |
   |  |             |recognized     |                                  |
   |  |             |type violated  |                                  |
   +--+-------------+---------------+----------------------------------+

                                  Table 9

   Notes:

   *  *Namespace* is scheme:action_class (the first two :-delimited
      segments).  Identifiers in the same namespace differ only below
      the Class; such mismatches are path-level (literal_mismatch /
      wildcard_requires_trailing_segment), not different_namespace.

   *  *Params normalization fires at the input boundary*: duplicate
      keys, over-precision/non-finite numbers, and size/depth overruns
      (Section 6.2) are detected before any grant-vs-operation
      comparison and therefore before every other layer of this table.

Wei                       Expires 31 March 2027                [Page 36]
Internet-Draft                   CLC-v1                   September 2026

   *  *Deny-when-declared*: layer 5 is evaluated on the grant/
      intersection side — an explicitly empty [] or {} param value
      denies the class regardless of the request, before any member or
      bound check.

   *  *params:{} ≡ absent*: the params constraint looks only at the *set
      of declared param names*, independent of carrier form — an absent
      params and "params":{} both mean "no param names declared" = *no
      param constraint* (consistent across entailment and intersection;
      the same representation cannot have different semantics in
      different functions).  Therefore:

      -  Direct grant: a grant with params:{} *allows* an op carrying
         any params (.{x:1} does not trigger undeclared_param);

      -  Intersection: a params:{} source *contributes no param
         constraint* ({limit:50} ∩ {} = {limit:50});

      -  Key closure (next bullet) applies only when the grant declares
         a *non-empty* set of param names.

   *  *Layer 7 key closure runs both directions*: granted keys must be
      present in the operation (params_missing) and operation keys must
      be declared by the grant (undeclared_param); the missing-key check
      resolves first, and both precede the layer 8–9 value checks.
      (Applies only to a non-empty set of declared names, see the bullet
      above.)

   *  *Layer 11 runs last by construction*: constraints are evaluated
      only after coverage and parameters pass.

   *  *Multi-grant aggregation (normative)*: Authorize operates on a
      *ordered list of grants*, not a single grant.  Authorization
      semantics are order-independent; only reason selection (rule 4)
      uses the input order.  Rules:

      1.  *Any one covering-and-allowing grant allows* (∃ g:
          Entails(g,op) ∧ no params-layer rejection ∧ no constraint-
          layer rejection);

      2.  Residual obligations = unresolved *union across the covering
          grants that allow* (normalized + sorted).  A covering grant
          rejected at the params/constraint layer contributes none — it
          does not authorize, so its residuals are not carried (they are
          never silently dropped from a decision it does not produce);

      3.  No grant covers → capability_not_authorized; op layer-1 errors
          always precede any coverage/aggregation decision;

Wei                       Expires 31 March 2027                [Page 37]
Internet-Draft                   CLC-v1                   September 2026

      4.  Some grant covers but all covering grants reject at the
          params/constraint layer → deny, reason = the layer 5–11
          rejection reason of the *first covering grant in canonical
          order* (deterministic). *Canonical order* = the appearance
          order in the input grant list (the capability-record body
          order); an implementation MUST NOT choose the reason by
          internal hash/iteration order.  Counter-examples pinned
          (forbidden): NOT any-one-allows — otherwise, with multiple
          grants held, a narrow grant would wrongly deny the legal
          operation of a broad grant; NOT first-match-wins — otherwise
          grant order would change the authorization outcome; NOT all-
          must-pass — synonymous with "any-one-covers authorizes",
          letting a narrow grant's existence invalidate a broad grant.

   *  *Op-ID validation errors propagate their specific layer-1 code*
      (missing_capability_id / unsupported_wildcard /
      invalid_capability_id), never capability_not_authorized and never
      an invented catch-all: step 1 reports the very code that describes
      the operation id.  Only *coverage* failures (layers 3–4 and 10)
      collapse to capability_not_authorized.

   *  *Layer 1 validates the operation id only.* A malformed id in a
      *grant* makes that grant non-matching for every operation: Entails
      reports the specific layer-1 code as its false reason, and
      Authorize folds the non-match into coverage
      (capability_not_authorized) — the grant's own code is not surfaced
      by the decision.

   *  An *absent/empty effective grant* drops straight to layer 10
      (capability_not_authorized); the evaluator MUST return this
      decision rather than raising.  An absent operation drops to layer
      1 (missing_capability_id).

   *  A *language revision mismatch* (Section 12.1) is resolved before
      any layer and reports unsupported_language_revision (fail-closed,
      no downgrade).

   *  *Resolve (Section 8.5) is post-decision, not a layer.* It consumes
      a Decision and never re-runs Authorize; only invalid_resolution /
      invalid_timestamp (Section 9.2) and a {type}:violated obligation
      result are introduced by it, and only on an allow_unresolved
      input.

9.2.  Reason Codes (normative)

   Reason codes are stable identifiers. v1 defines:

Wei                       Expires 31 March 2027                [Page 38]
Internet-Draft                   CLC-v1                   September 2026

   *Canonical code = everything before the first :*.  An implementation
   MAY append : <detail> (e.g. the offending parameter name) as a
   diagnostic suffix; the canonical code is unchanged.  All tooling and
   compatibility checks MUST compare the canonical prefix only.

   +====================================+==============================+
   | Reason code                        | Meaning                      |
   +====================================+==============================+
   | unsupported_wildcard               | Wildcard shape forbidden in  |
   |                                    | v1 (bare *, partial          |
   |                                    | segment, **, {a,b}, [a-z])   |
   +------------------------------------+------------------------------+
   | invalid_capability_id              | CapabilityId does not        |
   |                                    | conform to the Section 3     |
   |                                    | grammar                      |
   +------------------------------------+------------------------------+
   | missing_capability_id              | Operation has no id (layer   |
   |                                    | 1)                           |
   +------------------------------------+------------------------------+
   | invalid_params_duplicate_key       | Params contain a duplicate   |
   |                                    | JSON key (Section 6.2        |
   |                                    | representation, Section 9.1  |
   |                                    | layer 2)                     |
   +------------------------------------+------------------------------+
   | invalid_params_number              | The params input has no      |
   |                                    | canonical form: a numeric    |
   |                                    | param is non-finite, out of  |
   |                                    | IEEE-754 range, or over-     |
   |                                    | precision (> 17 significant  |
   |                                    | decimal digits)              |
   |                                    | (Section 6.2 step 3), or     |
   |                                    | the text is not well-formed  |
   |                                    | Unicode / not parseable as   |
   |                                    | a JSON object (Section 6.2   |
   |                                    | step 7).  One stable code    |
   |                                    | covers every "cannot be      |
   |                                    | normalized" params failure   |
   |                                    | (Section 6.2                 |
   |                                    | representation, Section 9.1  |
   |                                    | layer 2)                     |
   +------------------------------------+------------------------------+
   | invalid_params_size                | Params exceed the 512-byte   |
   |                                    | serialized size or the       |
   |                                    | depth-32 nesting limit       |
   |                                    | (Section 6.2                 |
   |                                    | representation, Section 9.1  |
   |                                    | layer 2)                     |
   +------------------------------------+------------------------------+

Wei                       Expires 31 March 2027                [Page 39]
Internet-Draft                   CLC-v1                   September 2026

   | invalid_params_binding             | A param_bounds bound is      |
   |                                    | malformed (unknown member,   |
   |                                    | mixed families, min > max,   |
   |                                    | step ≤ 0, min_items >        |
   |                                    | max_items), or a key is      |
   |                                    | declared in both params and  |
   |                                    | param_bounds (Section 6.5,   |
   |                                    | Section 9.1 layer 2)         |
   +------------------------------------+------------------------------+
   | different_namespace                | Grant and operation differ   |
   |                                    | in namespace (scheme +       |
   |                                    | action Class, Section 9.1    |
   |                                    | layer 3)                     |
   +------------------------------------+------------------------------+
   | literal_mismatch                   | Literal identifiers differ   |
   +------------------------------------+------------------------------+
   | wildcard_requires_trailing_segment | Wildcard has no remaining    |
   |                                    | segment (...:* does not      |
   |                                    | cover ...)                   |
   +------------------------------------+------------------------------+
   | capability_not_authorized          | No grant in the effective    |
   |                                    | set covers the operation     |
   +------------------------------------+------------------------------+
   | no_overlap                         | Intersection of multiple     |
   |                                    | sources is empty             |
   +------------------------------------+------------------------------+
   | absent_source                      | Intersection over zero       |
   |                                    | sources — no effective set   |
   |                                    | (Section 7 rule 5)           |
   +------------------------------------+------------------------------+
   | params_exceed_grant                | Request parameters exceed    |
   |                                    | the granted bound            |
   +------------------------------------+------------------------------+
   | params_missing                     | Grant bounds a parameter     |
   |                                    | but the request omits it,    |
   |                                    | or the request has no        |
   |                                    | params field at all (fail-   |
   |                                    | closed, Section 6.3 step 4)  |
   +------------------------------------+------------------------------+
   | undeclared_param                   | Operation parameter key not  |
   |                                    | declared by the grant's      |
   |                                    | params (key closure,         |
   |                                    | Section 6.2; Section 9.1     |
   |                                    | layer 7 request side)        |
   +------------------------------------+------------------------------+
   | empty_bound_denies_class           | An explicitly empty bound    |
   |                                    | at a parameter value         |
   |                                    | ([]/{}) denies the class.    |

Wei                       Expires 31 March 2027                [Page 40]
Internet-Draft                   CLC-v1                   September 2026

   |                                    | params:{} is not an empty    |
   |                                    | bound: it is equivalent to   |
   |                                    | an absent params (Section 7  |
   |                                    | rule 6)                      |
   +------------------------------------+------------------------------+
   | not_in_enum                        | Request value is not a       |
   |                                    | member of the allowed set    |
   |                                    | granted as an array          |
   |                                    | (Section 6.2 enum rule)      |
   +------------------------------------+------------------------------+
   | params_cardinality                 | Request cardinality is       |
   |                                    | outside a param_bounds       |
   |                                    | min_items/max_items          |
   |                                    | (Section 6.5)                |
   +------------------------------------+------------------------------+
   | params_out_of_range                | Request value is outside a   |
   |                                    | param_bounds min/max         |
   |                                    | inclusive bound              |
   |                                    | (Section 6.5)                |
   +------------------------------------+------------------------------+
   | params_not_multiple                | Request value is not an      |
   |                                    | integer multiple of a        |
   |                                    | param_bounds step, per the   |
   |                                    | IEEE-754 rule (Section 6.5)  |
   +------------------------------------+------------------------------+
   | invalid_params_null                | null parameter value         |
   |                                    | (rejected in v1)             |
   +------------------------------------+------------------------------+
   | unsupported_language_revision      | Declared CLC revision is     |
   |                                    | incompatible with the        |
   |                                    | implementation               |
   |                                    | (Section 12.1; fails         |
   |                                    | closed, no silent            |
   |                                    | downgrade)                   |
   +------------------------------------+------------------------------+
   | unknown_constraint                 | Unknown constraint type      |
   |                                    | (fail-closed)                |
   +------------------------------------+------------------------------+
   | invalid_constraint                 | Recognized type whose value  |
   |                                    | fails its Section 8.1 value  |
   |                                    | grammar                      |
   +------------------------------------+------------------------------+
   | {type}:violated                    | A known constraint is        |
   |                                    | violated (e.g.               |
   |                                    | max_rows:violated); also     |
   |                                    | reported by Resolve for a    |
   |                                    | discharged-as-violated       |
   |                                    | obligation (Section 8.5)     |

Wei                       Expires 31 March 2027                [Page 41]
Internet-Draft                   CLC-v1                   September 2026

   +------------------------------------+------------------------------+
   | invalid_resolution                 | A Resolve resolution entry   |
   |                                    | is malformed (unknown        |
   |                                    | status, non-string/empty     |
   |                                    | constraint) (Section 8.5)    |
   +------------------------------------+------------------------------+
   | invalid_timestamp                  | The Resolve now argument is  |
   |                                    | not a valid UTC instant      |
   |                                    | (Section 8.5)                |
   +------------------------------------+------------------------------+

                                  Table 10

   Other schemes MAY define additional codes, but MUST NOT redefine
   these.

10.  Satisfaction Function (Evidence Side)

Satisfy(evidence_set, requirement) → Satisfaction
Satisfaction = { verdict: "SATISFIED"|"UNSATISFIED", reason: string|null }

   Algorithm:

   1.  Verify each evidence artifact under its native rules.

   2.  For each required evidence role, check that an artifact fills it.

   3.  Check that each artifact is bound to the exact action via Match
       (Section 6.4).

   4.  Evaluate freshness, consumption, and role constraints.  The
       evidence-side value grammar is defined in this revision: varwof/
       evidence-v1:freshness:sec:<n>, :consumption:once,
       :quorum:distinct:<n>, :exclusion:initiator|executor
       (Section 8.2).  A recognized constraint whose evaluation belongs
       to the enforcement point (consumption) evaluates to unknown,
       which at the top level yields UNSATISFIED (never SATISFIED) — the
       evidence side has no allow_unresolved; unresolved is an
       authorization-side channel (Section 8.4, Section 11).

   5.  All required roles filled and bound → SATISFIED.

   6.  Any role unfilled, unbound, or violated → UNSATISFIED.

   *Tri-state evaluation, binary report.* A recognized evidence-side
   constraint is evaluated three-valued (satisfied / violated /
   unknown).  Satisfaction itself is binary.  A constraint that
   evaluates to unknown at the top level — including one whose

Wei                       Expires 31 March 2027                [Page 42]
Internet-Draft                   CLC-v1                   September 2026

   evaluation belongs to the enforcement point (consumption) — MUST
   produce UNSATISFIED with a stable reason, never SATISFIED.  unknown
   is an internal evaluation result, not a third top-level verdict.

   Properties: deterministic, fail-closed, stable reason codes.

11.  Semantic Boundary

   CLC-v1 defines the *shared minimal vocabulary* and *evaluation
   algorithms* for both authorization and evidence.

   CLC-v1 does NOT define:

   *  *Trust models*: who signs what, issuer trust, delegation chains
      (belongs to AIC-JWT [AIC-JWT], OAuth, SPIFFE, etc.)

   *  *Native verification*: signature checking, schema validation,
      freshness enforcement (belongs to each native artifact's spec)

   *  *Execution lifecycle*: consumption, invocation, reconciliation,
      outcome classification (belongs to EMILIA AEB [EMILIA-AEB] or
      equivalent)

   *  *Receipt or token formats*: the wire formats for carrying grants,
      evidence, or bindings (belongs to protocol-specific specs)

   The boundary is:

   *  CLC-v1 defines *what* to evaluate (grant ⊆ operation, evidence ↔
      action)

   *  Consumers define *how* to evaluate (native verification, trust
      anchors)

   *  CLC-v1 defines *what* the output means (allow/deny/
      allow_unresolved, SATISFIED/UNSATISFIED) — *allow and
      allow_unresolved are distinct enum values*; a consumer MUST NOT
      treat allow_unresolved as allow (Section 8.4)

   *  Consumers define *what to do* with the output (invoke, record,
      reconcile)

   *  CLC-v1 defines each known type's *value grammar* (what counts as a
      legal constraint value); the declaring scheme defines *how* that
      value is evaluated (whether this window/CIDR currently forms a
      boundary)

Wei                       Expires 31 March 2027                [Page 43]
Internet-Draft                   CLC-v1                   September 2026

   *  *allow_unresolved is an authorization result, not evidence.* It
      marks an unresolved authorization (or policy) condition.  A
      consumer that can evaluate the obligation under a pinned rule may
      release it; one that cannot MUST refuse.  It takes on an evidence
      role only where a relying party separately defines one together
      with the native verifier for it (AEB); the language itself makes
      no such claim, and unresolved MUST NOT be read as "evidence still
      required"

   *  *A Section 6.2 refusal does not transfer to a decoded value.* The
      input-boundary checks are defined over the text as received
      (Section 6.2).  A deployment that must reproduce a refusal, or
      that applies a permit to a request it did not evaluate, therefore
      anchors the decision to the *received octets* or to a digest of
      them (Section 4.3) — never to a value a decoder has already
      normalized.  A pipeline that can apply a permit to a request whose
      text was never checked has left the CLC boundary

   *  CLC-A stays the *scope language*: material-action identity is
      referenced from CAID [CAID], heterogeneous evidence evaluation
      from AEC [AEC], the boundary lifecycle from AEB [EMILIA-AEB], and
      durable consumption/accounting from BCR [BCR]; the narrow
      crosswalk between them is the composition point

   *  Principal/agent delegation and token attenuation stay with PAP
      [PAP] and AAT [AAT]; CLC consumes their authorization outcome
      rather than redefining it

12.  Conformance

   CLC-v1 defines *three conformance classes*:

   *CLC-A (authorization side)* — the v1 baseline.  A conforming
   implementation MUST implement: grammar (Section 3), entailment
   (Section 6.1), intersection (Section 7), decision function
   (Section 9), rejection of non-recognized (scheme,type) constraints
   (unknown_constraint, Section 8.1), rejection of non-conforming
   recognized-type values (invalid_constraint, Section 8.1), exposure of
   recognized-but-unevaluated constraints via the decision's unresolved
   field on an independent allow_unresolved verdict (never silently
   dropped, Section 8.4), multi-grant aggregation (Section 9.1), and
   stable reason codes (Section 9.2).

   *CLC-D (delegation side)* — the containment relation, defined in
   *Section 13*.  A conforming implementation MUST implement
   Contains(parent, child) with its ordered layers, the relation's two
   stable reason codes (child_exceeds_parent, params_not_narrower) and
   its profile contract (Section 13.8.1), and MUST pass containment-

Wei                       Expires 31 March 2027                [Page 44]
Internet-Draft                   CLC-v1                   September 2026

   vectors.json.  The third CLC-D code, delegation_mode_not_narrower, is
   produced by the binding profile's delegation-mode pre-check
   (Section 13.4.5) — never by Contains, which takes no mode argument —
   and belongs to the profile's obligations.  A delegation policy that
   requires each hop to stay inside the previous hop's boundary reads
   Contains, not Section 7: entailment and intersection over the
   *declared* sets are necessary but not sufficient, because they do not
   compare a child's boundary against a parent's.  An operation fitting
   a grant proves nothing about a child staying inside its parent, and
   where containment cannot be established a binding profile MUST NOT
   authorize the delegation.  Contains is a language relation over grant
   values, not a wire format; the effective subset a delegation record
   carries is still the profile's to define.

   *CLC-E (evidence side)* — optional conformance profile, *implemented
   and pinned by a corpus in this revision, but NOT claimed*.
   Section 6.4 (match) and Section 10 (satisfaction) define the
   evidence-side relations; this revision also defines the evidence-side
   constraint value grammar (varwof/evidence-v1:freshness:sec:<n>,
   :consumption:once, :quorum:distinct:<n>,
   :exclusion:initiator|executor) and ships a reference implementation
   and a corpus.

   The class is withheld *on principle, not for lack of material*:
   agreement is the bar, and the bar is two _independent_
   implementations (Section 12 of the principles document, P12;
   Section 12 here, "Independence of implementations").  Parity between
   implementations that share an author does not meet it.  A second
   precondition is stewardship: the evidence-side semantics are the
   subject of joint review with EMILIA, so no claim is made ahead of
   that review.

   A future revision that claims CLC-E would carry these obligations,
   and an implementation that claims it today MUST:

   *  evaluate the four evidence-side types over eligible evidence
      facts, returning a three-valued result (satisfied / violated /
      unknown) where unknown is never read as satisfied, and report a
      recognized constraint whose evaluation belongs to the enforcement
      point (consumption) as unknown rather than satisfied;

   *  take eligibility from integrity-protected native results only: a
      fact that did not reach VERIFIED, or whose protected subject
      identifier is absent, MUST NOT be counted for quorum or exclusion;

   *  implement instance identity and binding (Section 4.2/Section 6.4):
      an ActionId is the digest of the JCS canonical serialization of
      the *declared material projection*, an undeclared field MUST NOT

Wei                       Expires 31 March 2027                [Page 45]
Internet-Draft                   CLC-v1                   September 2026

      affect it, a missing declared material field makes the action non-
      matchable (never inferred or defaulted), and a comparison across
      suites or action types is INDETERMINATE — a mapping problem, never
      a match — unless a relying-party-pinned Action-Mapping Profile
      projects it;

   *  implement CLC-REQUIREMENT-v1 as a *closed* object (an undefined
      member is rejected) whose expression uses the bounded grammar —
      AND/OR with equal binding strength, evaluated strictly left to
      right, parentheses as the only precedence mechanism — and treat an
      identifier with no eligible component as false;

   *  take the requirement from relying-party configuration: a
      requirement supplied by the presenter MUST NOT be accepted or
      weakened;

   *  pass evidence-vectors.json (Section 12 conformance corpora).

   An implementation that implements only CLC-A MUST NOT claim CLC-E.
   CLC-E does not add a wire format: carriers that need one (e.g. an
   Action Evidence Envelope) profile Section 6.4/Section 10 themselves.

   *Conformance corpora.* The suites below are published in the
   repository tree pinned as [CLC-CORPUS]; repository paths written as
   capability/... throughout this document are relative to that pinned
   tree, so the exact vectors named here are retrievable.  CLC-A
   conformance is exercised by two machine-readable reference suites at
   capability/data/_vectors/clc-v1/: vectors.json — 123 vectors mapped
   to Appendix B — and property-cases.json — 1184 cases pinning the
   Section 7 meet-law, identifier narrowing and source-order
   independence.  Their syntax is defined by vectors.schema.json;
   offline-vectors.json is a timestamped snapshot mirror.  A conforming
   implementation MUST pass both suites.  CLC-D conformance (Section 13)
   is exercised by containment-vectors.json — 64 vectors mapped to
   Section 13 — together with containment-property-cases.json (784 cases
   pinning the Section 13.3–Section 13.7 narrowing laws).  containment-
   crosswalk-vectors.json carries 44 cross-vendor vectors that map six
   adjacent capability representations into CLC grants through pinned
   profiles and assert the containment verdict the unchanged relation
   reaches (Appendix C).

   *Genericity is exercised, not asserted.* crosswalk-vectors.json in
   the same directory carries 13 vectors in both directions: 5 that
   project a CLC decision into the members an AEB crossing record asks
   of a native source (the members CLC can establish, and the ones it
   explicitly does not), and 8 that map five foreign capability
   representations — OAuth RAR authorization_details, an AIC-JWT
   delegation authorization, an Action Evidence Graph capability class,

Wei                       Expires 31 March 2027                [Page 46]
Internet-Draft                   CLC-v1                   September 2026

   a UCAN {with, can} capability, and a delegation chain — into CLC
   grants through pinned cross-walk profiles and assert the decision the
   unchanged core reaches.  A profile is a few lines of mapping written
   by whoever owns the foreign format; the core is not modified for any
   of them.

   The evidence side ships evidence-vectors.json in the same directory —
   32 vectors covering the four evidence-side constraint types, their
   value-grammar rejections, requirement-expression binding, the closed
   requirement object, ActionId computation (Section 4.2: declared
   material projection, undeclared fields excluded, missing material
   field non-matchable, suite-tagged identifiers) and Match verdicts
   (Section 6.4: MATCH / NOT_EQUIVALENT / INDETERMINATE).  Its runner
   ships with the reference implementation (register), and its syntax
   mirrors vectors.json.

   Implementations MUST NOT: redefine semantics, accept v1-forbidden
   wildcards, or broaden bounds during canonicalization.

   *Independence of implementations (honest scope).* The three
   implementations named in this repository's README (Go, Python,
   TypeScript) are *not independent evidence*: they share an author, and
   their agreement is a regression test for the specification, not
   third-party validation.  An independent implementation is invited;
   until one exists, the parity claim in this document is scoped to
   "same-author, three languages, one corpus".  Reviewers SHOULD treat a
   single-author parity claim as evidence that the text is
   _implementable_, not that it has been independently _interpreted_.

   *Experimental neighbours are not CLC.* The WIT/WPT interop study in
   varwof/aic-jwt (wit-wpt-interop/) is an *experimental* research
   artifact that implements a _different_, wider wildcard surface (**,
   {a,b}, [a-z]) which this revision rejects as unsupported_wildcard
   (Section 9.2).  It is not a CLC-A implementation and MUST NOT be
   cited as one; it exists to study WIT/WPT provisioning and carries its
   own EXPERIMENTAL banner.

12.1.  Language Revision

   Every implementation declares a language revision CLC-<major>.<minor>
   — this document declares *CLC-1.15*.  A capability input (grant,
   operation, or OCM) SHOULD carry the revision it was authored against;
   an input without a declared revision is treated as CLC-1.0.

   *  *Compatible reading*: an implementation MAY evaluate an input
      whose declared major equals its own AND whose declared minor is ≤
      its own (so an implementation of CLC-1.3 reads a CLC-
      1.0/1.1/1.2/1.3 input, but not CLC-1.4 or CLC-2.0).

Wei                       Expires 31 March 2027                [Page 47]
Internet-Draft                   CLC-v1                   September 2026

   *  *CLC-1.2 is additive*: it adds the unresolved field to the
      Decision shape and the invalid_constraint reason code without
      changing v1 verdicts on existing inputs; a CLC-1.1 implementation
      MAY claim CLC-1.1 against this document but is not CLC-A
      conformant (Section 12) until it exposes unresolved and rejects
      non-conforming recognized-type values.

   *  *CLC-1.3 is additive, with one verdict value re-scoped*: it adds
      the allow_unresolved verdict value, closes the decision loop for
      residual obligations, and re-scopes allow to mean "fully enforced"
      only; allow/deny outputs on inputs with *no* residual obligations
      do not change.  A CLC-1.2 implementation MAY claim CLC-1.2 against
      this document but is not CLC-A conformant (Section 12) until it
      emits the allow_unresolved value and applies the Section 8.1
      (scheme,type) identity and cross-midnight window grammar.

   *  *CLC-1.9 is additive*: it adds the containment relation and
      conformance class CLC-D (Section 13) without changing any CLC-A
      verdict, reason code or vector.  A CLC-1.8 implementation that
      does not claim CLC-D MAY claim CLC-1.8 against this document and
      remains CLC-A conformant; an implementation that claims CLC-D MUST
      pass containment-vectors.json (Section 12).

   *  *CLC-1.10 is additive with a minor gate*: it adds the optional
      param_bounds field and its four reason codes (Section 6.5).  Every
      input that declares no param_bounds is unchanged, so a CLC-1.10
      implementation reads every CLC-1.x input.  An input that *uses*
      param_bounds MUST declare CLC-1.10 or later; an implementation
      that does not implement Section 6.5 MUST refuse such an input
      through the minor gate (unsupported_language_revision) and MUST
      NOT ignore the field — silently dropping bounds would grant more
      than the input declares (fail-closed).

   *  *CLC-1.11 is additive*: it adds the Resolve function (Section 8.5)
      and its two input-error reason codes (invalid_resolution,
      invalid_timestamp) without changing any Authorize verdict, reason
      code or vector.  A CLC-1.10 implementation that does not implement
      Resolve MAY claim CLC-1.10 against this document and remains CLC-A
      conformant; Resolve is exercised only when a consumer feeds a
      decision back, so no input shape is reinterpreted.  A time:window
      obligation is still delivered as allow_unresolved (Section 8.4) —
      the core clock evaluator of Section 8.5 runs only inside Resolve
      with a supplied now.

   *  *CLC-1.12 is additive*: it adds the derived function
      ConstraintUnion (Section 7.1) without changing any other relation,
      verdict, reason code or vector.  The function is exactly the
      constraint projection of Intersect rule 3, so Intersect's output

Wei                       Expires 31 March 2027                [Page 48]
Internet-Draft                   CLC-v1                   September 2026

      is unchanged; an implementation that does not implement
      ConstraintUnion MAY claim CLC-1.11 against this document and
      remains CLC-A conformant.

   *  *CLC-1.13 is additive and CLC-D-scoped*: it adds
      AuthorizeWithChain (Section 13.11), a CLC-D function that fixes
      the order of Contains and Authorize over an effective-chain
      Intersect.  It changes no CLC-A verdict, reason code or vector; an
      implementation claiming only CLC-A is unaffected, and a CLC-D
      implementation adds it to its CLC-1.9-era Contains surface.

   *  *CLC-1.14 is additive*: it defines the intersection of
      param_bounds (BoundMeet, Section 6.6) so that Intersect can
      combine a grant carrying the bounds Section 6.5 defines.  It
      changes no verdict, reason code or vector for an input without
      param_bounds; it changes the refusal of an input *with*
      param_bounds from invalid_params_binding to the correct meet or
      empty-meet result.  An implementation that does not implement
      Section 6.6 MUST refuse a param_bounds input through the minor
      gate rather than intersect it wrongly. *Honest scope of that
      change (noted in rev CLC-1.15).* One subset of inputs does *not*
      evaluate identically across the CLC-1.10→1.14 minor range: the
      inputs that declare param_bounds _and_ reach a relation that meets
      them — Intersect (Section 7) or AuthorizeWithChain step 3
      (Section 13.11) — with two or more sources declaring a Bound for
      the same key.  On that subset the direction of the change is *deny
      → allow*: what CLC-1.10–1.13 uniformly refused
      (invalid_params_binding) becomes the correct meet result (usually
      an allow-side effective grant); where the meet is empty, the
      denial's reason changes from invalid_params_binding to no_overlap.
      Entailment against a single grant (Entails/Authorize),
      containment, and every input without param_bounds are verdict-
      stable across the whole 1.x range.  The compatible-reading rule
      above is therefore intact — it governs _readability_, not verdict
      stability — but verdict stability across minor revisions does not
      hold for that one subset, and a consumer MUST NOT assume it.  (Rev
      CLC-1.15 moves the same subset again, in the opposite direction,
      for cross-family meets only; see its entry below.)

   *  *CLC-1.15 is a corrective revision* (review-driven; see the
      Revision History): the cross-family numeric ∩ enum meet exception
      of CLC-1.14 is removed, so every numeric × enum meet refuses with
      invalid_params_binding in either source order (Section 6.6); enum
      membership equal is defined as JSON type-sensitive equality
      (Section 6.5 layer 8); ConstraintUnion's deterministic ordering is
      pinned to UTF-8 byte order (Section 7.1);
      delegation_mode_not_narrower is attributed to the binding
      profile's mode pre-check and moved out of the core reason

Wei                       Expires 31 March 2027                [Page 49]
Internet-Draft                   CLC-v1                   September 2026

      commitments (Section 13.4.5, Section 13.5, Section 13.6,
      Section 13.11); Contains antisymmetry is stated on semantic
      equivalence classes (Section 13.3); and AuthorizeWithChain states
      the caller's complete-authenticated-root-first-chain obligation
      (Section 13.11).  It changes no verdict, reason code or vector for
      an input without param_bounds.  On the Section 6.6 meet subset the
      direction *partly reverses* CLC-1.14: a cross-family numeric ×
      enum intersection that CLC-1.14 reduced to a filtered enum — an
      allow-side effective grant — now refuses (*allow → deny*,
      invalid_params_binding), because the filtered enum was broader
      than either source (Section 6.6); same-family meets are unchanged.
      The same review adjudicated the scalar × nested pair (numeric or
      enum vs. object, either order) to the same cross-family refusal:
      that pair was already denied in CLC-1.14 (as no_overlap), so the
      direction there changes the *reason code only* (→
      invalid_params_binding), not the verdict (Section 6.6, design-
      notes D12).  The Section 8.4 residual-obligation list and the
      Resolve output re-use the Section 7.1 UTF-8 byte-order collation;
      an implementation whose native default ordering is UTF-16 code-
      unit order MUST apply the pinned comparison explicitly.  An
      implementation that does not apply this revision MUST declare CLC-
      1.14 or earlier and let the minor gate (Section 12.1) resolve any
      input that relies on the corrected behavior.

   *  *CLC-A conformance and the minor gate are the two sides of one
      rule.* Claiming CLC-A (this section) means implementing the CLC-
      A-relevant semantics of the revision claimed — so an
      implementation that advertises CLC-1.15 MUST implement
      param_bounds (grammar and the Section 6.6 meet), Resolve,
      ConstraintUnion and the Section 6.2 canonicalization, not merely
      tolerate their inputs.  The minor gate is the complement for an
      implementation that *lags*: it declares an older revision and
      refuses any input that uses a field or function introduced after
      it.  The two MUST agree — an implementation MUST NOT claim a
      revision higher than it implements (which would silently evaluate
      newer inputs), nor claim CLC-A while refusing a well-formed input
      of its own declared revision.

   *  *Incompatible reading MUST fail closed* with
      deny("unsupported_language_revision").  An implementation MUST NOT
      silently evaluate under a different revision — no downgrade, no
      warning-then-allow.

   *  The revision check resolves *before any Section 9.1 layer* and
      yields the single resolved reason code
      unsupported_language_revision.

Wei                       Expires 31 March 2027                [Page 50]
Internet-Draft                   CLC-v1                   September 2026

   Vectors: revision-001 (input CLC-1.0 against an implementation
   declaring CLC-1.3 → eval normally, allow); revision-002 (input CLC-
   2.0 → deny unsupported_language_revision).

13.  Delegation Containment

   This section defines *containment*, the third grant-level relation of
   CLC-v1 alongside entailment (Section 6.1) and intersection
   (Section 7), and the conformance class *CLC-D* that exercises it.  It
   is additive: it changes no CLC-A verdict, reason code or vector, and
   a CLC-A implementation that does not claim CLC-D is unaffected.  It
   was folded into this document from the formerly separate containment
   extension, which is retired.

13.1.  Motivation

   The two relations the core defines answer different questions:

   *  *Entailment* (Section 6.1): does an _operation_ fit inside a
      _grant_?

   *  *Intersection* (Section 7): what is the effective set when several
      grants cover one authority?

   Neither answers the question a delegation chain asks of every hop:
   *is the child's _declared_ grant inside the parent's _declared_
   grant?* Intersection delivers a set that is necessarily common to the
   sources, but a result fitted to two grants proves nothing that one of
   the grants' boundaries would not also authorize on its own — it
   reasons about the _combined set_, not about a _child_ that must be a
   subset of a _specific parent_.

   The AIC ecosystem needs the child-parent question at two concrete
   boundaries:

   1.  *Delegation* (DelegationAuthTBS vs. a principal's grant): a sub-
       agent's requested capabilities, constraints and delegation mode
       must lie inside what the principal authorized.  Without a shared
       relation this obligation is the _profile's_ job and is re-
       implemented per profile, so two profiles can differ on the same
       sub-agent grant.

   2.  *Publication bound* (rule signing): a rule a certificate holder
       publishes must not exceed the holder's own grant.  This section
       states the general relation; register/ruleexec already applies
       the same idea in one instance (RuleWithinSignerGrant), and is a
       candidate early adopter.

Wei                       Expires 31 March 2027                [Page 51]
Internet-Draft                   CLC-v1                   September 2026

   Containment is intentionally *additive, not a CLC-v2 feature*: it
   does not change any CLC-A verdict, does not touch the CLC-A corpus,
   and can be adopted by any implementation that claims CLC-A without
   breaking compatible reading of existing inputs.

13.2.  Terminology and Notation

   Grant and Operation are as defined in Section 2/Section 5 (identifier
   per Section 3, parameters per Section 6.2, constraints per
   Section 8).  Contains(GP, GC) names the relation parent GP × child
   GC.

   *  *Declared set* — the parameters a grant carries as constraints on
      operations (Section 6.2 value semantics: numbers bind as upper
      bounds, arrays as membership sets, objects recursively per key,
      explicit empty []/{} deny the class, {}≡absent).

   *  *Bound* — a declared constraint value as interpreted by Section 
      6.2/Section 8.1 value semantics.

   *  *Narrower* — a child grant is narrower than its parent when every
      value it declares is within the parent's declared bounds and its
      key set is closed by the parent's.  Where the carrier defines a
      delegation mode, the child's mode must not widen the parent's
      either — that half is the binding profile's pre-check, not part of
      the language relation (Section 13.4.5).

   *  *Mode lattice* — an abstract carrier-defined order over delegation
      modes, exercised by the binding profile's pre-check
      (Section 13.4.5), never by Contains; the AIC-JWT ordering is
      pinned in Section 13.8.

13.3.  Relation Signature

Contains(GP: Grant, GC: Grant) -> ContainmentResult

ContainmentResult = {        // JSON object (not ASN.1)
  "contains": <boolean>,     // false <=> one of the Section 13.4 layers failed
  "reason":   <string>       // resolved reason code (core Section 9.2 code)
}

   *  contains is true *only when* every layer of Section 13.4 passes.

   *  reason on success is empty; on failure it carries the *first
      failing layer's* reason code, per Section 13.4 layer order
      (deterministic: input order of the two grants never influences
      which layer reports first).

Wei                       Expires 31 March 2027                [Page 52]
Internet-Draft                   CLC-v1                   September 2026

   *  The relation is *antisymmetric on semantic equivalence classes*,
      not on Grant objects: Contains(A,B) and Contains(B,A) both hold
      exactly when A and B fall in the same class — they denote the same
      identifier coverage, the same declared parameter key set and the
      same bounds under the Section 6.2/Section 6.5 value semantics,
      with surface-equivalent spellings identified (params:{} ≡ absent,
      Section 6.2).  Two syntactically different Grant objects in one
      class (an equivalence, not an identity of JSON text) therefore
      contain each other; containment of grants in two _distinct_
      classes in both directions is a contradiction and MUST NOT be
      reported.  Delegation modes are not part of the relation
      (Section 13.4.5), so mode equality is neither required nor
      observable here — where a carrier binds modes, the profile's pre-
      check owns that comparison.

13.4.  Containment Algorithm

   The relation resolves layers strictly in order; the first failure
   determines the reason code.  Every layer is fail-closed: any doubt
   yields false.

13.4.1.  Layer 1: Grant validity

   *  If a grant identifier is malformed per Section 3, Contains returns
      false with the matching CLC-A syntax code (invalid_capability_id /
      missing_capability_id).  Validation errors are reused from the
      core, not re-invented.

   *  If GC.Params violates the grant-side parameter grammar
      (Section 6.2), the child is not a valid grant and Contains yields
      false (invalid_params_<n> codes as in the core).

   *  An invalid parent grant also yields false: a boundary that cannot
      itself be evaluated must not authorize a child.

13.4.2.  Layer 2: Identifier coverage

   The child identifier must be covered by the parent identifier using
   *exactly the CLC-v1 path-coverage relation* (the rules Entails
   applies to identifiers, Section 6.1/Section 6.3, with parameters
   excluded — the identical keyset empty-object edge is not relevant
   here because parameters are excluded from this layer by
   construction):

   *  same namespace (scheme:action-class) — else different_namespace;

Wei                       Expires 31 March 2027                [Page 53]
Internet-Draft                   CLC-v1                   September 2026

   *  same segment depth with equal literal segments, OR parent trailing
      * wildcard covering the child's trailing segments ("*" matches one
      or more segments, never zero) — else child_exceeds_parent.

   Mid-identifier wildcards remain unsupported_wildcard per Section 3:
   this section does not enlarge the v1 wildcard surface.

13.4.3.  Layer 3: Parameter narrowing

   Every parameter the child declares must be _within_ the parent's
   declared bounds, using the Section 6.2 value-subset semantics, *and
   the key sets must be identical* (symmetric closure).  The child and
   parent key sets are each the union of params and param_bounds keys
   (Section 6.5):

   *  *number (from params)*: child value ≤ parent bound (upper bound
      only in params; richer bounds are expressed in param_bounds,
      below);

   *  *string / boolean*: exact equality with the parent's value;

   *  *array (enum)*: every child element must equal a member of the
      parent's set; a child empty array inside a non-empty parent enum
      is vacuously within it (child denotes "nothing", which is inside
      anything) — but a *parent* empty set denies the class (deny-when-
      declared), and a child empty set under a parent empty set is
      therefore false (params_not_narrower): the class is denied, not
      "narrowed to nothing";

   *  *object*: recursion per shared key, with the same symmetric key
      closure at every depth (a child object may not add a key the
      parent object does not declare, and may not omit one the parent
      declares);

Wei                       Expires 31 March 2027                [Page 54]
Internet-Draft                   CLC-v1                   September 2026

   *  *param_bounds bound (Section 6.5)*: for a key with a Bound on the
      parent's side, the child's Bound for that key must be within it —
      a numeric family bound must have min raised or equal and max
      lowered or equal (min_child ≥ min_parent / max_child ≤ max_parent,
      and the child MUST declare a min/max the parent declares), a step
      must be an integer multiple of the parent's step (a coarser-or-
      equal grid whose values are a subset of the parent's allowed
      values), an enum family must be a subset with min_items/max_items
      tightened or equal, and a nested bound recurses.  A parent key
      that is required (optional absent or false) forces the child's key
      to be required; a child MAY keep an optional key optional or make
      it required, but MUST NOT turn a required parent key optional.  A
      child Bound that adds a family the parent does not declare, or
      omits a bound the parent declares, is params_not_narrower (the
      child would allow a value the parent denies);

   *  *key closure*: the child key set MUST equal the parent key set,
      both ways.  A child that omits a parent-declared key would allow
      operations the parent denies (the missing key is params_missing in
      entailment); a child that adds a key the parent does not declare
      allows operations the parent denies (undeclared_param).  Either
      failure is params_not_narrower.  This is the same symmetric
      closure Section 6.2 layer 7 applies to an operation vs. a grant,
      carried over to grant-vs-grant comparison;

   *  *extra rule*: the parent's {} (or absent) params object is
      unconstrained and contains any child params; a child {} under a
      _bounded_ parent is false (params_not_narrower) — declaring
      nothing is not the same as declaring a subset of the parent's
      bounds.

   The presence semantics match Section 6.2 exactly: a grant (parent or
   child) whose params are present-but-empty {} is unconstrained,
   identical to an absent params object (Section 9.2/Section 6.2).

13.4.4.  Constraints are outside the relation

   Constraints are *not* part of Contains.  A grant carries constraints
   (Section 8), but they are a separate axis with a different
   composition rule:

   *  *Constraints compose by union (conjunction), not by subset.* Along
      a delegation chain the effective constraint set is the _union_ of
      every link's constraints (Section 7 Intersect already unions
      them).  A child therefore need not re-declare its parent's
      constraints, and adding or tightening a constraint only narrows.

Wei                       Expires 31 March 2027                [Page 55]
Internet-Draft                   CLC-v1                   September 2026

   *  *Contains compares identifier and parameters only.* It does not
      read, compare, or validate the constraints field: a constraint
      difference never changes the Contains verdict.

   *  *Constraint grammar and evaluation stay where they already are.*
      Whether a constraint identity is recognized, whether its value is
      in grammar, and whether an operation satisfies it are the CLC-A
      concern of the consumer and Intersect/the decision function
      (including the allow_unresolved residual-obligation channel,
      Section 8.4).  This section neither re-decides nor weakens them.

   Consequences a reviewer should take as intended: Contains(P, C) does
   *not* by itself assert that C's operation set is inside P's when
   constraints are in play — the parent's constraints are carried
   forward by the chain's union (Intersect), and a consumer that uses
   Contains as its _only_ gate must compose the chain's intersections as
   well (Section 13.8).  This is the honest reading: containment is a
   relation over the declared *identifier and parameter* boundary, and
   constraints are enforced by the union, not by this relation.

13.4.5.  Delegation-mode lattice (binding-profile pre-check)

   Where a carrier defines a delegation mode, the child's mode must not
   widen the parent's.  This check is a *required binding-profile pre-
   check*, not a layer of the language relation: as with constraints
   (Section 13.4.4), mode is a carrier-level concept — Grant values
   carry no mode, and the relation Contains(GP, GC) takes no mode
   argument, so a core Contains verdict can never express a mode
   decision.  A binding profile (Section 13.8.1) that maps a mode-
   carrying carrier MUST run the mode-lattice check itself, before or
   alongside each Contains call, and MUST report
   delegation_mode_not_narrower *from the profile* when the child's mode
   widens the parent's; it MUST NOT rely on Contains for that check and
   MUST NOT present a Contains verdict as evidence that the mode
   narrowed.  The lattice order is carrier-defined (CLC-D does not
   invent modes); the AIC-JWT order is authorized < representative — a
   child may be authorized under a representative parent, never the
   reverse.  A carrier without a mode concept has no pre-check to run.

13.5.  Reason Codes

   CLC-D registers exactly three child-level reason codes; *two of them
   are returned by the relation*, and everything else a Contains result
   carries reuses CLC-A codes.  The third is produced only by the
   binding-profile delegation-mode pre-check (Section 13.4.5): the
   relation takes no mode argument and MUST NOT return it.  An
   implementation MAY collapse child_exceeds_parent for identifier
   failures into the core's capability_not_authorized at a boundary that

Wei                       Expires 31 March 2027                [Page 56]
Internet-Draft                   CLC-v1                   September 2026

   must not reveal policy shape (Section 11), but MUST NOT collapse
   params_not_narrower; a profile that maps a mode-carrying carrier MUST
   NOT collapse delegation_mode_not_narrower either.

   +==============================+=================+=================+
   | Code                         | Produced by     | Meaning         |
   +==============================+=================+=================+
   | child_exceeds_parent         | Contains, layer | child           |
   |                              | 2 (Section      | identifier not  |
   |                              | 13.4.2)         | covered by      |
   |                              |                 | parent          |
   |                              |                 | identifier      |
   +------------------------------+-----------------+-----------------+
   | params_not_narrower          | Contains, layer | a child         |
   |                              | 3 (Section      | parameter is    |
   |                              | 13.4.3)         | not within the  |
   |                              |                 | parent's        |
   |                              |                 | declared bounds |
   |                              |                 | / key set       |
   +------------------------------+-----------------+-----------------+
   | delegation_mode_not_narrower | binding-profile | child           |
   |                              | pre-check       | delegation mode |
   |                              | (Section        | (a carrier      |
   |                              | 13.4.5) — never | concept) widens |
   |                              | by Contains     | the parent's    |
   +------------------------------+-----------------+-----------------+

                                 Table 11

   There is deliberately no constraint reason code: constraints are not
   part of the relation (Section 13.4.4).

13.6.  Conformance Class CLC-D

   *CLC-D* is an optional conformance class stacked on CLC-A.  A
   conforming implementation:

   *  MUST implement Contains (Section 13.4) and the relation's two
      Section 13.5 reason codes, and pass the CLC-D corpus
      (Section 13.7); where the implementation also ships a binding
      profile for a mode-carrying carrier, that profile owns the
      delegation_mode_not_narrower pre-check (Section 13.4.5);

   *  MUST (rev CLC-1.13) implement AuthorizeWithChain (Section 13.11)
      and pass the authorize-chain-vectors.json corpus, so the one-call
      chain check is exercised by the same class that owns containment;

Wei                       Expires 31 March 2027                [Page 57]
Internet-Draft                   CLC-v1                   September 2026

   *  MUST NOT alter any CLC-A verdict, reason code, or the CLC-A corpus
      — this section is strictly additive;

   *  MUST treat containment as *declared-set comparison*: it declares
      no consequence about execution lifecycle, time windows after the
      fact, or post-hoc bounds; where a binding profile cannot establish
      containment at delegation time, the profile MUST NOT authorize.

   An implementation claiming CLC-A does *not* claim CLC-D.  A
   delegation profile (a binding profile (Section 13.8), an AIC-JWT DA
   validator, a certificate-issuance stack) that needs the boundary
   check SHOULD require CLC-D of the module it delegates to, and MUST
   NOT substitute intersection (Section 7) for containment.

   *Implementation status (single-author parity).* Contains is
   implemented in all three reference implementations (Go: register/
   semantics.Contains; Python: aic-capability-demo/clc_semantics.py
   contains; TypeScript: ts/clc_semantics.ts contains) and mirrors the
   cases this section pins.  Agreement between implementations that
   share an author is a regression test for this text, not independent
   validation — the Section 12 honesty rule applies unchanged to CLC-D
   (see Section 13.7).

13.7.  Corpus

   Because CLC-D is a new relation, its corpus is new and independent of
   the CLC-A suites (vectors.json / property-cases.json / crosswalk-
   vectors.json / evidence-vectors.json).  The corpus ships as
   capability/data/_vectors/clc-d/containment-vectors.json (*64*
   vectors) and a forward-closure property file
   capability/data/_vectors/clc-d/containment-property-cases.json (*784*
   cases × 39 shared operations, generated by capability/scripts/gen-
   contain-property-cases.py), plus a cross-walk corpus
   capability/data/_vectors/clc-d/containment-crosswalk-vectors.json
   (*44* vectors) that maps a carrier's native representation (AIC-JWT
   DA, OAuth RAR, UCAN, delegation chain, and the adjacent agent drafts
   ATN, AAT, AIP, AAE, AOA, AEGIS) to a grant on each side and asserts
   Contains (Section 13.9.3).  Rev CLC-1.13 adds
   capability/data/_vectors/clc-d/authorize-chain-vectors.json (*15*
   vectors) pinning the fused AuthorizeWithChain relation
   (Section 13.11).  The vectors cover:

   *  *identifier coverage* (layer 2): trailing-wildcard coverage,
      namespace mismatch, depth mismatch, same-length literal mismatch,
      unsupported_wildcard kept stable, wildcard-requires-a-trailing-
      segment;

Wei                       Expires 31 March 2027                [Page 58]
Internet-Draft                   CLC-v1                   September 2026

   *  *parameter narrowing* (layer 3): number upper bound at/under/over,
      enum subset / element-absent / scalar-member, empty child enum
      under non-empty parent, parent empty set deny-class, key-closure
      violation in both directions (child omits, child adds), nested
      object recursion and nested key closure, child {} under bounded
      parent, child null (layer-1 grammar), unconstrained parent {};

   *  *constraint non-participation* (Section 13.4.4): a tighter, wider,
      added, dropped, or differently-identified child constraint, and a
      child constraint the parent lacks — every one MUST leave the
      verdict unchanged (all assert contains);

   *  *validity* (layer 1): malformed parent/child identifiers, null
      params;

   *  *symmetry*: both-directions containment identity, antisymmetry
      probe pair (contain-040/041), a fully-narrower combined grant.

   The *delegation-mode lattice* is _not_ part of the language-level
   corpus: the relation Contains(GP, GC) takes no mode, so a profile
   that binds a carrier mode (the AIC-JWT DA validator, Section 13.8)
   exercises it itself.  The language corpus therefore ships no mode
   vectors; a carrier adopting CLC-D MUST add mode vectors to its own
   profile corpus.

   The *property* the property corpus checks is _forward closure_: for
   every (parent P, child C) and every operation o in the shared sample,
   Contains(P, C) MUST NOT raise, and Contains(P, C) ∧ Entails(C, o) ⟹
   Entails(P, o).  This is the declared-set consequence that makes a
   delegation boundary sound, and the reason the Section 13.5 collapse
   of layer-2 failures into child_exceeds_parent cannot weaken the
   boundary.  On the shipped corpus the Go and Python/TS runs report the
   same 120 contained pairs and 30576 operation checks with zero
   violations.

   The parity bar and the "independence of implementations" honesty rule
   of Section 12 apply unchanged to CLC-D: until two _independent_
   implementations agree, the corpus is evidence that this text is
   implementable, not that it has been independently interpreted.

13.8.  AIC-JWT / AIC Certificate Binding

   This subsection is a _cross-walk profile of the relation_, not part
   of the language.  At the AIC delegation boundary the parent grant is
   produced from the principal's authorization and the child grant from
   the DelegationAuthTBS/AIC-JWT DA capabilities:

Wei                       Expires 31 March 2027                [Page 59]
Internet-Draft                   CLC-v1                   September 2026

   *  *Parent grant* GP: the principal's capability entry (scheme:id
      params), plus principal-level authorizationConstraints projected
      as parent constraints, plus the principal's own delegation mode.

   *  *Child grant* GC: the sub-agent's requested capability
      (Capability.SchemeId:CapabilityId, Parameters), its
      authorizationConstraints, and its DelegationMode.

   *  *Boundary result*: P_effective = P_principal ∩ C_agent ∩ P_gateway
      keeps its intersection meaning — intersection determines the
      _effective_ set; containment (Section 13.4) is the _per-child_
      admission predicate that runs before the intersection is composed,
      on each (parent-capability, child-capability) pair.  The child's
      constraints are *not* compared by containment; they are carried
      forward by the intersection's union (Section 13.4.4), so the
      principal's constraints remain in force in P_effective.

   *  A sub-agent that requests a capability the principal did not grant
      fails at layer 2 (child_exceeds_parent); a sub-agent whose
      requested params exceed the principal's declared bounds fails at
      layer 3; a sub-agent that widens the carrier's delegation mode
      fails the lattice (Section 13.4.5).  Binding profiles MUST surface
      the reason code into their audit trail, and MUST compose the
      intersection so the principal's constraints stay in force.

13.8.1.  Profile contract (for any binding profile)

   A *binding profile* maps a carrier's native authorization structure
   to the CLC grants Contains compares (Section 13.9.3).  The profile is
   the carrier's, not the language's (a carrier's field names appear in
   its own profile, never in the language text).  Because every
   Section 13.9.3 mapping is a profile, this subsection states the
   obligations a conforming profile carries.  It adds no CLC-A or CLC-D
   verdict and changes no relation.

   1.  *Resolve the carrier's own inheritance and defaults before
       mapping.* The language has no inheritance, no default values and
       no "absent means inherit" rule (Section 13.10.1).  A carrier that
       resolves an absent dimension against an ancestor (AIP
       Section 4.4) MUST do so in the profile and emit the _resolved_
       declared set.  Contains is defined over the grants the profile
       emits, so an unresolved default is a profile defect, not a
       containment result.

   2.  *Preserve every identity dimension the carrier treats as
       identity.* If the carrier treats two artifacts with the same name
       but different metadata as distinct capabilities (ATN Section 9.1:
       same id, different schema.digest), the profile MUST carry that

Wei                       Expires 31 March 2027                [Page 60]
Internet-Draft                   CLC-v1                   September 2026

       metadata into the mapped grant — here, a trailing identifier
       segment — so a mismatch fails closed.  A profile MUST NOT drop an
       identity dimension and then compare the artifacts as equal.

   3.  *Residualize semantics the relation cannot see; never drop them
       silently.* Dimensions the mapped grant has no field for (AEGIS
       allowed_roles, environment, risk_level; AAE validity) are not
       compared by Contains.  The profile either models them in its own
       document and enforces them outside the relation, or declares them
       out of scope — but it MUST NOT report containment as if they had
       been checked (fail-closed: what the relation cannot see, it does
       not permit).

   4.  *Do not invent carrier semantics the carrier does not state.* A
       profile must not widen what the carrier leaves undefined into an
       allow.  AEGIS's dotted ids are hierarchical, but AEGIS grants
       capabilities individually and does not define domain-level
       containment, so the profile does not turn a bare domain into a
       namespace wildcard (ccx-042 fails closed).

   5.  *Non-goals (recorded, not prohibitions on carriers).* Three
       properties are deliberately outside both the relation and the
       profile contract:

       *  *union of authority sources* — a sub-agent that combines
          narrow delegated authority with broad independent authority
          (AEGIS Section 5.1, AOA) composes over a _set_ of grants,
          which is carrier composition governance, not a per-pair
          predicate;

       *  *chain-level verification* — Contains is a per-pair predicate,
          not a transitive closure; a chain check is the profile's
          iteration of Contains over hops (Section 13.9.3, delegation-
          chain->clc-v1);

       *  *cross-carrier identity* — CLC defines no equivalence between
          two carriers' capability names; a profile MAY publish its own
          mapping, but the language asserts none.

13.9.  Related Work and Positioning

   This subsection is *informative* and carries no requirements.  It
   records why a language-level containment relation is needed at all,
   how CLC relates to the authorization work already in flight, and what
   the adoption risks are.

Wei                       Expires 31 March 2027                [Page 61]
Internet-Draft                   CLC-v1                   September 2026

13.9.1.  The gap CLC fills

   CLC *does not define a carrier* — it is not a credential format, a
   transport, or a policy store.  It defines the _evaluation semantics_
   that carrier-facing work leaves open: a deterministic decision,
   stable reason codes, and (here) a containment relation.  The
   recurring shortfall in today's landscape is not "how do I carry an
   authorization" but "how do two independent implementations agree on
   what a carried authorization _means_, and on whether a delegated one
   stayed inside its parent".

13.9.2.  What containment is for

   1.  *A standardizable evaluation core.* Structured authorization
       payloads exist, but their semantics are pinned by each profile's
       own type vocabulary, so cross-implementation agreement is not
       guaranteed.  CLC supplies the deterministic decision function and
       the stable reason-code registry those profiles can reference
       instead of re-inventing.

   2.  *Verifiable attenuation.* A delegation chain must only narrow.
       Declaring attenuation is easy; _verifying_ it at the receiving
       endpoint requires a shared relation.  Contains (Section 13.4)
       plus its reason codes (child_exceeds_parent et al., Section 13.5)
       turn "only narrows" from an application-layer assumption into a
       checkable language-level fact.

   3.  *One model for authorization and evidence.* The core deliberately
       uses the same identifier and constraint model on the
       authorization side (the grant) and, prospectively, on the
       evidence side (the observed action), so an audit trail can be
       reasoned about with the same relation that authorized it.

13.9.3.  How CLC relates to adjacent work

   The comparison below is by role, not by feature count: the other
   works are carriers, profiles, or architecture, whereas CLC is the
   semantic layer.

   Two principles keep the relationship complementary rather than
   competitive:

   1.  *CLC defines evaluation, not carriage.* CLC does not define token
       formats, trust models, or policy languages.  The mapping from a
       carrier's native authorization structure to a CLC grant is the
       *carrier profile's responsibility*; CLC only fixes what the
       mapped grant then _means_.

Wei                       Expires 31 March 2027                [Page 62]
Internet-Draft                   CLC-v1                   September 2026

   2.  *The core already ships consumption mappings.* Appendix A
       ("Consumption Mapping") lists example consumers (AIC-JWT DA, RAR
       authorization_details, EMILIA AEB, delegation chains), and
       capability/data/_vectors/clc-v1/crosswalk-vectors.json pins
       executable cross-walk cases for the oauth-rar->clc-v1, aic-jwt-
       da->clc-v1, emilia-aeg->clc-v1, ucan->clc-v1, and delegation-
       chain->clc-v1 profiles.  A carrier adopting CLC-D adds a
       containment case to that crosswalk rather than a new mapping
       mechanism.  CLC-D ships its own such corpus:
       capability/data/_vectors/clc-d/containment-crosswalk-vectors.json
       maps a native representation (AIC-JWT DA, OAuth RAR, UCAN,
       delegation chain, and the adjacent drafts ATN, AAT, AIP, AAE,
       AOA, AEGIS) to a grant on each side and asserts Contains — the
       attenuation analogue of the CLC-A cross-walk.

   The mappings that are already pinned, and the ones a carrier would
   add, are:

   +=====================+===================================+=====================================+============+
   |Carrier / native form|Maps to CLC                        |Relation                             |Status      |
   +=====================+===================================+=====================================+============+
   |AIC-JWT DA           |one Grant per entry                |Entailment; CLC-D Contains for the   |pinned (aic-|
   |capability[] ({id,   |                                   |C_agent ⊆ P_grants boundary          |jwt-da->clc-|
   |params, constraints})|                                   |                                     |v1, ccx-    |
   |                     |                                   |                                     |001..005,   |
   |                     |                                   |                                     |ccx-013)    |
   +---------------------+-----------------------------------+-------------------------------------+------------+
   |OAuth RAR            |one Grant per action (type:action) |Entailment                           |pinned      |
   |authorization_details|                                   |                                     |(oauth-rar- |
   |({type, actions[],   |                                   |                                     |>clc-v1,    |
   |params})             |                                   |                                     |ccx-        |
   |                     |                                   |                                     |006..007)   |
   +---------------------+-----------------------------------+-------------------------------------+------------+
   |UCAN {with, can}     |Grant {with:can}                   |Entailment                           |pinned      |
   |                     |                                   |                                     |(ucan->clc- |
   |                     |                                   |                                     |v1, ccx-    |
   |                     |                                   |                                     |008..009)   |
   +---------------------+-----------------------------------+-------------------------------------+------------+
   |EMILIA AEG           |Grant {capability_class}           |Entailment                           |pinned      |
   |capability_class     |                                   |                                     |(emilia-aeg-|
   |                     |                                   |                                     |>clc-v1)    |
   +---------------------+-----------------------------------+-------------------------------------+------------+
   |Delegation chain     |Intersect of the links             |Intersection; CLC-D Contains per hop |pinned      |
   |(per-hop granted     |                                   |for attenuation                      |(delegation-|
   |sets)                |                                   |                                     |chain->clc- |
   |                     |                                   |                                     |v1, ccx-    |
   |                     |                                   |                                     |010..012)   |
   +---------------------+-----------------------------------+-------------------------------------+------------+

Wei                       Expires 31 March 2027                [Page 63]
Internet-Draft                   CLC-v1                   September 2026

   |ATN Capability       |one Grant per capability: atn/     |CLC-D Contains is the per-capability |pinned (atn-|
   |Manifest (draft-     |manifest-                          |form of ATN's intersection; ATN's    |manifest-   |
   |somoza-dmsc-atn-     |v1:<id>:<action>[:<schema_digest>];|identity rule (same id *and* same    |>clc-v1,    |
   |agent-trust-         |resource_bounds and numeric        |schema digest) is carried by the     |ccx-        |
   |negotiation Section  |conditions → params                |digest path segment; preconditions   |014..017,   |
   |5.1/Section 9.1/     |                                   |are a *union* axis (Section 9.2),    |ccx-        |
   |Section 9.2)         |                                   |never compared                       |043..044)   |
   +---------------------+-----------------------------------+-------------------------------------+------------+
   |Attenuating Agent    |one Grant per tool: aat/toolset-   |CLC-D Contains for tools(derived) ⊆  |pinned (aat-|
   |Tokens I4 (draft-    |v1:<tool>; the argument-constraint |tools(parent) and the per-type       |i4->clc-v1, |
   |niyikiza-oauth-      |map → params                       |constraint subsumption               |ccx-        |
   |attenuating-agent-   |                                   |                                     |018..023)   |
   |tokens Section 4.5)  |                                   |                                     |            |
   +---------------------+-----------------------------------+-------------------------------------+------------+
   |AIP attenuation walk |Grant aip/scope-v1:<capability>;   |CLC-D Contains for the per-dimension |pinned (aip-|
   |(draft-prakash-aip   |budget ceiling → params            |narrower-or-equal walk (scope,       |attenuation-|
   |Section 4.4)         |                                   |budget)                              |>clc-v1,    |
   |                     |                                   |                                     |ccx-        |
   |                     |                                   |                                     |024..029)   |
   +---------------------+-----------------------------------+-------------------------------------+------------+
   |AAE mandates and     |Grant aae/mandate-v1:<action>;     |CLC-D Contains for AAE's per-element |pinned (aae-|
   |CONSTRAINTS (draft-  |unwrapped CONSTRAINTS values →     |"equal to or more restrictive"       |constraint- |
   |kroehl-agentic-trust-|params                             |                                     |>clc-v1,    |
   |aae Section 2.3/     |                                   |                                     |ccx-        |
   |Section 3)           |                                   |                                     |030..034)   |
   +---------------------+-----------------------------------+-------------------------------------+------------+
   |AOA operation scope  |Grant aoa/scope-v1:<operation>     |CLC-D Contains for the "scope string |pinned (aoa-|
   |(draft-liu-agent-    |                                   |containment" the AS validates        |scope->clc- |
   |operation-           |                                   |                                     |v1, ccx-    |
   |authorization        |                                   |                                     |035..037)   |
   |Section 6.2)         |                                   |                                     |            |
   +---------------------+-----------------------------------+-------------------------------------+------------+
   |AEGIS AIAM-1         |Grant aegis/action-                |CLC-D Contains for monotonic         |pinned      |
   |delegation (aegis-   |v1:<domain>:<operation>; numeric   |authority narrowing;                 |(aegis-     |
   |initiative/aegis-    |context bounds → params            |allowed_roles/environment/risk_level/|delegation- |
   |governance, AIAM1-   |                                   |scope have no CLC field and are left |>clc-v1,    |
   |DEL-010)             |                                   |to the carrier, not compared (see    |ccx-        |
   |                     |                                   |Appendix C)                          |038..042)   |
   +---------------------+-----------------------------------+-------------------------------------+------------+
   |W3C ZCAPs capability |Grant (delegation schema/type → id;|Entailment; CLC-D Contains for       |profile to  |
   |document             |caveats → params/constraints)      |chained delegation                   |be written  |
   |                     |                                   |                                     |by the ZCAPs|
   |                     |                                   |                                     |adopter     |
   +---------------------+-----------------------------------+-------------------------------------+------------+

                                  Table 12

Wei                       Expires 31 March 2027                [Page 64]
Internet-Draft                   CLC-v1                   September 2026

   Two mapping notes the pinned profiles make explicit, because they are
   places a reader could mistake a carrier's rule for the relation
   itself:

   1.  *A carrier's own "contains"/"subset" may name a _constraint
       type_, not the capability relation.* AAT Section 4.5 defines
       argument-constraint types named contains and subset (a required-
       set superset and an allowed-set subset).  Those are values
       _inside_ params; the capability-level relation is still Contains,
       and the constraint axes compose by union across a chain.  The
       same applies to AAE's allowed_domains and AEGIS's context bounds.

   2.  *A profile resolves a carrier's inheritance and defaults before
       mapping, and must not invent semantics the carrier does not
       state.* AIP resolves an absent dimension to its nearest ancestor
       (Section 4.4), so the profile maps an absent dimension to _no
       bound_, not to a bound.  AEGIS's dotted ids are hierarchical, but
       AEGIS grants capabilities individually and does not define
       domain-level containment, so the profile does *not* widen a bare
       domain into a namespace wildcard (ccx-042 fails closed).
       Contains is defined over the _declared_ sets the profile emits:
       the profile carries the carrier's resolution, and what the
       carrier leaves undefined the profile leaves undefined — it does
       not fill the gap with an allow.  The full contract is
       Section 13.8.1.

   The rows marked _profile to be written_ are placeholders: this
   document does not guess another draft's field grammar.  A carrier
   profile lands as a MapProfile case plus cross-walk vectors, exactly
   as the pinned profiles did.

   +==============+============================+=======================+
   |Work          | Role                       | Relationship to CLC   |
   +==============+============================+=======================+
   |OAuth Rich    | Structured                 | RAR defines the       |
   |Authorization | authorization_details      | _carriage_ of         |
   |Requests (RAR)| carried in the             | structured            |
   |              | authorization request      | authorization; CLC    |
   |              |                            | is a candidate        |
   |              |                            | _evaluation           |
   |              |                            | language_ for the     |
   |              |                            | capabilities RAR      |
   |              |                            | declares.             |
   |              |                            | Attenuating-agent-    |
   |              |                            | token work observes   |
   |              |                            | that RAR expresses a  |
   |              |                            | request but does not  |
   |              |                            | itself define how a   |

Wei                       Expires 31 March 2027                [Page 65]
Internet-Draft                   CLC-v1                   September 2026

   |              |                            | holder derives or     |
   |              |                            | verifies a            |
   |              |                            | _narrower_ token —    |
   |              |                            | the layer Contains    |
   |              |                            | addresses             |
   +--------------+----------------------------+-----------------------+
   |OpenID Connect| Agent capability claims in | A profile could       |
   |agent-identity| an ID Token                | define how OIDC-      |
   |claims        |                            | delivered             |
   |              |                            | capabilities are      |
   |              |                            | evaluated and how a   |
   |              |                            | delegation chain is   |
   |              |                            | containment-checked   |
   +--------------+----------------------------+-----------------------+
   |WIMSE AI      | Informational best-        | CLC is a candidate    |
   |Identity      | practice framework reusing | concrete evaluation   |
   |Management    | WIMSE and OAuth;           | language for the      |
   |System (draft-| explicitly identifies gaps | authorization step    |
   |ietf-wimse-   | rather than defining a     | AIMS describes and a  |
   |aims)         | capability algebra         | candidate answer to   |
   |              |                            | the "capability       |
   |              |                            | containment" gap it   |
   |              |                            | leaves open; the two  |
   |              |                            | are complementary,    |
   |              |                            | not competing         |
   +--------------+----------------------------+-----------------------+
   |WIMSE agent   | Token format, chain        | A carrier-level peer  |
   |delegation    | linkage (par_hash), a      | that defines _how_ a  |
   |chain ([ASOR],| scope/constraint           | narrower token is     |
   |draft-asor-   | vocabulary with            | carried and           |
   |wimse-agent-  | subsumption rules, and an  | verified; CLC is the  |
   |delegation-   | 8-step offline             | carrier-neutral       |
   |chain)        | verification algorithm     | semantics for the     |
   |              | carried in an RFC 9068 JWT | per-hop subset        |
   |              | profile                    | judgement such a      |
   |              |                            | verifier performs.    |
   |              |                            | Contains is the       |
   |              |                            | relation its          |
   |              |                            | DT(child) ⊆           |
   |              |                            | DT(parent) check      |
   |              |                            | needs, and its        |
   |              |                            | scopes/constraints    |
   |              |                            | map onto CLC's        |
   |              |                            | Grant/params/         |
   |              |                            | constraints; the two  |
   |              |                            | are complementary,    |
   |              |                            | not competing (the    |
   |              |                            | AAT row makes the     |

Wei                       Expires 31 March 2027                [Page 66]
Internet-Draft                   CLC-v1                   September 2026

   |              |                            | same split one layer  |
   |              |                            | up)                   |
   +--------------+----------------------------+-----------------------+
   |W3C ZCAPs     | Linked-Data-Proof signed   | ZCAPs defines the     |
   |              | capability documents with  | document/carrier;     |
   |              | caveats and chaining       | CLC can define the    |
   |              |                            | containment relation  |
   |              |                            | over its              |
   |              |                            | capabilities          |
   +--------------+----------------------------+-----------------------+
   |UCAN          | DID/IPLD authorization     | Same split: UCAN is   |
   |              | tokens with delegation and | the carrier, CLC the  |
   |              | attenuation                | semantics             |
   +--------------+----------------------------+-----------------------+
   |AEGIS         | Hierarchical dotted        | Closest in _goal_     |
   |capability    | capability registry, per-  | (capability           |
   |registry and  | grant scope/constraints, a | declaration +         |
   |AIAM-1        | deterministic decision     | deterministic         |
   |delegation    | algorithm with verdicts    | evaluation +          |
   |([AEGIS],     | (allow/constrain/escalate/ | narrowing); differs   |
   |aegis-        | deny), and monotonic       | in _form_ — AEGIS is  |
   |initiative/   | authority narrowing        | a governance          |
   |aegis-        | (AIAM1-DEL-010);           | architecture with a   |
   |governance)   | composition is explicitly  | policy/registry       |
   |              | _not_ closed under         | layer, CLC a          |
   |              | transitivity (AIAM1-CAP-   | carrier-neutral       |
   |              | 011)                       | decision function     |
   |              |                            | over grants.          |
   |              |                            | Contains is the       |
   |              |                            | relation AEGIS's      |
   |              |                            | monotonic-narrowing   |
   |              |                            | check needs; AEGIS's  |
   |              |                            | non-transitive        |
   |              |                            | composition rule is   |
   |              |                            | compatible (CLC-D     |
   |              |                            | Contains is also      |
   |              |                            | non-transitive: it    |
   |              |                            | is a per-pair         |
   |              |                            | predicate, not a      |
   |              |                            | closure)              |
   +--------------+----------------------------+-----------------------+
   |Agent Identity| Delegation-chain token     | Overlaps CLC's        |
   |Protocol      | with a Datalog policy      | entailment/           |
   |([AIP], draft-| layer and a structural     | intersection          |
   |prakash-aip)  | attenuation walk (V4) over | _functionally_, but   |
   |              | scope, budget, time,       | pins a Datalog        |
   |              | domains, principal         | policy language.      |
   |              |                            | AIP Section 4.4       |

Wei                       Expires 31 March 2027                [Page 67]
Internet-Draft                   CLC-v1                   September 2026

   |              |                            | makes the same        |
   |              |                            | distinction CLC-D     |
   |              |                            | does — attenuation    |
   |              |                            | is a property of      |
   |              |                            | capability content,   |
   |              |                            | not of the append-    |
   |              |                            | only container — so   |
   |              |                            | CLC can be the        |
   |              |                            | shared deterministic  |
   |              |                            | semantics such a      |
   |              |                            | checker is validated  |
   |              |                            | against               |
   +--------------+----------------------------+-----------------------+
   |Agent Trust   | Capability Manifest JSON   | Overlaps the          |
   |Negotiation   | with schema binding,       | capability-container  |
   |([ATN], draft-| dimension semantics, and a | target and defines    |
   |somoza-dmsc-  | Capability Intersection    | an intersection;      |
   |atn-agent-    | Algebra (Section 9) with   | CLC-D supplies the    |
   |trust-        | per-dimension rules        | _single-pair          |
   |negotiation)  | including preconditions    | containment_          |
   |              | union                      | predicate that runs   |
   |              |                            | before and alongside  |
   |              |                            | that intersection     |
   |              |                            | (Section 13.8).       |
   |              |                            | ATN's ordered         |
   |              |                            | dimension lattices    |
   |              |                            | (effects,             |
   |              |                            | external_calls, …)    |
   |              |                            | are the carrier-      |
   |              |                            | level analogue of     |
   |              |                            | CLC-D's mode lattice  |
   |              |                            | (Section 13.4.5),     |
   |              |                            | which stays out of    |
   |              |                            | the language          |
   |              |                            | relation              |
   +--------------+----------------------------+-----------------------+
   |Agent         | Operation-proposal/        | Same "no escalation   |
   |Operation     | authorization JWTs with a  | beyond the original   |
   |Authorization | delegation_chain; the AS   | scope" goal; AOA's    |
   |([AOA], draft-| validates that a sub-      | scope-string          |
   |liu-agent-    | operation is "strictly     | containment is        |
   |operation-    | narrower in scope"         | exactly a carrier     |
   |authorization)| (Section 6.2) via policy   | instance of           |
   |              | templates, OPA, or scope-  | Contains, and AOA's   |
   |              | string containment         | delegation_chain is   |
   |              |                            | the carrier for the   |
   |              |                            | chain CLC-D reasons   |
   |              |                            | over                  |

Wei                       Expires 31 March 2027                [Page 68]
Internet-Draft                   CLC-v1                   September 2026

   +--------------+----------------------------+-----------------------+
   |Attenuating   | Token chain with a         | The closest formal    |
   |Agent Tokens  | capability lattice         | neighbour at the      |
   |([AAT], draft-| (C(child) ⊆ C(parent),     | language level:       |
   |niyikiza-     | Section 4.1) and six       | C(child) ⊆ C(parent)  |
   |oauth-        | attenuation invariants; I4 | is the property       |
   |attenuating-  | defines per-type           | Contains decides,     |
   |agent-tokens) | constraint subsumption     | and AAT's naming of   |
   |              | with Decidable/Sound/      | a constraint type     |
   |              | Deterministic requirements | contains is a         |
   |              | (Section 3.5.1)            | caution that the      |
   |              |                            | _relation_ and a      |
   |              |                            | _constraint value_    |
   |              |                            | must not be           |
   |              |                            | conflated             |
   +--------------+----------------------------+-----------------------+
   |Agent         | Verifiable Credential      | AAE defines the       |
   |Authorization | envelope with              | carrier blocks and a  |
   |Envelope      | MANDATE/CONSTRAINTS/       | closed,               |
   |([AAE], draft-| VALIDITY blocks and an     | deterministic         |
   |kroehl-       | explicit "equal to or more | constraint language;  |
   |agentic-trust-| restrictive" definition    | CLC-D's               |
   |aae)          | per element (Section 3);   | params_not_narrower   |
   |              | warns of delegation        | /                     |
   |              | amplification              | child_exceeds_parent  |
   |              | (Section 7.4)              | are the stable        |
   |              |                            | reason codes that     |
   |              |                            | make AAE's "strictly  |
   |              |                            | subordinate" check    |
   |              |                            | reportable            |
   +--------------+----------------------------+-----------------------+
   |External      | Standardizes _how_ an      | Orthogonal and        |
   |Verifier      | external verifier is       | complementary: EVC    |
   |Contract      | invoked and returns a      | is the verdict        |
   |([EVC], draft-| verdict (allow/deny/denial | _interface_, CLC-D    |
   |kondoju-evc)  | codes, fail-closed exit    | is the decision       |
   |              | semantics)                 | _semantics_ and its   |
   |              |                            | reason codes.  An     |
   |              |                            | EVC implementation    |
   |              |                            | can compute CLC-D's   |
   |              |                            | verdict and surface   |
   |              |                            | the same reason       |
   |              |                            | codes                 |
   +--------------+----------------------------+-----------------------+
   |Agent-auth    | "Agent as workload",       | A natural consumer:   |
   |architecture  | reusing existing           | CLC can be the        |
   |drafts (e.g.  | mechanisms; notes that no  | evaluation language   |
   |draft-klrc-   | single existing policy     | such a framework      |

Wei                       Expires 31 March 2027                [Page 69]
Internet-Draft                   CLC-v1                   September 2026

   |aiagent-auth) | engine covers the full     | calls into            |
   |              | delegation-chain           |                       |
   |              | verification need          |                       |
   +--------------+----------------------------+-----------------------+
   |Dual-identity | Bind agent identity to     | Answers _who          |
   |/ attenuating-| owner identity; define how | delegated_ and _how   |
   |token drafts  | a holder derives and a     | derivation is         |
   |(e.g. draft-  | verifier checks a narrower | carried_; CLC         |
   |ni-wimse-ai-  | token                      | answers _what was     |
   |agent-        |                            | delegated and         |
   |identity, AAT |                            | whether it narrowed_  |
   |above)        |                            |                       |
   +--------------+----------------------------+-----------------------+
   |Agent         | Interaction and delegation | Capability-based      |
   |interaction/  | flow over capability       | sibling; CLC is the   |
   |delegation    | systems                    | evaluation layer      |
   |protocols     |                            | rather than the       |
   |(e.g. AIDP)   |                            | interaction flow      |
   +--------------+----------------------------+-----------------------+

                                  Table 13

   The external names above are recorded as context and MUST be re-
   verified against their current revisions before any submission or
   citation; this subsection makes no claim about their exact present
   contents.  The mapping rows were checked against these revisions on
   2026-09-21: draft-somoza-dmsc-atn-agent-trust-negotiation-00
   (Section 5, Section 6, Section 9), draft-niyikiza-oauth-attenuating-
   agent-tokens-01 (Section 3.5, Section 4), draft-prakash-aip-01
   (Section 3.3, Section 4.4), draft-kroehl-agentic-trust-aae-02
   (Section 2.3, Section 2.5, Section 3), draft-liu-agent-operation-
   authorization-02 (Section 3, Section 6), draft-ietf-wimse-aims-00,
   and the aegis-initiative/aegis-governance repository (AIAM-1 v0.1,
   AIAM1-DEL-010), and draft-asor-wimse-agent-delegation-chain-01
   (Section 3, Section 4, Section 5, Section 6, checked 2026-09-26).
   Each of these is an individual draft, an informational draft, or a
   non-IETF repository, with one exception: the WIMSE working group
   adopted draft-ietf-wimse-aims on 2026-09-09, so it is cited here as a
   working-group document rather than as an individual draft.  The AEGIS
   material is an informational architecture, not a specification with a
   formal standing.

Wei                       Expires 31 March 2027                [Page 70]
Internet-Draft                   CLC-v1                   September 2026

13.9.4.  Where the demand actually is

   The need for a shared evaluation layer is increasingly explicit in
   the authorization community: the recurring complaint is not a lack of
   _carriage_ formats but a lack of agreement on _what a carried
   authorization means_ and on how a delegation chain is verified to
   only narrow.  The following distinction determines what "demand"
   should be taken to mean here:

   *  *If demand means "adopted as a WG standard":* uncertain.  Several
      in-flight efforts (AIP, ATN, AOA, AEGIS, above) are attacking the
      same problem from different angles, and there is no consensus that
      a _separate_ authorization language is wanted.  A standalone
      individual draft is unlikely to be adopted directly on that basis
      alone.

   *  *If demand means "used by implementers":* the need is real and
      immediate.  Agent frameworks and enterprise security teams each
      re-implement an authorization check and each ask the same question
      — "what may this agent actually do, and did the delegation only
      narrow?".  A small, carrier-neutral, _tested_ decision function
      plus corpus is directly reusable there.

   The strategic consequence is that adoption is _earned by use_, not
   awaited from a standards vote: the corpus and the reference
   implementations are the contribution, and the standards reference
   follows if and when downstream implementers cite it.

13.9.5.  Adoption risks

   *  *Carrier dependence.* CLC only matters once a carrier (AIC, WIMSE,
      ZCAPs, UCAN, a RAR profile) binds it.  The Section 13.8 AIC-JWT
      cross-walk is one such binding; without bindings CLC has no reach.

   *  *Competing semantics, not a blank field.* AIP, ATN, AOA, and AEGIS
      each define (or assume) evaluation semantics of their own.  CLC
      enters a field with several incumbents, so it must be _smaller_ (a
      decision function, not a policy language or registry), _carrier-
      neutral_, and _tested_ to be worth citing.

   *  *Ecosystem competition.* A working group could prefer to define
      its own authorization meta-syntax rather than reference an
      external language.  The response is scope discipline: CLC defines
      the _evaluation_ and nothing else, and stays small enough to be
      cited.

Wei                       Expires 31 March 2027                [Page 71]
Internet-Draft                   CLC-v1                   September 2026

   *  *Implementation independence.* The three current implementations
      share an author, so they are a regression test, not independent
      validation (Section 12, restated for CLC-D in Section 13.7).
      Independent implementation remains the gating risk for any
      standards claim.

13.9.6.  Positioning statement

   CLC is best positioned not as a standalone standard but as the
   *language specification other standards reference* when they need to
   define authorization evaluation and delegation narrowing.
   Concretely: when an AIP-style Datalog checker, an ATN-style condition
   evaluator, or an AEGIS-style policy evaluator needs a shared,
   deterministic semantic to validate against or interoperate with, CLC
   is a candidate.  Publishing CLC as the capability language core of a
   wider agent-authorization architecture (the AIC direction) is exactly
   that positioning; containment is kept additive so such a reference
   can be made without disturbing the CLC-A core any adopter already
   implements.

13.10.  Open Issues

   Section 13.10 records the consciously-deferred gaps surfaced in
   review; the items already landed as core revisions are marked
   _closed_, the rest are recorded for later discussion.  Items marked
   _candidate v1.x_ are candidates for the _core language_ to adopt
   without breaking CLC-A inputs; items under "CLC-D" would extend
   containment itself.

13.10.1.  Parameter model rigidity (closed in CLC-1.10)

   Landed as Section 6.5, the optional param_bounds field:

   *  *Number*: inclusive min/max intervals and a step multiple rule.

   *  *Enums*: array membership plus min_items/max_items cardinality.

   *  *Optional keys*: an optional marker exempts a declared key from
      the omission half of layer 7.

   *  *Defaults*: a param_defaults grammar with the precedence explicit
      > default > absent.

   The rejected shape is a {min,max} object inside params — it would
   collide with object recursion (Section 6.2), so the bounds live in a
   sibling field and no existing grant changes meaning.

Wei                       Expires 31 March 2027                [Page 72]
Internet-Draft                   CLC-v1                   September 2026

13.10.2.  Consumer obligations for allow_unresolved (closed in CLC-1.11)

   Section 8.4 delivers residual obligations on an allow_unresolved
   verdict; Section 8.5 *now defines the consumer's feedback loop* and
   this item is closed:

   *  Resolve(decision, resolutions, now?) -> Decision, with each
      unresolved constraint reported as satisfied / violated / unknown;

   *  a propagation rule for partially-evaluated sets (all satisfied →
      allow, any violated → deny with {type}:violated, remainder →
      allow_unresolved);

   *  staleness/*TTL* semantics for time:window residuals: with now, the
      core clock evaluates the window and the discharge horizon is the
      current segment's end, so a cached allow expires with the window.

   This went beyond the original "Resolve is core, TTL is profile
   policy" split: both are now core (Section 8.5).  The coarser
   identity-level consumer gate Discharge (the reference
   implementations' helper) remains as the satisfied-only case.

13.10.3.  Constraints as a union axis (closed in CLC-1.12)

   This revision makes an explicit design decision: constraints are
   *not* part of the containment relation (Section 13.4.4).  They
   compose by _union_ (conjunction) along a chain, which Intersect
   already implements, and no Contains layer reads them.

   Contains therefore stays the smaller relation — containment over
   (identifier, parameters) — and the "what does the chain collectively
   require?" question is answered by the separate derived function
   ConstraintUnion (Section 7.1, new in CLC-1.12).  Folding the union
   into Contains was considered and rejected: it would make the relation
   no longer a pure subset on the declared tuple, and a null constraint
   check would then be able to hide a broken identifier/parameter
   boundary.  A consumer wanting "the child's whole authority is inside
   the parent's" composes the two: Contains per hop plus ConstraintUnion
   over the chain.

Wei                       Expires 31 March 2027                [Page 73]
Internet-Draft                   CLC-v1                   September 2026

13.10.4.  Containment as evidence, not authorization (closed in CLC-
          1.13)

   Section 13.4 keeps containment a declared-set comparison.  A
   delegation _certificate_ binds a child grant; an operation-time
   authorization still needs the decision function (Section 9).  CLC-
   1.13 adds the fourth relation AuthorizeWithChain(chain, op)
   (Section 13.11), the one-call chain check named here: it evaluates
   Contains per hop and then Authorize against the *intersection* of the
   chain, so the parent's constraints (a union axis, outside
   containment) are not lost.  It is a CLC-D function, not a core
   change; a CLC-A implementation is unaffected.

13.11.  AuthorizeWithChain (fused chain authorization)

   Contains is a declared-set comparison and Authorize is an operation-
   time decision; a delegating consumer that has a chain often wants
   both in one call.  AuthorizeWithChain is that convenience, defined at
   the CLC-D layer:

AuthorizeWithChain(chain, op) → Decision      // chain = ordered Grant[], root first

   1.  An *empty chain fails closed*: deny("absent_source") (Section 7
       rule 5).

   2.  For each adjacent pair (chain[i], chain[i+1]), evaluate Contains
       (Section 13.4).  The first hop that is not contained ends the
       call with deny(reason), where reason is that hop's Section 13.5
       code — child_exceeds_parent or params_not_narrower, the two codes
       the relation can return; delegation_mode_not_narrower never
       appears here, because the chain gate calls Contains, which takes
       no mode argument (Section 13.4.5).  This chain gate runs *before*
       op validation: a broken chain is reported even when the operation
       is also absent, because the chain is the subject of this
       function.

   3.  Otherwise compute the effective chain grant G =
       Intersect(chain...) (Section 7).  Because constraints are outside
       containment (Section 13.4.4), this step is what brings every
       ancestor's params *and* constraints into force — authorizing
       against the leaf grant alone would let an operation pass that
       violates an ancestor's constraints (the union axis is not in
       Contains).  An Intersect refusal (no_overlap,
       empty_bound_denies_class, invalid_params_binding) is returned as
       deny(reason).

   4.  Return Authorize(G, op) (Section 9) unchanged — allow /
       allow_unresolved / deny with its own Section 9 reason codes.

Wei                       Expires 31 March 2027                [Page 74]
Internet-Draft                   CLC-v1                   September 2026

   *Caller obligation.* AuthorizeWithChain evaluates the chain *as
   presented*: the caller MUST supply the complete, authenticated, root-
   first chain.  The function fetches no missing link, verifies no
   signature or trust anchor, and detects no truncation or reordering —
   a verdict over a truncated, reordered or unauthenticated chain is a
   verdict about the presented sequence, not about the delegation it
   does not carry (authentication is the carrier's concern, Section 11).
   This obligation adds no decision rule: the steps above are unchanged
   by it.

   AuthorizeWithChain is a *CLC-D function*: it is not part of CLC-A,
   and an implementation claiming only CLC-A is unaffected.  It
   introduces no new core semantics — it fixes the order of two existing
   relations and refuses fail-closed at each step.  A chain carrying
   param_bounds is authorizable: step 3's Intersect(chain...) combines
   the hops' bounds with the Section 6.6 meet, so an ancestor's bound
   (e.g. max:100) stays in force over a narrower child (e.g. max:50).
   Because Section 13.4.3 requires the declaration site of every key to
   match at each hop, a valid chain never presents the cross-site case
   Section 6.6 refuses.  The two-grant form AuthorizeWithChain(parent,
   child, op) named in Section 13.10.4 is the degenerate case chain =
   [parent, child].

13.12.  Revision and Governance

   Containment is folded into this document's revision stream: its
   changes are recorded in the Revision History and its conformance
   class CLC-D is declared in Section 12.1 in step with the language
   revision (this revision is CLC-1.15; CLC-D first appeared in CLC-1.9,
   folded from EXT-00 rev 0).

   *  A CLC-A input is unaffected by the addition of CLC-D; compatible
      reading of CLC-A inputs is the floor.

   *  CLC-D adoption is per-implementation: an implementation may claim
      CLC-A without claiming CLC-D.

   *  The corpus (64 containment vectors, 784 property cases, 44 cross-
      walk vectors, 15 AuthorizeWithChain vectors) is a draft snapshot;
      the README in capability/data/_vectors/clc-d/ maintains the live
      count and the date the snapshot was generated.

14.  Security Considerations

   *  *Fail-closed*: undefined/malformed/unknown → deny.

   *  *Deny-when-declared*: empty bounds deny the class.

Wei                       Expires 31 March 2027                [Page 75]
Internet-Draft                   CLC-v1                   September 2026

   *  *No canonical broadening*: segment-boundary, not lexical prefix.

   *  *Composition narrows only*: an intersection removes authority.
      Whether a _delegated_ grant stays inside its parent's boundary is
      the containment question Section 13 answers (Contains);
      intersection alone (Section 7) does not answer it, and a
      delegation profile MUST NOT substitute one for the other.

   *  *Containment is fail-closed by construction*: every Contains layer
      defaults to false; a grant pair any layer cannot validate is
      refused, with no warning-then-allow path (Section 13.4).

   *  *Containment is not a constraint oracle*: Contains does not read
      constraints (Section 13.4.4).  A consumer that uses Contains as
      its _only_ gate MUST compose the delegation chain's Intersect so
      the parent's constraints stay in force; otherwise a child that
      omits a parent constraint could be admitted while the constraint
      is unenforced.  allow_unresolved is orthogonal to containment and
      MUST NOT be read as a containment result.

   *  *Containment reason-code leakage*: child_exceeds_parent reveals
      that a child requested an identifier the parent does not cover.  A
      boundary that must not leak policy shape MAY collapse that single
      code into capability_not_authorized (never params_not_narrower,
      Section 13.5).

   *  *Mode lattice must be carrier-pinned*: an implementer that maps
      authorized/representative the wrong way round inverts the
      boundary; Section 13.8 pins the AIC-JWT ordering, and other
      carriers MUST pin theirs in the profile that adopts CLC-D.

   *  *Stable reason codes*: same input → same reason across
      implementations.

   *  *Evidence binding is separate from native verification*: Match
      checks content correlation; native verification is the consumer's
      responsibility.

   *  *Parsing divergence must not change the decision*: params are
      normalized at the input boundary per Section 6.2 (JCS
      serialization, duplicate keys, non-finite/over-precision numbers,
      size/depth caps).  A consumer that decodes into a re-orderable map
      and re-encodes loses duplicate keys and cannot represent non-
      finite numbers; two such consumers would reach different verdicts
      on the same raw input.  Decisions are made on the boundary-
      validated form, not on a lossy re-serialization.

Wei                       Expires 31 March 2027                [Page 76]
Internet-Draft                   CLC-v1                   September 2026

   *  *Recognized-but-unevaluated is not silent acceptance*: a
      constraint the core recognizes but cannot evaluate MUST appear in
      the decision's unresolved field — never dropped (Section 8.4).
      The consumer must evaluate or confirm each such constraint before
      acting, otherwise it MUST deny (AAC Section 6.6).

   *  *A core-clock discharge is time-bounded*: when Resolve
      (Section 8.5) evaluates a time:window obligation with a supplied
      now, the discharge is valid only for the segment containing now.
      A consumer MUST NOT cache the resulting allow past that segment's
      end — it re-invokes Resolve with a current now before acting, or
      derives the horizon from the segment end.  Caching a clock-based
      allow turns a window into an unbounded permit.

   *  *Reason-code detail suffix is diagnostic-only*: everything after
      the first : (e.g. the offending param name) MUST NOT change the
      verdict and MUST NOT be relied upon for decisions.  Consumers
      match on the code prefix before the : (Section 9.2).

   *  *Revision mismatch is fail-closed*: an incompatible language
      revision (Section 12.1) yields
      deny("unsupported_language_revision") resolved before any layer —
      never a silent downgrade or best-effort re-interpretation.

   *  *Resource exhaustion is bounded at the input boundary*: the
      512-byte serialized-size cap and the depth-32 nesting cap
      (Section 6.2 step 4) apply to raw_params as much as to every other
      input, keeping recursive evaluators safe from deep-nesting and
      oversized-params blowup.

15.  IANA Considerations

   This document requests no IANA actions.

   Constraint types (max_rows, time, network) and reason codes are
   defined by this document as fixed sets.  Should this work be adopted
   by a working group, that group may wish to consider whether either
   set warrants a registry; this revision does not propose one.

   The containment relation (Section 13) registers three additional
   reason codes (child_exceeds_parent, params_not_narrower,
   delegation_mode_not_narrower — the last produced by the binding
   profile's delegation-mode pre-check, Section 13.4.5, never by the
   relation itself); they are part of the same fixed set, and no
   constraint reason code is defined for containment because constraints
   are outside the relation (Section 13.4.4).

Wei                       Expires 31 March 2027                [Page 77]
Internet-Draft                   CLC-v1                   September 2026

16.  Privacy Considerations

   The language itself transports and stores nothing.  Privacy exposure
   comes from what carriers put into it and from what evaluators report:

   *  Capability identifiers and parameter values describe policy.  They
      can reveal organizational structure, service topology, network
      ranges (network constraints), working hours (time windows), tenant
      names, or purposes.  Deployments should treat grants as policy-
      confidential material.

   *  Distinct reason codes reveal the shape of a grant: the difference
      between params_missing, undeclared_param and not_in_enum tells an
      observer what the grant constrains.  Where the requester is
      untrusted, a consumer should consider collapsing reason codes at
      the boundary, as this specification already does for identifier-
      level failures (Section 9.1).

   *  Parameter values may carry personal data if a scheme defines them
      that way.  Scheme authors should avoid personal identifiers as
      parameter names or values.

   *  Residual obligations (unresolved, Section 8.4) and any audit
      record built from decisions can persist policy and usage
      information; retention is the carrier's responsibility
      (Section 11).

   *  The reference corpus published with this document is synthetic and
      contains no personal data.

   *  Containment reasons (Section 13) are emitted to the child's
      requestor at the delegation step, not to end-users, and the child
      receives the failure code, not the parent's declared bounds.
      Where bounds themselves are sensitive (e.g. network/scope
      declarations), a profile SHOULD log codes, not values.

17.  References

17.1.  Normative References

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119,
              DOI 10.17487/RFC2119, March 1997,
              <https://www.rfc-editor.org/rfc/rfc2119>.

   [RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
              2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
              May 2017, <https://www.rfc-editor.org/rfc/rfc8174>.

Wei                       Expires 31 March 2027                [Page 78]
Internet-Draft                   CLC-v1                   September 2026

   [RFC3339]  Klyne, G. and C. Newman, "Date and Time on the Internet:
              Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002,
              <https://www.rfc-editor.org/rfc/rfc3339>.

   [RFC7493]  Bray, T., Ed., "The I-JSON Message Format", RFC 7493,
              DOI 10.17487/RFC7493, March 2015,
              <https://www.rfc-editor.org/rfc/rfc7493>.

   [RFC8785]  Rundgren, A., Jordan, B., and S. Erdtman, "JSON
              Canonicalization Scheme (JCS)", RFC 8785,
              DOI 10.17487/RFC8785, June 2020,
              <https://www.rfc-editor.org/rfc/rfc8785>.

17.2.  Informative References

   [AIC-JWT]  Wei, J., "AI Agent Identity Certificate (AIC) JSON Web
              Token Profile", September 2026,
              <https://datatracker.ietf.org/doc/draft-wei-aic-jwt/>.

   [EMILIA-AEB]
              Schrock, I., "The Action Evidence Boundary for
              Consequential Agent Effects", Work in Progress, Internet-
              Draft, draft-schrock-action-evidence-boundary-07,
              September 2026, <https://datatracker.ietf.org/doc/draft-
              schrock-action-evidence-boundary/>.

   [AEC]      Schrock, I., "Authorization Evidence Chains: Composing
              Heterogeneous Agent-Action Evidence (EP-AEC)", Work in
              Progress, Internet-Draft, draft-schrock-ep-authorization-
              evidence-chain-06, September 2026,
              <https://datatracker.ietf.org/doc/draft-schrock-ep-
              authorization-evidence-chain/>.

   [BCR]      Schrock, I., "Bounded Capability Receipts and Durable
              Spend Control for Agent Actions", Work in Progress,
              Internet-Draft, draft-schrock-ep-bounded-capability-
              receipts-06, September 2026,
              <https://datatracker.ietf.org/doc/draft-schrock-ep-
              bounded-capability-receipts/>.

   [PAP]      Baur, T., "Principal Agent Protocol (PAP)", Work in
              Progress, Internet-Draft, draft-baur-pap-02, June 2026,
              <https://datatracker.ietf.org/doc/draft-baur-pap/>.

Wei                       Expires 31 March 2027                [Page 79]
Internet-Draft                   CLC-v1                   September 2026

   [ASOR]     Asor, R., "Verifiable Attenuated Delegation for AI Agent
              Chains", Work in Progress, Internet-Draft, draft-asor-
              wimse-agent-delegation-chain-01, September 2026,
              <https://datatracker.ietf.org/doc/draft-asor-wimse-agent-
              delegation-chain/>.

   [RFC9396]  Lodderstedt, T., Richer, J., and B. Campbell, "OAuth 2.0
              Rich Authorization Requests", RFC 9396,
              DOI 10.17487/RFC9396, May 2023,
              <https://www.rfc-editor.org/rfc/rfc9396>.

   [CAID]     Schrock, I., "The Canonical Action Identifier (CAID)",
              Work in Progress, Internet-Draft, draft-schrock-canonical-
              action-identifier-03, September 2026,
              <https://datatracker.ietf.org/doc/draft-schrock-canonical-
              action-identifier/>.

   [ATN]      "Agent Trust Negotiation", n.d.,
              <https://datatracker.ietf.org/doc/draft-somoza-dmsc-atn-
              agent-trust-negotiation/>.

   [AAT]      "OAuth 2.0 Attenuating Agent Tokens", n.d.,
              <https://datatracker.ietf.org/doc/draft-niyikiza-oauth-
              attenuating-agent-tokens/>.

   [AIP]      "Agent Identity Protocol", n.d.,
              <https://datatracker.ietf.org/doc/draft-prakash-aip/>.

   [AAE]      "Agentic Trust Agent Authorization Envelope", n.d.,
              <https://datatracker.ietf.org/doc/draft-kroehl-agentic-
              trust-aae/>.

   [AOA]      "Agent Operation Authorization", n.d.,
              <https://datatracker.ietf.org/doc/draft-liu-agent-
              operation-authorization/>.

   [EVC]      "External Verifier Contract", n.d.,
              <https://datatracker.ietf.org/doc/draft-kondoju-evc/>.

   [CLC-CORPUS]
              Wei, J., "Capability Language Core -- conformance corpus,
              schemas and working documents", September 2026,
              <https://github.com/varwof/capability/tree/
              b15b51b8f94125b7a00aa281f98405806e6ea95c>.

   [AEGIS]    "AEGIS Governance (AIAM-1 v0.1)", n.d.,
              <https://github.com/aegis-initiative/aegis-governance>.

Wei                       Expires 31 March 2027                [Page 80]
Internet-Draft                   CLC-v1                   September 2026

Appendix A.  Consumption Mapping

   The rows below are examples of consumers of this shared vocabulary,
   not required profiles: conformance to CLC-A does not depend on any of
   them.  RAR authorization details [RFC9396] are one such carrier; CLC
   is a candidate evaluation language for the capabilities they declare.

Wei                       Expires 31 March 2027                [Page 81]
Internet-Draft                   CLC-v1                   September 2026

   +=====================+=================+============+===========+================+
   |Consumer             |Grammar          |Binding     |Verdict    |Notes           |
   +=====================+=================+============+===========+================+
   |AIC-JWT DA           |capability[].id  |Entailment  |Decision   |AIC-JWT         |
   |                     |                 |(Section    |(Section 9)|Section 5       |
   |                     |                 |6.1)        |           |binding         |
   +---------------------+-----------------+------------+-----------+----------------+
   |EMILIA AEB           |AEG              |Match       |SATISFIED  |AEB Section 3   |
   |                     |capability_class |(Section    |(Section   |decision levels |
   |                     |                 |6.4) +      |10) +      |(VERIFIED/MATCH/|
   |                     |                 |Entailment  |Decision   |SATISFIED) +    |
   |                     |                 |(Section    |(Section 9)|Section 5.1     |
   |                     |                 |6.1)        |           |ObservedAction +|
   |                     |                 |            |           |Section 7 AEC   |
   |                     |                 |            |           |slots; a        |
   |                     |                 |            |           |VERIFIED        |
   |                     |                 |            |           |authorization   |
   |                     |                 |            |           |artifact carries|
   |                     |                 |            |           |the Grant       |
   +---------------------+-----------------+------------+-----------+----------------+
   |RAR                  |type="capability"|Entailment  |Decision   |[RFC9396] format|
   |authorization_details|                 |(Section    |(Section 9)|                |
   |                     |                 |6.1)        |           |                |
   +---------------------+-----------------+------------+-----------+----------------+
   |Delegation chain     |each hop's       |Intersection|Decision   |intersection    |
   |                     |declared set     |(Section 7) |(Section 9)|over declared   |
   |                     |                 |            |           |sets only; a hop|
   |                     |                 |            |           |that must stay  |
   |                     |                 |            |           |inside its      |
   |                     |                 |            |           |parent is       |
   |                     |                 |            |           |checked with    |
   |                     |                 |            |           |Containment     |
   |                     |                 |            |           |(Section 13)    |
   +---------------------+-----------------+------------+-----------+----------------+
   |Delegation           |parent and child |Contains    |Containment|per-hop child ⊆ |
   |containment          |boundaries       |(Section 13)|verdict    |parent; carrier |
   |                     |                 |            |(Section   |vocabularies and|
   |                     |                 |            |13)        |reason-code     |
   |                     |                 |            |           |mapping in      |
   |                     |                 |            |           |Appendix C      |
   +---------------------+-----------------+------------+-----------+----------------+

                                  Table 14

Wei                       Expires 31 March 2027                [Page 82]
Internet-Draft                   CLC-v1                   September 2026

Appendix B.  Reference Vectors

   *Grouping vs kind mapping*: The appendix groups vectors by semantic
   category (B.1–B.6).  The machine-readable vectors.json uses a kind
   field that collates these groups differently: kind=entail (47) covers
   B.2 (8), the 35 params vectors that sit under kind=entail (B.3's 39
   rows minus params-028/-029/-034/-036, which are kind=decide), and the
   four scheme stress-test entail vectors (clinical-001/-002, payments-
   001, data-002); kind=decide (50) covers the B.5 rows below (34), the
   seven combined decision vectors, payments-002 and data-001, the four
   params boundary decisions that sit under kind=decide (params-028/-
   029/-034/-036, all four also listed in B.3) and the three nested key-
   closure vectors (nested-001/-002/-003); kind=intersect (17) covers
   B.4 (10), the four combined vectors that call the intersect function
   (combined-004/-005/-008/-011) and the three CLC-1.15 cross-type audit
   vectors (intersect-011/-012/-013, outside the B.1–B.6 tables);
   kind=syntax (9) is exactly B.1.  These counts are reproducible from
   the corpus itself: every vector in vectors.json carries its kind, so
   the mapping is machine-checkable rather than maintained by hand.

B.1.  Syntax (9 vectors)

   +==+=====================+=============================+===========+
   |# |Input                | Expected                    |Derivation |
   +==+=====================+=============================+===========+
   |S1|std/database-        | valid                       |literal    |
   |  |v1:query:SELECT      |                             |identifier |
   +--+---------------------+-----------------------------+-----------+
   |S2|std/database-        | valid                       |trailing   |
   |  |v1:query:*           |                             |wildcard   |
   +--+---------------------+-----------------------------+-----------+
   |S3|*:query:SELECT       | deny                        |bare *     |
   |  |                     |                             |illegal    |
   +--+---------------------+-----------------------------+-----------+
   |S4|std/database-        | deny                        |partial    |
   |  |v1:query:SEL*        |                             |segment    |
   |  |                     |                             |wildcard   |
   +--+---------------------+-----------------------------+-----------+
   |S5|std/database-        | deny                        |alternation|
   |  |v1:query:{read,write}|                             |not v1     |
   +--+---------------------+-----------------------------+-----------+
   |S6|std/database-        | deny                        |character  |
   |  |v1:query:[a-z]       |                             |class not  |
   |  |                     |                             |v1         |
   +--+---------------------+-----------------------------+-----------+
   |S7|database:query       | deny(invalid_capability_id) |Section 3  |
   |  |                     |                             |scheme     |
   |  |                     |                             |grammar: no|

Wei                       Expires 31 March 2027                [Page 83]
Internet-Draft                   CLC-v1                   September 2026

   |  |                     |                             |vendor "/" |
   |  |                     |                             |product    |
   |  |                     |                             |"-v" major |
   |  |                     |                             |(the spec's|
   |  |                     |                             |own        |
   |  |                     |                             |counter-   |
   |  |                     |                             |example)   |
   +--+---------------------+-----------------------------+-----------+
   |S8|bad:op               | deny(invalid_capability_id) |Section 3  |
   |  |                     |                             |scheme     |
   |  |                     |                             |grammar:   |
   |  |                     |                             |scheme bad |
   |  |                     |                             |does not   |
   |  |                     |                             |match      |
   |  |                     |                             |vendor/    |
   |  |                     |                             |product-vN |
   |  |                     |                             |(snips the |
   |  |                     |                             |lax-intake |
   |  |                     |                             |hole)      |
   +--+---------------------+-----------------------------+-----------+
   |S9|std/data-            | valid                       |multi-     |
   |  |v1:fetch:item:42     |                             |segment    |
   |  |                     |                             |action +   |
   |  |                     |                             |conforming |
   |  |                     |                             |scheme     |
   |  |                     |                             |(positive  |
   |  |                     |                             |boundary)  |
   +--+---------------------+-----------------------------+-----------+

                                 Table 15

B.2.  Entailment (8 vectors)

   +==+=================+======================+========+==============+
   |# | Grant           | Operation            |Expected|Derivation    |
   +==+=================+======================+========+==============+
   |E1| std/database-   | std/database-        |allow   |literal match |
   |  | v1:query:SELECT | v1:query:SELECT      |        |              |
   +--+-----------------+----------------------+--------+--------------+
   |E2| std/database-   | std/database-        |allow   |trailing      |
   |  | v1:query:*      | v1:query:SELECT      |        |wildcard      |
   +--+-----------------+----------------------+--------+--------------+
   |E3| std/database-   | std/database-        |allow   |wildcard      |
   |  | v1:query:*      | v1:query:SELECT:deep |        |multi-segment |
   +--+-----------------+----------------------+--------+--------------+
   |E4| std/database-   | std/database-        |deny    |different     |
   |  | v1:query:*      | v1:admin:DDL         |        |namespace     |
   +--+-----------------+----------------------+--------+--------------+

Wei                       Expires 31 March 2027                [Page 84]
Internet-Draft                   CLC-v1                   September 2026

   |E5| std/database-   | std/database-        |deny    |no trailing   |
   |  | v1:query:*      | v1:query             |        |segment       |
   +--+-----------------+----------------------+--------+--------------+
   |E6| std/database-   | std/database-        |deny    |literal       |
   |  | v1:query:SELECT | v1:query:INSERT      |        |mismatch      |
   +--+-----------------+----------------------+--------+--------------+
   |E7| std/database-   | std/database-        |deny    |class-        |
   |  | v1:*            | v1:query:SELECT      |        |position      |
   |  |                 |                      |        |(product-     |
   |  |                 |                      |        |segment)      |
   |  |                 |                      |        |wildcard is   |
   |  |                 |                      |        |v1-forbidden: |
   |  |                 |                      |        |only a        |
   |  |                 |                      |        |trailing      |
   |  |                 |                      |        |action        |
   |  |                 |                      |        |segment may   |
   |  |                 |                      |        |be *          |
   |  |                 |                      |        |(Section 3;   |
   |  |                 |                      |        |Section 9.1   |
   |  |                 |                      |        |layer 3)      |
   +--+-----------------+----------------------+--------+--------------+
   |E8| std/database-   | std/database-        |deny    |same class-   |
   |  | v1:*            | v1:admin:DDL         |        |position      |
   |  |                 |                      |        |wildcard; the |
   |  |                 |                      |        |v1-forbidden  |
   |  |                 |                      |        |shape denies  |
   |  |                 |                      |        |regardless of |
   |  |                 |                      |        |the action it |
   |  |                 |                      |        |faces         |
   |  |                 |                      |        |(Section 3;   |
   |  |                 |                      |        |Section 9.1   |
   |  |                 |                      |        |layer 3)      |
   +--+-----------------+----------------------+--------+--------------+

                                  Table 16

B.3.  Params (39 vectors)

   +===+===============================+===============================+==================================+=====================+
   |#  |Grant                          |Operation                      |Expected                          |Derivation           |
   +===+===============================+===============================+==================================+=====================+
   |P1 |{"limit":100}                  |{"limit":50}                   |allow                             |50 ≤ 100             |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P2 |{"limit":100}                  |{"limit":150}                  |deny                              |150 > 100            |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P3 |{"tables":["a","b"]}           |{"tables":["a"]}               |allow                             |subset               |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P4 |{"tables":["a"]}               |{"tables":["a","b"]}           |deny                              |"b" absent           |

Wei                       Expires 31 March 2027                [Page 85]
Internet-Draft                   CLC-v1                   September 2026

   |   |                               |                               |                                  |(not_in_enum)        |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P5 |{"columns":{"t":["id"]}}       |{"columns":{"t":["id","name"]}}|deny                              |"name" absent        |
   |   |                               |                               |                                  |(not_in_enum)        |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P6 |{}                             |{"limit":50}                   |allow                             |unconstrained ({} ≡  |
   |   |                               |                               |                                  |absent; the literal  |
   |   |                               |                               |                                  |{} is pinned by      |
   |   |                               |                               |                                  |decide-028,          |
   |   |                               |                               |                                  |params-006 pins the  |
   |   |                               |                               |                                  |absent form)         |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P7 |{"tables":[]}                  |{"tables":["a"]}               |deny                              |explicit empty bound |
   |   |                               |                               |                                  |denies the class     |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P8 |{"limit":100}                  |{"limit":null}                 |deny                              |null invalid in v1   |
   |   |                               |                               |                                  |(invalid_params_null)|
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P9 |{"station":[1,2,3]}            |{"station":2}                  |allow                             |scalar member of     |
   |   |                               |                               |                                  |array set            |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P10|{"station":[1,2,3]}            |{"station":9}                  |deny                              |9 ∉ allowed set      |
   |   |                               |                               |                                  |(not_in_enum)        |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P11|{"station":[1,3]}              |{"station":[1,3]}              |allow                             |array, every element |
   |   |                               |                               |                                  |a member             |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P12|{"station":[1,3]}              |{"station":[1,2,3]}            |deny                              |2 ∉ allowed set      |
   |   |                               |                               |                                  |(not_in_enum)        |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P13|{"station":[1],"speed":0.3}    |{"station":1,"speed":0.25}     |allow                             |member + number bound|
   |   |                               |                               |                                  |unaffected           |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P14|{"station":[3]}                |{"station":2}                  |deny                              |categorical: granting|
   |   |                               |                               |                                  |3 does not cover 1/2 |
   |   |                               |                               |                                  |(not_in_enum)        |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P15|{"columns":{"t":["id","name"]}}|{"columns":{"t":["id"]}}       |allow                             |object recursion:    |
   |   |                               |                               |                                  |request element is a |
   |   |                               |                               |                                  |member of the granted|
   |   |                               |                               |                                  |set (Section 6.2     |
   |   |                               |                               |                                  |v1.1)                |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P16|{"limit":100}                  |raw {"limit":100,"limit":150}  |deny(invalid_params_duplicate_key)|duplicate JSON key   |
   |   |                               |                               |                                  |rejected at input    |
   |   |                               |                               |                                  |normalization        |
   |   |                               |                               |                                  |(Section 6.2 step 2) |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+

Wei                       Expires 31 March 2027                [Page 86]
Internet-Draft                   CLC-v1                   September 2026

   |P17|{"limit":100}                  |raw {"limit":1e400}            |deny(invalid_params_number)       |non-finite number    |
   |   |                               |                               |                                  |literal (Section 6.2 |
   |   |                               |                               |                                  |step 3)              |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P18|{"s":"x"}                      |raw {"s":"<600 chars>"}        |deny(invalid_params_size)         |serialized form > 512|
   |   |                               |                               |                                  |B (Section 6.2 step  |
   |   |                               |                               |                                  |4)                   |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P19|{"d":0}                        |raw depth-33 object            |deny(invalid_params_size)         |nesting beyond depth |
   |   |                               |                               |                                  |32 (Section 6.2 step |
   |   |                               |                               |                                  |4)                   |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P20|{"s":"x"}                      |raw {"s":"<512 B>"}            |allow                             |serialized = exactly |
   |   |                               |                               |                                  |the 512 B limit (≤); |
   |   |                               |                               |                                  |positive boundary of |
   |   |                               |                               |                                  |P18 (Section 6.2 step|
   |   |                               |                               |                                  |4)                   |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P21|{"d":0}                        |raw depth-32 object            |allow                             |depth exactly the 32 |
   |   |                               |                               |                                  |limit (≤); positive  |
   |   |                               |                               |                                  |boundary of P19      |
   |   |                               |                               |                                  |(Section 6.2 step 4) |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P22|{"flag":true}                  |{"flag":1}                     |deny(params_exceed_grant)         |boolean is exact and |
   |   |                               |                               |                                  |is NOT a number: 1   |
   |   |                               |                               |                                  |must not satisfy a   |
   |   |                               |                               |                                  |granted true (params-|
   |   |                               |                               |                                  |022)                 |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P23|{"flag":true}                  |{"flag":true}                  |allow                             |positive side of P22 |
   |   |                               |                               |                                  |(params-023)         |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P24|{"x":null}                     |(no params)                    |deny(invalid_params_null)         |layer 6 (null)       |
   |   |                               |                               |                                  |precedes layer 7     |
   |   |                               |                               |                                  |(presence) (params-  |
   |   |                               |                               |                                  |024)                 |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P25|{"s":"x"}                      |raw {"s":"<512 B>","s":"dup"}  |deny(invalid_params_size)         |multi-fault: size (4)|
   |   |                               |                               |                                  |precedes duplicate   |
   |   |                               |                               |                                  |keys (2) (params-025)|
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P26|{"s":"x"}                      |raw {"n":1e400,"s":"<512 B>"}  |deny(invalid_params_size)         |multi-fault: size (4)|
   |   |                               |                               |                                  |precedes number shape|
   |   |                               |                               |                                  |(3) (params-026)     |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P27|{"a":1}                        |raw {"a":1,"a":2,"n":1e400}    |deny(invalid_params_duplicate_key)|multi-fault under the|
   |   |                               |                               |                                  |size limit: duplicate|
   |   |                               |                               |                                  |keys (2) precede     |

Wei                       Expires 31 March 2027                [Page 87]
Internet-Draft                   CLC-v1                   September 2026

   |   |                               |                               |                                  |number shape (3)     |
   |   |                               |                               |                                  |(params-027)         |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P28|{}                             |raw {"n":1e-6,"s":"<494 a>"}   |deny(invalid_params_size)         |the received text is |
   |   |                               |                               |                                  |511 octets but the   |
   |   |                               |                               |                                  |JCS form writes 1e-6 |
   |   |                               |                               |                                  |as 0.000001, so the  |
   |   |                               |                               |                                  |Section 6.2 step 4   |
   |   |                               |                               |                                  |size is 515 (params- |
   |   |                               |                               |                                  |033)                 |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P29|{}                             |{"n":1e-6,"s":"<494 a>"}       |deny(invalid_params_size)         |decoded counterpart  |
   |   |                               |(decoded)                      |                                  |of P28: both         |
   |   |                               |                               |                                  |boundaries agree     |
   |   |                               |                               |                                  |(params-034)         |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P30|{}                             |raw {"n":1.0,"s":"<498 a>"}    |allow                             |the received text is |
   |   |                               |                               |                                  |514 octets but the   |
   |   |                               |                               |                                  |JCS form writes 1.0  |
   |   |                               |                               |                                  |as 1, so the size is |
   |   |                               |                               |                                  |512 and inside the   |
   |   |                               |                               |                                  |cap (params-035)     |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P31|{}                             |{"n":1.0,"s":"<498 a>"}        |allow                             |decoded counterpart  |
   |   |                               |(decoded)                      |                                  |of P30: a raw check  |
   |   |                               |                               |                                  |counting the received|
   |   |                               |                               |                                  |token would refuse it|
   |   |                               |                               |                                  |(params-036)         |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P32|{}                             |raw {"s":"<100×U+1F600>"}      |allow                             |100 literal astral   |
   |   |                               |                               |                                  |characters are 408   |
   |   |                               |                               |                                  |JCS octets; counting |
   |   |                               |                               |                                  |UTF-16 code units    |
   |   |                               |                               |                                  |would double the     |
   |   |                               |                               |                                  |count and refuse     |
   |   |                               |                               |                                  |(params-037)         |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P33|{}                             |raw {"s":"<100×\ud83d\ude00>"} |allow                             |escaped spelling of  |
   |   |                               |                               |                                  |P32: literal and     |
   |   |                               |                               |                                  |escaped forms of one |
   |   |                               |                               |                                  |string MUST reach the|
   |   |                               |                               |                                  |same verdict and the |
   |   |                               |                               |                                  |same size (params-   |
   |   |                               |                               |                                  |038)                 |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P34|{}                             |raw {"s":"a\nb"} (literal      |deny(invalid_params_number)       |a literal control    |
   |   |                               |U+000A)                        |                                  |character is not     |
   |   |                               |                               |                                  |valid JSON text; Go  |

Wei                       Expires 31 March 2027                [Page 88]
Internet-Draft                   CLC-v1                   September 2026

   |   |                               |                               |                                  |and Python refused it|
   |   |                               |                               |                                  |already (params-039) |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P35|{}                             |{"x":"<260×é>"} (decoded)      |deny(invalid_params_size)         |260 U+00E9 code      |
   |   |                               |                               |                                  |points serialize to  |
   |   |                               |                               |                                  |528 JCS octets > 512;|
   |   |                               |                               |                                  |non-ASCII sizes are  |
   |   |                               |                               |                                  |measured on the      |
   |   |                               |                               |                                  |canonical UTF-8 form,|
   |   |                               |                               |                                  |never in code points |
   |   |                               |                               |                                  |or UTF-16 units      |
   |   |                               |                               |                                  |(params-028, rev CLC-|
   |   |                               |                               |                                  |1.4)                 |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P36|{}                             |{"x":"<251×é>"} (decoded)      |allow                             |251 U+00E9 serialize |
   |   |                               |                               |                                  |to 510 octets ≤ 512; |
   |   |                               |                               |                                  |the positive side of |
   |   |                               |                               |                                  |P35 (params-029, rev |
   |   |                               |                               |                                  |CLC-1.4)             |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P37|{"s":"x"}                      |raw {"s":"<251×\u00e9          |allow                             |the same 510-octet   |
   |   |                               |escapes>"}                     |                                  |string spelled with  |
   |   |                               |                               |                                  |\u00e9 escapes       |
   |   |                               |                               |                                  |reaches the same     |
   |   |                               |                               |                                  |verdict and the same |
   |   |                               |                               |                                  |size as the literal  |
   |   |                               |                               |                                  |form of P36 (params- |
   |   |                               |                               |                                  |030, rev CLC-1.4)    |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P38|{"limit":100}                  |raw {"s":"\ud800"}             |deny(invalid_params_number)       |a lone surrogate     |
   |   |                               |                               |                                  |escape is not valid  |
   |   |                               |                               |                                  |Unicode: refused at  |
   |   |                               |                               |                                  |the raw boundary,    |
   |   |                               |                               |                                  |never repaired to    |
   |   |                               |                               |                                  |U+FFFD (RFC 8785     |
   |   |                               |                               |                                  |Section 3.2.2.2;     |
   |   |                               |                               |                                  |Section 6.2 step 2;  |
   |   |                               |                               |                                  |params-031, rev CLC- |
   |   |                               |                               |                                  |1.6)                 |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+
   |P39|{}                             |raw {"s":"\ud83d\ude02"}       |allow                             |a valid surrogate    |
   |   |                               |                               |                                  |pair is one character|
   |   |                               |                               |                                  |(U+1F602, four UTF-8 |
   |   |                               |                               |                                  |octets) and counts as|
   |   |                               |                               |                                  |such under the size  |
   |   |                               |                               |                                  |rule (params-032, rev|
   |   |                               |                               |                                  |CLC-1.6)             |
   +---+-------------------------------+-------------------------------+----------------------------------+---------------------+

Wei                       Expires 31 March 2027                [Page 89]
Internet-Draft                   CLC-v1                   September 2026

                                  Table 17

B.4.  Intersection (10 vectors)

   Shorthand: params shown compact; constraints use colon notation.

   +===+====================+=============================================+===================+==========+
   |#  |Source A            |Source B                                     |Expected           |Derivation|
   +===+====================+=============================================+===================+==========+
   |I1 |{"tables":["a","b"]}|{"tables":["a"]}                             |{"tables":["a"]}   |overlap   |
   +---+--------------------+---------------------------------------------+-------------------+----------+
   |I2 |{"tables":["a"]}    |{"tables":[]}                                |deny               |deny-when-|
   |   |                    |                                             |                   |declared  |
   +---+--------------------+---------------------------------------------+-------------------+----------+
   |I3 |(unconstrained)     |(no grant)                                   |deny               |absent    |
   |   |                    |                                             |                   |source    |
   +---+--------------------+---------------------------------------------+-------------------+----------+
   |I4 |{"limit":100}       |{"limit":50}                                 |{"limit":50}       |tighter   |
   |   |                    |                                             |                   |bound     |
   +---+--------------------+---------------------------------------------+-------------------+----------+
   |I5 |(unconstrained)     |constraint                                   |recognized, not    |constraint|
   |   |                    |time:window:[{"start":"00:00","end":"01:00"}]|evaluated →        |added     |
   |   |                    |                                             |unresolved carried |          |
   |   |                    |                                             |on allow_unresolved|          |
   |   |                    |                                             |verdict            |          |
   +---+--------------------+---------------------------------------------+-------------------+----------+
   |I6 |{"tables":["a"]}    |{"tables":["b"]}                             |deny               |no overlap|
   +---+--------------------+---------------------------------------------+-------------------+----------+
   |I7 |(no source / null)  |—                                            |deny(absent_source)|zero      |
   |   |                    |                                             |                   |sources:  |
   |   |                    |                                             |                   |fail-     |
   |   |                    |                                             |                   |closed    |
   |   |                    |                                             |                   |(Section 7|
   |   |                    |                                             |                   |rule 5)   |
   +---+--------------------+---------------------------------------------+-------------------+----------+
   |I8 |{"limit":50}        |{}                                           |{"limit":50}       |empty     |
   |   |                    |                                             |                   |params    |
   |   |                    |                                             |                   |declares  |
   |   |                    |                                             |                   |no        |
   |   |                    |                                             |                   |constraint|
   |   |                    |                                             |                   |→ bound   |
   |   |                    |                                             |                   |preserved |
   |   |                    |                                             |                   |(bounded  |
   |   |                    |                                             |                   |then      |
   |   |                    |                                             |                   |empty,    |
   |   |                    |                                             |                   |Section 7 |
   |   |                    |                                             |                   |rule 6)   |
   +---+--------------------+---------------------------------------------+-------------------+----------+

Wei                       Expires 31 March 2027                [Page 90]
Internet-Draft                   CLC-v1                   September 2026

   |I9 |{}                  |{"limit":50}                                 |{"limit":50}       |source    |
   |   |                    |                                             |                   |order must|
   |   |                    |                                             |                   |not matter|
   |   |                    |                                             |                   |(empty    |
   |   |                    |                                             |                   |then      |
   |   |                    |                                             |                   |bounded,  |
   |   |                    |                                             |                   |Section 7 |
   |   |                    |                                             |                   |rule 6)   |
   +---+--------------------+---------------------------------------------+-------------------+----------+
   |I10|{}, id query:SELECT |{}, id query:*                               |{}                 |narrower  |
   |   |                    |                                             |                   |identifier|
   |   |                    |                                             |                   |wins;     |
   |   |                    |                                             |                   |identifier|
   |   |                    |                                             |                   |comparison|
   |   |                    |                                             |                   |is params-|
   |   |                    |                                             |                   |free      |
   |   |                    |                                             |                   |(Section 7|
   |   |                    |                                             |                   |rule 2)   |
   +---+--------------------+---------------------------------------------+-------------------+----------+

                                  Table 18

B.5.  Decision (34 vectors)

   +===+==================+=====================================+=====================+
   |#  |Scenario          |Expected                             |Derivation           |
   +===+==================+=====================================+=====================+
   |D1 |valid grant, valid|allow                                |all checks pass      |
   |   |op, constraints   |                                     |                     |
   |   |pass              |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D2 |no matching grant |deny("capability_not_authorized")    |fail-closed          |
   +---+------------------+-------------------------------------+---------------------+
   |D3 |unknown constraint|deny("unknown_constraint")           |fail-closed          |
   |   |type              |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D4 |malformed         |deny("invalid_capability_id")        |input validation     |
   |   |capability_id     |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D5 |same input twice  |same output                          |deterministic        |
   +---+------------------+-------------------------------------+---------------------+
   |D6 |constraint        |deny("{type}:violated")              |constraint fail      |
   |   |violation         |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D7 |grant bounds a    |deny("params_missing")               |Section 6.3 step 4   |
   |   |param, operation  |                                     |fail-closed          |
   |   |omits it          |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+

Wei                       Expires 31 March 2027                [Page 91]
Internet-Draft                   CLC-v1                   September 2026

   |D8 |operation param   |deny("invalid_params_null")          |Section 6.2 null     |
   |   |value is null     |                                     |rule; may carry :    |
   |   |                  |                                     |<param> detail       |
   |   |                  |                                     |(Section 9.2)        |
   +---+------------------+-------------------------------------+---------------------+
   |D9 |bounded grant,    |deny("params_missing")               |Section 6.3 step 4   |
   |   |operation has *no |                                     |fail-closed          |
   |   |params field at   |                                     |                     |
   |   |all*              |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D10|Authorize called  |deny("capability_not_authorized")    |Section 9 fail-      |
   |   |with an *absent/  |                                     |closed, no exception |
   |   |empty grant*      |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D11|input declares    |allow                                |same major, 1.0 ≤ 1.3|
   |   |CLC-1.0 against a |                                     |→ compatible         |
   |   |CLC-1.3           |                                     |(Section 12.1)       |
   |   |implementation    |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D12|input declares    |deny("unsupported_language_revision")|different major →    |
   |   |CLC-2.0 against a |                                     |fail-closed          |
   |   |CLC-1.3           |                                     |(Section 12.1)       |
   |   |implementation    |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D13|operation carries |deny("undeclared_param")             |Section 6.2 key      |
   |   |a param key the   |                                     |closure, Section 9.1 |
   |   |grant does not    |                                     |layer 7 request side |
   |   |declare (key      |                                     |                     |
   |   |closure)          |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D14|both a missing    |deny("params_missing")               |layer-7 order:       |
   |   |grant key and an  |                                     |missing before       |
   |   |undeclared request|                                     |undeclared           |
   |   |key               |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D15|absent/empty grant|deny("capability_not_authorized")    |Section 9.1 pre-check|
   |   |*and* absent      |                                     |resolves before any  |
   |   |operation         |                                     |layer, incl. the     |
   |   |                  |                                     |absent-operation case|
   +---+------------------+-------------------------------------+---------------------+
   |D16|grant valid,      |deny("missing_capability_id")        |layer 1              |
   |   |operation has *no |                                     |                     |
   |   |id*               |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D17|grant carries     |deny("invalid_constraint")           |a single segment     |
   |   |time:window with a|                                     |crossing midnight is |
   |   |*cross-midnight   |                                     |out of grammar       |
   |   |single segment*   |                                     |(decide-019) — a     |

Wei                       Expires 31 March 2027                [Page 92]
Internet-Draft                   CLC-v1                   September 2026

   |   |(22:00→06:00)     |                                     |crossing must be     |
   |   |                  |                                     |split into two       |
   |   |                  |                                     |segments             |
   +---+------------------+-------------------------------------+---------------------+
   |D18|grant carries     |allow_unresolved,                    |recognized, no core  |
   |   |network:cidr      |unresolved:[<constraint>]            |evaluator → residual |
   |   |(array form, IPv4/|                                     |obligation           |
   |   |IPv6)             |                                     |(Section 8.4; decide-|
   |   |                  |                                     |020)                 |
   +---+------------------+-------------------------------------+---------------------+
   |D19|time:window scalar|deny("invalid_constraint")           |out of Section 8.1   |
   |   |second form       |                                     |value grammar        |
   |   |(window:3600)     |                                     |(decide-021)         |
   +---+------------------+-------------------------------------+---------------------+
   |D20|network:cidr      |deny("invalid_constraint")           |out of Section 8.1   |
   |   |without prefix    |                                     |value grammar        |
   |   |length            |                                     |(decide-022)         |
   +---+------------------+-------------------------------------+---------------------+
   |D21|max_rows          |deny("max_rows:violated")            |fail-closed op-absent|
   |   |constraint, op    |                                     |(decide-023)         |
   |   |carries *no*      |                                     |                     |
   |   |max_rows value    |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D22|time:window split-|allow_unresolved,                    |cross-midnight split-|
   |   |form multi-segment|unresolved:[<constraint>]            |segment grammar      |
   |   |window            |                                     |(decide-024)         |
   |   |(22:00→00:00 +    |                                     |                     |
   |   |00:00→06:00)      |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D23|op scheme bad     |deny("invalid_capability_id")        |Section 3 scheme     |
   |   |passes no vendor/ |                                     |grammar (decide-025) |
   |   |product-vN        |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D24|grant id bad:op   |deny("capability_not_authorized")    |Section 3 fails in   |
   |   |against a valid   |                                     |Entails; ID-level    |
   |   |operation         |                                     |reason collapses     |
   |   |                  |                                     |(decide-026)         |
   +---+------------------+-------------------------------------+---------------------+
   |D25|max_rows exactly  |allow                                |violation is strict  |
   |   |at the bound      |                                     |>; core-evaluated so |
   |   |                  |                                     |no unresolved        |
   |   |                  |                                     |(decide-027)         |
   +---+------------------+-------------------------------------+---------------------+
   |D26|grant params:{},  |allow                                |{} ≡ absent (decide- |
   |   |op any params →   |                                     |028, Section 9.1)    |
   |   |unconstrained     |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D27|multi-grant: G1   |allow                                |any-one-covers       |

Wei                       Expires 31 March 2027                [Page 93]
Internet-Draft                   CLC-v1                   September 2026

   |   |{limit:10} denies,|                                     |authorizes (decide-  |
   |   |G2 {limit:100}    |                                     |029, Section 9.1)    |
   |   |allows            |                                     |                     |
   +---+------------------+-------------------------------------+---------------------+
   |D28|multi-grant: G1   |deny("params_exceed_grant")          |all covering grants  |
   |   |{limit:10} + G2   |                                     |reject → first reason|
   |   |{limit:6}, op     |                                     |in canonical order   |
   |   |{limit:50}        |                                     |(decide-030,         |
   |   |                  |                                     |Section 9.1)         |
   +---+------------------+-------------------------------------+---------------------+
   |D29|multi-grant       |allow_unresolved, unresolved:[both]  |residual obligations |
   |   |allow_unresolved: |                                     |union across covering|
   |   |G1 network:cidr + |                                     |grants (decide-035,  |
   |   |G2 time:window    |                                     |Section 9.1/         |
   |   |                  |                                     |Section 8.4)         |
   +---+------------------+-------------------------------------+---------------------+
   |D30|operation id uses |deny("unsupported_wildcard")         |wildcard-shape       |
   |   |a forbidden       |                                     |detection precedes   |
   |   |wildcard shape    |                                     |the base grammar, and|
   |   |(*:query:SELECT)  |                                     |layer 1 propagates   |
   |   |                  |                                     |the specific code    |
   |   |                  |                                     |rather than          |
   |   |                  |                                     |invalid_capability_id|
   |   |                  |                                     |(decide-018, Section |
   |   |                  |                                     |3/Section 9.1)       |
   +---+------------------+-------------------------------------+---------------------+
   |D31|grant bounds      |deny("max_rows:violated")            |op-side value outside|
   |   |max_rows:10,      |                                     |the Section 8.1      |
   |   |operation carries |                                     |domain (finite non-  |
   |   |max_rows:"garbage"|                                     |negative integer)    |
   |   |                  |                                     |fails closed, never  |
   |   |                  |                                     |passes unchecked     |
   |   |                  |                                     |(decide-031, rev CLC-|
   |   |                  |                                     |1.4)                 |
   +---+------------------+-------------------------------------+---------------------+
   |D32|grant bounds      |deny("max_rows:violated")            |boolean is outside   |
   |   |max_rows:10,      |                                     |the Section 8.1      |
   |   |operation carries |                                     |domain (decide-032,  |
   |   |max_rows:true     |                                     |rev CLC-1.4)         |
   +---+------------------+-------------------------------------+---------------------+
   |D33|grant bounds      |deny("max_rows:violated")            |a negative is outside|
   |   |max_rows:10,      |                                     |the Section 8.1      |
   |   |operation carries |                                     |domain (decide-033,  |
   |   |max_rows:-1       |                                     |rev CLC-1.4)         |
   +---+------------------+-------------------------------------+---------------------+
   |D34|grant bounds      |deny("max_rows:violated")            |a fraction is outside|
   |   |max_rows:10,      |                                     |the Section 8.1      |
   |   |operation carries |                                     |domain (decide-034,  |

Wei                       Expires 31 March 2027                [Page 94]
Internet-Draft                   CLC-v1                   September 2026

   |   |max_rows:1.5      |                                     |rev CLC-1.4)         |
   +---+------------------+-------------------------------------+---------------------+

                                  Table 19

B.6.  Combined (11 vectors)

    +===+==================+===============================+==========+
    |#  | Scenario         | Expected                      |Derivation|
    +===+==================+===============================+==========+
    |C1 | wildcard grant + | allow                         |E2 + P1   |
    |   | params within    |                               |          |
    |   | bounds           |                               |          |
    +---+------------------+-------------------------------+----------+
    |C2 | wildcard grant + | deny                          |E2 + P2   |
    |   | params exceed    |                               |          |
    |   | bounds           |                               |          |
    +---+------------------+-------------------------------+----------+
    |C3 | intersection +   | deny                          |I4 + D6   |
    |   | constraint       |                               |          |
    |   | violation        |                               |          |
    +---+------------------+-------------------------------+----------+
    |C4 | two-source       | allow, narrowest              |I1 + I4   |
    |   | intersection,    |                               |          |
    |   | both narrow      |                               |          |
    +---+------------------+-------------------------------+----------+
    |C5 | grant with empty | deny                          |deny-when-|
    |   | constraint bound |                               |declared  |
    +---+------------------+-------------------------------+----------+
    |C6 | unknown scheme   | deny("unknown_constraint")    |fail-     |
    |   | in grant         |                               |closed    |
    +---+------------------+-------------------------------+----------+
    |C7 | grant: std/      | deny                          |E2 + P4   |
    |   | database-        |                               |          |
    |   | v1:query:*, op:  |                               |          |
    |   | std/database-    |                               |          |
    |   | v1:query:SELECT, |                               |          |
    |   | params mismatch  |                               |          |
    +---+------------------+-------------------------------+----------+
    |C8 | three-source     | deny                          |I3        |
    |   | intersection,    |                               |          |
    |   | one absent       |                               |          |
    +---+------------------+-------------------------------+----------+
    |C9 | valid grant +    | allow                         |D1        |
    |   | valid constraint |                               |          |
    |   | + valid op       |                               |          |
    +---+------------------+-------------------------------+----------+
    |C10| malformed id in  | deny("invalid_capability_id") |D4        |

Wei                       Expires 31 March 2027                [Page 95]
Internet-Draft                   CLC-v1                   September 2026

    |   | operation        |                               |          |
    +---+------------------+-------------------------------+----------+
    |C11| delegation       | deny                          |deny-when-|
    |   | chain,           |                               |declared  |
    |   | intermediate hop |                               |propagates|
    |   | declares empty   |                               |          |
    |   | bound            |                               |          |
    +---+------------------+-------------------------------+----------+

                                  Table 20

   *Total: 123 vectors*

      Decisions D17–D28 are the corpus pin for the residual-obligation
      channel unresolved / allow_unresolved, the Section 8.1
      (scheme,type) identity, the invalid_constraint value grammar and
      the Section 9.1 multi-grant aggregation.  decide-025/-026 and
      syntax-007/-008 pin the Section 3 scheme grammar; decide-021/-022,
      decide-019/-024 pin Section 8.1 time/network value shapes;
      decide-019 pins the no-cross-midnight rule; decide-028/-029/-030
      pin Section 9.1 ({}≡absent, any-allow union, deterministic deny
      reason); decide-035 (D29) pins multi-grant residual-obligation
      union across covering grants; decide-031/-032/-033/-034 (D31–D34)
      pin the Section 8.1 max_rows request-side value domain (rev CLC-
      1.4); decide-018 (D30) pins Section 3 wildcard-shape detection
      ahead of the base grammar.

Wei                       Expires 31 March 2027                [Page 96]
Internet-Draft                   CLC-v1                   September 2026

      The corpus additionally carries 6 scheme stress-test vectors
      (clinical-001/-002, payments-001/-002, data-001/-002) exercised
      against std/{clinical,payments,data}-v1: their enum/bound outcomes
      follow the grouping rules above (→ B.3 params semantics), and
      payments-002 additionally exercises Section 8 fail-closed
      unknown_constraint for a scheme-scoped constraint type.  These
      vectors add no new normative rule; they exist to record the v2
      requirements evidence, not to extend v1. undeclared-001/-002 (→
      B.5 D13/D14) pin the Section 6.2 key-closure rule. params-006/
      params-013 (→ B.3 P6/P13) close two previously-unmapped rows;
      params-020/021 (→ P20/P21) are the positive boundary cases of
      params-018/019; decide-016/017 (→ D15/D16) pin the Section 9.1
      pre-check and layer-1 paths for absent/empty grant and id-less
      operation; intersect-007..010 (→ I7..I10) pin Section 7 rules 5–6
      including empty-params sources, order independence, and a params-
      free identifier comparison; intersect-011/-012/-013 (rev CLC-1.15)
      are the cross-type audit pins of Section 7 rule 6 (string × number
      merge refusal, type-sensitive enum-member equality, 1.0 ≡ 1) and
      sit outside the B.1–B.6 tables, taking the corpus total to 123.
      B.5 row labels Dn are semantic row numbers, not corpus ids (D15/
      D16 ↔ decide-016/-017, D29 ↔ decide-035); D10 (absent/empty grant,
      operation present) has no dedicated vector — the pre-check path is
      pinned by decide-016 (D15).

B.7.  Containment (external corpus)

   The delegation-containment relation (Section 13) is pinned by a
   separate corpus at capability/data/_vectors/clc-d/: containment-
   vectors.json (64 vectors, groups: identifier coverage, parameter
   narrowing, constraint non-participation, validity, symmetry),
   containment-property-cases.json (784 forward-closure cases over 39
   shared operations) and containment-crosswalk-vectors.json (44 cross-
   vendor vectors).  It is not re-tabulated here; the corpus README
   maintains the live count and the snapshot date.

B.8.  Extended parameter bounds (external corpus)

   The Section 6.5 param_bounds grammar is pinned by
   capability/data/_vectors/clc-v1/param-bounds-vectors.json (43
   vectors, groups: numeric interval/step, enum cardinality, optional
   keys, nested recursion, the binding rule, malformed bounds, scheme
   defaults), with param-bounds-vectors.schema.json.  It is not re-
   tabulated here.  These vectors exercise a CLC-1.10 field; a CLC-1.9
   or earlier implementation refuses them through the Section 12.1 minor
   gate rather than ignoring the bounds.  The JSON type-sensitive enum
   equality of Section 6.5 layer 8 (rev CLC-1.15) is pinned by the
   companion file param-bounds-equality-vectors.json in the same
   directory.

Wei                       Expires 31 March 2027                [Page 97]
Internet-Draft                   CLC-v1                   September 2026

B.9.  Resolve (external corpus)

   The Section 8.5 Resolve function is pinned by
   capability/data/_vectors/clc-v1/resolve-vectors.json (26 vectors,
   groups: terminal pass-through, all-satisfied → allow, partial →
   allow_unresolved, violated → deny, the time:window core clock in and
   out of window, TTL re-evaluation, bare assertion without now,
   conflict precedence and malformed input), with resolve-
   vectors.schema.json.  Vectors that exercise the core clock carry now;
   a CLC-1.10 implementation that does not implement Resolve is
   unaffected by this corpus.

B.10.  ConstraintUnion (external corpus)

   The Section 7.1 derived ConstraintUnion projection is pinned by
   capability/data/_vectors/clc-v1/constraint-union-vectors.json (12
   vectors: union over one/many grants, duplicate folding across
   sources, deterministic ordering, empty constraints, and the empty-
   chain absent_source refusal), with constraint-union-
   vectors.schema.json.  The UTF-8 byte-order collation pinned in rev
   CLC-1.15 (Section 7.1) — where UTF-16 code-unit order would diverge —
   is exercised by the companion file constraint-union-collation-
   vectors.json in the same directory.

B.11.  AuthorizeWithChain (external corpus)

   The Section 13.11 fused chain check is pinned by
   capability/data/_vectors/clc-d/authorize-chain-vectors.json (15
   vectors: empty chain, broken identifier/parameter hop, a broken hop
   reported before op validation, the degenerate two-grant form,
   ancestor-constraint enforcement via the effective intersection, the
   param_bounds refusal, and pass-through of allow / allow_unresolved /
   operation-layer deny), with authorize-chain-vectors.schema.json.

B.12.  BoundMeet (external corpus)

   The Section 6.6 intersection of param_bounds is pinned by
   capability/data/_vectors/clc-v1/param-bounds-meet-vectors.json
   (groups: numeric min/max/step meet including the coarser-grid and
   fail-closed step cases, enum intersection and tightened cardinality,
   optional conjunction, nested recursion, the empty meets (min>max,
   disjoint enums — empty _within one value family_), the
   unrepresentable crosses (numeric∩enum in either order and
   scalar∩nested, refused since rev CLC-1.15; cardinality-only enum,
   incommensurable steps → invalid_params_binding), the identity empty
   Bound, and the cross-site refusal), with param-bounds-meet-
   vectors.schema.json.  The rev CLC-1.15 meet invariant — every
   successful meet authorizes only what *every* source authorizes — is

Wei                       Expires 31 March 2027                [Page 98]
Internet-Draft                   CLC-v1                   September 2026

   exercised by param-bounds-meet-property-cases.json in the same
   directory.

Appendix C.  Containment Carrier Vocabulary and Reason-Code Mapping

   This appendix is *informative*.  It puts a carrier's own vocabulary
   and its failure codes beside CLC-D's, so an adopter can explain a
   containment verdict in the carrier's terms — and can see exactly
   where the mapping is many-to-one and therefore not reversible without
   carrier context.  The profiles named here are the pinned ones in
   Section 13.9.3; the profile obligations are Section 13.8.1.

C.1.  Vocabulary

    +=========+====================+==============+===================+
    | Carrier | Carrier term       | CLC term     | Note              |
    |         |                    | used here    |                   |
    +=========+====================+==============+===================+
    | CLC-D   | identifier         | CapabilityId | the relation's    |
    |         | scheme:action:path |              | left/right        |
    |         |                    |              | identity          |
    +---------+--------------------+--------------+-------------------+
    | CLC-D   | parameter bound    | params       | declared bound;   |
    |         |                    |              | absent or {} =    |
    |         |                    |              | unconstrained     |
    +---------+--------------------+--------------+-------------------+
    | CLC-D   | constraint         | constraints  | *union* axis,     |
    |         | (varwof/           |              | outside Contains  |
    |         | constraint-v1:*)   |              | (Section 13.4.4)  |
    +---------+--------------------+--------------+-------------------+
    | ATN     | resource_bounds,   | params       | Section 9.2 takes |
    |         | numeric conditions | (upper       | the per-dimension |
    |         |                    | bounds)      | min               |
    +---------+--------------------+--------------+-------------------+
    | ATN     | id + schema.digest | identifier   | digest mismatch ⇒ |
    |         |                    | segments     | distinct          |
    |         |                    |              | capability (ccx-  |
    |         |                    |              | 043)              |
    +---------+--------------------+--------------+-------------------+
    | ATN     | preconditions      | — (union     | never compared by |
    |         |                    | axis)        | Contains          |
    +---------+--------------------+--------------+-------------------+
    | AAT     | tools(derived) ⊆   | identifier   |                   |
    |         | tools(parent)      | coverage     |                   |
    +---------+--------------------+--------------+-------------------+
    | AAT     | argument           | params       | AAT's own         |
    |         | constraints        |              | contains/subset   |
    |         | (exact/range/      |              | are _constraint   |

Wei                       Expires 31 March 2027                [Page 99]
Internet-Draft                   CLC-v1                   September 2026

    |         | one_of/…)          |              | types_, not the   |
    |         |                    |              | relation          |
    +---------+--------------------+--------------+-------------------+
    | AIP     | scope, budget,     | identifier + | absent ⇒ nearest  |
    |         | expiry, domains    | params       | ancestor,         |
    |         |                    |              | resolved by the   |
    |         |                    |              | profile           |
    +---------+--------------------+--------------+-------------------+
    | AAE     | mandate.actions    | identifier   |                   |
    |         |                    | coverage     |                   |
    +---------+--------------------+--------------+-------------------+
    | AAE     | CONSTRAINTS value  | params       | numeric ≤,        |
    |         | (unwrapped)        |              | allowlist ⊆       |
    +---------+--------------------+--------------+-------------------+
    | AAE     | validity           | — (carrier/  | not a declared    |
    |         |                    | time, CLC-A  | Contains input    |
    |         |                    | R1)          |                   |
    +---------+--------------------+--------------+-------------------+
    | AOA     | operation scope    | identifier   | * maps to a CLC   |
    |         | string             | path         | trailing wildcard |
    +---------+--------------------+--------------+-------------------+
    | AEGIS   | dotted capability  | identifier   |                   |
    |         |                    | path         |                   |
    +---------+--------------------+--------------+-------------------+
    | AEGIS   | numeric context    | params       |                   |
    +---------+--------------------+--------------+-------------------+
    | AEGIS   | allowed_roles,     | — (*not      | identity/policy   |
    |         | environment,       | mapped*)     | dimensions with   |
    |         | risk_level, scope  |              | no CLC field; the |
    |         |                    |              | profile leaves    |
    |         |                    |              | them to the       |
    |         |                    |              | carrier (C.3)     |
    +---------+--------------------+--------------+-------------------+

                                  Table 21

C.2.  Failure-code mapping

   Carrier failure codes collapse onto CLC-D's three codes.  The mapping
   is many-to-one and is therefore *not reversible* without the carrier
   recording which of its own codes applied before mapping.

Wei                       Expires 31 March 2027               [Page 100]
Internet-Draft                   CLC-v1                   September 2026

    +=================+========================+======================+
    | Carrier         | Carrier code / failure | CLC-D reason         |
    +=================+========================+======================+
    | ATN Section 9.1 | different id           | different_namespace  |
    | identity rule   |                        |                      |
    +-----------------+------------------------+----------------------+
    | ATN Section 9.1 | different              | child_exceeds_parent |
    | identity rule   | schema.digest          |                      |
    +-----------------+------------------------+----------------------+
    | ATN Section 9.2 | raised resource_bounds | params_not_narrower  |
    +-----------------+------------------------+----------------------+
    | AAT I4          | tool outside parent    | different_namespace  |
    |                 | set                    |                      |
    +-----------------+------------------------+----------------------+
    | AAT I4          | constraint not         | params_not_narrower  |
    |                 | subsumed               |                      |
    +-----------------+------------------------+----------------------+
    | AIP Section 4.4 | aip_scope_insufficient | child_exceeds_parent |
    +-----------------+------------------------+----------------------+
    | AIP Section 4.4 | aip_budget_exceeded    | params_not_narrower  |
    +-----------------+------------------------+----------------------+
    | AIP Section 4.4 | aip_depth_exceeded     | child_exceeds_parent |
    +-----------------+------------------------+----------------------+
    | AAE Section 3   | action not a subset    | different_namespace  |
    +-----------------+------------------------+----------------------+
    | AAE Section 3/  | constraint not more    | params_not_narrower  |
    | Section 7.4     | restrictive            |                      |
    +-----------------+------------------------+----------------------+
    | AOA Section 6.2 | scope not strictly     | child_exceeds_parent |
    |                 | narrower               |                      |
    +-----------------+------------------------+----------------------+
    | AEGIS           | authority exceeds the  | params_not_narrower  |
    | AIAM1-DEL-010   | delegator's            |                      |
    +-----------------+------------------------+----------------------+
    | AEGIS           | capability the         | different_namespace  |
    | AIAM1-DEL-011   | delegator lacks        |                      |
    +-----------------+------------------------+----------------------+

                                  Table 22

   The collapse is visible in the right column: child_exceeds_parent is
   reached by both AIP scope and AIP depth failures and by an ATN digest
   mismatch, and params_not_narrower by every carrier's bound-widening.
   A carrier that needs its own code back must keep it alongside the
   CLC-D reason; CLC-D guarantees the stable language-level reason, not
   the carrier's.

Wei                       Expires 31 March 2027               [Page 101]
Internet-Draft                   CLC-v1                   September 2026

C.3.  Profile obligations exercised by the corpus

   Two of the Section 13.8.1 obligations are visible in the pinned
   vectors:

   *  *Identity dimensions the language cannot carry fail closed.* ATN's
      schema digest is carried as an identifier segment (ccx-043) rather
      than dropped, so a digest mismatch is child_exceeds_parent instead
      of a silent match.

   *  *Semantics the relation cannot see are a profile limitation, not a
      CLC gap.* AEGIS's allowed_roles/environment/risk_level and AAE's
      validity have no CLC field; the profile leaves them to the carrier
      and asserts nothing about them.  They are not modelled here, and a
      profile MUST NOT claim containment of a dimension the relation
      cannot see.

Acknowledgements

   Iman Schrock (EMILIA Protocol) reviewed the intersection and
   constraint semantics against the revision 1.1 corpus and supplied the
   adversarial cases that revisions 1.2 and 1.3 fix: nested partial
   overlap in intersection, constraint value handling for max_rows, and
   the public entry-point contract.  For revision 1.4 he re-ran the Go,
   Python and TypeScript implementations and the 1,184 property cases,
   and closed the two objections he had raised against the max_rows
   value domain and the UTF-8 size bound.  For revision 1.5 he re-ran
   the three Go suites — 105 authorization, 30 evidence and 13 crosswalk
   cases — and supplied the four corrections that revision carries: the
   collapse from three-valued evaluation to the binary report
   (Section 10), the separation of allow_unresolved from evidence
   (Section 11), the scope of delegation (Section 12), and the boundary
   between this projection identity and CAID (Sections 4.2, 4.3 and
   6.4).  For revisions 1.6 to 1.8 he re-ran the three implementations
   and reported the raw-boundary cases those revisions fix: the HTML-
   escaping and key-order defects in the canonical serializer, the
   decoded paths that disagreed with the raw path on malformed Unicode,
   and the raw size checks that counted a number by its received
   spelling and a literal astral character by UTF-16 code unit.
   Revision 1.9 folds the delegation-containment relation and the CLC-D
   class into this document (Section 13, Appendix C), retiring the
   formerly separate containment extension, and pins it against six
   adjacent capability drafts (ATN, AAT, AIP, AAE, AOA, AEGIS) reviewed
   on 2026-09-21.

Author's Address

Wei                       Expires 31 March 2027               [Page 102]
Internet-Draft                   CLC-v1                   September 2026

   Jijie Wei
   Individual
   Email: pki@varwof.com
   URI:   https://varwof.com

Wei                       Expires 31 March 2027               [Page 103]