Files
alkhttp/tasks/adapters/review-001-openapi-projection-fidelity.md
T

8.3 KiB
Raw Blame History

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
review-001-gateway-stream-errors
review-001-gateway-publish-semantics
moderate medium component implementation
adapters
review-001
to-openapi

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: /search and /schema 200 responses are documented without the envelope wrapper ({request_id, result, output}) and with wrong item fields (description that doesn't exist; missing namespace/op_type/visibility/access_control/channel_open/ publish_schema). Clients generated from the doc are broken day one.
  • PRJ-03: documented 400 INVALID_INPUT vs 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_status is never consulted; RATE_LIMITED surfaces as 500). Either project honestly under 500 or honor http_status at runtime — with the review's note that operation_errors_projected_onto_call currently enshrines the wrong behavior.
  • PRJ-05: /subscribe documented statuses are structurally unreachable (always 200 + SSE per GW-12); in-band event:error frames undocumented. Document the 200+in-band-error contract.
  • PRJ-11/HY-03: remaining guarded expects in to_openapi (:440, :505, :512) — replace with if 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-practice components.schemas — use $ref or drop components so shape changes can't drift.
  • PRJ-15: /search documents 401/403 that cannot occur, omits the 404 that can; no securitySchemes anywhere 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)
  • /subscribe doc documents the 200 + event:error in-band contract (GW-12's asymmetry called out); /search statuses 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 replaced
  • components.securitySchemes declares the Bearer scheme
  • cargo test and cargo clippy --all-targets -- -D warnings pass

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_TYPE to 422 (the shared mapper's real output) and documents the axum extractor-rejection gap explicitly on the 400 responses (text/plain body, 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_status is still projected in the doc (so the declared codes stay visible), but every projection whose codes are not HTTP_<status>-prefixed carries x-runtime-behavior: 500 plus an explicit description note — the runtime mapper is purely code-driven and ErrorDefinition.http_status is never consulted. The enshrining test was rewritten to assert the annotation instead of the old behavior. Honoring http_status at 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/search 200 now documents the envelope ({request_id, result, output} with the operations array under output) and the real item fields (name, namespace, op_type; no description). The description-field lie is gone from both the schema and the summary.
  • PRJ-02/schema 200 documents the envelope wrapping a full OperationSpecOutput component (op_type, visibility, input_schema, output_schema, error_schemas, access_control, channel_open, publish_schema).
  • PRJ-03 — 422 (CallErrorInvalidInput/CallErrorInvalidOperationType oneOf) replaces the bogus 400 INVALID_INPUT on dispatch paths; /call///batch///subscribe//schema 400s document the plain-text extractor-rejection bodies with an explicit gap note; /batch 400 also covers the 100-op BatchCapExceeded JSON shape.
  • PRJ-04 — decision: project-honest with annotation (x-runtime-behavior: 500); see Notes. Enshrining test replaced by operation_errors_projected_onto_call_with_runtime_behavior_annotated.
  • PRJ-05/subscribe documents exactly 200 (SSE) + 400 (extractor). The 200 description spells out the GW-12 asymmetry: pre-dispatch denials and handler failures arrive as terminal event:error frames carrying the serialized CallError, with the retry: 15000 hint 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_mut guards are gone with the deleted mutation-based merge machinery, replaced by struct-built JSON).
  • PRJ-12gather_operation_errors folds registry iteration into BTreeMap/BTreeMap<String, _> before anything is emitted; status keys and per-status code enums are sorted. same_registry_yields_byte_identical_docs pins determinism; error_codes_are_sorted_within_each_status_key pins the ordering.
  • PRJ-14 — request bodies (/call, /batch items, /subscribe) reference #/components/schemas/CallRequest; the doc now defines components.schemas (19 components) + components.responses (6 shared error responses) and every endpoint's status map references them. No inline duplication of the CallRequest shape remains.
  • PRJ-15/search drops 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-level security + per-operation overrides.
  • ADR-045info.version bumped 1.1.01.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). The server::adapter integration test was aligned to the new version.
  • Golden test — 31 tests in src/adapters/to_openapi.rs assert 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 via openapiv3.

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.