1361 lines
81 KiB
Markdown
1361 lines
81 KiB
Markdown
# 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?
|