Skip to content

feat(codegen): lower open string discriminator unions into ordered anyOf with <Union>Base - #87

Open
gsmith85 wants to merge 1 commit into
feat/codegen-ast-reifyfrom
feat/codegen-open-unions
Open

gsmith85 wants to merge 1 commit into
feat/codegen-ast-reifyfrom
feat/codegen-open-unions

Conversation

@gsmith85

@gsmith85 gsmith85 commented Oct 3, 2026 •

Copy link
Copy Markdown

Working...

Summary

Implements Task 7 (PR 3b) of the two-layer ucp-schema code-generation pipeline: open string discriminator union lowering (lower_conditional_unions in src/codegen/reify.rs), stacked on #86.

Changes

  • Conditional allOf if/then.$ref Union Lowering (src/codegen/reify.rs):
    • Detects schemas whose allOf branches consist of if: { properties: { <disc>: { const | enum([single]) } } } paired with then: { "$ref": "#/$defs/<Variant>" }.
    • Merges parent properties and required into each referenced variant definition, removes any back-reference {"$ref": "#/$defs/<ParentName>"} from the variant's allOf, sets the variant's discriminator property to {"type": "string", "const": "<tag>"}, and strips "default" from variant discriminator properties (invariant OU-4).
  • Open vs. Closed Discriminator Handling (OU-1 through OU-4):
    • When the parent discriminator property is an open string (type == "string" without const or enum, or a $ref to such a string schema like ReverseDomainName), synthesizes <ParentName>Base in $defs with all base properties, additionalProperties: true, and <disc>: { "type": "string", ..., "not": { "enum": [<sorted_known_tags>] } } (invariant OU-2), and strips "default" from the base discriminator property (invariant OU-4).
    • Rewrites <ParentName> to an ordered anyOf containing sorted $refs to the known variants followed by {"$ref": "#/$defs/<ParentName>Base"} as the final fallback branch (invariant OU-1), without emitting the OpenAPI "discriminator" keyword (invariant OU-3).
    • When the parent discriminator is closed (enum or const), emits anyOf with only the sorted variant $refs and no <ParentName>Base definition.

Stacked PR Series Roadmap

  1. PR 1a (feat(codegen): add identifier naming, UCP keyword stripping, and $defs ref rewriting #81): Identifier qualification, UCP keyword stripping, $ref rewriting (src/codegen/normalizer.rs)
  2. PR 1b (feat(codegen): add directional schema slicing and $ref alignment #82): Directional schema slicing & $ref alignment (src/codegen/normalizer.rs)
  3. PR 1c (feat(codegen): add capability reachability, composition, and generate_types() API #83): Capability/extension reachability, composition & generate_types() library API (src/codegen/{mod,reachability,hoist,compose}.rs)
  4. PR 1d (feat(cli): add generate-types subcommand #84): CLI generate-types subcommand (src/bin/ucp-schema.rs, tests/generate_types_test.rs)
  5. PR 2 (feat(codegen): add self-contained .well-known/ucp profile scoping for generate-types #85): Self-contained .well-known/ucp --profile scoping (src/codegen/profile.rs)
  6. PR 3a (feat(codegen): add AST shape normalizers and inline conditional variant hoisting #86): Codegen AST shape normalizers & inline conditional variant hoisting (src/codegen/normalizer.rs, src/codegen/reify.rs)
  7. PR 3b (this PR): Open string discriminator union lowering (<Union>Base) (src/codegen/reify.rs)
  8. PR 3c (feat(codegen): discover and register decentralized extended subtypes into open unions #88): Cross-file extended subtype discovery & registration (src/codegen/reify.rs)
  9. PR 4a (python-sdk#101): Python SDK direct JSON Schema codegen (generate_models.sh, postprocess_models.py, src/ucp_sdk/models/)
  10. PR 4b (js-sdk#87): JS/TS SDK direct JSON Schema codegen (generate_models.sh, scripts/generate-zod-from-types.mjs, src/spec_generated.ts)
  11. PR 5: OpenAPI 3.1 REST service & type binding (ucp-schema generate-openapi, src/openapi/mod.rs)

Test Plan

  • cargo fmt --check
  • cargo test --all-targets
  • cargo clippy --all-targets -- -D warnings

@damaz91 damaz91 added the status:needs-triage Signal that the PR is ready for human triage label Oct 3, 2026
@gsmith85
gsmith85 force-pushed the feat/codegen-ast-reify branch from 63a971e to 8ebe945 Compare October 3, 2026 02:03
@gsmith85
gsmith85 force-pushed the feat/codegen-open-unions branch from 33e031a to 233fbe2 Compare October 3, 2026 02:07
@gsmith85
gsmith85 force-pushed the feat/codegen-open-unions branch from 233fbe2 to 9971a9c Compare October 5, 2026 23:57
@gsmith85
gsmith85 force-pushed the feat/codegen-ast-reify branch from a9d9753 to 57d290f Compare October 6, 2026 00:12
@gsmith85
gsmith85 force-pushed the feat/codegen-open-unions branch 2 times, most recently from 7940a92 to cd8c9b3 Compare October 7, 2026 06:00
@gsmith85
gsmith85 force-pushed the feat/codegen-ast-reify branch from 57d290f to 63aa281 Compare October 7, 2026 06:00
@gsmith85
gsmith85 force-pushed the feat/codegen-ast-reify branch from 63aa281 to 831c2df Compare October 7, 2026 22:04
@gsmith85
gsmith85 force-pushed the feat/codegen-open-unions branch from cd8c9b3 to ff6d9c1 Compare October 7, 2026 22:04
@gsmith85
gsmith85 force-pushed the feat/codegen-open-unions branch from ff6d9c1 to 792c4ed Compare October 8, 2026 00:32
@gsmith85
gsmith85 force-pushed the feat/codegen-ast-reify branch from 831c2df to 2d102e4 Compare October 8, 2026 00:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:needs-triage Signal that the PR is ready for human triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants