- build_error_schemas: default/wildcard response keys dropped with a warn instead of emitting a dead HTTP_0 ErrorDefinition — /search never advertises a code that can't match (the runtime mapper already synthesizes HTTP_<actual> for unmapped statuses) - check_parameter_style: non-default style/explode parameter forms (spaceDelimited, pipeDelimited, deepObject, form+explode:false, simple+explode:true) fail import with a feature-naming SchemaParse; wire-equivalent defaults (form, simple) import unchanged — no more silent "[1,2]" array mis-serialization - servers overrides rejected at import at all three levels (document, path, operation) — the adapter pins one base_url at assembly time - trace-only paths: skip is now logged (warn naming path + methods), documented-as-inert instead of silent - detect_op_type + build_output_schema sweep 2XX/default keys for text/event-stream — a default-declared SSE stream classifies as Sub instead of returning one giant text body Tests: 11 new (error-drop, style rejections + default accept, servers 3-level rejections + baseline, trace skip, SSE default/2XX detection). Verified: cargo test (299), --all-features (370 + suites), clippy --all-targets -D warnings (default + all-features), fmt --check. Tasks: review-001-openapi-loud-degradation
6.6 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| review-001-openapi-loud-degradation | Loud unsupported-OpenAPI-feature handling (OAI-06) | completed | narrow | low | component | implementation |
|
Description
Review 001 finding OAI-06 — deliberately deferred during decomposition ("revisit after the input-schema work"): unsupported OpenAPI features still degrade silently, each producing an op that misbehaves only at call time (verified still true post-remediation):
default/wildcard response keys becomeErrorDefinition { code: "HTTP_0", http_status: None }(from_openapi.rs:build_error_schemas, ~:238-243) — entries that never match a real status.traceops silently skipped (openapi_spec.rs);serversoverrides ignored; parameterstyle/explodeunsupported and silent (arrays serialize"[1,2]"); adefault-declared SSE stream is missed bydetect_op_typeand would return one giant text string.
Since OAI-03's fix, the codebase already has the right pattern —
in: cookie fails import with a clear SchemaParse naming the feature
and the remediation. Extend that posture: unsupported features that
would produce a wrong-behaving op fail import loudly (or degrade in
a way that is documented, tested, and visibly warned), never silently.
Per-feature decision, implementer's judgment with the review's map:
reject at import (cookie-style, preferred for anything that changes
wire semantics: style/explode non-default forms, servers override
on a non-matching base), or support (HTTP_0's real fix is mapping
default → a documented catch-all code or dropping it with a warning
— pick one and test it), or document-as-inert (trace skip is
defensible if logged).
Acceptance Criteria
- A spec using each unsupported feature either imports with a documented, warned, tested behavior or fails import with a feature-naming error (tests per feature)
HTTP_0no longer emitted (default responses mapped or dropped loudly) —/searchnever advertises a code that can't matchstyle/explodenon-default forms do not silently mis-serialize arrayscargo testandcargo clippy --all-targets -- -D warningspass
References
- docs/reviews/001-initial-implementation-review.md (Part E, OAI-06)
- tasks/adapters/review-001-input-schema-enforcement.md (the loud-unsupported pattern to extend)
Notes
Per-feature decisions (the review's map, applied):
default/wildcard responses → dropped loudly (warn, not reject): a catch-all code was considered and rejected — the runtime mapper (forward.rs::error_envelope) already synthesizesHTTP_<actual>for unmapped statuses, so adefaultErrorDefinition could never match anything: keeping it would only let/searchadvertise a deadHTTP_0code.build_error_schemasnow skips any non-numeric response key attracing::warnlevel, naming the operation, namespace, and response key, with the remediation in the message (declare explicit statuses). Wildcards (5XX,4XX) fall out of the sameparse::<u16>()check — they were the same dead entry class.style/explodenon-default forms → reject at import (cookie-style). Newcheck_parameter_styleinopenapi_spec.rs;parse_operationnow returnsResult<Option<Operation>, ParameterStyleError>so the caller converts the refusal into aSchemaParsenaming the parameter, the method+path, the offending declaration, and the remediation. Wire-equivalent forms accepted silently:form(query/path default, explode=true) andsimple(header/path default, explode=false) — a spec authoring these explicitly gets identical serialization to omitting them. Rejected with feature-naming errors:spaceDelimited,pipeDelimited,deepObject,matrix,label(the generic arm), plusform+explode:false(comma-glue, the?a=1,2mis-serialization the review flagged) andsimple+explode:true.serversoverrides → reject at import at all three levels (document root, per-path, per-operation) in one sweep infrom_value. The adapter pins onebase_urlat assembly time and cannot honor per-location servers; the error names every offending location and the remediation (remove the entries, or one import per base URL). This check lives inopenapi_spec.rs(parse time), so bothfrom_openapiand any future raw-doc consumer inherit it.trace→ documented-as-inert, now logged.HTTP_METHODSremains withouttrace(scope guard: no new feature support), but a path entry carrying only unsupported methods was previously dropped without a trace;from_valuenow emits atracing::warnnaming the path and the skipped methods. Tested as "skips without erroring, the rest of the doc imports" (the log line itself is the visibility mechanism, consistent with the module's other warns).default/non-200 2XX-declared SSE → supported (small superset).detect_op_typeandbuild_output_schemasweep200..206, 226, defaultfortext/event-streaminstead of only200/201— adefault-declared stream now classifies asSubwith the SSE output schema instead of degrading to a giant single text body. This was the one place where "reject" would have been user-hostile: declaring streams underdefaultis a real-world pattern, and detection is a two-line change.
Summary
src/adapters/from_openapi.rs:build_error_schemasdropsdefault/wildcard keys loudly (never emitsHTTP_0);detect_op_typebuild_output_schemasweep 2XX/defaultfortext/event-stream.
src/adapters/openapi_spec.rs: document/path/operation-levelserversrejection;check_parameter_style(ParameterStyleError) rejecting non-defaultstyle/explodewith feature-naming errors while accepting the wire-equivalent defaults; unsupported-method-only paths logged at warn.- Tests (8 new):
default_response_key_is_dropped_not_advertised_as_http_0,default_declared_sse_stream_classifies_as_subscription,non_default_2xx_sse_stream_classifies_as_subscription,non_default_style_parameter_fails_import_naming_the_feature,deep_object_style_is_rejected_like_the_other_non_default_forms,form_style_with_explode_false_is_rejected,default_style_and_explode_forms_still_import,servers_override_at_document_level_fails_import,servers_override_at_path_and_operation_level_fails_import,servers_absent_baseline_still_imports,trace_only_path_is_skipped_and_documented_inert. - Verified:
cargo test(299),--all-features(370 + suites),clippy --all-targets -- -D warnings(default + all-features),fmt --check.