Skip to content

Decide the openapi-generator relationship and 0.2 maintenance plan before merging the pure-Julia rewrite (#103) #104

Description

@tanmaykm

Context

#103 replaces the 0.2.x runtime-library model with a pure-Julia pipeline: OpenAPI.client / OpenAPI.server parse OAS 3.0/3.1/3.2 documents and emit generated modules directly, with no involvement from the openapi-generator Java toolchain. Today, however, this package's primary consumers are modules produced by openapi-generator's julia-client / julia-server targets, whose templates call the public 0.2.x runtime API (OpenAPI.Clients, OpenAPI.Servers, validate_param, property_type, …) — an API that #103 removes entirely.

This issue is to decide, before merge/tag, what happens to that lane. It is not a proposal to change the direction of #103 — the rewrite has been evaluated in depth against real workloads (including a full Kubernetes-client trial across 27 group documents) and the new pipeline wins on merit for 3.x work.

Evaluation status caveat: that depth applies to the client side. Server generation is present and symmetric (OpenAPI.server / serverplan, a register!(router, impl) contract deliberately shaped like the 0.2.x target's register, HTTP.Router support via OpenAPIHTTPExt, and a server_source seam for other frameworks) and has a substantial test suite — but it has not been exercised against a live production-shaped service the way the client was. A smoke test implementing one real spec (e.g. a JuliaHub runner spec) end to end should happen before the server path is declared ready.

Why we should not try to bridge the two generators

The obvious "integration" — retargeting the openapi-generator mustache templates to emit #103-style code — is structurally unworkable:

  1. Generated modules embed _SPEC data and compiled schema information produced by the normalize→plan pipeline and the JSON Schema engine. Mustache templates render the Java CodegenModel; reproducing this would mean reimplementing normalize.jl + planning.jl in Java.
  2. The fidelity gains are precisely where templates are weakest: the Java generator collapses oneOf/anyOf to value::Any, emits hand-picked per-constraint validation call sites, and only wires parameter styling for query parameters. Pure-julia OpenAPI internals rewrite #103 produces typed unions and full schema-driven validation.
  3. OAS coverage has inverted: openapi-generator's 3.1 support is incomplete upstream, while Pure-julia OpenAPI internals rewrite #103 handles 3.0/3.1/3.2 natively (and passes the official JSON Schema suite). The one capability moving the other way is Swagger 2.0 input, which openapi-generator converts internally and Pure-julia OpenAPI internals rewrite #103's loader rejects.

A shell-out from the Java generator to Julia is against openapi-generator's pure-JVM architecture and would not be accepted upstream.

Proposal: clean split, with the legacy lane explicitly maintained

  1. Create a release-0.2 maintenance branch before v1 is tagged. The openapi-generator targets, and every package currently built on them, keep tracking the 0.2.x runtime; recent fixes like fix(client): discard truncated JSON document on mid-stream EOF #97–fix(client): :http streaming stalls small chunks until 8KB buffer fills #102 show this lane still needs a home for backports.
  2. One housekeeping PR to openapi-generator: document that julia-client/julia-server track OpenAPI.jl 0.2.x and point Julia-native users at OpenAPI.client/OpenAPI.server; delete the stale docs/generators/julia.md (advertises an unregistered julia generator); fix the exportOperations config bug in JuliaClientCodegen.java (the flag writes through exportModels, so the two options aren't separable).
  3. Update this repo's README/CONTRIBUTING, which currently route codegen contributions to the openapi-generator repo.

Release blockers to resolve before tagging 1.0

  • Compat breakage for existing generated code. The Julia targets emit no Project.toml, so openapi-generator's samples-julia.yaml CI and any fresh environment doing Pkg.add("OpenAPI") against legacy generated code will resolve to 1.x and fail on the missing OpenAPI.Clients. Options: pin OpenAPI = "0.2" in that workflow and have the upstream generator emit a compat'd Project.toml; or release the rewrite under a new package name. Worth deciding deliberately rather than discovering via broken CI.
  • The generated-module ↔ runtime contract is private. Generated modules import and extend OpenAPI.Runtime internals (_decode/_encode), so they are version-coupled to the exact OpenAPI release that generated them. That's acceptable policy for baked artifacts, but it should be documented, and a small public surface (e.g. decode(Module, T, json)) would soften it — once 1.0 tags, this contract freezes.
  • Documentation generation is missing. The openapi-generator lane emits per-model and per-API markdown (docs/*.md) plus a usage README.md; Pure-julia OpenAPI internals rewrite #103 emits none of that. Generated client operations do get Julia docstrings (signature + spec summary + METHOD /path), but models get no docstrings at all — schema and property description fields from the spec are dropped entirely, and in large specs those descriptions are most of the documentation value. Two asks, both cheap relative to the pipeline work already done: carry description through to model/field docstrings at emission time, and offer a markdown-docs emitter as a separate function over the same ClientPlan. At minimum the first should land before 1.0; otherwise this freezes into the "what the native path doesn't do" list.
  • State the remaining dropped capabilities. Relative to the openapi-generator lane: no Swagger 2.0 input (document "convert first"), no user-overridable templates, single-file emit instead of per-model/per-API files, and no runtime-fix-without-regeneration. All defensible — but they should be release-notes content, not surprises.

Nice-to-have

  • Adopt openapi-generator's petstore/sample corpus as additional conformance fixtures — it encodes a decade of weird-spec regressions and Pure-julia OpenAPI internals rewrite #103 already has external-corpus test infrastructure.

Activity

  1. quinnj commented on Aug 17, 2026

    @quinnj
    Contributor

    This all sounds like a good plan + reasonable things to do pre-1.0 release.

  2. tanmaykm commented on Aug 27, 2026

    @tanmaykm
    MemberAuthor

    Status update (2026-08-27)

    The runtime-contract and docstring blockers were addressed on the #103 branch (details). I verified the claims against the PR head (8124eec) directly — regenerated a sample client/server pair, loaded them, and exercised the failure paths — rather than taking the summary at face value. Everything checked out.

    Blocker-by-blocker

    1. Compat breakage for existing generated code — OPEN. Untouched by the PR, deliberately: the 0.2 compat pin for samples-julia.yaml, the upstream Project.toml emission, and cutting release-0.2 are decisions for this repo and openapi-generator, not for #103. The release-0.2 branch cut is the most time-sensitive item on this whole list — it gets harder with every commit to main.

    2. Generated-module ↔ runtime contract — RESOLVED, via the "private by policy, guarded" route:

    • Runtime.CONTRACT_VERSION (currently 3) plus Runtime.require_contract(version, generator); both documented as permanently frozen names.
    • Every generated module (client, server, and the server_module_source extension path) stamps the producing OpenAPI.jl version in its banner and calls the guard at load time, before the private import OpenAPI.Runtime: list — verified live: a stale module fails with an actionable "regenerate" error naming both versions and both contract numbers.
    • The silent-failure hazard is gone at the source: generated code no longer bakes positional Dialect literals (standard dialects emit as SchemaEngine.dialect(:draft202012) lookups; custom ones use a keyword-only constructor), Runtime.Spec keywords are required, and a tripwire test pins CONTRACT_VERSION so shape changes without a bump fail CI.
    • Policy consequence, now locked in: generated modules are fully version-pinned baked artifacts (documented in README and MIGRATION.md). The optional softener this issue floated — a small public surface like decode(Module, T, json) — was not built, so every future contract bump forces downstream regeneration. Coherent, but worth stating explicitly since 1.0 freezes it.

    3. Documentation generation — MINIMUM MET. Spec descriptions now flow into generated model docstrings (schema description plus a bullet per documented field) and operation docstrings (summary, METHOD /path, parameter/request-body bullets), for both client and server output. Hostile-content tests cover quotes, $, """, backslashes, and unicode, and generated output remains byte-deterministic. Verified live on a sample spec.

    The second ask — a markdown-docs emitter over ClientPlan (the docs/*.md equivalent of the openapi-generator lane) — was not built. It should either get its own post-1.0 issue or be consciously dropped into the MIGRATION.md dropped-capabilities list; otherwise it silently freezes into "what the native path doesn't do", which is exactly what this issue warned about.

    4. Dropped capabilities stated — RESOLVED. MIGRATION.md now covers the 0.2.x → 1.0 transition: the openapi-generator lane, client/server API mapping, dropped capabilities, and generated-artifact compatibility.

    Remaining before tag, in suggested order

    1. Cut release-0.2 from current main (time-sensitive, zero cost).
    2. Decide the compat-pin approach for legacy generated code / samples-julia.yaml.
    3. Live server smoke test against a production-shaped spec (per the caveat in this issue's description).
    4. The package's own docs site: the PR removes docs/ and the docdeploy workflow entirely, so gh-pages will keep serving 0.2 documentation for an API that no longer exists. Needs either a Documenter setup for the new API or a deliberate teardown/redirect.
    5. Decide the markdown-docs emitter's fate (post-1.0 issue vs. documented as dropped).
    6. The upstream openapi-generator housekeeping PR (docs pointer, stale julia.md, exportOperations fix).
  3. tanmaykm commented on Aug 27, 2026

    @tanmaykm
    MemberAuthor

    Cut release-0.2 from main at d4471f3 (== v0.2.8): https://github.com/JuliaComputing/OpenAPI.jl/tree/release-0.2. The branch runs CI on push, and its README/CONTRIBUTING now state that 0.2.x fixes target it while main hosts the 1.x rewrite. Item 1 of the proposal is done; remaining on the compat blocker is the samples-julia.yaml pin / upstream Project.toml emission decision.

  4. tanmaykm commented on Aug 27, 2026

    @tanmaykm
    MemberAuthor

    Upstream housekeeping PR opened: OpenAPITools/openapi-generator#24789 — emits a compat'd Project.toml from both Julia generators (resolving the compat-breakage blocker at the source, per the decision above), repoints samples-julia.yaml from the v0.2.0 tag to release-0.2, fixes the exportOperations config bug, documents the 0.2.x runtime boundary in generated READMEs, and deletes the stale docs/generators/julia.md. That completes item 2 of the proposal; the compat blocker closes when it merges.

  5. tanmaykm commented on Aug 29, 2026

    @tanmaykm
    MemberAuthor

    Stock-take before merging #103 (2026-08-27)

    All four release blockers are now resolved:

    1. Compat breakage — closed by [julia] Emit Project.toml, track OpenAPI.jl release-0.2, fix exportOperations OpenAPITools/openapi-generator#24789 (merged): generated code ships a Project.toml pinning OpenAPI = "0.2", and samples-julia.yaml tests against release-0.2 (green on the PR twice and on the post-merge master push).
    2. Runtime contract — contract stamp + load guard, positional-literal elimination, tripwire test; policy documented (baked artifacts).
    3. Documentation generation — spec descriptions flow into model/field/operation docstrings; the markdown-docs emitter is explicitly documented as dropped in MIGRATION.md's dropped-capabilities section, so it no longer freezes in silently.
    4. Dropped capabilities — stated in MIGRATION.md.

    Proposal items: release-0.2 cut ✔, upstream housekeeping PR merged ✔, README/CONTRIBUTING routing updated on release-0.2 (and #103 replaces both on main) ✔.

    Still recommended before tagging v1.0.0 (from this issue's own caveat + review notes):

    • The live server smoke test against a production-shaped spec — the server path has a full test suite but has not been exercised against a real service the way the client was.
    • gh-pages: Pure-julia OpenAPI internals rewrite #103 removes docs/ and the docdeploy job, so the site will keep serving 0.2 docs against a 1.x package; needs a redirect/teardown or a minimal Documenter site at merge time.
    • Minor: SHA is in Pure-julia OpenAPI internals rewrite #103's [deps] without a [compat] entry (the other stdlibs have one) — worth adding before registration to keep AutoMerge happy.

    #103 itself is mergeable, up to date with main (includes v0.2.8), CI green (Julia 1.10, Julia 1, corpus), version = "1.0.0", same package UUID.

  6. tanmaykm commented on Aug 29, 2026

    @tanmaykm
    MemberAuthor

    Server smoke test complete — all five findings from the live validation are fixed and up for review:

    • Server smoke-test fixes: typed parameters, sequential JSON, undocumented success, external security schemes #108 (→ main): typed-parameter double decode (petstore findByStatus returned 400 on every call), buffered sequential-JSON array-vs-item handling, undocumented-success operations now answering nothing with an empty 200 plus a :missing_success_response planning warning, and permissive-mode tolerance for externally declared security schemes (strict still rejects). Validated by the full suite plus live petstore (same-lane and cross-lane with the unmodified 0.2 client), gap fixtures, and the unmodified JuliaHub secrets document end to end.
    • fix(client): keep form-style CSV separators literal in query strings #107 (→ release-0.2): the 0.2 client now serializes form-style CSV query parameters with literal separator commas per the OAS Style Examples/RFC6570, restoring interop with 1.x generated servers. Full 0.2 suite green with live servers (3080 tests); suggest tagging v0.2.9 on merge.

    With these merged, the caveat on the server path is discharged; remaining before the v1.0.0 tag are only the gh-pages docs cleanup and the SHA compat entry.

  7. tanmaykm commented on Aug 29, 2026

    @tanmaykm
    MemberAuthor

    Docs update: #109 adds the Documenter manual for the 1.0 line (option A from the docs discussion) — 12-page manual, API reference with enforced public-name coverage, build-time-executed examples, CI deploy job, and a trimmed README. Also noted while investigating: gh-pages currently serves v0.1.23 as stable and has no v0.2.x versioned docs at all (tag-triggered deploys silently stopped after v0.1.23); the preview deploy on #107 just succeeded, so the deploy key itself works. Merging #109 revives dev, and tagging v1.0.0 / v0.2.9 will publish versioned docs and move stable correctly.

  8. tanmaykm commented on Sep 2, 2026

    @tanmaykm
    MemberAuthor

    Closing (2026-09-02)

    Everything this issue set out to decide is done, and all four release blockers are verified against the live repo state:

    Two loose ends found while verifying, neither a blocker:

    1. Tag-triggered docs deploys never fired. TagBot creates tags through the release API, so the push: tags trigger in CI never ran for v1.0.0 or v0.2.9 (zero workflow runs on either ref) and gh-pages stable still pointed at v0.1.23. This is also the root cause of the "deploys silently stopped after v0.1.23" observation above. Fixed today by re-pushing both tags from a user account (same tag objects, same commits) and re-publishing the GitHub releases that the tag re-push had reverted to drafts. Tag CI runs are in progress for v1.0.0 and v0.2.9; their docs jobs will publish versioned docs and move stable. Future tags will hit the same problem unless TagBot's ssh push actually triggers workflows — worth checking on the next release.
    2. SHA has no [compat] entry in Project.toml on main. Cosmetic (stdlib; AutoMerge passed).

    Nice-to-have (openapi-generator sample corpus as conformance fixtures) was not adopted and can get its own issue if wanted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions