Skip to content

fix(cli): resolve allOf composition bugs in V3 OpenAPI importer#14873

Merged
jsklan merged 34 commits intomainfrom
jsklan/allof-openapi-import
Apr 15, 2026
Merged

fix(cli): resolve allOf composition bugs in V3 OpenAPI importer#14873
jsklan merged 34 commits intomainfrom
jsklan/allof-openapi-import

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented Apr 10, 2026

jsklan and others added 4 commits April 9, 2026 19:12
Add two test-definition fixtures exercising allOf edge cases:
- allof: default settings (extends mode)
- allof-inline: inline-all-of-schemas enabled, shares the same spec

Covers array items narrowing, primitive constraint narrowing via $ref,
required propagation, metadata inheritance, and multi-parent overlap.
Add debug-allof-pipeline.ts for running allof/allof-inline fixtures
through the full CLI pipeline (openapi-ir → write-definition → ir).
Include generated IR snapshots confirming remaining allOf bugs.
Add V3 importer test fixture and assertion tests for the allOf edge
cases reported in Pylon #18189 / FER-9158. Tests validate:
- Case A: array items narrowing preserves parent required status
- Case B: property-level allOf with $ref + inline primitive
- Case C: required field preservation through allOf composition

7 of 8 assertions currently fail, confirming the bugs. The task is
complete when all 8 pass.
- Enhance shouldMergeAllOf path to resolve $ref elements instead of bailing out
- Add cycle detection via Set to prevent infinite loops on circular refs
- Handle property-level allOf with $ref to non-object types (e.g. enums)
  by referencing the original type instead of creating synthetic copies
- Update test infrastructure to serialize IR discriminants for assertions
- Update snapshots for allof-spscommerce fixture

Co-Authored-By: bot_apk <apk@cognition.ai>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

…eConverter

