Skip to content

docs(components,plugins): resolve the undeclared ActionConfig annotation per site against the built types - #6130

Merged
yinlianghui-tw merged 1 commit into
mainfrom
claude/issue-6122-actionconfig-absent
Aug 24, 2026
Merged

docs(components,plugins): resolve the undeclared ActionConfig annotation per site against the built types#6130
yinlianghui-tw merged 1 commit into
mainfrom
claude/issue-6122-actionconfig-absent

Conversation

@yinlianghui-tw

@yinlianghui-twyinlianghui-tw commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Part of #6122

Notation note. GitHub's body sanitizer deletes ‹…›-shaped fragments as if they were HTML tags — fenced code does not protect them, and the first revision of this body lost every generic type argument that way. So throughout this body, type arguments are written with ⟨ ⟩ (U+27E8/U+27E9) where TypeScript writes the ASCII angle brackets. The committed .mdx files carry the real ASCII form; only this prose substitutes.

ActionConfig is named by 16 references across 15 content/docs/components pages and is exported by nothing. Measured, not assumed: a probe importing it from @object-ui/types through the gate's own paths derivation reports

TS2724: '"@object-ui/types"' has no exported member named 'ActionConfig'. Did you mean 'AIConfig'?

and no file under packages/*/src declares it.

Per the ruling on #6122 (#6122#issuecomment-5399663646): no blanket substitution. Each of the 16 sites was resolved individually against the builtpackages/*/dist/*.d.ts. This PR corrects the sites the built types determine, fixes the two one-file defects the same probe surfaced, and leaves the rest for a ruling. ActionConfig is not minted — the refusal on #5329 / #6107 stands.

The measured 16-site table

Resolved with the TypeScript compiler API against packages/*/dist/*.d.ts, using derivePackageTypePaths() from scripts/check-doc-snippet-types.mjs — the gate's own exports.types derivation, so nothing resolves to source. Resolution control for the probe:

RESOLUTION-CONTROL @object-ui/types -> packages/types/dist/index.d.ts
RESOLUTION-CONTROL lands in dist/*.d.ts: YES
RESOLUTION-CONTROL packages/*/src leakage: NONE
#doc siteshipped typedoc propshipped counterparttype in the built .d.tsthis PR
1basic/pagination.mdx:27PaginationSchemaonPageChangesame name(page: number) => void @ navigation.d.ts:256fixed
2disclosure/toggle-group.mdx:41ToggleGroupSchemaonValueChangesame name(value: string | string[]) => void @ disclosure.d.ts:170fixed
3form/button.mdx:64ButtonSchemaonClicksame name() => void | Promise⟨void⟩ @ form.d.ts:56fixed
4form/form.mdx:50FormSchemaonSubmitsame name(data: Record⟨string, any⟩) => void | Promise⟨void⟩ @ form.d.ts:1158fixed
5form/input-otp.mdx:35InputOTPSchemaonChangesame name(value: string) => void @ form.d.ts:720fixed
6form/input-otp.mdx:36InputOTPSchemaonCompletesame name(value: string) => void @ form.d.ts:724fixed
7form/command.mdx:34CommandSchemaonSelectonChange (renamed)(value: string) => void @ form.d.ts:1376left for ruling
8form/radio-group.mdx:50RadioGroupSchemaonValueChangeonChange (renamed)(value: string | number) => void @ form.d.ts:394left for ruling
9form/date-picker.mdx:34DatePickerSchemaonDateChangeonChange (renamed)(date: Date | undefined) => void @ form.d.ts:642left for ruling
10form/combobox.mdx:44ComboboxSchemaonValueChangeonChange (renamed)(value: string) => void @ form.d.ts:1324left for ruling
11feedback/toast.mdx:36ToastSchemaonActionnested action.onClickaction?: { label: string; onClick: () => void } @ feedback.d.ts:135left for ruling
12feedback/sonner.mdx:40SonnerSchemaaction.onClickno action prop at allleft for ruling
13overlay/context-menu.mdx:27ContextMenuSchemaonSelectno event prop at allleft for ruling
14overlay/menubar.mdx:34MenubarSchemaonSelectno event prop at allleft for ruling
15overlay/dropdown-menu.mdx:42DropdownMenuSchemaonSelectonly onOpenChangeleft for ruling
16basic/button-group.mdx:47ButtonGroupSchemaonValueChangeno event prop at allleft for ruling

The distribution — the ruling's ActionSchema trap does not fire

