# API Contract Artifacts Contract (S2 contract 9)

Status: DRAFT — awaiting ratification (webui-audit S2, contract 9 of 9).
Authority: `docs/API/README.md` declares the single API documentation
boundary: `docs/API/OPENAPI.yaml` as the canonical machine-readable
HTTP/WebSocket contract and `docs/API/ENDPOINTS.md` as the
human-readable endpoint, authentication, permission, and error index —
both declared, neither present (the webui-audit REPORT's prerequisite
9). `docs/SITEMAP.md` records that canonical scope requires maintainer
approval; ratification of this contract by the repository maintainer
supplies that approval for these two artifacts. Contract 5 is the
**Tool↔Gateway Mapping Contract (D8)** — S2 contract 5, PR #1438,
landing as `docs/requirements/tool-gateway-mapping.md` in the same S2
ruling batch as this contract; it is not yet on the integration trunk,
so its file does not exist in this branch's tree. Its §4 binds the
per-operation request/result/error/audit envelope, and its §4.2
defines the closed per-family error taxonomy this contract's
references bind to. Every "contract 5" reference in this document
resolves against that document as ratified; §6.5 states the ordering
dependency. This contract binds only how the
two artifacts are produced, what they must cover, and how they are
kept true.

Revision 2 (luna review F1–F5): the generation model now defines its
complete input — a declared route-metadata layer every production
route must carry, with the generator failing closed on any route
lacking extractable metadata, and the migration of today's local-DTO,
inline-typed, and raw-handler routes named as implementing-PR scope
(F1). The §7.2 inventory is closed over the real surface: raw
framework mounts, delegated surfaces, conditional/tier-gated
controllers, SSE, and Socket.IO events in both directions, with an
explicit delegation/exclusion list (F2). The `ENDPOINTS.md`
no-duplication rule is reattributed as a new disclosed policy with a
structured entry format and a mechanical checker (F3). Witnesses are
added or made executable for 3.1 validity, family coverage in
`ENDPOINTS.md`, per-entry required content, and the retirement
equivalence algorithm (F4). §8 discloses every untraced binding rule,
and the SITEMAP approval claim is restated in the sitemap's own terms
(F5).

Revision 3 (luna re-review residuals + F6): the route-metadata layer
is now a defined closed record with a per-field completeness
predicate, and "production route" is a defined, decidable term (F1).
The §7.2 scans cover all production source with a named
registration-primitive list, a defined deployment-profile set, and a
literal-event-name rule backed by one typed event registry (F2).
`ENDPOINTS.md` gets an exact entry grammar and a repository checker
script with defined reject rules (F3). §7.4 enumerates its
normalization and comparison rules, defines covering-entry selection
and the legacy-derivation rule, and §7.6 pins a named validator (F4).
§8 discloses the §7 witness machinery itself (F5). Delegated surfaces
get a machine-checkable delegation allowlist with a below-prefix
boundary check (F6).

Revision 4 (luna re-review residuals + F7/F8): the route-metadata
record is redefined as the route's **registration input** — the
registration primitive consumes the record and derives the installed
auth guard and permission enforcement from it, so record-vs-code
divergence on those fields is structurally impossible, and a new
source-consistency witness (§7.7) cross-checks the remaining fields
(F1). The auth-class enumeration is pinned in this contract (four
values, amendment-only), and Socket.IO event records get
direction-specific field semantics replacing the status-code response
map (F1). The scan roots are `apps/`, `packages/`, and `plugins/` —
all three named everywhere — and registration-primitive detection is
by TypeScript symbol identity against the framework's type
declarations, closing the wrapper/alias/dynamic-name escapes (F2).
§2.2 defines an exact entry grammar (heading form, literal field
labels, cardinality, link syntax, anchor syntax, event-item syntax)
and §7.5's reject rules cover every Markdown node type plus a
one-entry-per-operationId uniqueness rule (F3, and the covering-entry
ambiguity in F4). §7.4 defines cycle handling, `$ref` scope,
per-(path, method) matching, no-body equality, and a common
representation for error-code comparison (F4). The delegation
boundary requires the named module to contain exactly the mount call
and nothing below the prefix from any module, with field-by-field
mirror equality (F6). The exclusion list gets a closed, machine-
checked eligibility rule: an excluded route emitted by any
production-profile inventory pass is a witness failure (F7). The
contract 5 dependency is named explicitly — PR #1438, same ruling
batch — with an ordering clause and a contingency amendment rule
(F8).

Revision 5 (luna r4 residuals F1/F2/F4 + F9): the auth-class
enumeration grows to six values so the live alternation guards are
representable — `admin` (session-with-admin-privilege OR admin bearer
token) and `federation` (mTLS client identity plus federation grant)
— and the generator carries a closed `ApiAuthClass` → OpenAPI
security-requirement mapping (F1, F4). §7.7 gains an emitted-status
closure: every status-emission site in a route's handler closure must
resolve to a literal status contained in the record's response map,
with unresolvable emissions a scan failure (F1). The §7.2 scan
requires every registered path argument and decorator path value to
resolve to a compile-time string constant equal to the record's path
template; an unresolvable or mismatched path is a scan failure (F2).
The response map admits an explicit no-body marker per status, and
§7.4 canonicalizes body-less responses (including legacy
description-only responses) to that marker so the deep equality has a
defined input (F4). The §4.6 registry gains a total, single-valued
event-ownership map to `ENDPOINTS.md` family names; §2.2 binds each
event item to its owning family's entry; §7.5 rejects duplicate and
wrong-family event items; and §7.2/§7.5 assert bidirectional set
equality between the event inventory and the file's event items (F9).

Revision 6 (luna r5 residuals F1/F2/F4/F9 + NF1–NF5): the
emitted-status closure becomes a defined, total algorithm over five
named site classes — record-derived auth-guard refusals (each
`ApiAuthClass` carries a closed refusal-status set in the §1.6
mapping table), bootstrap-registered global interceptors, decorator
status metadata, the handler's type-checker call-graph closure with
unresolved-edge failure, and raw response sinks by Node symbol
identity — and the comparison becomes bidirectional, rejecting
phantom map statuses no site produces (F1, NF1). §7.2 classifies
registration primitives as path-bearing or non-path-bearing, defines
the raw-mount rule for hook-internal URL dispatch, and defines the
framework path composition (class-prefix arrays × method suffixes,
default empty paths, normalization) applied before the exact
template comparison (F2, NF2). The `ApiAuthClass` → OpenAPI mapping
table is pinned in-contract with exact requirement-set shapes under
OpenAPI 3.1 OR/AND semantics (admin = two requirement objects;
federation = one object with two schemes), a new guard-equivalence
witness (§7.7(e)) ties the mapping to the live guard semantics, and
§7.4 resolves document-level security inheritance before comparison
(F4, NF3). The event scan gains a mechanical server/client boundary
by socket-package type identity, the comparison element is defined
as the (direction, name) pair, and the two registries' value sets
must be pairwise disjoint (F9, NF4). The ownership map's domain must
exactly equal the registry union (stale keys rejected), and
`Operations:` becomes conditional so an event-only family is
grammatical (NF5).

Revision 7 (luna r6 residuals F1/F2/F4/F9 + NF1–NF4 + NF6): the
§7.7(d) status-site class (2) becomes an interception-component
closure over EVERY binding scope — global-by-call, global-by-DI-token,
controller-scoped, and route-scoped pipes/filters/interceptors/guards
— with per-component source-asserted status sets, scope-exact route
attribution, and unenumerated-binding failure, and the §7.2 primitive
list names the global binding calls and DI tokens (F1). The raw-mount
rule records one route per (dispatched prefix × accepted method)
branch, requires segment-boundary prefix semantics, and fails on
non-constant method dispatch or a boundary-escaping prefix predicate
(F2, NF2). The §1.6 mapping table pins requirement-object exactness
(exact scheme keys, empty scope arrays), binds each scheme to a
concrete credential predicate, declares multi-credential evaluation
order, and REDEFINES each refusal-status set as the computed union of
the guard's refusal-branch statuses; §7.7(e) drives every enumerated
refusal branch and every mixed presentation and asserts the written
set equal to the computed union in both directions (F4, NF3). The
socket-event boundary gains a decorator-site rule — a subscribe
decorator resolved to the socket framework package on a
gateway-decorated class is a server-side inbound site by compile-time
package identity — and the event closure additionally requires the
registry union to equal the inventoried server-event set, rejecting
stale registry events (F9, NF4). §7.7(d)(4) call edges into
non-production packages that receive the reply/response object become
declared delegated-emission sites joined to a pinned external status
contract, and §7.7(d)(5) framework defaults each require a
pinned-version citation plus a runtime probe (NF1). §2.2 requires
every family to have a non-empty surface, rejected by a new §7.5 rule
(NF6).

