build(core): stabilize generated schema alias names against dep churn - #626
Conversation
ts-json-schema-generator names anonymous/aliased definitions from their source position (e.g. alias-1623388915-2282-2347-1623388915-0-228060). Those names churn whenever an upstream dependency reshuffles its types, even when the resulting schema is identical in shape — so benign dep bumps (e.g. the ai-sdk group) trip the schemas/ drift gate and require a manual regenerate. Dependabot can't run the codegen, so those PRs fail CI on nothing but renamed internal aliases. Extend normalize-schema.cjs to rename each volatile alias to a stable alias-<contenthash> derived from the definition's structure (with alias refs neutralized so mutual recursion doesn't leak the volatile names into the hash), then rewrite all $refs. The artifact now only changes when the schema's actual shape changes. Refactor the script to export a pure normalizeSchema() and add unit tests covering the deprecated-string fix and alias stabilization, including the key property that a pure position-hash change normalizes to identical output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the core schema normalization pipeline to prevent spurious schemas/ drift caused by ts-json-schema-generator’s position-derived alias naming, making generated artifacts stable across benign dependency bumps.
Changes:
- Extends
normalize-schema.cjsto rewrite volatilealias-<position>definition names into stablealias-<contenthash>names and updates all$refs accordingly. - Refactors the normalization script to export a pure
normalizeSchema()function for direct unit testing, while keeping the CLI entrypoint. - Adds Vitest coverage for both deprecated-string normalization and alias stabilization, and updates the committed
webagent-event.jsonartifact to use stable aliases.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/core/scripts/normalize-schema.cjs | Adds content-hash-based alias stabilization + exports normalizeSchema() while retaining CLI behavior. |
| packages/core/test/normalize-schema.test.ts | Introduces unit tests validating deprecated normalization and stable alias renaming behavior. |
| packages/core/schemas/webagent-event.json | Updates generated schema artifact to use stabilized alias names and rewritten $refs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const rename = {}; | ||
| const used = new Set(); | ||
| for (const oldName of volatileNames) { |
There was a problem hiding this comment.
Good catch — this was reachable, so I took it.
Confirmed before changing anything: I planted a definition under the exact key the alias shape hashes to, and the re-key loop overwrote it, dropping the definition and leaving its $refs pointing at the alias shape. Test fails before the change, passes after.
Fixed in 8247cc9 by seeding used with the non-volatile definition names, so the existing disambiguation loop routes around them the same way it already routes around generated-vs-generated collisions — consistent with the defensive posture the suffix loop had already established.
The committed artifact is unchanged, confirming no such collision exists in the real schema today.
stabilizeAliasNames() seeded its collision set with only the names it generated, so a stable alias-<contenthash> could match a definition key already present in the schema. The re-key loop then overwrote that definition, silently dropping it and leaving its $refs pointing at the alias shape instead. Seed the set with the non-volatile definition names up front, so the existing disambiguation loop routes around them the same way it already routes around generated-vs-generated collisions. Reachability confirmed by a test that plants a definition under the key the shape hashes to; it fails before the change and passes after. The committed artifact is unchanged — no such collision exists today. Reported by Copilot review on #626. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Problem
ts-json-schema-generatornames anonymous/aliased definitions from their source position, e.g.:Those numbers encode where the AI SDK's
JSONValuetype happens to sit in its source files. Any upstream dependency bump that reshuffles types changes the names — even when the resulting schema is identical in shape. That trips theschemas/drift gate incheck:schemas.Dependabot can't run codegen, so every ai-sdk group bump fails CI on nothing but renamed internal aliases and needs a manual regenerate. Most recently #616, and #597 before it.
Fix
Extend
normalize-schema.cjsto rename each volatile alias to a stablealias-<contenthash>derived from the definition's structure, then rewrite every$refto match. Alias references are neutralized to a sentinel before hashing so mutual recursion (JSONValue↔ its object form) doesn't leak the volatile names back into the hash.The committed artifact now changes only when the schema's actual shape changes.
Also refactors the script to export a pure
normalizeSchema()so it can be unit-tested directly, and adds tests covering both normalization passes.Verification
The property that matters is that the artifact survives a real dependency bump. Tested directly:
main:check:schemaspasses.pnpm run checkgreen on both: 948 core / 229 cli / 103 server / 273 extension tests, typecheck, format.Once this lands, #616 can be rebased and should go green without hand-edits.
🤖 Generated with Claude Code