fix(generator): make Anthropic + OpenAI clients compile + add CI regression guard#16
Merged
Merged
Conversation
Regenerating the clients for the Anthropic and OpenAI specs against the
post-conformance generator surfaced four codegen bugs introduced by the
recent batch:
1. Optional request bodies (T11) were chained as
`req.post(url).multipart(form)` even when `form: Option<Form>`. The
chain doesn't typecheck for optional bodies.
Fix: generate_request_body now emits statements that mutate `req`,
wrapped in `if let Some(...)` for optional bodies.
2. $ref-typed parameters (T10) emitted `(StructType).to_string()` for
non-enum schema refs (e.g. `Metadata` — a wrapper struct). Structs
don't implement Display.
Fix: at analysis time, only keep schema_ref on a parameter when the
referenced component is a string-with-enum/const. Struct refs fall
back to `String` until proper deepObject/form serialization (T14).
3. The previous fix exposed a second issue: enum-typed query params
called `.as_ref()` on the enum value, but generated string enums
didn't implement AsRef<str> or Display.
Fix: generate_string_enum now emits `as_str()`, `Display`, and
`AsRef<str>` impls so the enum's serde-renamed wire form drops
straight into headers, query strings, and path segments.
4. Query/path/header emitters used `param.rust_type == "String"` to
decide whether to call `.as_ref()` before `.to_string()`. After T10,
that's wrong for enum-typed params (rust_type stays "String" but
schema_ref carries the enum name).
Fix: param_uses_as_ref_str() helper consults schema_ref too.
Verified via the new scripts/spec-compile.sh: regenerates clients for
specs/anthropic.yaml and specs/openai.yaml and runs `cargo check` on
both — both compile cleanly.
ci(spec-compile): run scripts/spec-compile.sh in a new CI job
Regression guard: any generator change that emits invalid Rust against
the Anthropic or OpenAI specs fails the PR. Unit tests exercise the
generator surface but can pass while real-world specs break — this
catches that gap.
chore: ignore /tmp/{spec-compile,gen-*}/ build artifacts
Refs #14
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Generating the Anthropic and OpenAI clients with the post-#15 generator surfaced four real-world codegen bugs introduced by the conformance batch. This PR fixes them and locks the regression in with a new CI job.
Bugs fixed
Optional request bodies didn't typecheck. T11 wraps non-required bodies in
Option<T>, butgenerate_request_bodywas still chaining.multipart(form)/.body(...)directly off the request builder, which doesn't compile when the value isOption<Form>. The fix moves body application out of the chain intoif let Some(...) { req = req.body(...) }.$ref-typed parameters with non-enum targets. T10 made
get_param_rust_typehonorschema_refalways — but for object refs (e.g. OpenAI'sMetadatawrapper struct) that emitted(Metadata).to_string(), which doesn't compile because structs don't implementDisplay. The fix is at analysis time: only keepschema_refon a parameter when the referenced component is astringwithenumorconst. Struct refs fall back toStringuntil we have proper deepObject / form serialization (T14).String enums had no
Display/AsRef<str>. Query/path/header emitters call.as_ref()and.to_string()on enum-typed parameters; the generated enum needs both impls.generate_string_enumnow emitsas_str(),Display, andAsRef<str>impls that return the serde-renamed wire form.rust_type == "String"was the wrong gate. After T10, the gate that decides between.as_ref().to_string()and.to_string()needs to also checkschema_ref. Introducedparam_uses_as_ref_str()as a single source of truth.Regression guard (CI)
New
scripts/spec-compile.shand aspec-compileGitHub Actions job:specs/anthropic.yamlandspecs/openai.yaml.reqwest 0.12+reqwest-middleware 0.4w/multipartfeature +thiserror).cargo checkon both.Any generator change that emits invalid Rust against these reference specs now fails the PR. The script is also developer-runnable:
scripts/spec-compile.sh(or a subset likescripts/spec-compile.sh anthropic).Adding more specs in the future is one line in
SPECS=(...)inside the script.Test plan
cargo test --tests— 205/205 passcargo clippy --all-features -- -D warnings— cleancargo fmt --check— cleanscripts/spec-compile.sh— both Anthropic and OpenAI compile cleanly locallyspec-compilejob passes (will verify on push)🤖 Generated with Claude Code