Revision 8 (luna r7 residuals F1/F2/NF1/NF2 + NF7): §7.7(d)(2)
attributes interception components to their actual execution
pipeline — framework components (all four binding scopes) attach to
framework-routed routes ONLY, adapter-level hooks attach to exactly
the routes their dispatch predicate accepts, and a raw-mounted route
(dispatched by a hook before the framework router) never carries a
framework component's status — with a raw-hook phantom control (F1).
The §7.2 raw-mount rule gains method-partition exhaustiveness: every
control-flow path is an acceptance path (pinned to one method
constant, one record), a refusal path (constant-status refusal, no
forwarding — the only place a complement or unguarded test is
admissible), or a scan failure; a forwarding edge into a
non-production package escapes failure only through a declared
external method contract, which then defines the branch's record set
(F2, NF2). The §7.7(d)(5) default rule becomes an exhaustive
inventory: total emitting/non-emitting classification over every
option key of the pinned framework and adapter options types plus
every configured composition-root option, an unclassified key a
witness failure (the adapter body-size limit is the migration
example), citation-plus-probe per emitting entry (NF1). §2.2 defines
three entry kinds — surface, delegated-surface (`Delegated:` with
exact four-sub-item syntax mirroring the §4.4 allowlist), and
exclusion (`Excluded:` with exact per-route sub-item syntax
mirroring the §4.1 script list) — so non-surface entries are
grammatical without phantom operations; §7.5(k) becomes the
kind-partition rule and new rule (l) checks the exclusion mirror
(NF7).

Revision 9 (luna r8 residual NF1 + NF8/NF9): the §7.7(d)(5) default
inventory attributes by execution pipeline, never by configuration
scope alone — each response-emitting entry names its emitting
lifecycle stage, and its status attributes to framework-routed
routes plus exactly those raw-mounted routes whose dispatch point
executes after that stage; a pre-parsing raw hijack therefore never
carries a body-parsing rejection status, the body-size-limit probe
becomes a two-pipeline pair, and a body-limit status on a raw route
must have a status site in the route's own callback chain (NF1).
§7.3's content witness partitions by the §2.2 entry kind — surface
fields are checked on surface entries only; delegated-surface and
exclusion entries are checked for their own kinds' field content,
with the §4.4/§4.1 mirrors staying in §7.5(g)/(l) (NF8). The §7.2
method partition gains a URL fall-through class: a path dominated
by the negation of the mount's URL-dispatch predicate may forward
only to the hook chain's continuation, emits nothing, needs no
method pinning, and contributes no record — the method partition
binds exactly the URL-accepted branch, and a URL-missed path
forwarding anywhere but the continuation is still a scan failure
(NF9).

Revision 10 (luna r9 residual NF1): the §7.7(d)(5) AFTER relation
becomes decidable. The witness produces a **pinned lifecycle-order
map** — the totally ordered, citation-backed list of the pinned
adapter version's documented request-lifecycle phases — and the
§7.2 primitive classification records each hook registration's
lifecycle phase as part of the primitive's identity. A raw mount's
dispatch point resolves to the map entry of its recorded hook
phase; an emitting entry resolves through its citation; AFTER is
strict map order. An unresolved dispatch point or emitting entry,
an unresolvable hook phase, and a same-stage pair are each a
FAILURE naming the site — attribution is never assumed for an
unordered pair, and the repair is either restoring resolvability or
a per-(route, entry) probe pair in the body-size pair's style whose
recorded observations decide the attribution; an attribution
carried by neither the map order nor a recorded probe pair is a
phantom.

This contract binds the artifact definitions (§2), the production
model (§3), coverage obligations (§4), legacy migration (§5), phase
timing (§6), witnesses (§7), and disclosed drafting additions (§8).
Operation semantics stay with contract 5 and the owning S2 contracts;
documentation placement rules stay with the docs atlas
(`docs/README.md`).

## 1. Definitions

1. **The artifacts**: `docs/API/OPENAPI.yaml` and
   `docs/API/ENDPOINTS.md`, exactly as declared by `docs/API/README.md`.
2. **Coverage**: the set of Gateway HTTP routes (however registered),
   SSE routes, and Socket.IO events (both directions) the artifacts
   describe.
3. **Drift**: any difference between an artifact and the production
   surface it documents — an undocumented live route or event, a
   documented phantom, or a divergent schema/auth/error description.