Co-Authored-By: bot_apk <apk@cognition.ai>
hex-security-app[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 3 commits April 10, 2026 02:17
Co-Authored-By: bot_apk <apk@cognition.ai>
@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-09T04:46:50Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 95s 138s 85s -10s (-10.5%)
go-sdk square 107s 134s 104s -3s (-2.8%)
java-sdk square 290s 346s 158s -132s (-45.5%)
php-sdk square 85s 119s 83s -2s (-2.4%)
python-sdk square 130s 166s 128s -2s (-1.5%)
ruby-sdk-v2 square 115s 153s 113s -2s (-1.7%)
rust-sdk square 93s 95s 92s -1s (-1.1%)
swift-sdk square 104s 448s 97s -7s (-6.7%)
ts-sdk square 98s 134s 93s -5s (-5.1%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-09T04:46:50Z). Trigger benchmark-baseline to refresh.

@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-10T04:56:48Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 100s 135s 93s -7s (-7.0%)
go-sdk square 104s 137s 107s +3s (+2.9%)
java-sdk square 157s 191s 161s +4s (+2.5%)
php-sdk square 86s 122s 86s +0s (+0.0%)
python-sdk square 124s 167s 132s +8s (+6.5%)
ruby-sdk-v2 square 120s 152s 121s +1s (+0.8%)
rust-sdk square 92s 94s 93s +1s (+1.1%)
swift-sdk square 105s 476s 97s -8s (-7.6%)
ts-sdk square 102s 129s 97s -5s (-4.9%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-10T04:56:48Z). Trigger benchmark-baseline to refresh.

@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-10T04:56:48Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 100s 135s 93s -7s (-7.0%)
go-sdk square 104s 137s 107s +3s (+2.9%)
java-sdk square 157s 191s 164s +7s (+4.5%)
php-sdk square 86s 122s 86s +0s (+0.0%)
python-sdk square 124s 167s 126s +2s (+1.6%)
ruby-sdk-v2 square 120s 152s 115s -5s (-4.2%)
rust-sdk square 92s 94s 96s +4s (+4.3%)
swift-sdk square 105s 476s 104s -1s (-1.0%)
ts-sdk square 102s 129s 100s -2s (-2.0%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-10T04:56:48Z). Trigger benchmark-baseline to refresh.

@jsklan jsklan self-requested a review April 10, 2026 15:42
devin-ai-integration Bot and others added 5 commits April 10, 2026 15:43
Co-Authored-By: bot_apk <apk@cognition.ai>
…oss-schema cycle detection

Co-Authored-By: bot_apk <apk@cognition.ai>
Co-Authored-By: bot_apk <apk@cognition.ai>
Co-Authored-By: bot_apk <apk@cognition.ai>
Co-Authored-By: bot_apk <apk@cognition.ai>
@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-10T04:56:48Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 100s 135s 93s -7s (-7.0%)
go-sdk square 104s 137s 111s +7s (+6.7%)
java-sdk square 157s 191s 167s +10s (+6.4%)
php-sdk square 86s 122s 85s -1s (-1.2%)
python-sdk square 124s 167s 130s +6s (+4.8%)
ruby-sdk-v2 square 120s 152s 119s -1s (-0.8%)
rust-sdk square 92s 94s 94s +2s (+2.2%)
swift-sdk square 105s 476s 106s +1s (+1.0%)
ts-sdk square 102s 129s 107s +5s (+4.9%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-10T04:56:48Z). Trigger benchmark-baseline to refresh.

@jsklan
Copy link
Copy Markdown
Contributor

jsklan commented Apr 10, 2026

@claude review

claude[bot]

This comment was marked as resolved.

…tcuts

Deduplicate $ref entries in the merged allOf array after mergeWith
array-concatenation to prevent false-positive cycle detection in
diamond inheritance patterns (A → B, C; B, C → D).

Add allOf/oneOf/anyOf checks to the resolved-schema guard in
maybeConvertSingularAllOfReferenceObject so the shortcut only fires
for true scalar/enum primitives, not schemas defined via composition
keywords whose inline constraints would be silently discarded.
@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-10T04:56:48Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 100s 135s 85s -15s (-15.0%)
go-sdk square 104s 137s 107s +3s (+2.9%)
java-sdk square 157s 191s 161s +4s (+2.5%)
php-sdk square 86s 122s 87s +1s (+1.2%)
python-sdk square 124s 167s 132s +8s (+6.5%)
ruby-sdk-v2 square 120s 152s 116s -4s (-3.3%)
rust-sdk square 92s 94s 94s +2s (+2.2%)
swift-sdk square 105s 476s 89s -16s (-15.2%)
ts-sdk square 102s 129s 103s +1s (+1.0%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-10T04:56:48Z). Trigger benchmark-baseline to refresh.

- Preserve outer `inlined` status through allOf merge path instead of
  hardcoding `inlined: true`
- Guard variants-flattening against $ref-resolved schemas to prevent
  destructive flattening of named union types
- Seed dedup seenRefs from resolvedRefs to prevent duplicate property
  declarations in diamond inheritance
- Bail out of allOf shortcut when inline elements contain `type: "null"`
  to preserve nullable semantics in OpenAPI 3.1 patterns
- Add optional guard for required executionContext field in Case B test
- Remove debug script (scripts/debug-allof-pipeline.ts)
- Remove orphaned allof-spscommerce snapshot files
- Move changelog to changes/unreleased/ per release-versioning convention
@jsklan jsklan enabled auto-merge (squash) April 10, 2026 22:17
@github-actions
Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-10T04:56:48Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 100s 135s 86s -14s (-14.0%)
go-sdk square 104s 137s 104s +0s (+0.0%)
java-sdk square 157s 191s 165s +8s (+5.1%)
php-sdk square 86s 122s 95s +9s (+10.5%)
python-sdk square 124s 167s 129s +5s (+4.0%)
ruby-sdk-v2 square 120s 152s 117s -3s (-2.5%)
rust-sdk square 92s 94s 90s -2s (-2.2%)
swift-sdk square 105s 476s 80s -25s (-23.8%)
ts-sdk square 102s 129s 98s -4s (-3.9%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-10T04:56:48Z). Trigger benchmark-baseline to refresh.

claude[bot]

This comment was marked as resolved.

claude[bot]

This comment was marked as resolved.

@jsklan jsklan disabled auto-merge April 13, 2026 12:48
- Fix inlined: true -> inlined: this.inlined in single-element allOf path
- Propagate inlinedTypes in both allOf shortcuts in SchemaOrReferenceConverter
- Update stale test comment and remove duplicate biome-ignore
- Update v3-sdks snapshots

Co-Authored-By: judah <jsklan.development@gmail.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 13, 2026

SDK Generation Benchmark Results

Comparing PR branch against latest nightly baseline on main (2026-04-15T04:55:58Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 93s 137s 86s -7s (-7.5%)
go-sdk square 104s 142s 107s +3s (+2.9%)
java-sdk square 160s 188s 167s +7s (+4.4%)
php-sdk square 85s 116s 87s +2s (+2.4%)
python-sdk square 111s 146s 112s +1s (+0.9%)
ruby-sdk-v2 square 115s 154s 118s +3s (+2.6%)
rust-sdk square 99s 92s 94s -5s (-5.1%)
swift-sdk square 84s 124s 84s +0s (+0.0%)
ts-sdk square 100s 143s 101s +1s (+1.0%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-04-15T04:55:58Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-04-15 14:56 UTC

claude[bot]

This comment was marked as resolved.

- SchemaConverter.ts: Use separate localResolvedRefs set for tracking refs
  within current allOf array. Check this.visitedRefs for ancestor cycles
  only. Same-array duplicate $refs now skip (continue) instead of
  triggering hasCycle=true.

- SchemaOrReferenceConverter.ts: Add !s.format guard to inline elements
  check and !resolved.format guard to resolved schema check in
  maybeConvertSingularAllOfReferenceObject, preventing the shortcut from
  discarding inline format constraints like {format: "date-time"}.

Co-Authored-By: bot_apk <apk@cognition.ai>
claude[bot]

This comment was marked as resolved.

Co-Authored-By: bot_apk <apk@cognition.ai>
claude[bot]

This comment was marked as resolved.

Copy TYPE_INVARIANT_KEYS (description, deprecated, example, default,
readOnly, writeOnly, etc.) from the outer this.schema into mergedSchema
before constructing mergedConverter, so metadata fields are not silently
dropped for schemas routed through the shouldMergeAllOf path.

Co-Authored-By: bot_apk <apk@cognition.ai>
claude[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@jsklan jsklan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foo

jsklan added 2 commits April 14, 2026 16:33
Replace the generic lodash.mergeWith deep merge in the shouldMergeAllOf
path with a purpose-built mergeAllOfSchemas function that uses explicit
per-key strategies for OpenAPI schema keywords:

- required: set union
- properties/items: recursive deep merge
- example/default: deep merge objects
- deprecated/readOnly/writeOnly: OR (true if any says true)
- min/max constraints: most restrictive value
- description/type/format/etc: outer schema wins
- allOf from children: flattened before merge (prevents leakage)

This eliminates the post-merge fixup code (allOf dedup block,
TYPE_INVARIANT_KEYS loop) that was the source of cascading review
issues. Also adds !s.items and array-form null-type guards to the
allOf shortcut in SchemaOrReferenceConverter.
jsklan added 4 commits April 15, 2026 10:34
…` in tests

Use `"items" in s` instead of `s.items` to avoid TS error on NonArraySchemaObject,
and replace all `as any` casts in mergeAllOfSchemas tests with proper OpenAPI types.
…tening, and metadata loss

Filter unresolved $ref objects from nested allOf flattening to prevent
spurious top-level $ref keys corrupting merged schemas. Make
flattenNestedAllOf recursive so deeply-nested allOf structures are fully
expanded. Add oneOf/anyOf to SKIP_FROM_CHILDREN so composition keywords
from resolved child schemas don't leak into the merged result and
erroneously trigger union conversion. Propagate outer schema metadata
(description, deprecated) through the single-element allOf path for
non-object types.
Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 6 additional findings.

Open in Devin Review

@github-actions
Copy link
Copy Markdown
Contributor

Docs Generation Benchmark Results

Comparing PR branch against main baseline.

Fixture main PR Delta
docs N/A 312.5s (35 versions) N/A

Docs generation runs fern generate --docs --preview end-to-end against the benchmark fixture with 35 API versions (each version: markdown processing + OpenAPI-to-IR + FDR upload).
Delta is computed against the nightly baseline on main.
Last updated: 2026-04-15 14:58 UTC

@jsklan jsklan merged commit 5901753 into main Apr 15, 2026
271 checks passed
@jsklan jsklan deleted the jsklan/allof-openapi-import branch April 15, 2026 15:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant