Skip to content

generate: make union emission independent of map order - #78

Merged
ThomasK33 merged 2 commits into
mainfrom
egm/generate-union-order
Oct 29, 2026
Merged

ThomasK33 merged 2 commits into
mainfrom
egm/generate-union-order

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

Fixes three latent defects in cmd/generate/internal/emit. No variant in the current schema triggers either one, and regenerating leaves every *_gen.go file byte-identical.

  1. Map order. emitUnion and WriteHelpersJen took the fallback discriminator as the first const property found by ranging over a map, and emitUnion collected the const injections for MarshalJSON the same way. A variant with two or more const properties could produce different output on each run. Both now scan properties in sorted key order (sortedPropertyKeys, firstConstPropertyKey).
  2. Phantom variants. emitUnion emits no type for a variant with no $ref, no properties, no null type, and no title, but WriteHelpersJen still emitted a constructor for it, which referenced a type that does not exist. Both now use isPhantomVariant.
  3. Helper names. WriteHelpersJen repeated emitUnion's variant naming, but it never saw the names that WriteTypesJen registers for inline object properties first. A variant whose preferred name was taken got a fallback name in types_gen.go, while helpers_gen.go kept the preferred name, so the package did not compile. emitUnion now records each oneOf variant it emits, and WriteHelpersJen builds the types once (buildTypesJen) and creates its constructors from that record. The second copy of the naming logic is gone.

Review focus: buildTypesJen in types.go and the shorter WriteHelpersJen in helpers.go. Helpers now trust emitUnion's record (field, type, discriminator, required keys, properties), so check that the record holds what the old helper code computed. The byte-identical regeneration says it does for the current schema.

Evidence

  • The new tests in union_test.go failed before the fix (types_gen.go differs between runs, helpers_gen.go builds undeclared type PickVariant2, and helpers_gen.go sets PickRequest.Item to &PickItem{...}, but the field has type *PickRequestItem) and pass after it, 10 of 10 runs with -count=10. The third defect was found by Codex review on docs(generate): add EGM contracts for cmd and cmd/generate #79.
  • cd cmd/generate && go test ./... passes.
  • cd cmd/generate && env -u GOPATH -u GOMODCACHE go run . leaves git diff -- '*_gen.go' empty.

Second PR of a 4-PR stack. It touches only cmd/generate/internal/emit and does not depend on the examples change below it.


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh

@ThomasK33
ThomasK33 added this pull request to stack #81 October 29, 2026 12:24
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T13:27:32.746023Z f93847e Manual request
🔒 Security Review ✅ Completed 2026-10-29T13:30:50.758756Z f93847e Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: f0af47e68e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: f0af47e68e

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: f93847ea47

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: f93847ea47

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Base automatically changed from egm/examples-stdin to main October 29, 2026 17:36
Two latent defects in cmd/generate/internal/emit. No variant in the
current schema triggers either one, and the regenerated *_gen.go files
are byte-identical.

- emitUnion and WriteHelpersJen picked the fallback discriminator as the
  first const property found by ranging over a map, and emitUnion
  collected the const injections for MarshalJSON the same way. A
  variant with two or more const properties could produce different
  output on each run. Both now scan properties in sorted key order
  (firstConstPropertyKey, sortedPropertyKeys).
- emitUnion skips a phantom variant (no $ref, no properties, no null
  type, no title), but WriteHelpersJen did not, so it emitted a
  constructor for a type that does not exist. Both now use
  isPhantomVariant, and WriteHelpersJen skips at the same point as
  emitUnion so later variant names stay in step.

New tests in union_test.go failed before this change ("types_gen.go
differs between runs", "helpers_gen.go builds undeclared type
PickVariant2") and pass now, 10 of 10 runs.

---
_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
WriteHelpersJen repeated emitUnion's variant naming, but it seeded
usedTypeNames with schema definition names only. WriteTypesJen also
registers names for inline object properties before it names union
variants, so a variant whose preferred name was already taken got a
fallback name in types_gen.go while helpers_gen.go kept the preferred
name: the constructor built the wrong or a missing type and the package
did not compile.

emitUnion now records each oneOf variant it emits (field, type,
discriminator, required keys, properties), and WriteHelpersJen builds
the types once and creates constructors from that record. The second
copy of the naming logic is gone, so the two emitters cannot disagree.
WriteTypesJen keeps its signature and now renders buildTypesJen's file.

TestWriteHelpersJen_UsesUnionVariantTypes failed before this change
("helpers_gen.go sets PickRequest.Item to &PickItem{...}, but the field
has type *PickRequestItem") and passes now. No variant in the current
schema collides, and the regenerated *_gen.go files are byte-identical.

Found by Codex review on #79.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33
ThomasK33 force-pushed the egm/generate-union-order branch from f93847e to fb4c16d Compare October 29, 2026 17:36
@ThomasK33
ThomasK33 merged commit 3da3cea into main Oct 29, 2026
1 check passed
@ThomasK33
ThomasK33 deleted the egm/generate-union-order branch October 29, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant