docs: api-artifacts contract revision 3 (luna re-review residuals + F6)
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
F1: route metadata record defined as a closed required-field list with a per-field completeness predicate; production route made a decidable term; generator computes the inventory route set first and fails naming route and field. F2: inventory scans all production source in apps/ and packages/ with an enumerated registration-primitive list; deployment profiles bound to the closed tier enumeration with a declared condition field; event names bound to one typed registry with a literal-name rule. F3: ENDPOINTS.md checker defined as a repository script with five exact reject rules over a Markdown AST parse, one control per rule. F4: retirement equivalence algorithm fully enumerated (canonicalization steps, legacy error-code derivation, covering-entry selection, permission equality); validator pinned to Redocly CLI. F5: section 8 items 11-12 disclose the definitions and the witness machinery. F6: delegation allowlist (prefix, engine package, engine contract, registering module) with a below-prefix boundary check; non-allowlisted prefixes are ordinary route-by-route surface.
This commit is contained in:
@@ -31,6 +31,20 @@ equivalence algorithm (F4). §8 discloses every untraced binding rule,
|
|||||||
and the SITEMAP approval claim is restated in the sitemap's own terms
|
and the SITEMAP approval claim is restated in the sitemap's own terms
|
||||||
(F5).
|
(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).
|
||||||
|
|
||||||
This contract binds the artifact definitions (§2), the production
|
This contract binds the artifact definitions (§2), the production
|
||||||
model (§3), coverage obligations (§4), legacy migration (§5), phase
|
model (§3), coverage obligations (§4), legacy migration (§5), phase
|
||||||
timing (§6), witnesses (§7), and disclosed drafting additions (§8).
|
timing (§6), witnesses (§7), and disclosed drafting additions (§8).
|
||||||
@@ -51,7 +65,29 @@ documentation placement rules stay with the docs atlas
|
|||||||
4. **Delegated surface**: a mounted path prefix whose request handling
|
4. **Delegated surface**: a mounted path prefix whose request handling
|
||||||
is delegated wholesale to an embedded third-party engine (for
|
is delegated wholesale to an embedded third-party engine (for
|
||||||
example an auth engine's own route tree): the Gateway registers the
|
example an auth engine's own route tree): the Gateway registers the
|
||||||
mount; the engine defines the routes.
|
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/` and
|
||||||
|
`packages/`, 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 generator input
|
||||||
|
(§3.1). Its fields, all REQUIRED: a unique operation id; the HTTP
|
||||||
|
method and path template (or the SSE marker, or the Socket.IO
|
||||||
|
event name and direction); the request schema reference (or an
|
||||||
|
explicit no-body marker); the response map — every documented
|
||||||
|
status code mapped to a schema reference, with at least one
|
||||||
|
success entry; the authentication requirement, one value from the
|
||||||
|
closed auth-class enumeration the implementing PR declares in the
|
||||||
|
shared types package; the permission rule reference (owning
|
||||||
|
contract plus rule identifier); and the error-code set, drawn from
|
||||||
|
the contract 5 §4.2 closed taxonomy. **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
|
## 2. Artifact contract
|
||||||
|
|
||||||
@@ -82,23 +118,30 @@ documentation placement rules stay with the docs atlas
|
|||||||
## 3. Production model
|
## 3. Production model
|
||||||
|
|
||||||
1. **Generation (per the ruling).** `OPENAPI.yaml` is GENERATED from
|
1. **Generation (per the ruling).** `OPENAPI.yaml` is GENERATED from
|
||||||
the Gateway source by a deterministic repository script whose input
|
the Gateway source by a deterministic repository script whose
|
||||||
is a **declared route-metadata layer**: every production HTTP/SSE
|
input is the **route metadata record** (§1.6) of every production
|
||||||
route carries machine-extractable metadata — route decorators plus
|
route (§1.5): route decorators plus typed request/response schemas
|
||||||
typed request/response schemas (the contract 5 §4.1 shared DTOs
|
(the contract 5 §4.1 shared DTOs where the operation is a mapped
|
||||||
where the operation is a mapped tool family; declared per-route
|
tool family; declared per-route schemas elsewhere), each record
|
||||||
schemas elsewhere) and its error-code set. **The generator fails
|
satisfying the §1.6 completeness predicate. **The generator fails
|
||||||
closed:** a production route lacking complete extractable metadata
|
closed:** it first computes the §7.2 inventory's route set, then
|
||||||
is a generation error, never a silently omitted or partially
|
requires a complete metadata record for every route in it; a
|
||||||
emitted operation. Today's local-DTO, inline-typed, and
|
missing record, a missing field, or an unresolvable reference is a
|
||||||
raw-handler routes therefore migrate to declared metadata in the
|
generation error naming the route and field — never a silently
|
||||||
implementing PR — that migration is in the implementing PR's
|
omitted or partially emitted operation. Because the route set
|
||||||
scope, and §7.2's independent inventory is the cross-check that no
|
comes from the inventory and completeness is the per-field §1.6
|
||||||
route escaped it. The generated output is committed, and CI
|
predicate, a byte-stable but incomplete artifact cannot pass: an
|
||||||
regenerates and byte-compares it (§7.1). Hand edits to
|
unrecorded route fails generation, and a recorded route emits
|
||||||
`OPENAPI.yaml` are non-conformant — a description that cannot be
|
every §1.6 field into its OpenAPI operation (operation id,
|
||||||
expressed from source annotations goes into the annotations, or
|
schemas, per-status responses, security requirement, and the
|
||||||
into `ENDPOINTS.md`.
|
permission reference and error-code set as declared extension
|
||||||
|
fields). 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
|
2. **Normative direction.** Generation documents the code; it does not
|
||||||
ratify it. The S2 contracts and their witnesses remain the
|
ratify it. The S2 contracts and their witnesses remain the
|
||||||
normative gates on what the surface may be; the artifacts make the
|
normative gates on what the surface may be; the artifacts make the
|
||||||
@@ -138,6 +181,32 @@ documentation placement rules stay with the docs atlas
|
|||||||
route or event lands with the regenerated `OPENAPI.yaml` and any
|
route or event lands with the regenerated `OPENAPI.yaml` and any
|
||||||
needed `ENDPOINTS.md` entry in the same PR, enforced by the §7.1
|
needed `ENDPOINTS.md` entry in the same PR, enforced by the §7.1
|
||||||
drift gate — there is no "docs to follow" state.
|
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: any route registration whose path falls under an
|
||||||
|
allowlisted prefix but does not originate from that entry's named
|
||||||
|
registration module fails the witness — an app-owned handler
|
||||||
|
cannot hide under a delegated prefix. `ENDPOINTS.md`'s
|
||||||
|
delegated-surface entries mirror the allowlist one-to-one.
|
||||||
|
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.** Every Socket.IO event name, in both
|
||||||
|
directions, is a literal member of one exported typed event
|
||||||
|
registry in the shared types package; emitting or subscribing with
|
||||||
|
a dynamically constructed name, or through a helper that does not
|
||||||
|
take its name from the registry, is non-conformant (§7.2 scans for
|
||||||
|
non-registry names). The registry is the comparison target for the
|
||||||
|
event inventory.
|
||||||
|
|
||||||
## 5. Legacy migration
|
## 5. Legacy migration
|
||||||
|
|
||||||
@@ -177,25 +246,35 @@ scanned source roots, and compared files.
|
|||||||
1. **Drift witness:** CI regenerates `OPENAPI.yaml` with the
|
1. **Drift witness:** CI regenerates `OPENAPI.yaml` with the
|
||||||
repository script and fails on any byte difference from the
|
repository script and fails on any byte difference from the
|
||||||
committed file; the witness proves it can fail by a control run
|
committed file; the witness proves it can fail by a control run
|
||||||
against a mutated copy. A second control proves the generator's
|
against a mutated copy. Fail-closed controls (§3.1): one control
|
||||||
fail-closed rule (§3.1): a fixture route stripped of its metadata
|
per §1.6 field class — a fixture route with the whole record
|
||||||
makes generation fail, not emit a partial document.
|
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
|
2. **Coverage witness:** an independent surface inventory — static
|
||||||
enumeration over `apps/` production code, tests excluded (the A5
|
enumeration over ALL production code in `apps/` and `packages/`,
|
||||||
§1.2 inventory style), covering framework-decorated controllers
|
tests excluded (the A5 §1.2 inventory style), not limited to the
|
||||||
and gateways, raw mount/hook registrations in the bootstrap
|
bootstrap module — covering: framework-decorated controllers and
|
||||||
path, SSE routes, conditional registrations (evaluated per
|
gateways; every raw registration primitive of the HTTP framework
|
||||||
deployment profile and unioned), and delegated-surface mounts —
|
(the implementing PR enumerates the primitive list — route, hook,
|
||||||
equals the documented set in both directions: no undocumented live
|
middleware-mount, and plugin-registration calls — and the witness
|
||||||
route, no documented phantom, each conditional route carrying its
|
asserts the list against the framework's registration API surface)
|
||||||
condition, each delegated surface present as a delegated-surface
|
wherever it occurs, including plugins and middleware modules; SSE
|
||||||
entry. Socket.IO events are inventoried in both directions —
|
routes; conditional registrations evaluated per §4.5 profile and
|
||||||
inbound subscription handlers by handler scan, server-emitted
|
unioned; and delegated-surface mounts, with the §4.4 boundary
|
||||||
events by emit-call scan over production code — and compared to
|
check that nothing app-owned registers below an allowlisted
|
||||||
`ENDPOINTS.md`'s event entries in both directions. Additionally,
|
prefix. The inventory equals the documented set in both
|
||||||
every HTTP route family in `OPENAPI.yaml` has an `ENDPOINTS.md`
|
directions: no undocumented live route, no documented phantom,
|
||||||
entry, and every `ENDPOINTS.md` operation reference resolves to an
|
each conditional operation carrying its §4.5 condition field, each
|
||||||
existing `operationId` — both directions, mechanical.
|
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
|
||||||
|
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:** every `ENDPOINTS.md` entry
|
3. **Content and taxonomy witness:** every `ENDPOINTS.md` entry
|
||||||
carries the §2.2 required fields — behavior, operation links,
|
carries the §2.2 required fields — behavior, operation links,
|
||||||
authentication requirement, owning-contract link, error semantics
|
authentication requirement, owning-contract link, error semantics
|
||||||
@@ -207,25 +286,48 @@ scanned source roots, and compared files.
|
|||||||
and not-found responses are identical in code, status, and shape.
|
and not-found responses are identical in code, status, and shape.
|
||||||
4. **Retirement witness:** before `docs/openapi-tess.yaml` is deleted,
|
4. **Retirement witness:** before `docs/openapi-tess.yaml` is deleted,
|
||||||
an automated comparison proves each of its 17 paths is covered by
|
an automated comparison proves each of its 17 paths is covered by
|
||||||
`OPENAPI.yaml` under a defined equivalence algorithm: both
|
`OPENAPI.yaml` under this equivalence algorithm, exactly:
|
||||||
documents canonicalized (references dereferenced, keys sorted),
|
(a) canonicalize both documents — dereference every `$ref` with
|
||||||
then per path — structural schema equality, security/authentication
|
cycle detection, delete the annotation-only keywords
|
||||||
requirement equality, and documented error-code set equality —
|
(`description`, `summary`, `example`, `examples`, `title`,
|
||||||
with permissions compared against the covering `ENDPOINTS.md`
|
`deprecated`), rename path-template parameters positionally,
|
||||||
entries, and the comparison emitting the exact per-path
|
lowercase HTTP methods, then sort all object keys; (b) per legacy
|
||||||
differences on failure. A repository-wide reference scan shows no
|
path: deep structural equality of the canonicalized request and
|
||||||
remaining consumer (including generated clients) resolves the
|
response schemas, equality of the security/authentication
|
||||||
legacy file.
|
requirement sets, and equality of the documented error-code sets —
|
||||||
5. **Boundary witness:** `ENDPOINTS.md` contains no schema definition
|
a legacy error-code set is DERIVED as the legacy operation's
|
||||||
for any operation that exists in `OPENAPI.yaml` — mechanically: no
|
documented non-2xx status codes plus any error-code enum values in
|
||||||
`requestBody`/`responses`/schema-fragment block attached to a
|
its response schemas; where the legacy document does not express a
|
||||||
linked operation, and every operation mention is an `operationId`
|
dimension at all (permissions), the comparison target is the live
|
||||||
link (§2.2's structured format makes this checkable); the witness
|
§1.6 metadata record of the covering route instead; (c) the
|
||||||
proves it can fail by a control run against a mutated copy
|
covering `ENDPOINTS.md` entry — selected as the entry containing
|
||||||
containing an inline schema block.
|
the `operationId` link of the operation that matched the legacy
|
||||||
|
path 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-path 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 structure via a Markdown AST
|
||||||
|
and rejects, exactly: (a) an entry missing any §2.2 required
|
||||||
|
field; (b) an operation mention that is not a Markdown link whose
|
||||||
|
target is an `OPENAPI.yaml` `operationId` anchor and whose link
|
||||||
|
text is that `operationId`; (c) a fenced code block, anywhere in
|
||||||
|
the file, containing any of the OpenAPI schema keywords
|
||||||
|
`requestBody`, `responses`, `parameters`, `schema`, or
|
||||||
|
`properties` — schema content belongs only in `OPENAPI.yaml`;
|
||||||
|
(d) an `operationId` link that does not resolve; (e) an event
|
||||||
|
entry whose event name is not in the §4.6 registry. 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
|
6. **Validity witness:** `OPENAPI.yaml` parses and validates as
|
||||||
OpenAPI 3.1 under a named validator run in CI; the witness proves
|
OpenAPI 3.1 in CI under Redocly CLI (`redocly lint`), pinned as a
|
||||||
it can fail by a control run against an invalidated copy.
|
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.
|
||||||
|
|
||||||
## 8. Drafting additions (PRD §12.1 disclosure)
|
## 8. Drafting additions (PRD §12.1 disclosure)
|
||||||
|
|
||||||
@@ -256,6 +358,19 @@ severable:
|
|||||||
10. The §6 phase placement: implementing-PR-after-ratification, the
|
10. The §6 phase placement: implementing-PR-after-ratification, the
|
||||||
drift gate as a required trunk check from first commit, and the
|
drift gate as a required trunk check from first commit, and the
|
||||||
build-first documentation sequencing.
|
build-first documentation sequencing.
|
||||||
|
11. The §1.5/§1.6 definitions themselves — the production-route
|
||||||
|
boundary and the closed route-metadata record with its per-field
|
||||||
|
completeness predicate — and the §4.4 delegation allowlist with
|
||||||
|
its below-prefix boundary rule, the §4.5 profile enumeration and
|
||||||
|
condition field, and the §4.6 typed event registry with the
|
||||||
|
literal-name rule.
|
||||||
|
12. The §7 witness machinery as such: the §7.2 independent inventory
|
||||||
|
with its registration-primitive list and bidirectional
|
||||||
|
family/operation checks, the §7.3 content and taxonomy witness,
|
||||||
|
the §7.5 checker script with its five reject rules, the §7.6
|
||||||
|
pinned validator (Redocly CLI), the per-field fail-closed
|
||||||
|
controls in §7.1, and the §7.4 canonicalization/derivation/
|
||||||
|
covering-entry algorithm.
|
||||||
|
|
||||||
## Ruling request
|
## Ruling request
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user