Uh oh!
There was an error while loading. Please reload this page.
docs(components): stop teaching #6124's retired handler keys as authorable props - #7350
Merged
Merged
Conversation
…rable props objectui#6124 (PR #7339) split the `on*` handler keys in two: 36 runtime slots keep their function type, and 22 keys nothing reads became `?: never` tombstones with named refusal arms on the zod mirror. Ten rows across eight `content/docs` pages went on listing some of the retired 22 as callable props — a key that is now a `tsc` error to assign and a refusal by name at authoring time. The card named three pages (`api/schema-reference.md`, `data-display/tree-view.mdx`, `form/input-otp.mdx`, five rows); a whole-docs census resolving each row's `(interface, key)` pair against the shipped declaration found five more rows on five more pages — `basic/button-group.mdx` (`ButtonGroupButton.onClick`), and `.onChange` on `form/calendar.mdx`, `form/combobox.mdx`, `form/command.mdx`, `form/radio-group.mdx`. A key NAME cannot decide this: `onChange` is retired on 8 schemas and live on 14, and `input-otp.mdx` carries both dispositions one line apart. Seven pages have the retired rows REMOVED. `basic/button-group.mdx` is the one "marked retired" page and not by taste: `button-group-doc-surface-6347` asserts set equality between that page's `ButtonGroupButton` block and the mirror's `.shape`, and a refusal arm is still a key of that shape, so the row must stay — spelled `never`, with the node-type pointer the tombstone JSDoc uses. That sibling pin's `onClick` case is updated to read the mirror's refusal instead of restating the now-stale `() => void`. The new pin measures the retired population off `packages/types/src` rather than hand-listing it, flags rows (not prose), and carries six runtime-slot rows as the blanket-sweep control — two of them on pages this change edits. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRRumy89BYdW9ogbcdHTho
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
|
This was referenced Sep 2, 2026
os-litant
marked this pull request as ready for review
September 2, 2026 12:55
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#7340
Docs half of #6124. PR #7339 split the
on*handler keys in two: 36 runtime slots keep their function type, and 22 keys nothing reads became?: nevertombstones with named refusal arms on the zod mirror (handlerKeyRefusal(key, 'retired', ...)). Doc pages went on listing some of the retired 22 as callable props — a key that is now atscerror to assign and a refusal by name at authoring time.Census 1 — the retired population, measured off the shipped tree
Anchored sweep of
on*?: nevermembers overpackages/types/src/*.tsonorigin/main@4704aa4bb: 22 members, splitcomplex.tsdata-display.tsfeedback.tsform.tsnavigation.tsoverlay.tsThe card's dispatch predicted
data-display 3— the real split is 4 (AlertSchema.onDismiss,ListItem.onClick,TreeViewSchema.onSelectChange,.onExpandChange). Total 22 matches PR #7339's own changeset list exactly. The pin derives this rather than restating it, and pins the count and the per-file split.Census 2 — which pages teach one, and why a key NAME cannot decide it
The 22 members carry only 13 distinct key names, and most of those names are live somewhere else:
onChangeis retired on 8 schemas and live on 14,onClickretired on 3 and live on 8,onCompleteretired onInputOTPSchemaand live onBatchOperationConfig.input-otp.mdxis the sharpest case — itsonChangerow is a live runtime slot one line above the retiredonComplete. So membership is the pair(interface, key), resolved against the shipped declaration.A whole-
content/docscensus (67on*rows, interface members and markdown prop-table rows, attributed by walking back to the owninginterfaceblock or section heading) found 10 rows on 8 pages, not the 5 rows on 3 pages the card measured on the PR #7339 branch:api/schema-reference.md:926,927KanbanSchema.onColumnAdd,.onCardAddcomponents/data-display/tree-view.mdx:46,47TreeViewSchema.onSelectChange,.onExpandChangecomponents/form/input-otp.mdx:36InputOTPSchema.onCompletecomponents/form/calendar.mdx:42CalendarSchema.onChangecomponents/form/combobox.mdx:42ComboboxSchema.onChangecomponents/form/command.mdx:41CommandSchema.onChangecomponents/form/radio-group.mdx:50RadioGroupSchema.onChangecomponents/basic/button-group.mdx:29ButtonGroupButton.onClickThe five extra rows are this card's scope by its own criterion (the card was measured before
mainmoved); no page's prose depended on any of them, so removal — the card's preferred disposition — was available everywhere except one page.Why
button-group.mdxkeeps its rowbutton-group-doc-surface-6347.test.tsasserts SET EQUALITY between that page'sButtonGroupButtonblock and the mirror's.shape, and a refusal arm is still a key of that shape — deleting the row would break that pin's central assertion. The row therefore stays, spellednever, carrying the node-type pointer the tombstone JSDoc uses. That sibling pin'sonClickcase is updated in this PR: it asserted the hard-coded text() => voidplus "not authorable in JSON"; it now reads the mirror's refusal (ahandlerKeyRefusalarm rejects every value, a function included) and the page'sneverspelling. Its header records the change as history rather than silently moving.The pin
packages/types/src/__tests__/component-docs-retired-handler-keys-7340.test.ts, in the #7239 family (docs asserted AND cross-checked against the shipped source):never; offenders reported aspage:line owner.key -> typeText.onCardAdd,onCollapsedChange,onColumnAdd,onConfirm,onExpandChange,onSelectChange,onSendMessage,onSlideChange), no row anywhere spells them callable — the net for doc-LOCAL interface names that rule 2 cannot resolve.?: never:KanbanSchema.onCardMove/.onCardClick,PaginationSchema.onPageChange,TreeViewSchema.onNodeClick,ButtonSchema.onClick,InputOTPSchema.onChange. Three sit on pages this PR edits, which is where an over-broad sweep would land first.Gates cannot see this drift:
check-doc-component-typesreads only thetypestring literals,check-doc-snippet-typescompilests/tsxfences, and every fence involved here isplaintext(verified per page) whileschema-reference.md's rows are a markdown table outside any fence.Evidence
Red-first (this pin against the unmodified pages): 3 failed / 20 passed. Rule 2 named all 10 rows, rule 3 named the 4 unambiguous ones, the button-group case named the
() => voidit found. CENSUS, CONTROL and PROSE stayed green — the predicted shape, recorded in the file header before the first run.Green after:
Test Files 3 passed (3)/Tests 96 passed (96)for this pin plus the 6347 and 7239 siblings.Ablation —
content/docs/components/data-display/tree-view.mdxrestored to itsorigin/mainblob, proven on disk by hash (91b90a055dccaf0f906e5edca8b98a0c78034661on disk ==origin/mainblob, != HEAD blob6d3b17cc91b8ba703d39db70d4cb125fc2ad23f0), restore leg proven the same way (on-disk == HEAD blob,git diff HEADempty), both legs trap-guarded with absolute paths:Exactly that page's two rows; the seven untouched pages, the button-group marked-retired case and all six control rows stayed green. No rebuild is involved: the pin reads files from disk at runtime and imports no package entry point, so there is no
distleg to go stale.Gate union, run after the final commit, at
f8f9bc774, joined with&&so one verdict covers all parts (os-verify-lock: VERDICT command-exit 0):pnpm exec vitest run packages/types/—Test Files 89 passed (89)/Tests 1486 passed (1486)check:doc-fences— "every TypeScript block in 224 document(s) is fenced ts/tsx/typescript ... No unknown fence spelling hides one."check:doc-types— "Every documented component type is registered."check:docs-route-closure— "all 13 packages ... are accounted for"docs:check-links— "Links are valid across 17 scan roots."check:control-bytes— "OK (scanned 6055 tracked text file(s); skipped 85 binary)."changeset:check— "No changeset declares amajorbump."check-changeset-presence— "2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) ... Every one of them has an EMPTY frontmatter."Also green, run separately:
pnpm --filter @object-ui/types type-check(exit 0;tsc -p tsconfig.test.json --listFilesshows both edited test files in the compiled set, so this is a measurement and not an exclusion) andpnpm --filter @object-ui/types lint(0 errors, 267 warnings, none on the changed files).check:doc-snippetswas NOT run locally: it exits 2 ("could not run") without a monorepo-wide build. Narrowing declared rather than claimed green — it compiles onlyts/tsx/typescriptfences, every fence on the seven edited component pages isplaintext(measured per page), and the twotypescriptfences inschema-reference.mdcontain none of the edited lines (all removals there are markdown table rows). CI runs it.Governed-surface predicate on the final 11-path file list:
0 of 11 path(s) hit the register— NOT governed.Changeset:
.changeset/7340-docs-retired-handler-keys.md, EMPTY frontmatter (precedent.changeset/component-docs-disabled-inherited-7239.md) — docs plus a test-only pin release nothing.🤖 Generated with Claude Code
https://claude.ai/code/session_01NRRumy89BYdW9ogbcdHTho
Generated by Claude Code