4. **Delegated surface**: a mounted path prefix whose request handling
   is delegated wholesale to an embedded third-party engine (for
   example an auth engine's own route tree): the Gateway registers the
   mount; the engine defines the routes. Only a prefix listed in the
   §4.4 delegation allowlist is a delegated surface; any other prefix
   is ordinary surface, documented route-by-route.
5. **Production route**: any HTTP or SSE route, or Socket.IO event
   (either direction), registered by production source (`apps/`,
   `packages/`, and `plugins/` — the three production roots, named
   identically in every scan in this contract; tests excluded) under
   ANY deployment profile (§4.5),
   excluding only entries on the §4.1 exclusion list and routes below
   a §4.4 delegated prefix. The term is decidable: a route is a
   production route iff the §7.2 inventory emits it, and the §7.2
   scan definition — not intent — is the boundary.
6. **Route metadata record**: the closed per-route registration input
   (§3.1). The record is not documentation of a handler — it IS the
   handler's registration configuration: the registration primitive
   consumes the record, and the installed authentication guard and
   permission enforcement are DERIVED from the record's declared auth
   class and permission rule reference, so the record cannot diverge
   from the code on those fields; a route registered without a record,
   or with enforcement not derived from its record, fails §7.7. Its
   fields, all REQUIRED for HTTP and SSE routes: a unique operation
   id; the HTTP method and path template (or the SSE marker); the
   request schema reference (or an explicit no-body marker); the
   response map — every status code the route can emit, mapped to a
   schema reference **or an explicit no-body marker** (a body-less
   response is declared, never implied by omission), with at least
   one success entry, and with one
   error entry per HTTP status the route's declared error-code set
   maps to under the contract 5 §4.2 status mapping (so the reachable
   error statuses are exactly the declared taxonomy's statuses,
   cross-checked by §7.7); the authentication requirement, one value
   from the closed enumeration **`ApiAuthClass`**, exported from the
   shared types package with exactly the values `none`
   (unauthenticated), `session` (authenticated account), `api-key`
   (agent/service key), `admin` (the alternation guard: an
   authenticated session holding platform-admin privilege OR the
   admin bearer token — the alternation itself is the class, so the
   live admin surface is representable without collapsing it into
   `session`), `federation` (mTLS client identity plus a valid
   federation grant — both required), and `bootstrap` (identity §3
   pre-epoch surface) — amendment-only, pinned here rather than
   deferred. Each `ApiAuthClass` value maps to exactly one OpenAPI
   security-requirement set under a closed mapping table pinned
   HERE (the generation script carries it verbatim, and §7.4(b)
   compares security through it). OpenAPI 3.1 semantics are
   binding: a `security` array with multiple requirement objects
   means OR across objects; multiple schemes inside one requirement
   object mean AND. The table, over scheme names the implementing
   PR defines exactly once in `OPENAPI.yaml`'s
   `components.securitySchemes`: `none` → the empty security array;
   `session` → exactly one requirement object naming the session
   scheme; `api-key` → exactly one requirement object naming the
   API-key scheme; `admin` → exactly two requirement objects, one
   naming the admin-privileged-session scheme and one naming the
   admin-bearer scheme (the alternation as OpenAPI OR);
   `federation` → exactly one requirement object naming both the
   mTLS scheme and the federation-grant scheme (the conjunction as
   OpenAPI AND); `bootstrap` → exactly one requirement object
   naming the bootstrap scheme. **Requirement-object exactness**:
   each requirement object in the table contains EXACTLY its named
   scheme key(s) and no other key, and every scheme key maps to
   exactly the empty scope array (these artifacts define no OAuth
   scopes); the generator emits these exact objects and §7.4(b)
   compares against them — a requirement object with an extra key, a
   missing named key, or a non-empty scope array is not the mapped
   set. **Scheme binding**: the table binds each scheme name to a
   concrete credential predicate — the credential's transport
   location (header, cookie, or TLS peer identity) and the
   acceptance predicate the record-derived guard implements for it —
   so "a request satisfying the scheme" is constructed from the
   binding, never improvised; a scheme with no bound predicate, or a
   binding the guard source does not implement, fails §7.7(e).
   **Evaluation order**: for a class whose guard evaluates multiple
   credential alternatives in a fixed order (the live admin guard
   checks the bearer credential before the session), the table
   declares that order as part of the class's entry; the OpenAPI OR
   states which credentials MAY authorize, the declared order states
   what a mixed presentation does, and §7.7(e) drives the mixed
   cases against the declaration. The table additionally assigns
   each class a closed **refusal-status set**, DEFINED rather than
   merely stated: the set EQUALS the union, over the refusal
   branches of the class's record-derived guard, of each branch's
   emitted status — branches and statuses enumerated mechanically by
   §7.7(e) applying the §7.7(d)(4) emission-site rules to the guard
   and its callee closure; the implementing PR writes each set into
   the table from that enumeration, and §7.7(e) asserts the written
   set equal to the enumerated union in both directions (a written
   status no branch emits, and a branch status missing from the
   written set, each fail). The sets are consumed by §7.7(d) class
   (1). The mapping is total over the enumeration and
   injective — no two classes map to the same requirement set; the
   permission rule reference (owning contract plus rule identifier);
   and the error-code set, drawn from the contract 5 §4.2 closed
   taxonomy. **Socket.IO event records** carry direction-specific
   fields in place of the response map: an inbound event record has
   the event name, direction, payload schema reference, and an ack
   schema reference or an explicit no-ack marker; an outbound event
   record has the event name, direction, and payload schema
   reference; the success-entry rule is defined as inapplicable to
   event records. **Completeness predicate**:
   every field present AND every reference resolvable, evaluated per
   field; a violation names the route and the exact failing field.

## 2. Artifact contract

1. **`OPENAPI.yaml`** is one file, valid OpenAPI 3.1 (witness §7.6),
   and the canonical machine-readable contract for the Gateway's HTTP
   surface. Socket.IO events, which OpenAPI cannot express natively,
   are documented in `ENDPOINTS.md` and referenced from the OpenAPI
   description block; inventing a pseudo-path encoding for them inside
   `OPENAPI.yaml` is non-conformant.
2. **`ENDPOINTS.md`** is the human index, in an exact entry grammar:
   the file is a sequence of entries, each entry exactly one `##`
   heading whose text is the endpoint family name (unique in the
   file), followed by a bullet list whose items begin with exactly
   one of the literal field labels `Summary:`, `Operations:`,
   `Auth:`, `Permissions:`, `Errors:`, `Events:`, `Delegated:`,
   `Excluded:` — each label at most once per entry. **Entry
   kinds**: every entry is exactly one of three kinds, decided by
   its labels. A **surface entry** carries `Summary:`, `Auth:`,
   `Permissions:`, and `Errors:` (REQUIRED) and neither
   `Delegated:` nor `Excluded:`; within a surface entry,
   `Operations:` is REQUIRED for a
   family with at least one `OPENAPI.yaml` operation and ABSENT for
   an **event-only family** (a family whose entire surface is
   Socket.IO events — such a family MUST carry `Events:`, which is
   how it satisfies the grammar with no operations to link), and
   `Events:` is REQUIRED for a family with Socket.IO events and
   absent otherwise. A **delegated-surface entry** carries exactly
   `Summary:` and `Delegated:` and no other label. An **exclusion
   entry** carries exactly `Summary:` and `Excluded:` and no
   other label, and the file contains at most one exclusion entry
   (absent when the §4.1 exclusion list is empty). **Non-empty
   surface**: every surface entry carries at least
   one of `Operations:` or `Events:` — a family with neither
   documents nothing; a delegated-surface or exclusion entry
   documents its surface through `Delegated:`/`Excluded:`
   instead, needing no phantom operation. An entry matching no
   kind, or more than one, is not grammatical (reject rule
   §7.5(k)),
   so a stale zero-surface family cannot survive the grammar.
   Field contents: `Summary:` — the family's behavior summary, free
   prose; `Operations:` — one or more Markdown links, each with link
   text equal to an `operationId` and link target
   `./OPENAPI.yaml#operation/{operationId}` (the anchor grammar:
   the fragment is the literal string `operation/` followed by the
   `operationId`; the link RESOLVES iff that `operationId` exists as
   an operation in the committed `OPENAPI.yaml`) — operations are
   referenced by link only, never by restated definition, and each
   `operationId` is linked from exactly one entry in the whole file;
   `Auth:` — the `ApiAuthClass` value (§1.6); `Permissions:` — the
   permission/authorization rule naming the owning contract
   (contract 2 for chain authorization, the SOT for workspace
   membership); `Errors:` — error semantics including the contract 5
   §4.2 taxonomy codes; `Events:` — one bullet sub-item per event,
   each exactly `inbound {event-name}` or `outbound {event-name}`
   where `{event-name}` is a member of the §4.6 event registry, the
   item appears in exactly the entry of the family the §4.6
   ownership map assigns to that event, and each (direction, name)
   pair appears at most once in the whole file; `Delegated:` —
   exactly four bullet sub-items, each exactly one of
   `prefix {mount-prefix}`, `engine {package-name}
{version-constraint}`, `contract {engine-contract-reference}`,
   and `module {registering-module}` (each sub-item label exactly
   once), whose values mirror one §4.4 allowlist entry
   field-by-field (string equality after whitespace trimming —
   the §4.4 mirror rule, reject rule §7.5(g)); `Excluded:` — one
   bullet sub-item per excluded route, each exactly
   `{HTTP-method} {path-template} {reason-class} {guard}` where
   `{reason-class}` is `test-only` or `dev-only`, mirroring the
   §4.1 generation-script exclusion list field-by-field under the
   same string equality (reject rule §7.5(l)).
   `ENDPOINTS.md` MUST NOT contain
   request/response schema definitions for any operation that exists
   in `OPENAPI.yaml` (mechanical rule: the reserved tokens
   `requestBody`, `responses`, `parameters`, `schema`, `properties`
   appear nowhere in the file, in any Markdown node type — witness
   §7.5; entry prose must avoid the reserved tokens, a disclosed
   strictness). This
   no-duplication rule and the entry grammar are new policy disclosed
   in §8 (the `docs/API/README.md` boundary as written binds guide
   books, not `ENDPOINTS.md` itself).
3. Both artifacts are committed repository files, reviewed in PRs like
   any contract text; neither is a build output that exists only in CI.

## 3. Production model

1. **Generation (per the ruling).** `OPENAPI.yaml` is GENERATED from
   the Gateway source by a deterministic repository script whose
   input is the **route metadata record** (§1.6) of every production
   route (§1.5): route decorators plus typed request/response schemas
   (the contract 5 §4.1 shared DTOs where the operation is a mapped
   tool family; declared per-route schemas elsewhere), each record
   satisfying the §1.6 completeness predicate. **The generator fails
   closed:** it first computes the §7.2 inventory's route set, then
   requires a complete metadata record for every route in it; a
   missing record, a missing field, or an unresolvable reference is a
   generation error naming the route and field — never a silently
   omitted or partially emitted operation. Because the route set
   comes from the inventory and completeness is the per-field §1.6
   predicate, a byte-stable but incomplete artifact cannot pass: an
   unrecorded route fails generation, and a recorded route emits
   every §1.6 field into its OpenAPI operation (operation id,
   schemas, per-status responses, security requirement, and the
   permission reference and error-code set as declared extension
   fields). Because the record is the registration input (§1.6), the
   generator's input and the route's installed enforcement share one
   source; §7.7 witnesses that no route bypasses record-derived
   enforcement and that the record fields the framework does not
   consume (error-code set, response map) match the code. Today's
   local-DTO, inline-typed, and raw-handler routes
   therefore migrate to complete metadata records in the implementing
   PR. The generated output is committed, and CI regenerates and
   byte-compares it (§7.1). Hand edits to `OPENAPI.yaml` are
   non-conformant — a description that cannot be expressed from
   source annotations goes into the annotations, or into
   `ENDPOINTS.md`.
2. **Normative direction.** Generation documents the code; it does not
   ratify it. The S2 contracts and their witnesses remain the
   normative gates on what the surface may be; the artifacts make the
   measured surface visible and machine-checkable. A generated
   description of a non-conformant route is drift evidence against
   that route's owning contract, not authority for it.
3. `ENDPOINTS.md` is hand-authored (its content — permission
   rationale, cross-contract links, event semantics — is judgment, not
   extraction), against the §7.2 coverage and §7.3 content witnesses.

## 4. Coverage obligations

1. **Closure over the real surface.** Coverage extends to every
   production route however registered: framework-decorated
   controllers and gateways AND raw framework mounts registered
   outside them (the bootstrap's own route/hook registrations), SSE
   routes, and conditionally registered (deployment-tier-gated)
   controllers — documented in the union, each conditional route
   marked with its condition. **Delegated surfaces** (§1.4) are
   documented as delegated-surface entries (the §2.2 entry kind,
   `Delegated:` field) naming the mount, the
   delegating engine, and that engine's own contract — not
   re-documented route-by-route. **Socket.IO events are covered in
   both directions:** subscribed (inbound handler) events and
   server-emitted events. Test-only and development-only surfaces are
   excluded via an exclusion list that is explicit in the generation
   script and enumerated in `ENDPOINTS.md` (the §2.2 exclusion
   entry, one `Excluded:` sub-item per route), never implicit. The
   list has a **closed, machine-checked eligibility rule**: each
   entry names the route, its reason class (`test-only` or
   `dev-only`), and the exact guard — the deployment-profile branch
   or build-time flag — that prevents its registration in every
   §4.5 production profile; eligibility is DEFINED as "not emitted
   by the §7.2 inventory in any production profile", and the §7.2
   witness fails if any excluded entry is emitted by any
   production-profile pass. A live route added to the list is
   therefore a witness failure, not a silent omission. The
   exclusion and delegation lists are new policy disclosed in §8.
2. Every documented operation carries: its authentication requirement;
   its authorization rule by reference to the owning contract; its
   error responses drawn from the contract 5 §4.2 closed taxonomy.
   Where contract 2's no-existence-oracle rule applies, the
   documentation shows authorization refusal and not-found as
   indistinguishable on the wire — documenting a distinguishing
   response there is a coverage defect, not a documentation style
   choice.
3. New surface ships documented: a PR that adds or changes a Gateway
   route or event lands with the regenerated `OPENAPI.yaml` and any
   needed `ENDPOINTS.md` entry in the same PR, enforced by the §7.1
   drift gate — there is no "docs to follow" state.
4. **Delegation allowlist.** The generation script contains one
   machine-readable delegation allowlist; each entry names: the mount
   prefix; the delegating engine package (name and version
   constraint); a reference to the engine's own route contract (the
   engine package's published API document); and the one production
   module that registers the mount. The §7.2 witness enforces the
   boundary in both halves: (a) the named registration module
   contains exactly one registration-primitive call — the mount
   itself — and no other; (b) any route registration, from ANY
   module including the named one, whose path resolves below an
   allowlisted prefix fails the witness — the single mount call is
   the only permitted registration under the prefix, so an app-owned
   handler cannot hide there, inside or outside the named module.
   `ENDPOINTS.md`'s delegated-surface entries mirror the allowlist
   **field-by-field**: prefix, engine package name and version
   constraint, engine contract reference, and registering module are
   each equal by string comparison after whitespace trimming —
   "mirror one-to-one" means this equality, checked by §7.5.
5. **Deployment profiles.** The authoritative profile set is the
   closed deployment-tier enumeration in the Gateway's configuration
   schema (the same enumeration the conditional registrations branch
   on); the implementing PR names it. The inventory is evaluated once
   per profile and unioned; each conditional operation carries a
   declared condition extension field naming the profile(s) it exists
   in. A conditional branch on anything outside the named enumeration
   fails the §7.2 witness.
6. **Event-name discipline.** The event registry is two exported
   `as const` readonly records in the shared types package — one
   inbound, one outbound — whose values are unique string literals;
   that shape is part of this contract, not implementing-PR
   discretion. Every Socket.IO event name, in both directions, is a
   member of the applicable registry, referenced by symbol (a
   registry property access), never by a re-typed string; emitting or
   subscribing with a dynamically constructed name, or through a
   helper whose name argument does not resolve (by the type-checker's
   symbol identity, §7.2) to a registry member, is non-conformant
   (§7.2 scans for non-registry names). The registry is the
   comparison target for the event inventory. **Event ownership:**
   the shared types package additionally exports one `as const`
   readonly ownership record mapping every registry event name (both
   directions) to exactly one `ENDPOINTS.md` family name (the §2.2
   `##` heading text). The map is total over the union of both
   registries and single-valued — every event has exactly one owning
   family — and it is the deterministic rule for which entry
   documents which event; a registry event absent from the map, or
   mapped to a family with no `ENDPOINTS.md` entry, fails §7.5.
   The map's domain EXACTLY equals the union of the two registries'
   event names: a map key naming no registry event (a stale key)
   fails §7.5 just as a missing key does. The two registries'
   literal value sets are pairwise DISJOINT — no event name appears
   in both the inbound and the outbound registry — witnessed by
   §7.5; disjointness is what makes the name-keyed ownership map
   well-defined over directional events. Everywhere this contract
   compares event sets (§7.2, §7.5), the comparison element is the
   **(direction, name) pair**, never the bare name.

## 5. Legacy migration

1. `docs/openapi-tess.yaml` (17 paths, Tess-scoped) remains labeled a
   scoped legacy artifact until the consolidated `OPENAPI.yaml`
   demonstrably covers it: the `docs/API/README.md` retirement rule
   (verify paths, schemas, authentication, permissions, error
   behavior, and generated/client references) is binding, witnessed by
   §7.4's equivalence algorithm.
2. Retirement is deletion in a reviewed PR after the §7.4 witness
   passes; until then no consumer may treat the legacy artifact as
   canonical for anything beyond its own 17 paths. (The
   deletion-in-reviewed-PR form and the interim-consumer restriction
   are disclosed in §8.)

## 6. Phase timing

1. The artifacts, the metadata migration (§3.1), and the generation
   script are produced by an implementing PR after this contract is
   ratified; they are S4-phase platform work, not part of any S2
   ruling PR.
2. From the first commit of `OPENAPI.yaml` onward, the §7.1 drift gate
   is a required CI check on the integration trunk; the §4.3
   same-PR rule binds every subsequent surface-changing PR.
3. The build-first tool families (A5 ranks, contracts 1–3, 5, 8) are
   documented as they land, under the same rule — the roll-up query,
   for example, ships with its OpenAPI operation and its
   `ENDPOINTS.md` entry in its implementing PR.
4. The phase placement and rollout sequencing in this section are
   disclosed in §8.
5. **Contract 5 ordering dependency.** This contract's taxonomy and
   envelope references bind to the Tool↔Gateway Mapping Contract
   (S2 contract 5, PR #1438, `docs/requirements/tool-gateway-
mapping.md`), ratified in the same S2 batch but not yet on the
   integration trunk. No implementing PR under this contract may
   land before contract 5's document is on the trunk and the
   shared-types taxonomy enums it requires exist. If contract 5 is
   not ratified, every §4.2-taxonomy reference here is unresolvable
   and this contract requires amendment before implementation —
   stated so the dependency is explicit rather than a dangling
   file reference (disclosed, §8).

## 7. Verification requirements

Binding on the implementing PRs; each witness names its commands,
scanned source roots, and compared files.

1. **Drift witness:** CI regenerates `OPENAPI.yaml` with the
   repository script and fails on any byte difference from the
   committed file; the witness proves it can fail by a control run
   against a mutated copy. Fail-closed controls (§3.1): one control
   per §1.6 field class — a fixture route with the whole record
   absent, and fixture routes each missing one field class (operation
   id, schema reference, response map, auth requirement, permission
   reference, error-code set) — each makes generation fail naming the
   route and field, not emit a partial document.
2. **Coverage witness:** an independent surface inventory — static
   enumeration over ALL production code in `apps/`, `packages/`, and
   `plugins/` (the §1.5 production roots),
   tests excluded (the A5 §1.2 inventory style), not limited to the
   bootstrap module — covering: framework-decorated controllers and
   gateways; every raw registration primitive of the HTTP framework
   (the implementing PR enumerates the primitive list — route, hook,
   middleware-mount, and plugin-registration calls, PLUS the
   application-composition calls and DI tokens that install
   cross-route interception components: the framework's global
   binding calls — `useGlobalPipes`, `useGlobalFilters`,
   `useGlobalInterceptors`, `useGlobalGuards` — and its `APP_PIPE`/
   `APP_FILTER`/`APP_INTERCEPTOR`/`APP_GUARD` provider tokens, both
   consumed by §7.7(d)(2) — and the witness
   asserts the list against the framework's registration API surface)
   wherever it occurs, including middleware modules; SSE
   routes; conditional registrations evaluated per §4.5 profile and
   unioned; and delegated-surface mounts, with the §4.4 boundary
   check (exactly-one-mount rule and the all-module below-prefix
   prohibition). **Detection is by symbol identity, not text:** the
   scan identifies registration-primitive call sites through the
   TypeScript type-checker, resolving each callee to its declaration
   in the framework package's type declarations — so an alias, a
   re-export, or a wrapper cannot escape (a wrapper's internal call
   to the primitive is itself a detected call site); a dynamic
   property access on the framework's registration object, or a
   callee the type-checker cannot resolve, is a scan FAILURE naming
   the site, never a silent skip — with one control per case
   (aliased call detected; wrapped call detected; dynamic-name
   access fails). **Primitive classification and path
   resolution:** the primitive list classifies every registration
   primitive as **path-bearing** (route registrations and mounts
   that take a path or prefix argument) or **non-path-bearing**
   (hook registrations such as request-lifecycle hooks, and plugin
   registrations with no path/prefix option); the classification
   is part of the asserted list, and an unclassified primitive is
   a witness failure. Path resolution applies to path-bearing
   sites; a non-path-bearing site is inventoried as a hook or
   plugin site with no path witness and is never a route entry by
   itself. Each hook registration's classification records the
   adapter lifecycle phase its registration primitive names (the
   phase is part of the primitive's identity in the asserted
   list); §7.7(d)(5)'s lifecycle-order map resolves raw-mount
   dispatch points to lifecycle stages through exactly this
   recorded phase, and a hook registration whose phase the
   checker cannot resolve is a scan failure naming the site. A hook or plugin callback that routes requests by URL —
   dispatching on the request path inside the callback, as the raw
   auth and MCP mounts do — makes the enclosing registration a
   **raw mount**: it must either appear in the §4.4 delegation
   allowlist or carry §1.6 records, **one per (dispatched prefix ×
   accepted HTTP method) branch**. The checker resolves BOTH
   dispatch dimensions: the prefix test against a compile-time
   string constant, AND the callback's method dispatch — every
   comparison of the request method against a constant, in the
   callback and its callee closure (the §7.7(d)(4) call-graph
   rules) — enumerating the accepted (prefix, method) pairs. Each
   pair carries a record whose path template equals the prefix
   constant and whose HTTP method equals the branch's method
   constant; an accepted branch with no matching record, a record
   no branch accepts, and a dispatch comparison (prefix or method)
   the checker cannot resolve to a constant are each a scan
   failure naming the site. **Prefix-boundary semantics**: a raw
   mount's record covers exactly the URLs equal to its prefix or
   extending it at a `/` segment boundary, and the code predicate
   must implement exactly that match — a predicate that also
   accepts a longer non-boundary URL (a bare prefix test accepting
   `/mcpfoo` under the prefix `/mcp`) is a scan failure naming the
   site: the code is corrected to a boundary test; the widened
   match is never absorbed by documentation.
   **Method-partition exhaustiveness**: enumerating constant
   method comparisons is necessary but not sufficient — the
   checker proves the callback's dispatch partitions its control
   flow. Every path from callback entry to a request-terminating
   action (a response emission or a forwarding of the request) is
   classified as exactly one of: (i) a **URL fall-through path** —
   dominated by the NEGATION of the mount's URL-dispatch predicate
   (the request does not match the dispatched prefix under the
   boundary semantics above): it forwards the request ONLY to the
   hook chain's continuation (the callback's done/next
   continuation, handing the request to later hooks and the
   framework router), emits nothing, needs no method pinning, and
   contributes no raw record — the raw MCP hook's prefix-miss
   branch that returns the request to the router is this class,
   not a scan failure; a URL-missed path that instead forwards
   into a handler, another dispatcher, or any package IS a scan
   failure naming the site; (ii) an **acceptance path** —
   dominated by the mount's URL-dispatch predicate holding AND
   positive equality tests of the request method
   against string constants that pin the path to exactly one
   method; that (prefix, method) pair carries a record; (iii) a
   **refusal path** — the ONLY classification where a complement
   test (`method !== X`) or an unguarded method test is
   admissible: the URL-dispatch predicate holds, and the
   path terminates in a constant-status refusal emission (by the
   §7.7(d)(4) emission-site rules) and forwards the request
   nowhere; or (iv) a **scan failure** — a URL-accepted
   complement-guarded or
   unguarded path that forwards the request (into a handler,
   another dispatcher, or any package — including back into the
   hook continuation, which for a URL-accepted request would
   double-dispatch it) accepts an unbounded
   method set and fails naming the site. The method partition
   therefore binds exactly the URL-accepted branch of the
   callback; the URL-miss branch is bound only to forward to the
   continuation and emit nothing. One carve-out mirrors
   the §7.7(d)(4) delegated-emission rule: a forwarding edge into
   a non-production package may instead carry a **declared
   external method contract** — the accepted-method set the
   external package's pinned version documents for that entry
   point, declared alongside the edge's external status
   contract; the branch then contributes exactly one
   (prefix × method) record per documented method, the branch's
   record set equal to the declared set in both directions, and a
   forwarding edge with neither dominating single-method tests
   nor a declared method contract is a scan failure naming the
   edge (the live MCP existing-session path, which forwards
   requests of any method into the external transport, is the
   migration example; its sibling non-`POST` complement branch
   must classify as a refusal path). For path-bearing sites, the effective
   path is computed by the framework's composition rules before
   comparison: the class-level prefix (a string or an array — an
   array expands to one route per element) concatenated with the
   method-level suffix (which may be empty — the default empty
   path contributes nothing), then normalized (exactly: join
   segments with `/`, collapse duplicate separators, strip one
   trailing slash, ensure one leading slash); the normalized
   string is compared for exact equality with the route's §1.6
   record path template, normalized by the same rule. A path
   argument, prefix element, or suffix the checker cannot resolve
   to a compile-time string constant (`app.get(computedPath(),
handler)`, a computed decorator value) is a scan FAILURE naming
   the site, never an inventory entry with an undefined path —
   with one control per case (computed path argument fails;
   computed decorator path fails; an array class prefix expands to
   all elements; an empty method suffix composes with its class
   prefix; hook-internal dispatch on a non-constant prefix fails;
   a resolved path unequal to its record's template fails; a
   raw-mount method branch with no matching record fails; a
   raw-mount record no branch accepts fails; a non-constant method
   comparison fails; a non-boundary prefix predicate — accepting
   `/mcpfoo` under `/mcp` — fails; a complement method branch that
   forwards the request fails; a complement branch terminating in
   a constant-status refusal is admitted as a refusal path; a
   forwarding edge with a declared external method contract
   yields exactly the contract's records; a forwarding edge with
   neither single-method domination nor a declared method
   contract fails). The inventory equals the documented set in both
   directions: no undocumented live route, no documented phantom,
   each conditional operation carrying its §4.5 condition field, each
   delegated surface present exactly once. Socket.IO events are
   inventoried in both directions — inbound subscription handlers by
   handler scan, server-emitted events by emit-call scan — over
   **server-side sites only**, with a mechanical boundary: a
   subscribe/emit call site is server-side iff its receiver
   expression's type resolves, through the type-checker, to the
   server package's declarations (the `socket.io` package's
   `Server`, `Namespace`, or server-side `Socket` types); a
   receiver typed by the `socket.io-client` package is a client
   site, excluded from BOTH direction inventories (the TUI socket
   hook and the Discord/Telegram plugin clients are client sites);
   a receiver whose type the checker cannot resolve to exactly one
   of the two packages is a scan failure naming the site.
   **Decorator sites** have no receiver expression, so the boundary
   there is compile-time package identity instead: a method
   decorated with the socket framework's subscribe decorator —
   resolved by the type-checker to the framework socket package's
   `SubscribeMessage` declaration — inside a class decorated with
   that same package's gateway decorator (`WebSocketGateway`) is a
   server-side inbound handler site; its event name is the
   subscribe decorator's argument, which must resolve to a §4.6
   registry member by symbol reference exactly as call-site names
   must; a subscribe decorator whose argument does not so resolve,
   or one on a class without the gateway decorator, is a scan
   failure naming the site. Controls: a client emit is not
   inventoried as server-emitted; a client subscription is not
   inventoried as an inbound handler; an unresolvable receiver
   fails; a decorator-declared handler IS inventoried as an
   inbound site; a subscribe decorator with a re-typed string
   argument fails. The comparison element is the
   (direction, name) pair (§4.6), the registry of the matching
   direction is the comparison target, and any non-literal or
   non-registry event name at a server-side site is a scan
   failure. **Event-set closure:** the
   inventoried event set (both directions) and the set of `Events:`
   sub-items across all `ENDPOINTS.md` entries are equal as sets of
   (direction, name) pairs —
   every inventoried event appears as an item in its owning family's
   entry, and every item corresponds to an inventoried event; an
   inventoried event absent from the file, and a documented event the
   inventory does not emit, each fail the witness, with one control
   per direction. The closure has a THIRD leg: the inventoried
   server-event set also equals the §4.6 registry union, as sets of
   (direction, name) pairs, in both directions — a server-side site
   using a non-registry name is already a scan failure above, and a
   registry event no server-side site subscribes to or emits (a
   stale registry event, which §7.5(j)'s ownership-map checks alone
   cannot catch because a mapped family entry can exist without the
   item) fails the witness naming the event — with a
   stale-registry-event control. Additionally, every HTTP
   route family in `OPENAPI.yaml` has an `ENDPOINTS.md` entry, and
   every `ENDPOINTS.md` operation reference resolves to an existing
   `operationId` — both directions, mechanical.
3. **Content and taxonomy witness:** the content check partitions by
   the §2.2 entry kind, the same partition §7.5(k) enforces. A
   **surface entry** carries the §2.2 surface fields — behavior,
   operation links, authentication requirement, owning-contract
   link, error semantics — presence-checked mechanically against
   the §2.2 entry grammar. A **delegated-surface entry** carries
   exactly `Summary:` and a `Delegated:` field whose four §2.2
   sub-items are each present and well-formed (their field-by-field
   equality to the §4.4 allowlist is §7.5(g)'s check); the surface
   fields are neither required of nor permitted on it. An
   **exclusion entry** carries exactly `Summary:` and an
   `Excluded:` field with at least one well-formed §2.2 sub-item
   (the two-way mirror to the §4.1 script list is §7.5(l)'s
   check). A content check that demands a surface field of a
   non-surface entry, or that passes a non-surface entry whose own
   kind's field content is missing or malformed, fails;
   every error response documented in `OPENAPI.yaml` uses a
   code from the contract 5 §4.2 closed enums **as exported from the
   shared types package** (contract 5 §4.1's package; the
   implementing PR that lands the enums names the exports, and this
   witness compares against those exports — §6.5 orders the
   dependency), and each family's
   documented codes exactly match the enum in the shared types
   package; where the no-oracle rule applies, the documented refusal
   and not-found responses are identical in code, status, and shape.
4. **Retirement witness:** before `docs/openapi-tess.yaml` is deleted,
   an automated comparison proves each of its 17 paths is covered by
   `OPENAPI.yaml` under this equivalence algorithm, exactly:
   (a) canonicalize both documents — dereference every `$ref`
   in-document only (an external `$ref` is a canonicalization
   failure naming the reference), with cycle detection that replaces
   a re-entered `$ref` by a canonical cycle marker carrying the
   cycle's entry pointer (deterministic, so two documents with the
   same cycle structure canonicalize identically), delete the
   annotation-only keywords
   (`description`, `summary`, `example`, `examples`, `title`,
   `deprecated`), rename path-template parameters positionally,
   lowercase HTTP methods, then sort all object keys; (b) per legacy
   **operation** — each (path, method) pair independently, so a
   multi-method legacy path yields one match per method: deep
   structural equality of the canonicalized request and
   response schemas (an absent request body matches only the
   canonical no-body marker; a response entry with no body schema —
   including a legacy response carrying only a description with no
   `content` — canonicalizes to the same explicit no-body marker, so
   the deep equality is defined for body-less responses and a
   no-body response matches only a no-body response; equality is
   evaluated over the
   canonical forms, so no-body vs any schema is a difference),
   equality of the security/authentication
   requirement sets — compared through the §1.6
   `ApiAuthClass` → security-requirement mapping, i.e. the legacy
   operation's EFFECTIVE security requirement set is compared with
   the set the mapping assigns to the covering route's declared
   auth class, where "effective" resolves OpenAPI inheritance
   before comparison: an operation object with no `security`
   member inherits the legacy document's top-level `security`; an
   operation-level `security` member, including the explicit empty
   array, overrides the document default; step (a) materializes
   the effective set onto every canonicalized operation, so the
   comparison never reads the document level —
   and equality of the documented error semantics
   compared in a **common representation with two components** — the
   HTTP error-status set (the legacy operation's documented non-2xx
   status codes vs the covering operation's emitted non-2xx status
   codes) and the taxonomy-code set as plain strings (any error-code
   enum values in the legacy response schemas vs the covering
   route's §1.6 error-code set; where the legacy schemas express no
   taxonomy codes, this component's comparison target is the
   covering route's §1.6 record alone, i.e. the record is asserted
   present and closed, exactly as the permissions dimension below);
   where the legacy document does not express a
   dimension at all (permissions), the comparison target is the live
   §1.6 metadata record of the covering route instead; (c) the
   covering `ENDPOINTS.md` entry — unique by §7.5's
   one-entry-per-`operationId` rule, selected as the entry containing
   the `operationId` link of the operation that matched the legacy
   operation in (b) — must carry a permission rule reference equal
   (same owning contract, same rule identifier) to that covering
   route's §1.6 permission reference. The comparison emits the exact
   per-operation differences on failure. A repository-wide reference scan
   shows no remaining consumer (including generated clients) resolves
   the legacy file.
5. **Boundary witness:** the repository checker script (shipped by
   the implementing PR and run in CI alongside §7.1) parses
   `ENDPOINTS.md` into the §2.2 entry grammar via a Markdown AST
   and rejects, exactly: (a) an entry violating the grammar — a
   non-`##` entry heading, a duplicate family name, a missing
   required field label under the §2.2 per-kind rules (including
   `Operations:` absent from a family with operations, or present
   on an event-only family), a duplicated field label, or an
   `Events:` field on a non-event family; (b) an operation mention that is not
   a Markdown link in the §2.2 anchor grammar (link text =
   `operationId`, target = `./OPENAPI.yaml#operation/{operationId}`);
   (c) any of the reserved tokens
   `requestBody`, `responses`, `parameters`, `schema`, or
   `properties` appearing in ANY Markdown node type — fenced code,
   indented code, HTML block, table, inline code, or prose —
   anywhere in the file (schema content belongs only in
   `OPENAPI.yaml`);
   (d) an `operationId` link that does not resolve, per the §2.2
   resolution rule; (e) an event
   item not matching the §2.2 `inbound {name}` / `outbound {name}`
   syntax or whose event name is not in the §4.6 registry's matching
   direction; (f) an `operationId` linked from more than one entry;
   (g) a delegated-surface entry whose `Delegated:` sub-item
   values are not field-by-field
   equal to the §4.4 allowlist entry (string comparison after
   whitespace trimming), a §4.4 allowlist entry with no
   delegated-surface entry, a `Delegated:` prefix naming no
   allowlist entry, or a malformed `Delegated:` sub-item (not the
   exact §2.2 four-sub-item syntax); (h) a (direction, event-name) item appearing
   more than once in the file, whether within one entry or across
   entries; (i) an event item in an entry other than the one the
   §4.6 ownership map assigns to that event; (j) a §4.6 registry
   event absent from the ownership map, a map key that names no
   registry event (a stale key — the map's domain must exactly
   equal the registry union), a family name in the map with no
   entry in the file, or an event name present in both the inbound
   and outbound registries (the §4.6 disjointness rule); (k) an
   entry that is not exactly one §2.2 kind — a surface entry
   carrying neither `Operations:` nor `Events:` (the
   non-empty-surface rule — a zero-surface family documents
   nothing), an entry mixing kind-defining labels (`Delegated:`
   or `Excluded:` together with `Operations:`/`Events:`, or with
   each other), a delegated-surface or exclusion entry carrying
   any label beyond its exact §2.2 set, or a second exclusion
   entry in the file; (l) an exclusion mismatch — an `Excluded:`
   sub-item not field-by-field equal to a §4.1 generation-script
   exclusion entry (string comparison after whitespace trimming),
   a script exclusion entry with no sub-item, a sub-item with no
   script entry (both directions), or a malformed sub-item (not
   the exact §2.2 `{HTTP-method} {path-template} {reason-class}
{guard}` syntax). The witness
   proves it can fail with one control per reject rule. (Prose
   duplication a scanner cannot see is handled by review, but every
   structural duplication channel above is mechanical.)
6. **Validity witness:** `OPENAPI.yaml` parses and validates as
   OpenAPI 3.1 in CI under Redocly CLI (`redocly lint`), pinned as a
   repository devDependency (substituting a different validator is an
   amendment to this contract); the witness proves it can fail by a
   control run against an invalidated copy.
7. **Source-consistency witness:** for the record fields the
   registration derivation (§1.6) does not structurally guarantee,
   the witness cross-checks record against code: (a) **derivation
   closure** — every production route's installed authentication
   guard and permission enforcement originate from its record's
   declared `ApiAuthClass` and permission rule reference (static
   assertion that no route installs a guard or permission check
   through any path other than record derivation), with a control
   route that installs a bypassing guard failing the assertion;
   (b) **error-code closure** — per route family, the set of
   contract 5 §4.2 taxonomy codes referenced by the family's
   production code (by symbol identity against the shared-types
   enum exports) equals the union of the family's records'
   error-code sets, in both directions, with one control per
   direction (a code emitted but not declared; a code declared but
   never emitted); (c) **response-map closure** — each record's
   error entries are exactly the HTTP statuses its error-code set
   maps to under the contract 5 §4.2 status mapping (mechanical
   recomputation), with a mutated-record control; (d)
   **emitted-status closure** — a TOTAL, bidirectional witness per
   production route. The route's **status-site set** is the union
   of five site classes, each mechanically identified: (1)
   auth-refusal statuses — the refusal-status set the §1.6 mapping
   table assigns to the route's declared `ApiAuthClass` (the guard
   is record-derived, so its refusal statuses attach to every
   route of the class); (2) interception-component statuses — a
   closure over EVERY binding scope the framework offers, not only
   the global ones: (i) global-by-call — components installed by
   the composition root's global binding calls (`useGlobalPipes`,
   `useGlobalFilters`, `useGlobalInterceptors`,
   `useGlobalGuards`), enumerated by the §7.2 primitive scan,
   whose list names those calls; (ii) global-by-provider —
   components registered under the framework's DI binding tokens
   (`APP_PIPE`, `APP_FILTER`, `APP_INTERCEPTOR`, `APP_GUARD`),
   identified by symbol identity against the framework's token
   declarations; (iii) controller-scoped and (iv) route-scoped —
   components bound by the framework's binding decorators
   (`UsePipes`, `UseFilters`, `UseInterceptors`, `UseGuards`) at
   class and at method level, resolved per route. Each bound
   component carries a status set asserted against the
   component's own source by the class-(4) emission-site rules
   (the global validation pipe's 400; a route-scoped exception
   filter's mapped status — the live interaction controller's
   runtime-approval-denied filter emitting 403 is the migration
   example), attributed to exactly the routes its binding scope
   covers WITHIN the component's execution pipeline.
   **Execution-pipeline attribution**: the production surface
   partitions into **framework-routed routes** — registered
   through the framework's routing primitives (decorated
   controllers/gateways and framework route registrations) and
   dispatched by the framework router — and **raw-mounted
   routes** — the §7.2 raw-mount records, dispatched by a hook
   callback before or outside the framework router. Framework
   interception components in all four binding scopes execute
   only in the framework dispatch pipeline, so they attribute to
   framework-routed routes ONLY: global (by call or by DI token)
   to every framework-routed route, controller-scoped to the
   declaring class's routes, route-scoped to that route alone; a
   framework component's status attributed to a raw-mounted
   route is a phantom — the component never runs for it — and
   the bidirectional comparison rejects it exactly as it rejects
   any other phantom map status. A raw-mounted route's
   interception statuses come instead from the **adapter hook
   chain**: every non-path-bearing hook registration (§7.2)
   whose callback can emit a response (per the class-(4) rules
   over the callback closure) is an interception component
   attributed to exactly the routes whose URLs its dispatch
   predicate accepts — a hook with no URL dispatch attributes to
   every route, framework-routed and raw alike; a raw mount's
   own dispatch attributes to its (prefix × method) records
   alone (the live raw MCP hook, which answers before the
   framework router runs and is therefore untouched by the
   framework's global pipes and guards, is the motivating
   example). A binding site in any of the four scopes the scan
   does not enumerate, a bound component with no source-asserted
   status set, and an emitting hook registration with no
   dispatch-derived route attribution, are each a witness
   failure naming the site; (3)
   decorator status metadata — the framework's
   route-level status metadata (explicit status-code decorators,
   each method decorator's default status, and the implicit
   default success status where no metadata overrides it),
   resolved per route; (4) handler-closure emissions — every
   status-emission site in the handler and its callee closure,
   where the closure is COMPUTED, not assumed: the type-checker
   call graph starting at the handler declaration, resolving each
   call site's callee declaration, following every edge into
   production source (the three roots) to a fixed point, not
   following edges into non-production packages — with ONE
   carve-out: a call edge into a non-production package that
   passes the framework reply/response object or the raw Node
   request/response pair as an argument is a **delegated-emission
   site**, because the external callee can emit statuses the
   closure cannot see; such a site requires a declared external
   status contract — the statuses the external package's pinned
   version documents for that entry point, written into the
   route's record response map and attributed to the site (the
   live MCP raw handler passing the response into the external
   MCP transport is the migration example) — and a
   delegated-emission site with no declared external status
   contract is a FAILURE naming the site, never a silent cut —
   and FAILING —
   naming the site, never silently skipping — on any call site
   whose callee the checker cannot resolve. Emission sites are
   identified by symbol identity against the framework's
   reply/response type declarations (the status setter,
   send-with-status calls, status-carrying error constructors) AND
   against Node's `http.ServerResponse` declarations (`writeHead`,
   `statusCode` assignment), so raw handlers are covered; (5)
   framework and adapter defaults — an EXHAUSTIVE default
   inventory, not a declared-only list: the implementing PR
   classifies, as response-emitting (with the emitted status and
   triggering condition) or non-emitting, every member of a
   closed domain with two mechanically enumerated parts — (i)
   every property key of the pinned framework
   application-options type AND the pinned adapter
   construction-options type, resolved from the packages' type
   declarations, and (ii) every key of every options object the
   composition root actually passes to the framework/adapter
   construction and server-setup calls (call sites identified by
   symbol identity). The classification is TOTAL: a domain key
   absent from the inventory is a witness failure naming the
   key, so an applicable default cannot be omitted by silence
   (the adapter-level body-size limit set at the composition
   root — an adapter rejection path the revision-7 declared-only
   rule could silently omit — is the migration example). The
   framework's documented route-level default statuses (the
   default unhandled-error status, the default
   validation/parse-failure statuses, the default not-found on a
   matched mount) are fixed members of the inventory regardless
   of configuration. Each response-emitting entry carries BOTH a
   citation into the pinned version's source or documentation
   for that behavior AND a runtime probe that triggers the
   default and asserts the observed status; a response-emitting
   entry missing its citation or its probe is a witness failure.
   Each entry's status attributes by the §7.7(d)(2)
   execution-pipeline rule, never by configuration scope alone:
   every response-emitting entry names its emitting pipeline
   stage — the lifecycle point where the pinned version's
   mechanism runs (connection handling, request-line/header
   processing, body parsing, routing, response serialization) —
   and the status attributes to framework-routed routes and to
   exactly those raw-mounted routes whose §7.2 dispatch point
   executes AFTER the emitting stage in the adapter's lifecycle.
   The AFTER relation is decided against a **pinned
   lifecycle-order map**, an explicit witness artifact: the
   totally ordered list of the pinned adapter version's documented
   request-lifecycle phases — connection handling,
   request-line/header processing, each named hook phase the
   pinned version documents, body parsing, routing and handler
   dispatch, response serialization — each map entry carrying a
   citation into the pinned version's lifecycle documentation,
   and the map itself failing the witness if any two of its
   entries lack a documented relative order. Every raw mount's
   §7.2 dispatch point resolves to the map entry of the hook
   phase its registration primitive names (§7.2 records the
   phase as part of each hook registration's classification), and
   every response-emitting entry resolves its emitting stage to a
   map entry through its citation; AFTER means strictly later in
   the map's total order. A dispatch point or emitting entry that
   resolves to no map entry, or a dispatch point whose map entry
   EQUALS the emitting entry's, is a witness FAILURE naming the
   site and the entry — attribution is never assumed in either
   direction for an unordered or same-stage pair. The
   implementation repairs such a failure either by making the
   order resolvable or by adding, for exactly that (route, entry)
   pair, a per-pipeline probe pair in the style of the body-size
   pair below — one triggering request down each pipeline — whose
   observed outcomes then decide the attribution and are recorded
   in the witness output; an attribution carried by neither the
   map order nor a recorded probe pair is a phantom.
   A raw mount whose hook terminates or forwards the request
   BEFORE a stage runs never carries that stage's status: the raw
   MCP mount hijacks in a pre-parsing hook and hands the unparsed
   request to the external transport, so the adapter's
   body-parsing rejection cannot fire there, and attributing the
   body-size-limit status to that route (or requiring it of the
   route's record) is a phantom the bidirectional comparison
   rejects. If a raw-mounted route's record declares a
   body-limit-shaped status, the route's own callback chain must
   contain a status site for it — a server-wide option is not
   that site. Every site's status argument must resolve
   to a literal or compile-time constant; an unresolvable status
   is a FAILURE naming the site. Both directions: every resolved
   status is a member of the route's record response map, AND
   every response-map status is produced by at least one site in
   the status-site set — a map status no site produces (a phantom)
   is a failure naming the route and status. Controls: a handler
   emitting an unclassified status (`reply.status(418)`) fails; a
   computed status argument fails; an unresolvable call edge
   fails; a guard refusal status absent from the map fails; a
   global-pipe status absent from the map fails; a route-scoped
   filter status absent from the map fails; a component bound
   through a DI binding token is detected and attributed; a
   framework global component's status attributed to (or
   required of) a raw-mounted fixture route fails as a phantom —
   the raw-hook control; a fixture adapter hook emitting a
   constant status with no URL dispatch is attributed to every
   route, raw mounts included; a
   fixture handler passing the reply object into a stub external
   package with no declared external status contract fails; a
   decorator
   status absent from the map fails; a raw `writeHead` status
   absent from the map fails; a phantom map status fails; an
   option key present in the pinned adapter options type but
   absent from the default inventory fails; a configured
   composition-root option key absent from the inventory fails;
   the body-size-limit probe is a PAIR driving one oversized
   request down each pipeline: the framework-routed fixture
   observes the adapter rejection status, and the pre-parsing
   raw-mounted fixture observes NO adapter rejection (the request
   reaches the raw callback unparsed) — with the body-limit
   status attributed to (or required of) the raw route failing as
   a phantom; the
   implicit default success status is attributed to the route and
   must appear in its response map; (e) **guard-equivalence** —
   for each `ApiAuthClass`, the witness asserts the derived
   guard's acceptance structure against the §1.6 mapping table's
   requirement-set shape: the `admin` guard accepts a request
   satisfying EITHER named scheme and refuses one satisfying
   neither (drive both alternatives and the refusal); the
   `federation` guard refuses a request satisfying only one of its
   two schemes and accepts one satisfying both (drive all three
   cases); each single-scheme class accepts its scheme and refuses
   its absence; `none` installs no guard. Every "satisfying" and
   "not satisfying" request is constructed from the scheme's §1.6
   credential-predicate binding (location and acceptance
   predicate), so the cases exercise the declared binding, not an
   implementer's stand-in. **Refusal-branch closure**: the witness
   additionally ENUMERATES the guard's refusal branches — every
   status-emission site in the guard and its callee closure,
   computed by the §7.7(d)(4) rules — and drives each branch with
   a request constructed to take exactly that branch, asserting
   the emitted status equals the branch's resolved status (the
   live admin guard's distinct 401 and 403 paths are the
   motivating example: representative alternatives are not
   enough); the class's written §1.6 refusal-status set is
   asserted EQUAL to the enumerated branch-status union in both
   directions — a written status no branch emits, and a branch
   status outside the written set, each fail. **Mixed
   presentations**: for a multi-credential class the witness
   drives each mixed case — an invalid credential of the
   earlier-evaluated scheme presented together with a valid
   credential of the later scheme, and the converse — asserting
   the outcome the §1.6 declared evaluation order requires (the
   live admin guard rejects an invalid bearer before considering
   a valid session; that precedence must be declared and is
   asserted, not discovered); a guard whose mixed-presentation
   behavior diverges from the declared order fails. Controls: a
   mutated table copy assigning `admin` a single-scheme
   requirement set is detected while the guard still accepts the
   alternation; a mutated written refusal-status set missing one
   branch's status is detected. This ties the OpenAPI mapping to
   the live guard semantics rather than to a lint-valid but wrong
   table.

## 8. Drafting additions (PRD §12.1 disclosure)

The two artifacts, their roles, and the retirement verification
dimensions are traced to `docs/API/README.md` and `docs/SITEMAP.md`.
Proposed drafting additions, visible here for ratification, each
severable:

1. The generated-with-committed-output production model, the declared
   route-metadata layer with its fail-closed generator, the metadata
   migration scope, and the CI drift gate (§3.1, §7.1) — the ruling
   below.
2. The same-PR documentation rule for surface changes (§4.3).
3. The explicit exclusion list and the delegated-surface
   documentation form (§4.1).
4. The Socket.IO placement rule (events in `ENDPOINTS.md`, no
   pseudo-paths) and the both-direction event coverage (§2.1, §4.1).
5. The no-oracle documentation requirement (§4.2).
6. The `ENDPOINTS.md` structured entry format and its no-duplication
   rule with the mechanical boundary witness (§2.2, §7.5).
7. The committed-repository-file rule — neither artifact exists only
   in CI (§2.3).
8. The normative-direction rule — generation documents, never
   ratifies (§3.2).
9. The retirement deletion-in-reviewed-PR form and the
   interim-consumer restriction (§5.2), and the §7.4 equivalence
   algorithm.
10. The §6 phase placement: implementing-PR-after-ratification, the
    drift gate as a required trunk check from first commit, and the
    build-first documentation sequencing.
11. The §1.5/§1.6 definitions themselves — the production-route
    boundary over the three named roots, the closed route-metadata
    record with its per-field completeness predicate, its
    record-as-registration-input derivation rule, the pinned
    six-value `ApiAuthClass` enumeration with its closed
    security-requirement mapping, and the event-record
    field semantics — and the §4.4 delegation allowlist with
    its exactly-one-mount and all-module below-prefix rules and
    field-by-field mirror equality, the §4.5 profile enumeration and
    condition field, and the §4.6 typed event registry with its
    two-record `as const` shape, symbol-reference rule, and total
    single-valued event-ownership map.
12. The §7 witness machinery as such: the §7.2 independent inventory
    with its registration-primitive list, symbol-identity detection
    with its failure-not-skip rule, and bidirectional
    family/operation checks, the §7.3 content and taxonomy witness,
    the §7.5 checker script with its twelve reject rules, the §7.6
    pinned validator (Redocly CLI), the §7.7 source-consistency
    witness, the per-field fail-closed
    controls in §7.1, and the §7.4 canonicalization/derivation/
    covering-entry algorithm.
13. The §2.2 entry grammar as exact syntax (heading form, literal
    field labels and cardinality, link and anchor grammar, event-item
    syntax, the one-entry-per-`operationId` rule, and the
    reserved-token prohibition including its prose strictness).
