OpenAPI 3.1 / 3.2 Conformance — Master Tracking Issue
This is the single source-of-truth issue for getting the generator to honestly support OpenAPI 3.1 and 3.2. We will comment on this issue as work progresses rather than filing 50+ child issues.
Background
Six parallel audit agents reviewed the codebase against the OpenAPI 3.1.2 (2025-09-19) and OpenAPI 3.2.0 (2025-09-19) specs. Reports live under tmp/openapi-specs/reports/ (and 00-SUMMARY.md consolidates).
Headline finding: the README claims OpenAPI 3.1 support; in practice the generator is OpenAPI 3.0-shaped with a thin 3.1 veneer. A 3.1- or 3.2-only spec parses without error and silently drops most semantics. Architectural root cause: every parsing struct ends with #[serde(flatten)] extra: BTreeMap<String, Value> and there is no deny_unknown_fields anywhere.
Conformance harness
The repo now contains a self-validating conformance harness:
tests/conformance/specs/ — committed copies of OAS 3.1.2 and 3.2.0 markdown.
tests/conformance/catalog.yaml — generated from the 3.2.0 spec by cargo run --bin catalog-gen. 30 OAS Objects, 141 fields, 57 JSON Schema 2020-12 keywords, 7 parameter-style combos. This is the denominator for "100% coverage".
tests/conformance/fixtures/ — atomic fixtures, each tagged with coverage: entries from the catalog and a fails_at: L0|L1|L3|... marker. The harness (tests/conformance.rs) only enforces ACTIVE_LAYERS; later layers light up as they are implemented.
tests/conformance/external/json-schema-test-suite/ — git submodule of the canonical JSON Schema 2020-12 corpus. Run via tests/conformance_json_schema.rs.
tests/conformance/external/apis-guru-sync.sh — lazy-clones the APIs.guru openapi-directory for nightly real-world smoke (APIS_GURU_SMOKE=1).
tests/conformance/status.toml — honest support claims, derived from harness results. README will pull from this.
Beads — units of independently-mergeable work
All 51 beads + 4 epics are described in tests/conformance/beads.yaml. The tables below are a rendered view; edit beads.yaml as the source of truth.
Parallelization rule: two beads may run concurrently iff their files sets are disjoint AND neither is in the other's depends_on closure.
Phase 0
|
ID |
Title |
Area |
Files |
Depends on |
| [ ] |
F1 |
Add #[serde(deny_unknown_fields)] across spec structs + version validator |
foundation |
src/openapi.rs src/cli.rs |
— |
| [ ] |
F2 |
Schema enum supports type as array (3.1 canonical nullability) |
schema |
src/openapi.rs src/analysis.rs |
F1 |
| [ ] |
F3 |
Reference Object as sum type for non-Schema reusable objects |
refs |
src/openapi.rs src/analysis.rs |
F1 |
| [ ] |
F4 |
Accept paths-less specs (components-only and webhooks-only) |
components |
src/analysis.rs |
F1 |
Phase 1
|
ID |
Title |
Area |
Files |
Depends on |
| [ ] |
T1 |
Wire header request parameters through codegen |
codegen |
src/client_generator.rs src/analysis.rs |
F1 |
| [ ] |
T2 |
Emit operations for options/head/patch/trace HTTP methods |
codegen |
src/client_generator.rs src/registry_generator.rs |
F1 |
| [ ] |
T3 |
Honor auth_config ApiKey + Custom variants |
security |
src/client_generator.rs src/config.rs README.md |
F1 |
| [ ] |
T4 |
Walk webhooks: in analysis |
webhooks |
src/openapi.rs src/analysis.rs src/client_generator.rs |
F1, F3 |
| [ ] |
T5 |
Path templating percent-encoding |
codegen |
src/client_generator.rs |
F1 |
| [ ] |
T6 |
Detect operationId collisions |
codegen |
src/analysis.rs |
F1 |
| [ ] |
T7 |
Handle non-JSON success response bodies |
bodies |
src/analysis.rs src/client_generator.rs |
F1 |
| [ ] |
T8 |
Wire range status codes (2XX/4XX/5XX) |
bodies |
src/client_generator.rs src/analysis.rs |
F1 |
| [ ] |
T9 |
Model Response Header object (typed response headers) |
bodies |
src/openapi.rs src/client_generator.rs |
F1, F3 |
| [ ] |
T10 |
Resolve $ref-typed parameter types |
refs |
src/client_generator.rs src/analysis.rs |
F1, F3 |
| [ ] |
T11 |
requestBody required:false → Option |
bodies |
src/client_generator.rs src/analysis.rs |
F1 |
| [ ] |
T12 |
additionalProperties: → BTreeMap<String, T> |
schema |
src/analysis.rs |
F1 |
| [ ] |
T13 |
Surface description/summary/deprecated/tags into rustdoc + module structure |
codegen |
src/client_generator.rs src/registry_generator.rs |
F1 |
| [ ] |
T14 |
Implement parameter style/explode matrix (RFC6570) |
paths |
src/openapi.rs src/analysis.rs src/client_generator.rs |
F1 |
| [ ] |
T15 |
SSE streaming auto-detection from text/event-stream responses |
bodies |
src/streaming.rs src/analysis.rs |
F1 |
Phase 2
|
ID |
Title |
Area |
Files |
Depends on |
| [ ] |
H1 |
Model Server Object + Server Variable + multi-server runtime |
security |
src/openapi.rs src/client_generator.rs src/cli.rs |
F1 |
| [ ] |
H2 |
Model SecurityScheme (apiKey/http/oauth2/openIdConnect/mutualTLS) |
security |
src/openapi.rs src/client_generator.rs src/streaming.rs |
F1, T3 |
| [ ] |
H3 |
Model SecurityRequirement (AND/OR + per-op + empty) |
security |
src/openapi.rs src/client_generator.rs |
H2 |
| [ ] |
H4 |
Model Encoding Object (multipart + form-urlencoded) |
bodies |
src/openapi.rs src/client_generator.rs |
F1 |
| [ ] |
H5 |
Model Header Object as request and response |
bodies |
src/openapi.rs |
F1, F3 |
| [ ] |
H6 |
Model Example Object (object map; remove deprecated singular example) |
bodies |
src/openapi.rs |
F1, F3 |
| [ ] |
H7 |
Model Link Object + runtime expressions |
components |
src/openapi.rs |
F1, F3 |
| [ ] |
H8 |
Model Callback Object |
components |
src/openapi.rs src/analysis.rs |
F1, F3, T4 |
| [ ] |
H9 |
Model Tag Object + tag-based module organization |
tags |
src/openapi.rs src/client_generator.rs src/analysis.rs |
F1, T13 |
| [ ] |
H10 |
Model ExternalDocs |
components |
src/openapi.rs |
F1 |
| [ ] |
H11 |
Path Item $ref + components/pathItems bucket |
components |
src/openapi.rs src/analysis.rs |
F1, F3 |
| [ ] |
H12 |
Discriminator allOf-parent polymorphism + not keyword |
schema |
src/openapi.rs src/analysis.rs |
F1 |
Phase 2b
|
ID |
Title |
Area |
Files |
Depends on |
| [ ] |
J1 |
$dynamicRef / $dynamicAnchor (replace $recursiveRef) |
schema |
src/openapi.rs src/analysis.rs |
F1 |
| [ ] |
J2 |
$defs enumeration in extract_schemas |
schema |
src/analysis.rs |
F1 |
| [ ] |
J3 |
Validation keywords as runtime checks (validator crate) |
schema |
src/openapi.rs src/generator.rs |
F1 |
| [ ] |
J4 |
prefixItems (tuple types) |
schema |
src/openapi.rs src/analysis.rs |
F1 |
| [ ] |
J5 |
patternProperties + propertyNames |
schema |
src/openapi.rs src/analysis.rs |
F1 |
| [ ] |
J6 |
unevaluatedProperties / unevaluatedItems |
schema |
src/openapi.rs src/analysis.rs |
F1 |
| [ ] |
J7 |
dependentSchemas / dependentRequired |
schema |
src/openapi.rs src/analysis.rs |
F1 |
| [ ] |
J8 |
contains / minContains / maxContains; contentEncoding/MediaType/Schema |
schema |
src/openapi.rs src/analysis.rs |
F1 |
| [ ] |
J9 |
format coverage: date-time, uuid, byte, binary, email, uri |
schema |
src/analysis.rs |
F1 |
| [ ] |
J10 |
jsonSchemaDialect at root; remove 3.0 nullable |
schema |
src/openapi.rs src/analysis.rs |
F1, F2 |
Phase 3
|
ID |
Title |
Area |
Files |
Depends on |
| [ ] |
D1 |
PathItem.additionalOperations + query method |
paths |
src/openapi.rs src/analysis.rs src/client_generator.rs |
F1, T2 |
| [ ] |
D2 |
Parameter in: querystring |
paths |
src/openapi.rs src/analysis.rs src/client_generator.rs |
F1 |
| [ ] |
D3 |
MediaType.itemSchema / prefixEncoding / itemEncoding |
bodies |
src/openapi.rs src/analysis.rs src/client_generator.rs |
F1, H4 |
| [ ] |
D4 |
OAuth deviceAuthorization flow + oauth2MetadataUrl |
security |
src/openapi.rs |
F1, H2 |
| [ ] |
D5 |
Tag.kind / parent / summary |
tags |
src/openapi.rs src/client_generator.rs |
F1, H9 |
| [ ] |
D6 |
$self keyword + Appendix F/G base URI rules |
refs |
src/openapi.rs src/analysis.rs |
F1 |
| [ ] |
D7 |
Components.mediaTypes bucket |
components |
src/openapi.rs src/analysis.rs |
F1 |
| [ ] |
D8 |
Server.name field |
security |
src/openapi.rs |
F1, H1 |
| [ ] |
D9 |
Discriminator.defaultMapping |
schema |
src/openapi.rs src/analysis.rs |
F1, H12 |
| [ ] |
D10 |
SecurityScheme.deprecated |
security |
src/openapi.rs src/client_generator.rs |
F1, H2 |
Parallel-execution batches
Beads grouped by dependency depth. Within a depth tier, beads with disjoint file sets can be worked in parallel; beads sharing files must serialize.
Tier 0
- Batch 0.1: F1 (touches src/cli.rs, src/openapi.rs)
Tier 1
- Batch 1.1: F2, T2 (touches src/analysis.rs, src/client_generator.rs, src/openapi.rs, src/registry_generator.rs)
- Batch 1.2: F3, T3 (touches README.md, src/analysis.rs, src/client_generator.rs, src/config.rs, src/openapi.rs)
- Batch 1.3: F4, T5, H10 (touches src/analysis.rs, src/client_generator.rs, src/openapi.rs)
- Batch 1.4: T1, J3 (touches src/analysis.rs, src/client_generator.rs, src/generator.rs, src/openapi.rs)
- Batch 1.5: T6, T13 (touches src/analysis.rs, src/client_generator.rs, src/registry_generator.rs)
- Batch 1.6: T7 (touches src/analysis.rs, src/client_generator.rs)
- Batch 1.7: T8 (touches src/analysis.rs, src/client_generator.rs)
- Batch 1.8: T11 (touches src/analysis.rs, src/client_generator.rs)
- Batch 1.9: T12, H1 (touches src/analysis.rs, src/cli.rs, src/client_generator.rs, src/openapi.rs)
- Batch 1.10: T14 (touches src/analysis.rs, src/client_generator.rs, src/openapi.rs)
- Batch 1.11: T15, H4 (touches src/analysis.rs, src/client_generator.rs, src/openapi.rs, src/streaming.rs)
- Batch 1.12: H12 (touches src/analysis.rs, src/openapi.rs)
- Batch 1.13: J1 (touches src/analysis.rs, src/openapi.rs)
- Batch 1.14: J2 (touches src/analysis.rs)
- Batch 1.15: J4 (touches src/analysis.rs, src/openapi.rs)
- Batch 1.16: J5 (touches src/analysis.rs, src/openapi.rs)
- Batch 1.17: J6 (touches src/analysis.rs, src/openapi.rs)
- Batch 1.18: J7 (touches src/analysis.rs, src/openapi.rs)
- Batch 1.19: J8 (touches src/analysis.rs, src/openapi.rs)
- Batch 1.20: J9 (touches src/analysis.rs)
- Batch 1.21: D2 (touches src/analysis.rs, src/client_generator.rs, src/openapi.rs)
- Batch 1.22: D6 (touches src/analysis.rs, src/openapi.rs)
- Batch 1.23: D7 (touches src/analysis.rs, src/openapi.rs)
Tier 2
- Batch 2.1: T4 (touches src/analysis.rs, src/client_generator.rs, src/openapi.rs)
- Batch 2.2: T9 (touches src/client_generator.rs, src/openapi.rs)
- Batch 2.3: T10, H5 (touches src/analysis.rs, src/client_generator.rs, src/openapi.rs)
- Batch 2.4: H2 (touches src/client_generator.rs, src/openapi.rs, src/streaming.rs)
- Batch 2.5: H6 (touches src/openapi.rs)
- Batch 2.6: H7 (touches src/openapi.rs)
- Batch 2.7: H9 (touches src/analysis.rs, src/client_generator.rs, src/openapi.rs)
- Batch 2.8: H11 (touches src/analysis.rs, src/openapi.rs)
- Batch 2.9: J10 (touches src/analysis.rs, src/openapi.rs)
- Batch 2.10: D1 (touches src/analysis.rs, src/client_generator.rs, src/openapi.rs)
- Batch 2.11: D3 (touches src/analysis.rs, src/client_generator.rs, src/openapi.rs)
- Batch 2.12: D8 (touches src/openapi.rs)
- Batch 2.13: D9 (touches src/analysis.rs, src/openapi.rs)
Tier 3
- Batch 3.1: H3 (touches src/client_generator.rs, src/openapi.rs)
- Batch 3.2: H8 (touches src/analysis.rs, src/openapi.rs)
- Batch 3.3: D4 (touches src/openapi.rs)
- Batch 3.4: D5 (touches src/client_generator.rs, src/openapi.rs)
- Batch 3.5: D10 (touches src/client_generator.rs, src/openapi.rs)
How we'll work
- Phase 0 lands first as a single PR (or as F1, F2, F3, F4 in sequence). Without
deny_unknown_fields, every later fix is unverifiable.
- Each subsequent bead is a focused PR, scoped to its
files set, with a conformance fixture flipping from fails_at: failing to passing.
- Progress is tracked in this issue's comments: one comment per merged bead with the bead ID, PR link, and which
status.toml entries flipped.
- We don't stage 50 child issues — the granularity lives in
beads.yaml and PR titles. This issue is the dashboard.
- Honesty gate:
tests/conformance/status.toml is regenerated by the harness; the README's support claims will be derived from it. Drift fails CI.
References
- Audit reports:
tmp/openapi-specs/reports/00-SUMMARY.md and per-area files (01-schema, 02-paths-parameters, 03-bodies-media, 04-servers-security, 05-components-refs-webhooks, 06-three-two-deltas).
- Spec:
tests/conformance/specs/openapi-3.2.0.md, openapi-3.1.2.md.
- Catalog:
tests/conformance/catalog.yaml.
- Beads source-of-truth:
tests/conformance/beads.yaml.
OpenAPI 3.1 / 3.2 Conformance — Master Tracking Issue
This is the single source-of-truth issue for getting the generator to honestly support OpenAPI 3.1 and 3.2. We will comment on this issue as work progresses rather than filing 50+ child issues.
Background
Six parallel audit agents reviewed the codebase against the OpenAPI 3.1.2 (2025-09-19) and OpenAPI 3.2.0 (2025-09-19) specs. Reports live under
tmp/openapi-specs/reports/(and00-SUMMARY.mdconsolidates).Headline finding: the README claims OpenAPI 3.1 support; in practice the generator is OpenAPI 3.0-shaped with a thin 3.1 veneer. A 3.1- or 3.2-only spec parses without error and silently drops most semantics. Architectural root cause: every parsing struct ends with
#[serde(flatten)] extra: BTreeMap<String, Value>and there is nodeny_unknown_fieldsanywhere.Conformance harness
The repo now contains a self-validating conformance harness:
tests/conformance/specs/— committed copies of OAS 3.1.2 and 3.2.0 markdown.tests/conformance/catalog.yaml— generated from the 3.2.0 spec bycargo run --bin catalog-gen. 30 OAS Objects, 141 fields, 57 JSON Schema 2020-12 keywords, 7 parameter-style combos. This is the denominator for "100% coverage".tests/conformance/fixtures/— atomic fixtures, each tagged withcoverage:entries from the catalog and afails_at: L0|L1|L3|...marker. The harness (tests/conformance.rs) only enforcesACTIVE_LAYERS; later layers light up as they are implemented.tests/conformance/external/json-schema-test-suite/— git submodule of the canonical JSON Schema 2020-12 corpus. Run viatests/conformance_json_schema.rs.tests/conformance/external/apis-guru-sync.sh— lazy-clones the APIs.guru openapi-directory for nightly real-world smoke (APIS_GURU_SMOKE=1).tests/conformance/status.toml— honest support claims, derived from harness results. README will pull from this.Beads — units of independently-mergeable work
All 51 beads + 4 epics are described in
tests/conformance/beads.yaml. The tables below are a rendered view; edit beads.yaml as the source of truth.Parallelization rule: two beads may run concurrently iff their
filessets are disjoint AND neither is in the other'sdepends_onclosure.Phase 0
F1src/openapi.rs src/cli.rsF2typeas array (3.1 canonical nullability)src/openapi.rs src/analysis.rsF3src/openapi.rs src/analysis.rsF4src/analysis.rsPhase 1
T1src/client_generator.rs src/analysis.rsT2src/client_generator.rs src/registry_generator.rsT3src/client_generator.rs src/config.rs README.mdT4src/openapi.rs src/analysis.rs src/client_generator.rsT5src/client_generator.rsT6src/analysis.rsT7src/analysis.rs src/client_generator.rsT8src/client_generator.rs src/analysis.rsT9src/openapi.rs src/client_generator.rsT10src/client_generator.rs src/analysis.rsT11src/client_generator.rs src/analysis.rsT12src/analysis.rsT13src/client_generator.rs src/registry_generator.rsT14src/openapi.rs src/analysis.rs src/client_generator.rsT15src/streaming.rs src/analysis.rsPhase 2
H1src/openapi.rs src/client_generator.rs src/cli.rsH2src/openapi.rs src/client_generator.rs src/streaming.rsH3src/openapi.rs src/client_generator.rsH4src/openapi.rs src/client_generator.rsH5src/openapi.rsH6example)src/openapi.rsH7src/openapi.rsH8src/openapi.rs src/analysis.rsH9src/openapi.rs src/client_generator.rs src/analysis.rsH10src/openapi.rsH11src/openapi.rs src/analysis.rsH12notkeywordsrc/openapi.rs src/analysis.rsPhase 2b
J1src/openapi.rs src/analysis.rsJ2src/analysis.rsJ3src/openapi.rs src/generator.rsJ4src/openapi.rs src/analysis.rsJ5src/openapi.rs src/analysis.rsJ6src/openapi.rs src/analysis.rsJ7src/openapi.rs src/analysis.rsJ8src/openapi.rs src/analysis.rsJ9src/analysis.rsJ10src/openapi.rs src/analysis.rsPhase 3
D1src/openapi.rs src/analysis.rs src/client_generator.rsD2in: querystringsrc/openapi.rs src/analysis.rs src/client_generator.rsD3src/openapi.rs src/analysis.rs src/client_generator.rsD4src/openapi.rsD5src/openapi.rs src/client_generator.rsD6src/openapi.rs src/analysis.rsD7src/openapi.rs src/analysis.rsD8src/openapi.rsD9src/openapi.rs src/analysis.rsD10src/openapi.rs src/client_generator.rsParallel-execution batches
Beads grouped by dependency depth. Within a depth tier, beads with disjoint file sets can be worked in parallel; beads sharing files must serialize.
Tier 0
Tier 1
Tier 2
Tier 3
How we'll work
deny_unknown_fields, every later fix is unverifiable.filesset, with a conformance fixture flipping fromfails_at:failing to passing.status.tomlentries flipped.beads.yamland PR titles. This issue is the dashboard.tests/conformance/status.tomlis regenerated by the harness; the README's support claims will be derived from it. Drift fails CI.References
tmp/openapi-specs/reports/00-SUMMARY.mdand per-area files (01-schema, 02-paths-parameters, 03-bodies-media, 04-servers-security, 05-components-refs-webhooks, 06-three-two-deltas).tests/conformance/specs/openapi-3.2.0.md,openapi-3.1.2.md.tests/conformance/catalog.yaml.tests/conformance/beads.yaml.