Uh oh!
There was an error while loading. Please reload this page.
fix(types): the remaining 11 ADR-0049 tombstones carry their remediation text - #7005
Merged
Conversation
…ion text `retirementTombstone()` (objectui#6105 / PR #6930) writes a guidance string ONCE into both author-facing channels — `z.never({ error })`, the parse-time issue message, and `.describe()`, the generated JSON-Schema and docs surface. Nine tombstones on `StaticTableColumnSchema` were converted there; eleven declarations were left on the bare `z.never().optional().describe(...)` spelling and kept answering with zod's generic `Invalid input: expected never, received string` — which names the key and says nothing about why it was retired or what to write instead. Five of the eleven sat on `StaticTableColumnSchema` itself, so its author read guidance on nine keys and zod's generic on five: a shape that teaches the message means something, then withholds it. Converted here: `headerIcon` / `fitContent` / `format` / `options` / `currency` (StaticTableColumnSchema), `hoverable` / `striped` (TableSchema), `timeScale` (TimelineSchema), `type` on both `MenuItemSchema` union arms, and `confirm` (ActionSchema) — the key that established the convention and was the last one still emitting the generic message. The accept set does not move. Measured member-by-member on the SOURCE modules (tsx; `packages/types/dist` does not exist in a fresh worktree): for all 11 conversions, the 9 already-converted controls and 6 live-value controls, `safeParse` reports the same `success`, `path`, `code` (`invalid_type`) and `expected` (`never`) before and after; only the message differs. All 132 `.description` values on the five affected schemas are byte-identical. `MenuItemSchema` is a union, so its top-level issue stays zod's own `invalid_union` / `Invalid input` at path `[]` and the guidance rides the per-arm issues; that is pinned in the tests rather than glossed. #6930's scope-boundary pin — which asserted the remaining seven still emitted the generic message — is flipped rather than deleted: it now holds them to the same standard as the nine, with the live column and table as in-test non-vacuity controls. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-sam
marked this pull request as ready for review
August 31, 2026 09:01
This was referenced Aug 31, 2026
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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.
Fixes#6931
retirementTombstone()(added by #6930 for #6105) writes a guidance string once into both author-facing channels —z.never({ error }), the parse-time issue message, and.describe(), the generated JSON-Schema and docs surface. Nine tombstones were converted there; eleven were left on the barez.never().optional().describe(...)spelling and kept answering with zod's ownInvalid input: expected never, received string. This converts the remaining eleven.Five of them sat on
StaticTableColumnSchema— the object #6105 half-fixed — so its author read guidance on nine keys and zod's generic on five: a shape that teaches the message means something, then withholds it.Population, re-derived on this branch (not taken from the card)
z.never(overpackages/types/src/zod/**= 18 hits: 5 prose in doc comments, 1 the helper's own body (tombstone.zod.ts), 1 already carrying{ error }(complex.zod.tsDashboardConfigSchema.aria, #5852) — leaving 11 generic call sites. That matches the card's 21 / 1 / 9 / 11 exactly (the card counted 21 declarations pre-#6930; the nine it converted no longer grep asz.never(). After this PR, zeroz.never().optional().describe(remain anywhere underpackages/types/src/zod/.StaticTableColumnSchemaheaderIcon,fitContent(#6424),format,options,currency(#6425)TableSchemahoverable,striped(#5474)TimelineSchematimeScale(#6355)MenuItemSchema, both union armstype(#6523)ActionSchemaconfirm(#4314) — the key that ESTABLISHED the convention, and the last one still emitting the generic messageThe accept set does not move (the inherited constraint from #6105)
Module resolution: the probe imported the source modules (
packages/types/src/zod/*.zod.ts) throughtsx;packages/types/distdoes not exist in this worktree (lsreports no such file), so no built artefact could have been read. The same probe ran before and after the edit.26 probes — the 11 conversions, the 9 already-converted keys as cross-contamination controls, and 6 live-value controls (live column, live static table, live gantt timeline, command item, divider,
confirmTextaction):success, the same issuepath, the same issuecode(invalid_type) and the sameexpected(never) before and after — including the nested per-arm issue structure of theMenuItemSchemaunion..description: 132 members compared byte-for-byte across the five affected schemas, 0 drifts.retirementTombstone()passes the same string.describe()already carried.Sample of the author-visible change (what
packages/cli'svalidate/checkprint verbatim):StaticTableColumnSchema.currencyInvalid input: expected never, received stringNOT on the static table surface (objectui#6425) — declared on the rich TableColumn only; use data-tableTableSchema.stripedInvalid input: expected never, received booleanRETIRED (objectui#5474) — the static table never implemented striping; style rows via className, or use data-tableTimelineSchema.timeScaleInvalid input: expected never, received stringRETIRED (objectui#6355) — author scale insteadActionSchema.confirmInvalid input: expected never, received objectRETIRED (objectui#4314) — author confirmText insteadz.never({ error })was kept, per the card: therefineroute would reportcustominstead ofinvalid_type, which is an observable contract change.One honest nuance —
MenuItemSchemais a unionIts top-level issue is still zod's own
invalid_union/Invalid inputat path[]; the converted guidance rides the per-arm issues underneath it. That is a property of the union, not of the tombstone, and the objectui CLI prints only top-level issues — so for these two members the improvement reaches consumers that walk arm errors and the.describe()metadata, not the CLI's printed line. Rather than gloss it, the union's shape is pinned inmenu-item-union.test.tsand documented at the declaration, so nobody later reads the unchanged top-level message as a failed conversion. Filed separately as the CLI-side gap: #7004.Routing re-checked, member by member (not assumed to follow #6930)
Measured against
@objectstack/spec@17.2.0in this worktree:timeScale— zero occurrences anywhere in the spec package.TimelineSchemais declared only here (packages/types/src/data-display.ts+ its zod mirror), andTimelineScaleSchemais a localz.enumindata-display.zod.ts, not a spec import. Spec'stimelinehits are the object list-view visualization-type vocabulary ("map" | "tree" | … | "timeline" | "gantt"), a different surface with notimeScale/scalekey. ObjectUI-native.typetombstones — spec declares noMenuItem/menu-itemvocabulary at all. Its onlyseparatorhit isSEPARATOR_NAV_ITEM_*on the navigation item surface (a divider declaringidandorder), which is a different surface from these overlay menu items and is mirrored in this repo byapp.zod.ts, notoverlay.zod.ts. ObjectUI-native.hoverable/striped— spec does carry astriped, but asview.striped, aretiredKey()on the spec's object view surface (spec fix(data-objectstack): declare the batch write-warning's unattributed-strip placeholder instead of a bare empty string #7176). This tombstone is on ObjectUI's own statictablecomponent (finding(components): the statictablerenderer reads 9 of the 20 keysTableSchema/TableColumndeclare —hoverable,striped,align,sortable,celland 6 more are inert, and the reference page documents two of them as working #5474), and spec's UI component-type vocabulary still has notableentry. Different surfaces;retiredKey()also prefixes its describe text with[REMOVED], which is why the two helpers must not be swapped.The #6930 scope-boundary pin: flipped, not deleted
static-table-narrow-surface.test.tsasserted that seven members (the five #6424/#6425 arrivals plushoverable/striped) still emittedZOD_GENERIC_NEVER, so #6105's untouched half was a recorded decision with a red test behind it. That assertion is now stale by construction. It is flipped in the same commit — same test, same seven members, now held to the standard the nine already meet (guidance present, generic absent, message derived-equal to.describe(),codeinvalid_type,path[key]), with the live column and live table as in-test non-vacuity controls. Deleting it would have removed a guard.Added beside it: a literal pin on
striped(the twin of the existingalignliteral, so the derived assertions cannot all drift together), and guidance assertions intimeline-timescale-retired.test.ts,menu-item-union.test.tsandphase2-schemas.test.tsfor the members that live outside this file.Verification
All commands run from the repo root, root-relative paths, on
3a9826149(the final commit):pnpm exec vitest runon the four affected test files → Test Files 4 passed (4) · Tests 89 passed (89) (4 paths passed, 4 files reported).pnpm exec vitest run packages/types/ packages/components/src/__tests__/menu-item-separator-dialect.test.tsx→ Test Files 79 passed (79) · Tests 968 passed (968).pnpm --filter @object-ui/types type-check→ exit 0; the script echoedtsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json, so the new test code is type-checked too.pnpm --filter @object-ui/types lint(plain form) →✖ 260 problems (0 errors, 260 warnings), exit 0 — all warnings are pre-existingno-explicit-anywarnings on thez.ZodTypelazy declarations this PR does not touch.node scripts/check-changeset-presence.mjs→✅ 7 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s).pnpm check:control-bytes→✅ check-control-bytes: OK (scanned 5814 tracked text file(s); skipped 85 binary).pnpm check:esm-specifiers→ exit 0,no un-ledgered package emits an extensionless relative specifier(this PR adds two./tombstone.zod.jsimports).pnpm check:spec-symbols→✅ spec symbol derivation: 1325 files scanned …, 0 untriaged collisions.Reverse verification (predicted RED, observed RED). With the four test files kept and the three source files reverted to
a5a799d63, the four files report 5 failed | 84 passed — exactly the flipped pin, thestripedliteral, and the three new guidance assertions. The mutation was confirmed on disk by blob hash (each file'sgit hash-objectequal to the base blob and unequal to the HEAD blob, never an empty hash), the restore leg usedgit checkout HEADwith absolute paths under anEXIT/INT/TERMtrap, and restoration was proven by hash equality with the HEAD blobs plus an emptygit diff HEAD. No build leg was involved or needed: these tests import the modules by relative source path andpackages/types/distdoes not exist.Lint scope: the repo-wide scan belongs to CI; the run above is a narrowed one, and the narrowing is measurable rather than assumed — the population came from eslint's own config (
packages/types'lintscript iseslint ., which linted the whole package, 260 warnings spread across files this PR never touches), the changed-file count came from--format json(7 files), and the config makes the narrowing sound:eslint.config.jsextendstseslint.configs.recommendedwith noparserOptions.project/projectService, so type-aware linting is off and an edit insidepackages/typescannot move any untouched file's verdict.Generated by Claude Code