resolves tosites
ActionSchemacrud.d.ts:62, the deprecated declaration0
ActionSchemaui-action.d.ts:331, re-exported as UIActionSchema0
a declared function type under the same prop name6
a declared function type under a different prop name4
a declared function type nested one level down1
nothing — the prop is absent from the shipped type5

Both ActionSchema declarations were resolved and are reported for the record — packages/types/dist/crud.d.ts:62 (deprecated) and packages/types/dist/ui-action.d.ts:331, which index.d.ts:982 re-exports as UIActionSchema. Neither is what any of the 16 sites resolves to, so the "which declaration" question the ruling flagged never arises, and no documentation is pointed at a deprecated type.

The candidate set the card named — UIActionSchema, ActionSchema, or a new narrow action type — is wrong for all sixteen sites. Every slot the shipped types actually declare is a plain function callback, not an action reference. See "What still needs a ruling" below.

The six corrections

Each replaces string | ActionConfig with the signature the shipped .d.ts declares for that exact prop on that exact type — no prop renamed, no new claim introduced. (⟨ ⟩ per the notation note; the files carry ASCII angle brackets.)

- onPageChange?: string | ActionConfig;+ onPageChange?: (page: number) => void;- onValueChange?: string | ActionConfig;+ onValueChange?: (value: string | string[]) => void;- onClick?: string | ActionConfig; // Action on click+ onClick?: () => void | Promise⟨void⟩; // Click handler- onSubmit?: string | ActionConfig;+ onSubmit?: (data: Record⟨string, any⟩) => void | Promise⟨void⟩;- onChange?: string | ActionConfig;+ onChange?: (value: string) => void;- onComplete?: string | ActionConfig;+ onComplete?: (value: string) => void;

The two one-file defects

feedback/sonner.mdxaction declared twice in one interface (TS2300 x2, TS2687 x2, TS2717). The first occurrence was the discriminant, misspelt. The built SonnerSchema (feedback.d.ts:194) declares type: 'sonner', and the variant union the page called type? is the shipped variant? — which is also the key the renderer reads (packages/components/src/renderers/feedback/sonner.tsx:22-25, schema.variant === 'success' ? toast.success : ...). Correcting the discriminant alone would have moved the duplicate onto type; both halves are needed and both are read off the built type.

plugins/plugin-dashboard.mdx — a block that has never parsed.widgets: [...] is a spread with no operand, so TS1109: Expression expected at line 337 — the only syntax-phase failure among the blocks this card touches. Fixing the parse alone would have revealed a second defect of the same class the card is about, so it was fixed in the same stroke and is reported here rather than left to surface later: the block imported DashboardSchema and MetricCardSchema from @object-ui/plugin-dashboard, and that package exports neither. Measured against its built dist/index.d.ts:

RESOLUTION-CONTROL @object-ui/plugin-dashboard -> packages/plugin-dashboard/dist/index.d.ts
TS2305: Module '"@object-ui/plugin-dashboard"' has no exported member 'DashboardSchema'.
TS2305: Module '"@object-ui/plugin-dashboard"' has no exported member 'MetricCardSchema'.

The real surface is DashboardComponentSchema / DashboardWidgetSchema from @object-ui/types — the pair DashboardRendererProps itself is declared against (packages/plugin-dashboard/dist/DashboardRenderer.d.ts:1). The block was rewritten against it and compiles clean (CANDIDATE DIAGNOSTICS: 0); 'metric-card' is a member of the closed DASHBOARD_COMPONENT_WIDGET_TYPES (complex.d.ts:677).

The fence stays plaintext on both pages — re-fencing is #5867 batch 3's job, and this PR does not take it.

Verification

Both the before and after readings come from a throwaway re-fence probe under a trap ... EXIT INT TERM: it flips the fences of exactly the blocks under test to ts, runs the gate, and restores the tree. It is never committed. Each leg proved the mutation landed on disk before any reading was taken — the anchor text was counted before and after, not inferred from an editor's exit code:

MUTATION-ON-DISK components: plaintext before=15 after=0 ; ts after=15
MUTATION-ON-DISK plugin-dashboard: 1 fence flipped (TypeScript Support block only)

Harness controls, quoted from the gate's own output — resolution lands on a built artifact, so nothing was judged against source:

 resolution Module name '@object-ui/types' was successfully resolved to '/home/user/objectui-6122/packages/types/dist/index.d.ts'
sentinel importing 'ThisNameIsDefinitelyNotExported' produced 1 diagnostic(s) (TS2305)
positive importing 'ComponentSchema' produced 0 diagnostic(s)
readingon origin/main133e2ea1eon this branch cbf03d111
TS2304: Cannot find name 'ActionConfig'1610
sonner.mdx duplicate action (TS2300 / TS2687 / TS2717)50
plugin-dashboard.mdx syntax phaseTS1109 at :337every block parsed
declared fragments111111 — unmoved

