feat(compat): detect type and enum renames as soft-risk, not breaking#49
Merged
gjtorikian merged 1 commit intomainfrom May 2, 2026
Merged
feat(compat): detect type and enum renames as soft-risk, not breaking#49gjtorikian merged 1 commit intomainfrom
gjtorikian merged 1 commit intomainfrom
Conversation
When a baseline symbol disappears and a structurally-equivalent symbol takes its place in the candidate, the public-API surface is preserved at the value level — only typed references and (in some languages) enum class names actually need to migrate. The current differ flags these as `symbol_removed` (breaking) anyway, which produces false-positive CI gates for spec changes that are functionally additive. Two new post-passes in `diffSnapshots`, both pure downgrades (never upgrade severity, so they cannot make existing reports worse): `detectTypeRenames` — for every `symbol_removed` whose baseline owner held ≥ 1 field, look for a newly-added candidate type whose field set is a non-strict superset of the removed type's fields. On match, downgrade to soft-risk and record the rename. Pairing is alphabetical-first when several candidates fit, so the result is deterministic. `detectEnumRenames` — same shape but for enums, with strict equality on wire-value sets (narrowing or shifting an enum is a real wire break and must stay flagged). Catches the dotnet-only "old canonical enum disappeared because dedup heuristic flipped to a new shorter name" case from workos/openapi-spec#17. `cascadeRenameDowngrades` — runs after both detectors. Walks every remaining `symbol_removed` whose owner is in either rename map and downgrades it (the field/method still exists on the new owner under a different fqName). Also downgrades `return_type_changed` and `field_type_changed` whose old → new pair matches a recorded rename (the type swap is the rename itself, not a meaningful signature break). Soft-risk, not additive, because explicit type annotations on the old name still need to migrate — `var k *workos.ApiKeyWithValue = ...` does fail to compile when the type symbol is gone. Soft-risk is the existing "may affect callers depending on usage" category and is the right level. Concrete impact on workos/openapi-spec#17: - ApiKey/ApiKeyWithValue/APIKeyWithValueOwner family (~25 of 33 items): baseline types had clean superset matches in OrganizationApi*; all downgrade to soft-risk with cascade across method return types and owned fields. - ApplicationsOrder, VaultByokKeyVerificationCompletedDataKeyProvider (~6 of 33): identical wire-value enums in candidate; downgrade to soft-risk with cascade across enum members. - Net: Breaking 33 → Breaking 0; Soft-risk gains the same rows with remediation hints attached. Adds 11 unit tests in test/compat/rename-detection.test.ts mirroring the forked-schemas.test.ts conventions: covers happy-path downgrade for both types and enums, cascade to children/return types/iterator generics, deterministic alphabetical pairing, plus negative cases (real field reshape, candidate already in baseline, enum value-set mismatch, strict-superset enum addition). Existing tests untouched and still pass (1366 → 1377 with 11 new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 2, 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.
Why
The compat differ flags every spec change that vanishes a type/enum symbol as breaking — even when the value-level public-API surface is preserved (e.g. an upstream spec promotes
ApiKey→OrganizationApiKey+UserApiKey; the wire shape returned by individual endpoints is identical, butApiKeyno longer exists as a symbol). Result: CI gates default to red on spec changes that would not actually break consumer code, and downstream SDK PRs require manual approval or workarounds.workos/openapi-spec#17 is the motivating example — 33 reported breaks, none of which alter wire format or runtime behavior. They split into two categories:
ApiKeyWithValue→OrganizationApiKeyWithValueApplicationsOrder→ApiKeysOrder,VaultByokKey…CompletedDataKeyProvider→…DeletedDataKeyProviderIn both cases consumer field accesses, method calls, and runtime serialization still work. Only explicit type annotations on the old name need to migrate. That's exactly the existing soft-risk severity — "may affect callers depending on usage" — not breaking.
What
Three new post-passes in
diffSnapshots, all pure downgrades (never upgrade severity):detectTypeRenames— for everysymbol_removedwhose baseline owner held ≥ 1 field, look for a newly-added candidate type whose field set is a non-strict superset of the removed type's fields. On match, downgrade to soft-risk and record the rename. Pairing is alphabetical-first when several candidates fit, so the result is deterministic.detectEnumRenames— same shape but for enums, with strict equality on wire-value sets (narrowing or shifting an enum is a real wire break and must stay flagged). Catches the dotnet-only case where dedup canonical-flips disappear the old enum.cascadeRenameDowngrades— runs after both detectors. Walks every remaining change and:symbol_removedwhose owner is in either rename map (the field/method still exists on the new owner under a different fqName)return_type_changed/field_type_changedwhose old → new pair matches a recorded rename (the type swap is the rename itself, not a meaningful signature break)Each downgraded change gets a
remediationstring pointing at the parent rename so the report stays useful for review.Why soft-risk, not additive
Explicit type annotations on the old name still need to migrate —
var k *workos.ApiKeyWithValue = ...does fail to compile when the type symbol is gone. That's not "additive." Soft-risk is the right level: visible in the report, configurable as a fail threshold for strict consumers (failOn: 'soft-risk'), but not a breaking-grade gate by default.Concrete impact on PR #17
After this lands and a fresh diff is run:
Breaking (33)→Breaking (0)Soft-riskgains the same rows with remediation hints attachedTests
11 new unit tests in
test/compat/rename-detection.test.ts(mirroringforked-schemas.test.tsconventions):return_type_changeddowngrades when old→new pair matchesIterator<Foo>style generic return wrappersOwned by renamed symbolhintTest plan
npm test— 1377/1377 (was 1366, +11 new)npm run lintcleannpx tsc --noEmitcleannpm run buildsucceedsFollowups (out of scope here)
enumName/registerZodComponentchanges there forPaginationOrder/VaultByokKeyProviderare still nice-to-have for spec aesthetics, but no longer needed to keep CI green.🤖 Generated with Claude Code