docs: API contract artifacts contract (S2 contract 9) #1443
@@ -129,6 +129,36 @@ 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).
|
||||
|
||||
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).
|
||||
@@ -207,13 +237,38 @@ documentation placement rules stay with the docs atlas
|
||||
`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. The table additionally assigns each
|
||||
class a closed **refusal-status set** — the HTTP statuses its
|
||||
record-derived guard emits on refusal — consumed by §7.7(d); the
|
||||
implementing PR states each set from the live guard source, and
|
||||
the §7.7(e) guard-equivalence witness ties both the
|
||||
requirement-set shapes and the refusal statuses to the guards'
|
||||
actual semantics. The mapping is total over the enumeration and
|
||||
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
|
||||
@@ -248,7 +303,10 @@ documentation placement rules stay with the docs atlas
|
||||
Socket.IO events — such a family MUST carry `Events:`, which is
|
||||
how it satisfies the grammar with no operations to link);
|
||||
`Events:` REQUIRED for a family with Socket.IO events and absent
|
||||
otherwise.
|
||||
otherwise. **Non-empty surface**: every entry carries at least
|
||||
one of `Operations:` or `Events:` — a family with neither
|
||||
documents nothing and 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
|
||||
@@ -479,7 +537,13 @@ scanned source roots, and compared files.
|
||||
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 — and the witness
|
||||
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
|
||||
@@ -509,12 +573,26 @@ scanned source roots, and compared files.
|
||||
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,
|
||||
each record's path template equal to a string constant the
|
||||
checker resolves from the callback's dispatch comparison (the
|
||||
prefix test against a compile-time constant); a dispatch
|
||||
comparison the checker cannot resolve to a constant is a scan
|
||||
failure naming the site. For path-bearing sites, the effective
|
||||
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. 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
|
||||
@@ -532,7 +610,11 @@ handler)`, a computed decorator value) is a scan FAILURE naming
|
||||
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). The inventory equals the documented set in both
|
||||
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). 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
|
||||
@@ -547,10 +629,24 @@ handler)`, a computed decorator value) is a scan FAILURE naming
|
||||
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 — with
|
||||
controls (a client emit is not inventoried as server-emitted; a
|
||||
client subscription is not inventoried as an inbound handler;
|
||||
an unresolvable receiver fails). The comparison element is the
|
||||
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
|
||||
@@ -562,7 +658,15 @@ handler)`, a computed decorator value) is a scan FAILURE naming
|
||||
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. Additionally, every HTTP
|
||||
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.
|
||||
@@ -669,7 +773,10 @@ handler)`, a computed decorator value) is a scan FAILURE naming
|
||||
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). The witness
|
||||
and outbound registries (the §4.6 disjointness rule); (k) an
|
||||
entry carrying neither `Operations:` nor `Events:` (the §2.2
|
||||
non-empty-surface rule — a zero-surface family documents
|
||||
nothing). 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.)
|
||||
@@ -703,13 +810,32 @@ handler)`, a computed decorator value) is a scan FAILURE naming
|
||||
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) global-interceptor statuses — every
|
||||
globally registered pipe, filter, or equivalent
|
||||
bootstrap-installed interceptor is enumerated by the same
|
||||
registration-primitive scan as §7.2, and each carries a
|
||||
declared status set the witness asserts against its source (the
|
||||
validation pipe's 400, for example), attributed to every route
|
||||
it covers; (3) decorator status metadata — the framework's
|
||||
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: global to every route, controller-scoped to the
|
||||
declaring class's routes, route-scoped to that route alone. A
|
||||
binding site in any of the four scopes the scan does not
|
||||
enumerate, and a bound component with no source-asserted
|
||||
status set, 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),
|
||||
@@ -719,7 +845,20 @@ handler)`, a computed decorator value) is a scan FAILURE naming
|
||||
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, and FAILING —
|
||||
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
|
||||
@@ -728,8 +867,15 @@ handler)`, a computed decorator value) is a scan FAILURE naming
|
||||
against Node's `http.ServerResponse` declarations (`writeHead`,
|
||||
`statusCode` assignment), so raw handlers are covered; (5)
|
||||
framework error defaults — the framework's own route-level
|
||||
default error statuses, where the implementing PR declares them
|
||||
route-attributable. Every site's status argument must resolve
|
||||
default error statuses (the default unhandled-error status, the
|
||||
default validation/parse-failure statuses, the default
|
||||
not-found on a matched mount): each default the implementing PR
|
||||
declares route-attributable carries BOTH a citation into the
|
||||
pinned framework version's source or documentation for that
|
||||
default AND a runtime probe that triggers the default and
|
||||
asserts the observed status; a declared default missing its
|
||||
citation or its probe is a witness failure — declaration alone
|
||||
attributes nothing. 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
|
||||
@@ -739,7 +885,12 @@ handler)`, a computed decorator value) is a scan FAILURE naming
|
||||
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 decorator
|
||||
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
|
||||
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; the
|
||||
implicit default success status is attributed to the route and
|
||||
@@ -752,12 +903,36 @@ handler)`, a computed decorator value) is a scan FAILURE naming
|
||||
`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. The refusal cases also
|
||||
assert the emitted status is in the class's §1.6 refusal-status
|
||||
set. Control: a mutated table copy assigning `admin` a
|
||||
single-scheme requirement set is detected while the guard still
|
||||
accepts the alternation. This ties the OpenAPI mapping to the
|
||||
live guard semantics rather than to a lint-valid but wrong
|
||||
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)
|
||||
@@ -805,7 +980,7 @@ severable:
|
||||
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 ten reject rules, the §7.6
|
||||
the §7.5 checker script with its eleven 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/
|
||||
@@ -844,6 +1019,22 @@ severable:
|
||||
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).
|
||||
|
||||
## Ruling request
|
||||
|
||||
|
||||
Reference in New Issue
Block a user