diff --git a/docs/requirements/api-artifacts.md b/docs/requirements/api-artifacts.md index 5f0bb5d6..b5504b91 100644 --- a/docs/requirements/api-artifacts.md +++ b/docs/requirements/api-artifacts.md @@ -81,6 +81,27 @@ 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). + 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). @@ -125,7 +146,9 @@ documentation placement rules stay with the docs atlas 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, with at least one success entry, and with one + 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, @@ -133,8 +156,20 @@ documentation placement rules stay with the docs atlas 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), and `bootstrap` (identity §3 pre-epoch - surface) — amendment-only, pinned here rather than deferred; the + (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 carried by + the generation script (§3.1): `none` maps to the empty security + array, and each other value maps to a named security-scheme + requirement set the implementing PR defines once in + `OPENAPI.yaml`'s components; the mapping is total over the + enumeration, and §7.4(b) compares security through it; 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 @@ -179,7 +214,10 @@ documentation placement rules stay with the docs atlas 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. + 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. `ENDPOINTS.md` MUST NOT contain request/response schema definitions for any operation that exists in `OPENAPI.yaml` (mechanical rule: the reserved tokens @@ -310,7 +348,15 @@ documentation placement rules stay with the docs atlas 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. + 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. ## 5. Legacy migration @@ -390,14 +436,32 @@ scanned source roots, and compared files. 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). The inventory equals the documented set in both + access fails). **Path-argument resolution:** for every detected + registration call site and every route decorator, the path + argument must be a string literal or resolve through the + type-checker to a compile-time string constant; the resolved + string is then compared for exact equality with the route's §1.6 + record path template. A path argument the checker cannot resolve + to a constant (`app.get(computedPath(), handler)`, a decorator + with a computed path 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; a resolved path unequal to its record's + template 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 — with the §4.6 registry as the comparison target and a scan failure on any - non-literal or non-registry event name. Additionally, every HTTP + non-literal or non-registry event name. **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 — + 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. 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. @@ -431,10 +495,19 @@ scanned source roots, and compared files. 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; equality is evaluated over 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, and equality of the documented error semantics + requirement sets — compared through the §1.6 + `ApiAuthClass` → security-requirement mapping, i.e. the legacy + operation's security requirement set is compared with the set the + mapping assigns to the covering route's declared auth class — + 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 @@ -478,7 +551,12 @@ scanned source roots, and compared files. direction; (f) an `operationId` linked from more than one entry; (g) a delegated-surface entry whose fields are not field-by-field equal to the §4.4 allowlist entry (string comparison after - whitespace trimming). The witness + whitespace trimming); (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, or mapped to a family name + with no entry in the file. 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.) @@ -505,7 +583,21 @@ scanned source roots, and compared files. 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. + recomputation), with a mutated-record control; (d) + **emitted-status closure** — for every production route, every + status-emission site in the handler and its production-source + callee closure (each site identified by symbol identity against + the framework's reply/response type declarations — the status + setter, shorthand send-with-status calls, and framework error + constructors carrying a status) must resolve, as a literal or a + compile-time constant, to a status present in the route's record + response map; a status argument the checker cannot resolve, or a + resolved status absent from the record's response map, is a + witness FAILURE naming the site — with controls: a handler + emitting an unclassified status (`reply.status(418)`) fails; a + computed status argument fails; a framework-default emission + path (implicit 200) is attributed to the route and must appear + in its response map. ## 8. Drafting additions (PRD §12.1 disclosure) @@ -540,17 +632,19 @@ severable: 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 - four-value `ApiAuthClass` enumeration, and the event-record + 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 and symbol-reference rule. + 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 seven reject rules, the §7.6 + the §7.5 checker script with its ten 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/ @@ -563,6 +657,16 @@ severable: 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). ## Ruling request