14. The §4.1 exclusion-list eligibility rule (closed, machine-checked
    against every production-profile inventory pass).
15. The §6.5 contract 5 ordering dependency and its contingency
    amendment rule.
16. The revision-5 closure rules: the `admin` and `federation` auth
    classes representing the live alternation and mTLS-plus-grant
    guards (§1.6); the response-map no-body marker and its §7.4
    body-less canonicalization; the §7.2 path-argument resolution
    rule (compile-time constant, equal to the record's path
    template, failure otherwise); the §7.7(d) emitted-status
    closure; and the event-set closure machinery — the §4.6
    ownership map, the §2.2 owning-family and at-most-once item
    rules, the §7.2 bidirectional event-set equality, and §7.5
    reject rules (h)–(j).
17. The revision-6 closure rules: the pinned `ApiAuthClass` →
    security-requirement table with its OpenAPI OR/AND
    requirement-set shapes, per-class refusal-status sets, and
    injectivity, plus the §7.7(e) guard-equivalence witness
    (§1.6); the §7.7(d) five-class status-site algorithm with
    computed call-graph closure, unresolved-edge failure, Node
    raw-sink symbols, and the bidirectional no-phantom
    comparison; the §7.2 primitive classification
    (path-bearing/non-path-bearing), the raw-mount
    hook-dispatch rule, and the path composition and
    normalization algorithm; the §7.2 server/client socket
    boundary by package type identity, the (direction, name)
    comparison element, registry disjointness, and the
    exact-domain ownership rule (§4.6, §7.5); the §7.4
    document-level security inheritance resolution; and the §2.2
    conditional `Operations:` rule for event-only families.
