8.3 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| review-001-openapi-projection-fidelity | to_openapi golden fidelity — runtime-truthful document (PRJ-01..PRJ-05, PRJ-11, PRJ-12, PRJ-14, PRJ-15) | completed |
|
moderate | medium | component | implementation |
|
Description
Review 001 Part F: the to_openapi document describes responses the
gateway does not emit. The runtime contract must be settled before
this doc work — hence the gateway dependencies (per the review's
sequencing note). Systematic diff of the projection against the runtime
(routes tests are the oracle):
- PRJ-01/PRJ-02:
/searchand/schema200 responses are documented without the envelope wrapper ({request_id, result, output}) and with wrong item fields (descriptionthat doesn't exist; missingnamespace/op_type/visibility/access_control/channel_open/publish_schema). Clients generated from the doc are broken day one. - PRJ-03: documented 400
INVALID_INPUTvs runtime 422 (error.rs:51); axum extractor rejections are plain-text bodies with none of the documented{code, message, retryable}shape; no 422 in the doc at all. - PRJ-04: op errors declared at non-
HTTP_*codes are projected under statuses the runtime never produces (ErrorDefinition.http_statusis never consulted;RATE_LIMITEDsurfaces as 500). Either project honestly under 500 or honorhttp_statusat runtime — with the review's note thatoperation_errors_projected_onto_callcurrently enshrines the wrong behavior. - PRJ-05:
/subscribedocumented statuses are structurally unreachable (always 200 + SSE per GW-12); in-bandevent:errorframes undocumented. Document the 200+in-band-error contract. - PRJ-11/HY-03: remaining guarded
expects into_openapi(:440, :505, :512) — replace withif let(convention). - PRJ-12: error dedupe iterates a
HashMap→ nondeterministic doc regeneration for identical registry state. Dedupe by(code, status)or sort. - PRJ-14:
schema_call_request()inlined 4× and unused-in-practicecomponents.schemas— use$refor drop components so shape changes can't drift. - PRJ-15:
/searchdocuments 401/403 that cannot occur, omits the 404 that can; nosecuritySchemesanywhere despite Bearer being the contract (ADR-004).
Acceptance Criteria
- Generated-doc golden test asserted against hand-written expectations matching the routes tests' actual bodies (the review's gate for this unit)
/subscribedoc documents the 200 +event:errorin-band contract (GW-12's asymmetry called out);/searchstatuses match reality- PRJ-04 decision landed (project-honest vs honor-
http_status) and the enshrining test aligned - Deterministic doc generation (same registry → byte-identical doc, test)
$refs in components or components removed;expects replacedcomponents.securitySchemesdeclares the Bearer schemecargo testandcargo clippy --all-targets -- -D warningspass
References
- docs/reviews/001-initial-implementation-review.md (Part F, PRJ-01..PRJ-05, PRJ-11, PRJ-12, PRJ-14, PRJ-15)
- docs/architecture/decisions/042-openapi-gateway-pattern.md, 045-to-openapi-gateway-spec-versioning.md
- docs/architecture/decisions/023-operation-error-schemas.md (error fidelity)
- src/gateway/routes.rs tests (the runtime oracle), src/gateway/error.rs (the mapping)
Notes
Sequenced after the gateway tasks; the documented contract now matches the settled runtime (review's Unit 6 ordering). The two decisions that shaped the implementation:
- PRJ-03 (doc-align, no runtime change): the doc maps dispatch-path
INVALID_INPUT/INVALID_OPERATION_TYPEto422(the shared mapper's real output) and documents the axum extractor-rejection gap explicitly on the400responses (text/plainbody, marked as a known runtime gap). Closing the extractor gap is a runtime change on routes.rs — another surface — so the doc states what the runtime actually does.- PRJ-04 (project-honest, no runtime change): registry-declared
http_statusis still projected in the doc (so the declared codes stay visible), but every projection whose codes are notHTTP_<status>-prefixed carriesx-runtime-behavior: 500plus an explicit description note — the runtime mapper is purely code-driven andErrorDefinition.http_statusis never consulted. The enshrining test was rewritten to assert the annotation instead of the old behavior. Honoringhttp_statusat runtime would change routes.rs/error.rs (another agent's surface) and the ADR-023 semantics — rejected here.
Summary
What landed (src/adapters/to_openapi.rs overhaul, plus a one-line
version assertion in src/server/adapter.rs tests):
- PRJ-01 —
/search200 now documents the envelope ({request_id, result, output}with theoperationsarray underoutput) and the real item fields (name,namespace,op_type; nodescription). Thedescription-field lie is gone from both the schema and the summary. - PRJ-02 —
/schema200 documents the envelope wrapping a fullOperationSpecOutputcomponent (op_type,visibility,input_schema,output_schema,error_schemas,access_control,channel_open,publish_schema). - PRJ-03 — 422 (
CallErrorInvalidInput/CallErrorInvalidOperationTypeoneOf) replaces the bogus 400 INVALID_INPUT on dispatch paths;/call///batch///subscribe//schema400s document the plain-text extractor-rejection bodies with an explicit gap note;/batch400 also covers the 100-opBatchCapExceededJSON shape. - PRJ-04 — decision: project-honest with annotation
(
x-runtime-behavior: 500); see Notes. Enshrining test replaced byoperation_errors_projected_onto_call_with_runtime_behavior_annotated. - PRJ-05 —
/subscribedocuments exactly200(SSE) +400(extractor). The 200 description spells out the GW-12 asymmetry: pre-dispatch denials and handler failures arrive as terminalevent:errorframes carrying the serializedCallError, with theretry: 15000hint and 15 s keep-alive comment frames — no HTTP error statuses are structurally reachable. - PRJ-11 — the last library-side
expects are gone; all emission paths use infallible construction (as_object_mutguards are gone with the deleted mutation-based merge machinery, replaced bystruct-built JSON). - PRJ-12 —
gather_operation_errorsfolds registry iteration intoBTreeMap/BTreeMap<String, _>before anything is emitted; status keys and per-status code enums are sorted.same_registry_yields_byte_identical_docspins determinism;error_codes_are_sorted_within_each_status_keypins the ordering. - PRJ-14 — request bodies (
/call,/batchitems,/subscribe) reference#/components/schemas/CallRequest; the doc now definescomponents.schemas(19 components) +components.responses(6 shared error responses) and every endpoint's status map references them. No inline duplication of theCallRequestshape remains. - PRJ-15 —
/searchdrops the unreachable 401/403 and documents the real set (200/404/500/504);components.securitySchemes.bearerAuth(HTTP bearer, ADR-004) is declared and applied via top-levelsecurity+ per-operation overrides. - ADR-045 —
info.versionbumped1.1.0→1.2.0(minor): the overhaul is additive at the shape level (newly documented statuses/envelope fields; nothing previously documented was removed or retyped into a break). Theserver::adapterintegration test was aligned to the new version. - Golden test — 31 tests in
src/adapters/to_openapi.rsassert the generated doc against hand-written expectations mirroring the routes tests' actual bodies (envelope shape, item fields, status sets, code enums, security scheme, print-level determinism), and the full doc re-validates viaopenapiv3.
Verification: cargo test (288 passed, including the full-surface
integration tests), cargo clippy --all-targets -- -D warnings (clean),
cargo fmt --check (clean) — verified in a clean worktree at HEAD
because other agents' in-flight edits were present in the shared tree.