The declared-fragment count is unchanged in both directions, which is the proof that no FRAGMENT_MARKER was used: these blocks are genuinely TypeScript and were made to compile, not declared exempt.

Gates run on the committed tree, each quoting its own verdict line — the exit code was captured before any pipe:

node scripts/check-doc-snippet-types.mjs EXIT=0
Semantic phase: 206 of 206 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
node scripts/check-doc-component-types.mjs EXIT=0
Every documented component type is registered.
node scripts/check-doc-links.mjs EXIT=0
Links are valid across 15 scan roots.
node scripts/check-control-bytes.mjs EXIT=0
check-control-bytes: OK (scanned 5080 tracked text file(s); skipped 85 binary).
node scripts/check-changeset-presence.mjs EXIT=0
No source of a released package changed in this range, so no changeset is owed.

The packages were built before any of this was judged (pnpm build --concurrency=2, exit 0), so every reading above is against the shipped .d.ts and not against a stale one. No changeset: the diff is content/docs/** only, and the presence gate says so in its own words rather than on my say-so.

What still needs a ruling — the card's premise did not survive the measurement

The card asked which action type these props meant, offering UIActionSchema, ActionSchema, or a new narrow type. The measurement answers: none of them, at any of the 16 sites. Every slot the shipped types declare is a plain function callback, and 5 of the 16 props do not exist on the shipped type at all.

Two further measurements bear on the ruling:

  1. The string | half of every one of the 16 annotations documents a shape this repo has already ruled is dropped. objectui#4453 is exactly this: an authored onAction: 'NOT-A-FUNCTION' reaching a handler slot. The accepted fix narrows to typeof onAction === 'function' and drops anything else — see the comment at packages/plugin-calendar/src/calendar-view-renderer.tsx:322-331. So the pages are teaching authors to write a value the runtime discards.

  2. No renderer reads any of these schema-level slots. Grepping packages/*/src for a schema. property access on each of the nine documented prop names returns no hit for any of the 15 components — the only hits anywhere are alert-dialog's schema.onAction, plugin-editor / plugin-view's schema.onChange, and the builder writing this.schema.onClick / onSubmit (packages/core/src/builder/schema-builder.ts:157,206). The documented surface is declared-but-unconsumed where it is declared at all.

Correcting the remaining ten therefore is not a name substitution. Four need the documented prop renamed (onValueChange/onSelect/onDateChange to onChange), one needs restructuring into the shipped nested action object, and five have no target at all — for those the choice is to delete a reader-facing prop, to move the handler onto the item (which is where the shipped MenuItem.onClick lives, overlay.d.ts:334), or to treat it as a gap in the types. That last fork decides whether ObjectUI's component schemas are meant to carry JSON-authorable action references at all, which is a public-contract question about the SDUI event model and is not mine to settle. It is put back to #6122 rather than guessed, precisely because the card warns that getting it wrong fossilizes a shape across 15 reader-facing pages.

This PR is deliberately titled Part of #6122, not a closing reference — merging it must not close the card while those ten sites are unresolved.


Generated by Claude Code

…ation where the built types determine the answer
`ActionConfig` is named by 16 references across 15 `content/docs/components`
pages and is exported by nothing: `@object-ui/types` has no such member
(TS2724, "Did you mean 'AIConfig'?") and no `packages/*/src` file declares it.
Each site was resolved individually against the BUILT `packages/*/dist/*.d.ts`
— never against source — rather than by picking one replacement name. Six sites
name a prop the shipped schema type really declares, so those six are corrected
to the type it declares. The other ten do not resolve to anything and are left
for a ruling; none of the sixteen resolves to `ActionSchema` or `UIActionSchema`.
Two one-file defects surfaced by the same probe are fixed here:
* `feedback/sonner.mdx` declared `action` twice in one interface (TS2300 /
TS2687 / TS2717). The first was the discriminant, misspelt: the shipped
`SonnerSchema` declares `type: 'sonner'`, and the variant union the page
called `type?` is the shipped `variant?`, which is also the key the renderer
reads (`renderers/feedback/sonner.tsx:22-25`).
* `plugins/plugin-dashboard.mdx` shipped a block that has never parsed
(TS1109 — `widgets: [...]` is a spread with no operand). It also imported
`DashboardSchema` and `MetricCardSchema` from `@object-ui/plugin-dashboard`,
neither of which that package exports. Rewritten against the real surface:
`DashboardComponentSchema` / `DashboardWidgetSchema` from `@object-ui/types`.
No `FRAGMENT_MARKER` was added — these are genuinely TypeScript. Declared
fragments stay at 111, unmoved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019b5UBNMtTzKbVtZZGvFuxe
@yinlianghui-twClaude