18. The revision-7 closure rules: the §7.7(d)(2)
    interception-component closure over all four binding scopes
    with scope-exact attribution and unenumerated-binding failure,
    and the §7.2 primitive-list extension naming the global binding
    calls and DI binding tokens; the §7.2 raw-mount
    per-(prefix × method) record rule with segment-boundary prefix
    semantics; the §1.6 requirement-object exactness, scheme →
    credential-predicate bindings, declared evaluation order, and
    computed refusal-status-set definition, with §7.7(e)'s
    refusal-branch closure and mixed-presentation cases; the §7.2
    decorator-site server boundary by socket-package identity and
    the registry-union third leg of the event-set closure; the
    §7.7(d)(4) delegated-emission rule with its pinned external
    status contract and the §7.7(d)(5) citation-plus-probe rule
    for framework defaults; and the §2.2 non-empty-surface rule
    with §7.5 reject rule (k).
19. The revision-8 closure rules: the §7.7(d)(2)
    execution-pipeline attribution — framework interception
    components to framework-routed routes only, adapter
    hook-chain components attributed by dispatch predicate,
    phantom rejection for raw-mounted routes, with the raw-hook
    control; the §7.2 method-partition exhaustiveness rule with
    its acceptance/refusal path classification, forwarding
    complement-branch failure, and declared external method
    contract; the §7.7(d)(5) exhaustive default inventory (total
    classification over the pinned options-type keys and the
    configured composition-root options, citation-plus-probe per
    emitting entry); and the §2.2 entry kinds with the
    `Delegated:`/`Excluded:` field syntax, the revised §7.5(k)
    kind-partition rule, and the §7.5(l) exclusion mirror. The
    revision-9 refinements: per-stage pipeline attribution of
    inventory defaults with the two-pipeline body-size probe pair
    (§7.7(d)(5)); the per-kind §7.3 content partition; and the
    §7.2 URL fall-through classification scoping the method
    partition to the URL-accepted branch. The revision-10
    refinement: the pinned lifecycle-order map with hook-phase
    recording in the §7.2 primitive classification, the strict
    stage-order AFTER rule, the unresolved/same-stage failure
    rule, and the per-(route, entry) probe-pair fallback
    (§7.7(d)(5)).

## Ruling request

Ruling requested (one decision): shall `OPENAPI.yaml` be generated
from Gateway source through a declared, fail-closed route-metadata
layer with a committed output and a CI drift gate (recommended — the
45-controller surface already exists, so generation is the only route
that starts true and stays true, and fail-closed metadata makes
incompleteness a build error) — or hand-authored contract-first, with
code conformance to the hand-written contract enforced by witnesses
instead?