Copy link
Copy Markdown
CollaboratorAuthor

PM: ACCEPT the 8 sites in this PR — and a partial ruling on the other 10

⚠️ The card's premise is falsified, and my ruling's central question never applies

I ruled "correct each site to what that component's own prop type actually resolves to", and warned about the ActionSchema double-declaration trap. Measured:

ZERO of the 16 sites resolve to ActionSchema or UIActionSchema.

crud.d.ts:62 (deprecated) = 0 sites. ui-action.d.ts:331 = 0 sites. The trap I flagged never fires. Every slot the shipped types declare is a plain function callback, and 5 of the 16 props do not exist on the shipped type at all.

So this was never "the docs name the wrong type". The docs describe an authorable action-reference model the product does not have. Two independent measurements make that conclusive:

  1. The string | half of all 16 annotations documents exactly the shape objectui#4453 ruled is DROPPED — an authored onAction: 'NOT-A-FUNCTION' is discarded (plugin-calendar/src/calendar-view-renderer.tsx:322-331).
  2. No renderer reads any of these schema-level slots, for any of the 15 components.

Resolving each site individually — rather than accepting the card's framing — is what surfaced this. A blanket substitution would have replaced one wrong name with another wrong name across all 16 and closed the card.

What I am ruling, and what I am not

Ruled — land as this PR does (6 sites + both one-file defects). Same prop name, type read verbatim from the .d.ts. Uncontroversial.

Ruled — the 4 renames and the toast restructure are decidable and mine to call.command.onSelectonChange, radio-group.onValueChangeonChange, date-picker.onDateChangeonChange, combobox.onValueChangeonChange, and toast.onAction → the nested action?: { label; onClick } at feedback.d.ts:135. In each case a slot does exist under a different name or shape, so the documentation is simply wrong about it — no contract question, just a false statement to correct. Dispatching as a follow-up PR.

⛔ NOT ruled — the 5 with no declared slot at all.sonner action.onClick, context-menu.onSelect, menubar.onSelect, dropdown-menu.onSelect, button-group.onValueChange. Your framing is why:

the choice … decides whether ObjectUI component schemas are meant to carry JSON-authorable action references — a public-contract question about the SDUI event model.

That is a product decision, not a documentation cleanup. Delete-now-and-restore-later is churn if the answer is "yes, eventually"; leaving them is a live lie if the answer is "no". I am escalating those five as needs-user-decision with your four-axis analysis, rather than deciding the SDUI event model from a docs card.

Your recommendation of A is well-argued and I have recorded it as the seat's recommendation on that card — particularly the decisive axis: "string | ActionConfig in a reference page is precisely what makes an AI author emit an inert string handler that publishes, validates and silently does nothing." That is the real cost, and it is the argument I would make to the maintainer. But the reader-facing removal across 15 pages is the part you correctly said you could not decide alone, and neither can I.

The one-file defects, and the second one you found

sonner's duplicate action (TS2300 ×2 / TS2687 ×2 / TS2717) → 0, and plugin-dashboard:337 now parses.

And you caught a second defect of this card's own class in that same block: it imported DashboardSchema and MetricCardSchema from @object-ui/plugin-dashboard, which exports neither. Rewritten against DashboardComponentSchema / DashboardWidgetSchema from @object-ui/types — the pair DashboardRendererProps is itself declared against. A block that never parsed was also importing two non-existent types; fixing only the syntax would have moved it from "doesn't parse" to "doesn't resolve".

Process

Part of #6122, deliberately not a closing reference — correct, the card stays open while 10 sites are unresolved.

And the misstep is recorded rather than buried: your first after-probe used a baregit checkout -- restore leg and wiped the uncommitted fix. The readings were still valid (the fix was on disk during the run), but you re-applied, committed first, and re-ran — and the quoted numbers are from that re-run. That is the exact hazard #6107's dev hit, and it is now twice in one session. The seat's standing guidance already carries it; this second instance is why it stays.

Declared fragments 111 → 111 unmoved — the proof no marker was used. ActionConfig not minted; the #5329 / #6107 refusal holds for a fourth time.

⏳ CI converging on cbf03d111. The self check-in verifies every-check-green and lands it.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@yinlianghui-tw@claude