Skip to content

fix(components): a bare-string trigger renders on all eight sibling overlay blocks - #9796

Merged
os-tesla merged 6 commits into
mainfrom
claude/issue-9710-overlay-trigger-aschild-siblings
Sep 18, 2026
Merged

os-tesla merged 6 commits into
mainfrom
claude/issue-9710-overlay-trigger-aschild-siblings

Conversation

@os-tesla

@os-tesla os-tesla commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #9710

Clause-②: yes

⚠️ The delivered diff is renderer-only. packages/types/src/zod/overlay.zod.ts is untouched and no published face moves, so the conservative yes on the claim may be overturned to no by review — that is the designed outcome of a conservative declaration, not a fault. The reasoning is in "Does any of them want a bare string at all?" below.

What lands

All eight sibling renderers in one change, plus a class-level pin that reddens — the binding direction from triage 5719728719:

renderer trigger element
overlay/alert-dialog.tsx AlertDialogTrigger
overlay/dialog.tsx DialogTrigger
overlay/drawer.tsx DrawerTrigger
overlay/dropdown-menu.tsx DropdownMenuTrigger
overlay/hover-card.tsx HoverCardTrigger
overlay/popover.tsx PopoverTrigger
overlay/sheet.tsx SheetTrigger
overlay/tooltip.tsx TooltipTrigger

The predicate from PR objectui#9708 is lifted into ONE seam, renderTriggerSlot in packages/components/src/lib/utils.tsx, rather than eight copies: it renders the slot once and asks Radix's own structural question of the result. Where Slot cannot serve, the primitive renders its own element around the content; where it can, the merge is unchanged.

⛔ RETRACTED — an earlier revision of this body claimed that a multi-node trigger array and an empty slot's null 「are the same structural mismatch wearing different values and take the same arm」. That is FALSE and it is the defect the clause-② review caught. Measured against the lockfile-pinned @radix-ui/react-slot@1.3.3, slot.tsx:45 reads if (children || children === 0) throw …; return children ⇒ a null child never threw, so the empty arm was never broken. Treating the two alike made a document with no trigger paint one empty focusable button on the five keys whose trigger is .optional(). The seam now carries BOTH rules: render nothing when the slot is empty, and ask Radix's structural question otherwise.

⛔ overlay/context-menu.tsx is deliberately untouched. It calls renderChildren(schema.trigger …) exactly like the others, and its asChild child is a real div, so the rendered slot goes INSIDE it and Children.only never sees the string. I checked it by reading the trigger element, and the pin below measures it green rather than taking my word for it. The discriminator is "is the asChild child a single React element", ⛔ not "does this file call renderChildren(schema.trigger)".

The census, re-run — and one published control that does not reproduce

The card orders the taker not to inherit its 8. Re-run on this branch's base (57c07f002), corpus enumerated with git ls-tree -r --name-only REV and read with git show REV:PATH, never off a working tree — 195 .tsx files under packages/components/src/renderers/.

probe reading
target: unconditional asChild over the authored slot, at base 8 — the table above
same probe at the pre-objectui#9708 merge-base 2923cea165 9, the ninth being collapsible.tsx
whole-tree sweep (4656 .ts/.tsx under packages + apps + examples) 8 — the class does not reach outside renderers/overlay/
lit control for that sweep: any RADIXTRIGGER tag in the same corpus 266 ⇒ instrument connected
declaration half: trigger: keys in overlay.zod.ts with the identical union 9
lit control: ANY trigger: key in that file 9 ⇒ the match is TOTAL, not partial
absent control, same file 0, exit 1
the same declaration probe against disclosure.zod.ts 1, this class's origin key

⚠️ The published lit control for the renderer half does not reproduce, and the reason is not tree drift. Comment 5719013007 states the conditional spelling reads 1 at head c90c68d63 and 0 at base, that hit being collapsible.tsx. Taken literally — asChild={ on the trigger line, with the next non-blank line within three matching renderChildren(\s*schema\.trigger — it reads 0 at today's base AND 0 at c90c68d63 itself, which I fetched and read. PR objectui#9708 hoists the slot into const trigger = renderChildren(schema.trigger); three lines above the trigger, so a FORWARD adjacency regex structurally cannot see it. The published instrument was already blind to the repair at the revision the reading was stamped on.

Repaired instrument: resolve a single-identifier slot back to its const NAME = renderChildren(schema.trigger…) binding, and apply that to BOTH arms — because an unconditional asChild over a hoisted const would be a defective site the published probe silently misses. Under it: target 8 / control 1 (collapsible.tsx) at base, and target 9 / control 0 at 2923cea165. ⇒ the probe moves when the repair moves, which is what that control was for.

⭐ The seat's refuted-count-kept-beside-the-right-one is the card's most copyable property, so this is recorded the same way: the published control's number is wrong, the target's is right, and the difference is an instrument that could not see a hoisted local.

Does any of them want a bare string at all?

Yes — all nine, and this is why nothing moves in packages/types. Both published faces already say so: SchemaNode names string explicitly (packages/types/src/base.ts) and the zod mirror types every one of the nine keys against that same union. Narrowing them would retract a capability both faces shipped, and it is the direction objectui#7105 already ruled against for node slots — they RELAX the renderer rather than narrow the declaration, quoted in renderChildren's own docblock. A trigger whose label is a string is the most natural thing an author writes. So this is the implementation catching up to a declaration that already said yes, ⛔ not a lenient fallback around off-spec metadata (AGENTS.md #0.1): the accept set is unchanged in both directions.

The pin, and the ninth renderer

packages/components/src/__tests__/overlay-trigger-bare-string-9710.test.tsx enumerates the REGISTRY for blocks declaring a trigger slot, ⛔ never a list of files — a pin naming these eight is green forever on the ninth. It writes down no count; the population is whatever the registry holds.

Controls: ENUMERATION_CONNECTED (the population reaches ui:collapsible, which lives in renderers/disclosure/, and ui:context-menu, which was never defective — so it is not a restatement of this diff); ASCHILD_STILL_ON (an element trigger is merged rather than nested — ⭐ repaired and now measured 8-of-8. It was briefly UNSOUND: its authored-element finder took the FIRST button whose text matched the label, so when a primitive WRAPS the authored button the finder searched above the wrapper and saw nothing — measured 1 of 8 under the exact over-reach it guards. The finder now takes the INNERMOST match (textContent equals the label AND it contains no button of its own), and under the same ablation it names all eight. ⚠️ Named residue, ⛔ not claimed away: it cannot see an over-reach whose wrapper is neither BUTTON, A nor SPAN — no Radix trigger draws a div today — and it assumes the label is unique in the subtree, which holds because every overlay's content is closed at first paint) · OVER_REACH_IS_VISIBLE (a STANDING control added in the same round: a deliberately over-reaching block is registered on a BUTTON-drawing primitive and the detector is observed catching it on every run, so the detector's own failure is witnessed in CI rather than in one ablation transcript. ⚠️ Its coverage is the primitive kind it registers on: an edit that blinds the detector to A or SPAN wrappers passes CI while hover-card over-reach goes unnamed — a second registered over-reacher on an anchor-drawing primitive would close that); REDDENS_FOR_A_NINTH (a deliberately defective block is registered inside the test, and THE assertion — the errored-out list is empty — is observed holding exactly that block's name and nothing else).

Reverse verification, from the committed state. The eight files were reverted to their base blobs, verified on disk by git hash-object against git rev-parse BASE:PATH per file, with a trap restoring from HEAD:

ABLATED_PIN_EXIT=1
AssertionError: expected [ Array(8) ] to deeply equal []
+ [ "ui:alert-dialog", "ui:dialog", "ui:drawer", "ui:dropdown-menu",
+   "ui:hover-card", "ui:popover", "ui:sheet", "ui:tooltip" ]

All eight named in one run, and ⛔ neither ui:collapsible nor ui:context-menu — the discriminator, measured rather than asserted. Restored and proved restored: git diff HEAD and git diff --cached both empty, and every file in renderers/overlay/ byte-identical to its HEAD blob.

Gates

Exit codes redirected to files and captured before reading, never through a pipe.

gate exit
pnpm exec vitest run packages/components/ (281 files, 2742 tests) 0
pnpm --filter '@object-ui/components^...' build 0
pnpm --filter @object-ui/components type-check (tsc --noEmit + tsconfig.test.json) 0
pnpm --filter @object-ui/components lint (eslint .) 0 — 0 errors; my files add no message
node scripts/check-changeset-presence.mjs 0
pnpm check:control-bytes · check:new-line-citations 0 · 0
pnpm check:component-surface-parity · check:registry-bare-names · check:element-data-source-declaration 0 · 0 · 0
pnpm check:unreferenced-sources · check:test-path-roots · check:vi-mock-specifiers 0 · 0 · 0
pnpm check:doc-types · check:prompt-keys · check:changeset-claims · check:pending-changeset-literals 0 each
pnpm check:sdui-registration-pins · check:readme-exports · check:dist-completeness · check:published-dist · check:side-effects-array 0 each, AFTER a full turbo run build
pnpm check (aggregate schema check, 632 files) 0
node scripts/check-governed-queue-guard.mjs --test PATHS 0 — NOT GOVERNED, 11 paths against 5 surfaces

⚠️ check:sdui-registration-pins and check:readme-exports first exited 2 and 1 with their own text saying the run had measured nothing ("No console build to weigh", "the population COLLAPSED"). Those are PREREQUISITE NOT MET, ⛔ not verdicts; both are green above only because the workspace was built first and they were re-run.

The repo-wide pnpm lint (turbo run lint) and the rest of the gate farm are CI's run, ⛔ not narrowed here.

Notes

  • The seam is package-internal on purpose: packages/components/src/index.ts is untouched, so no public API is added. The whole-tree sweep above is why — no plugin-* package carries this shape today. The pin's docblock records the matching limit: a block registering a trigger slot from another package is not in this file's population.
  • overlay/menubar.tsx declares no trigger key and carries no asChild trigger; it is not part of the class.
  • Session: session_018HrVaotisyhgmot9o2MLRq.

Generated by Claude Code


⛔ Seat correction to this body

Edited by the domain:ui#2 execution seat, session_018HrVaotisyhgmot9o2MLRq, ⛔ not by the dev. A dev writes a PR body once at create and never PATCHes it, so a body that goes false after a patch round is the seat's to repair — and this lane's own platform note is explicit that the double-footer condition is ⛔ not a reason to leave a false sentence standing in a body.

Two statements were false against the current head and are corrected above: the seam's NAME (asChildSlotProps became renderTriggerSlot, and its shape changed from a props spread to a render function, because an emptiness guard cannot live in a helper that only returns props), and the empty-slot MECHANISM (retracted in full). The ASCHILD_STILL_ON description was also stale and is now annotated with the defect the re-review measured. Contract-review records for this PR: 5725825584 (FAIL, head 77849a9) and 5726449675 (FAIL, head 5f775a19).

⭐ Update — contract review PASSED at head 434d410c (record 5726725216). The two earlier FAILs are 5725825584 (head 77849a9, the empty-slot regression) and 5726449675 (head 5f775a19, the blinded control). Both defects are closed and independently re-derived by the reviewer: the omitted-trigger arm reddens on exactly the five zod-optional keys, and the over-reach control reddens on all eight. Seat edit by session_018HrVaotisyhgmot9o2MLRq; a dev writes its body once at create and never PATCHes it.


Generated by Claude Code

… overlay blocks (objectui#9710)

The defect objectui#9701 repaired on `collapsible`, in eight more places:
the authored `trigger` slot handed straight to a Radix `*Trigger` with
`asChild` written unconditionally. `asChild` resolves the primitive to
its `Slot`, which merges onto its child through `React.Children.only` — a
single React element and nothing else — while both published faces admit
a bare string on the key. So `trigger: 'Open it'` validated twice and
then threw into `SchemaRenderer`'s error boundary.

`asChild` now asks Radix's own structural question through one shared
seam, `asChildSlotProps`, rather than eight copies of the predicate: a
multi-node array and an empty slot's `null` are the same structural
mismatch wearing different values and take the same arm, and the element
arm keeps the merge.

`context-menu` declares the same key and is deliberately untouched — its
`asChild` child is a real `div`, so the rendered slot goes inside it and
`Children.only` never sees the string.

The class pin enumerates the REGISTRY for blocks declaring a `trigger`
slot rather than naming files, so a ninth key is covered the day it
registers one; that property is itself asserted by registering a
deliberately defective block inside the test and observing the pin's own
assertion fail on it.

No published face moves: `packages/types` is untouched.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
A per-block `expect` inside the loop stopped at the first failure and
reported a single file, which for a card whose subject is a class of
eight is the wrong diagnostic. The survey now runs the whole population
first and asserts on the LIST of blocks that reached the error boundary,
so one red run prints all of them.

`REDDENS_FOR_A_NINTH` asserts that same list rather than a paraphrase of
it: with a deliberately defective block registered, the list holds
exactly that block's name and nothing else.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
@github-actions

github-actions Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 4 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6150-undeclared-but-consumed-keys.md

  • names overlay/hover-card.tsx → packages/components/src/renderers/overlay/hover-card.tsx — edited by this change

    | type | key | declared as | read at | |---|---|---|---| | TextSchema | content | string | renderers/basic/text.tsx — {schema.content \|\| schema.value} | | CarouselSchema | opts | RecordANGLE-BRACKETS(string, unknown) | complex/carousel.tsx — opts={schema.opts} | | CarouselSchema | orientation | 'horizontal' \| 'vertical' | complex/carousel.tsx | | CarouselSchema | itemClassName | string | complex/carousel.tsx — per-slide class | | FilterBuilderSchema | wrapperClass | string | complex/filter-builder.tsx | | TreeViewSchema | nodes | TreeNode[] | data-display/tree-view.tsx | | TreeViewSchema | title | string | data-display/tree-view.tsx | | TreeViewSchema | onNodeClick | (node: TreeNode) => void | data-display/tree-view.tsx — INVOKED | | CheckboxSchema | required | boolean | form/checkbox.tsx — drives the * marker | | FileUploadSchema | buttonText | string | form/file-upload.tsx | | FileUploadSchema | wrapperClass | string | form/file-upload.tsx | | HoverCardSchema | align | OverlayAlignment | overlay/hover-card.tsx | | ContextMenuSchema | trigger | SchemaNode \| SchemaNode[] | overlay/context-menu.tsx |

.changeset/6349-types-internal-name-collisions-batch-2.md

  • names overlay.ts → packages/types/src/overlay.ts — edited by this change

    MenuItem — renamed to AppMenuItem in app.ts. app.ts declared a flat, all-optional interface (type?: 'item' | 'group' | 'separator', label, icon, path, href, children, badge, hidden) — the @deprecated legacy navigation item that AppComponentSchema.menu, AppAction.items and menuItemToNavigationItem read. overlay.ts declared a discriminated union, MenuCommandItem | MenuDividerItem, whose command arm requires label and whose both arms tombstone type as type?: never (objectui#6523) — precisely the key app.ts declares as a three-value enum. Re-pointing either at the other would have made an authored type: 'separator' legal on one side and a type error on the other, so the two names had to part. @object-ui/types continues to publish overlay's union as MenuItem and app's interface as AppMenuItem, exactly as before.

.changeset/6939-overlay-trigger-mirror.md

  • names packages/types/src/overlay.ts → packages/types/src/overlay.ts — edited by this change

    patch, not minor: the accept set only widens toward what already renders. Every key involved is optional, and children stays legal — it is BaseSchema's own optional key, merely no longer demanded here. No document that validated before this change stops validating; documents the renderers already draw start validating. The TypeScript twins in packages/types/src/overlay.ts move in the same stroke, so the published declaration and the published validator keep saying the same thing.

.changeset/7963-alert-dialog-footer-keys-retired.md

  • names packages/components/src/renderers/overlay/alert-dialog.tsx → packages/components/src/renderers/overlay/alert-dialog.tsx — edited by this change

    What was measured. Tree-wide on 72bcd7783, a point-access probe scores schema.cancelLabel / schema.confirmLabel / schema.confirmVariant at 0 / 0 / 0, against firing controls on the very renderer under test (packages/components/src/renderers/overlay/alert-dialog.tsx): schema.cancelText = 15, read at :37, and schema.actionText = 5, read at :38. The keys did reach the Radix root through the renderer's rest-spread, so a grep alone was not a verdict — the DOM reading is, and it is kept as a live pin: varying one key per fixture through the real renderer leaves the normalised dialog HTML unmoved, against a CHANNEL control (open, unread and live through that same spread) and a WIRED control (cancelText / actionText drawing both footer buttons). The AlertDialog root renders a context provider rather than an element, so an unknown prop is dropped before reaching any node. An author who wrote the declared trio got an empty footer.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with 26ac50369 (merge-base with origin/main): 14 file(s) changed outside .changeset/, read against 1149 pending declaration(s) that publish a body (1681 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.2 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-BibZHU7Y.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 545.88KB 130.75KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.15KB 62.87KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.26KB 1.99KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.49KB 19.99KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.28KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.04KB 21.01KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 77849a9074e3da644ea80da4c330265a8cd969f6

① Derived judgments

  1. Clause ② and direction — renderer-only CONFIRMED; overturn yes to no. git diff --stat 57c07f002..77849a9 reads 11 files, 0 under packages/types, index.ts untouched. All nine trigger unions in overlay.zod.ts still spell z.union([SchemaNodeSchema, z.array(SchemaNodeSchema)]) and SchemaNode still names string. Making the bare string render is payment of an acceptance both faces already declared, not a widening: the accept set is identical before and after in both directions, and the runtime moves toward the declaration (the objectui#7105 direction, the same reasoning PR objectui#9708 declared no on). The conservative yes was correct at claim time and is wrong for the delivered diff.

  2. Probe re-derivation (item 6) — the delivery's instrument finding REPRODUCES exactly. Corpus 195 .tsx at every revision via git ls-tree / git show. Literal recipe (unconditional asChild on a Trigger line, next non-blank line within three matching renderChildren(\s*schema\.trigger): target 9 at 2923cea165, 8 at c90c68d63, 8 at 57c07f002; control (asChild={ spelling) reads 0 at ALL THREE, including c90c68d63, the revision comment 5719013007 stamped its 1 on. Cause confirmed by reading collapsible.tsx at c90c68d63: line 82 hoists const trigger = renderChildren(schema.trigger) and the trigger's next non-blank line is {trigger}, which no forward regex can bind. Const-resolving repair: control 0 / 1 / 1 at the same three revisions, target unchanged. The published lit control was blind at the revision it was stamped on; the target count was right. Plainly: the recipe as published cannot see a repaired site.

  3. Ablation (item 7) — REPRODUCES. In an exported copy of the head tree (not the checkout), the eight renderers were replaced by their 57c07f002 blobs, each proven by git hash-object equal to git rev-parse BASE:PATH. Pin exit 1; THE assertion reads expected [ Array(8) ] to deeply equal [] naming exactly ui:alert-dialog, ui:dialog, ui:drawer, ui:dropdown-menu, ui:hover-card, ui:popover, ui:sheet, ui:tooltip; neither ui:collapsible nor ui:context-menu. REDDENS_FOR_A_NINTH also reddens under ablation (nine names against the expected one), consistent and unquoted in the body. Restored to head blobs, hash-proved; pin at head 4/4 green.

  4. Scope vs triage 5719728719 — all eight repaired in one change (each Trigger asChild + renderChildren(schema.trigger) pair becomes one {...asChildSlotProps(schema.trigger)} spread). The pin enumerates ComponentRegistry.getAllConfigs() for inputs carrying { name: 'trigger', type: 'slot' }, dedupes by the stored namespaced type (correct: register writes type: fullType on both the bare and namespaced keys), writes down no count and no file list. Population today is ten: the eight, ui:collapsible, ui:context-menu. REDDENS_FOR_A_NINTH is a standing control, not a one-off demonstration: every run registers a defective block through the same register path a real ninth would take and observes THE assertion's own predicate catch it, so the property is re-asserted on every CI run. Caveat on its population predicate under ③.

  5. Named exclusion — context-menu.tsx blob b85571d8… identical at base and head; its asChild child is a real div element wrapping the slot, so Children.only never sees the string. The pin measures it rather than exempting it: ENUMERATION_CONNECTED requires it in the population, THE assertion surveys it, and the ablation run did not name it. menubar.tsx blob 01980120… identical; its inputs list only className, and MenubarSchema declares no trigger key. Both claims hold.

  6. Shared seam — package-INTERNAL as claimed. packages/components/src/index.ts re-exports lib/utils by NAME only (cn, renderChildren, renderNodeSlot, isEmptyNodeSlot); no export * anywhere under packages/components/src covers lib/; package.json exports exposes only . and ./style.css. asChildSlotProps is reachable from no published entry. The renderer-only story survives this check.

  7. Empty-slot arm — the delivery's mechanism claim is FALSE and hides a measured behaviour change. @radix-ui/react-slot@1.3.3 (lockfile-pinned, read from the npm tarball) returns a null child as-is at slot.tsx line 45 (if (children || children === 0) throw …; return children): a null child never threw. Measured on the exported tree, SchemaRenderer with { type, title: 'T' } and NO trigger: at base blobs, ui:dialog, ui:alert-dialog, ui:sheet, ui:drawer, ui:tooltip paint 0 button elements; at head each paints 1 EMPTY focusable button (type="button" aria-haspopup="dialog" aria-expanded="false" data-state="closed"; tooltip data-state="closed"). All five keys declare trigger .optional() in overlay.zod.ts, so this is a legal document whose rendering moves from nothing to an unlabeled tab stop. The template PR objectui#9708 has the same shape but collapsible.trigger is REQUIRED on its zod face, so no legal document reaches that arm there; the eight differ from the template on exactly this axis, and the per-key reading the card asked for did not take it. Neither pin (9701 or 9710) exercises the omitted-trigger arm. No in-tree document or TSX site omits a trigger today (10 catalog JSON files, all carry one), so there is no measured victim, the same standing the triage gave the original defect.

② Semver level

patch on @object-ui/components is the right LEVEL for the string and multi-node arms: a runtime defect repaired inside the declared accept set, no face moves, and anyone who "relied on the throw" relied on an error boundary, not a contract. The changeset's CLAIM is not right: its sentence "A multi-node trigger array and an empty slot's null are that same structural mismatch wearing different values and are answered on the same arm" is born false on the null half (①.7) and it publishes verbatim to the CHANGELOG. As delivered, a consumer reading the CHANGELOG is told nothing about the only rendering change on a legal input this diff makes. Remedy is narrow and does not move the level: have the seam return no trigger element when isEmptyNodeSlot(slot) (restoring base behaviour for the five optional keys), pin the omitted-trigger arm in the class test, and correct the sentence; or, if an empty trigger is the intended reading, declare it in the changeset and pin it.

③ Boundary flags

  • BLOCKING (basis of the verdict): the omitted-trigger arm regresses from "no trigger element" to "one empty focusable button" on five zod-optional keys, unpinned, while the changeset asserts the opposite mechanism. Everything else in this review passes; the fix is one predicate in asChildSlotProps, one assertion, one sentence.
  • The repaired probe is blind to the seam's spelling: the eight now carry no asChild attribute at all, so at head both recipe arms read target 0 / control 1 (collapsible only). A future re-runner cannot tell "repaired" from "deleted" with that instrument; the registry pin is the only live instrument for this class. The PR body does not say this.
  • ASCHILD_STILL_ON detects over-reach with a button button selector; HoverCardTrigger draws an anchor element and ContextMenuTrigger a span element (read from the installed Radix dists), so an over-reach on those two would nest without tripping the control. Weak control, not a fail.
  • The pin's population predicate is the registry inputs spelling type: 'slot', not the zod declaration; check-component-surface-parity reconciles input NAMES, not types. A tenth key declaring trigger in overlay.zod.ts but registering it under another input type escapes the pin. Today all ten use the slot spelling.
  • PR mergeable_state is behind (main moved to 3e4f6324…); not a contract matter.
  • No edits were made to either checkout; all runs were on an exported copy under scratch pr-9796/tree, restored to head blobs and hash-proved after ablation; the reviewer's probe test was removed.

Implemented-by: claude/issue-9710-overlay-trigger-aschild-siblings
Reviewed-by: session_018HrVaotisyhgmot9o2MLRq

VERDICT: FAIL


Generated by Claude Code

…wo arms are separated

Contract review found the round-1 seam regressed a legal input: a document
that omits `trigger` rendered one empty focusable button where the base
rendered nothing, on every key whose zod face carries `.optional()`. An
empty `*Trigger` still paints its own element, and an empty `button` is an
unlabeled tab stop with nothing to read.

The stated mechanism is what hid it, and it was false. Read from the
lockfile-pinned `@radix-ui/react-slot`, the throwing branch is guarded by
`if (children || children === 0)`, so a `null` child is returned as-is:
the empty arm NEVER threw. It is not the same structural mismatch as a
bare string or a multi-node array, and the changeset no longer says it
is — that sentence publishes verbatim to the CHANGELOG.

The seam is now `renderTriggerSlot`, which carries both rules in one
place: render nothing when the slot is empty (`renderNodeSlot`'s own
contract — chrome that disappears with its content), and ask Radix's
structural question about `asChild` otherwise. A call site that reached
for one rule and forgot the other is what this replaces.

`OMITTED_TRIGGER_PAINTS_NOTHING` pins the restored arm. Its population is
read from the published zod faces, NOT written down: the `.optional()`
wrapper is the operative fact, because `shape.trigger.isOptional()` is a
dead instrument on these keys — `SchemaNodeSchema` itself admits
`undefined`, so it answers true for all ten and cannot separate `dialog`
from `popover`. `ZOD_FACES_READ` asserts the derivation discriminates, so
the arm cannot go vacuously green.

`ASCHILD_STILL_ON` no longer uses a `button button` selector, which could
not see over-reach on `HoverCardTrigger` (an anchor) or
`ContextMenuTrigger` (a span). It now asks whether an ancestor wearing
Radix's trigger wiring is one of the tags Radix draws, with `div`
excluded and the reason named: `context-menu` authors its own `div` and
the primitive correctly merges onto it.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.5 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-Bo-dX6fx.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 545.74KB 130.75KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.23KB 62.88KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.71KB 20.07KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.28KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.05KB 21.01KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

The instrument is now blind — the pin is the only live one

Posted by the dispatching domain:ui#2 seat, session_018HrVaotisyhgmot9o2MLRq, on the dev's behalf and at its wording.

After this repair the eight call sites carry no asChild attribute and no Trigger tag of their own, so BOTH arms of the source-grep recipe published on this card read target 0 / control 1 at head. A future re-runner ⛔ cannot tell a repaired renderer from a deleted one with it. overlay-trigger-bare-string-9710.test.tsx is the only live instrument for this class: it enumerates the registry for blocks declaring a trigger slot and reads the zod faces for which of them may legally omit one, so it follows the class without being re-pointed. The same note is committed in the renderTriggerSlot docblock so it cannot be lost with this page.


⛔ Why this is a comment and not an edit to the PR body — a correction against this seat

The dispatch asked the dev to put this note in the PR body. That instruction was wrong, and the dev was right to refuse it rather than comply silently.

The standing os-dev contract says a dev writes the PR body once, on the create call, and ⛔ never PATCHes it — anything later is named in the report for the seat to carry. Per SKILL.md, 「无条件条款只住角色文件,冲突时它胜、错了修那里,⛔ 不靠派发词临时覆盖」. ⇒ a dispatch word does not get to override a role-file clause, and the dev flagged the conflict instead of picking a side quietly. That is the behaviour the contract wants.

⚠️ This seat also ⛔ does not PATCH the body: it is the artefact the contract review was taken against, this repo's double-footer condition is live over REST (confirmed independently on PR objectui#9805, whose stored body carries two footer blocks), and the angle-bracket sanitizer eats tag-shaped fragments from bodies while comments are safe.

⭐ And the durable half was already handled by the dev without being asked: the same note is committed in the renderTriggerSlot docblock and the pin's own header, where it cannot be lost with this page. The comment is the convenience copy; the code is the record.

domain:ui#2 execution seat · session_018HrVaotisyhgmot9o2MLRq · dev addendum carried by the seat · reading taken 2026-09-18T06:56Z


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5f775a19dbd32645d637aaa86182874f09805515

① Derived judgments

  1. Blocker status — CLOSED, measured. New head is a child of 77849a9; git diff --stat 57c07f002..5f775a19 reads the same 11 files, packages/types 0, index.ts 0. The seam is now renderTriggerSlot(Trigger, slot), built on renderNodeSlot: an empty slot returns null and no Trigger element is created; a present slot renders once and passes asChild from isValidElement. In the scratch copy at the new-head blobs (all 11 hash-proved), a document with no trigger on ui:dialog, ui:alert-dialog, ui:sheet, ui:drawer, ui:tooltip paints 0 buttons, 0 focusables, 0 elements wearing Radix wiring; ui:context-menu paints its placeholder div (wired, not focusable); ui:popover, ui:hover-card, ui:dropdown-menu paint nothing. Guard ablation (seam rewritten to render the Trigger unconditionally): pin exit 1, expected [ Array(5) ] to deeply equal [] naming exactly ui:alert-dialog, ui:dialog, ui:drawer, ui:sheet, ui:tooltip — not context-menu, not the required-trigger keys. Restored, hash-proved. The string arm still witnesses: eight renderers at base blobs give exit 1 naming all eight, neither ui:collapsible nor ui:context-menu.

  2. Zod derivation — SOUND, and for a stronger reason than the delivery gives. nodeUnionOptions in base.zod.ts carries z.undefined(), so shape.trigger.isOptional() is true on all ten — confirmed. But whole-document safeParse with minimal valid siblings supplied DOES discriminate: the key ABSENT fails trigger=invalid_type on popover, hover-card, dropdown-menu, collapsible and passes on the other six; the key present as undefined passes on all ten. So zod v4's object parser gates key omission on the .optional() wrapper, and the wrapper is the face's own rule for "may this key be omitted", not a documentation marker. The TypeScript face agrees exactly: trigger?: on dialog, alert-dialog, sheet, drawer, tooltip, context-menu; trigger: on popover, hover-card, dropdown-menu, collapsible. The delivery's stated reason for rejecting whole-object parsing ("confounded by other required keys") is true only for context-menu (items required); with siblings supplied the instrument gives the same 6/4 split. triggerOptionalByType() keys faces by the type literal; no two trigger-bearing faces share a literal (10 literals, each once), and ZOD_FACES_READ asserts every population member has an entry and that the map discriminates. The derivation agrees with both faces because it reads the mechanism both faces use, not by coincidence.

  3. OMITTED_TRIGGER_PAINTS_NOTHING — WITNESSES (guard ablation above) and does not over-claim. Asserting on focusables is the honest instrument available without a per-block exemption list: context-menu legitimately paints placeholder text inside a wired div, so "paints nothing" and "no wired element" both fail on the legal case. Named hole: a non-focusable stray trigger (an anchor without href, a span) on a future optional key whose primitive draws a or span would pass. Today every optional key's primitive draws button except context-menu's span, which merges onto the div.

  4. ASCHILD_STILL_ON — NOT SOUND, and the hole is not the div exclusion. The div exclusion is correct and necessary (context-menu's authored div and collapsible's root div both wear data-state legitimately). The defect is the authored-element finder: it takes the FIRST button in document order whose textContent equals the label. When a primitive wraps the authored button, the outer Radix-drawn button also has textContent equal to the label and comes first, so closest searches ABOVE the wrapper and finds nothing. Measured: seam mutated to asChild={false} on all eight — the exact over-reach this control guards — the control reddens for ui:hover-card ONLY; dialog, popover, tooltip (and by the same DOM shape alert-dialog, sheet, drawer, dropdown-menu) paint a wired button wrapping the authored button and read wrapped=false. The button button selector it replaced caught those seven and missed hover-card. Net effect of the "strengthening": 7-of-8 detection became 1-of-8 on this card's own renderers, on a SHARED seam where one future edit would ship seven nested triggers while the pin names one. Fix is one line — pick the innermost match (exclude any button that itself contains a button, or take the last match) — plus an observed-red ablation for this control in the record, which the round does not report.

  5. Nothing else moved in code. The eight renderers differ from the prior head only by the import line and the one trigger line ({renderTriggerSlot(XTrigger, schema.trigger)}); collapsible.tsx 7be440e7…, context-menu.tsx b85571d8…, menubar.tsx 01980120… are blob-identical to base. The PR BODY, however, did not move: at the new head it still names asChildSlotProps, still carries the retracted sentence that an empty slot's null "take[s] the same arm", and still describes ASCHILD_STILL_ON as "never nested inside a second button". The changeset was corrected; the body contradicts its own diff.

  6. Clause ② and semver re-confirmed. No published face moves; renderTriggerSlot is reachable from no entry (index.ts re-exports lib/utils by name, exports map is . and ./style.css); the render-function reshape is internal. Clause ② stays overturned to no; patch stands. The changeset's new empty-arm paragraph is accurate against react-slot@1.3.3 line 45 and against the measured five.

② Semver level

patch on @object-ui/components — correct. The string and multi-node arms are a runtime repair inside the declared accept set; the empty arm now matches base behaviour on every key whose faces permit omission; no export is added or changed. The changeset paragraph on the empty arm is true as written.

③ Boundary flags

  • BLOCKING (basis of the verdict): ASCHILD_STILL_ON is blind on every button-drawing primitive (①.4), measured 1-of-8 under the over-reach it exists to catch, and the round presents it as strengthened without an observed-red reading. A control that cannot be observed failing on the button primitives certifies nothing for seven of this card's eight renderers.
  • MUST FIX WITH IT: PR body is stale against the head (①.5) — seam name, the retracted null sentence, the old control description. The body is the PR's record and currently states the mechanism the changeset retracted.
  • Noted, not filed: trigger: undefined (explicit) validates on all ten keys on both faces — a TypeScript host can pass it; JSON cannot express it. On the eight it now paints nothing; on collapsible it paints an empty focusable button today (9708's file, outside this card's surface). The omitted arm does not cover it because collapsible reads as required-by-wrapper. Successor: whoever next edits disclosure/collapsible.tsx.
  • The focusable instrument lets a non-focusable, wired, empty anchor or span through on a future optional key drawing a or span (①.3); no such key exists today.
  • Everything re-derived: blocker closed, both arms witness under ablation, exclusions intact, faces unmoved, Clause ② no, patch. The remaining defects are one finder expression and one body edit.
  • No edits to either checkout; all runs on the exported scratch copy, every mutation restored and hash-proved; reviewer probes removed.

Implemented-by: claude/issue-9710-overlay-trigger-aschild-siblings
Reviewed-by: session_018HrVaotisyhgmot9o2MLRq

VERDICT: FAIL


Generated by Claude Code

…d button, and its own failure is now observed

Re-review measured the previous detector at 1-of-8 on this card's own
renderers — weaker than the `button button` selector it replaced, which
caught seven and missed only hover-card.

The hole was never the `div` exclusion, which is correct and necessary:
context-menu's authored div and collapsible's root div both wear
`data-state` legitimately. It was the authored-element finder. It took the
FIRST button whose `textContent` matched the label — and when a primitive
WRAPS the authored button, the Radix-drawn wrapper carries the same
`textContent` and comes first in document order. So the finder returned
the wrapper, `closest` searched above it, and the over-reach read as
clean. Seven of the eight draw a button wrapper; only hover-card, which
draws an anchor, was still caught.

The finder now takes the innermost match — the button containing no
button of its own.

`OVER_REACH_IS_VISIBLE` makes the failure standing rather than argued:
a block that withholds `asChild` where Radix could have served is
registered inside the test on a BUTTON-drawing primitive, and the list
`ASCHILD_STILL_ON` asserts empty is observed holding exactly that block.
With the first-match finder that list reads empty and this control
reddens — so it guards the very regression this commit repairs, on every
run, instead of resting on the reasoning above.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.6 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-BIFsTOUD.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 545.74KB 130.75KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.23KB 62.88KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.71KB 20.07KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.28KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.05KB 21.01KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 434d410cae141a9a0f66e23c44bddfa50113b1d3

① Derived judgments

  1. Scope of the round — ONE FILE, verified. git diff --name-status 5f775a19..434d410c is M packages/components/src/__tests__/overlay-trigger-bare-string-9710.test.tsx and nothing else. The seam (lib/utils.tsx), all eight renderers, the changeset, context-menu.tsx, collapsible.tsx and menubar.tsx are blob-identical between the second and third heads; against base the diff is still 11 files with packages/types 0 and index.ts 0. Pin at head: 7 of 7 green.

  2. LEG A re-derived — the 8-of-8 claim HOLDS and reverses my earlier 1-of-8. Seam mutated (exact-once anchor) to asChild={false}: ASCHILD_STILL_ON exit 1, expected [ Array(8) ] to deeply equal [] naming ui:alert-dialog, ui:dialog, ui:drawer, ui:dropdown-menu, ui:hover-card, ui:popover, ui:sheet, ui:tooltip; ui:collapsible and ui:context-menu (own asChild expressions, not on the seam) not named. OVER_REACH_IS_VISIBLE also reddens in that run (nine names against its expected one) — consistent. Restored, hash-proved.

  3. LEG B re-derived — HOLDS. Pin mutated back to the first-match finder (node.textContent === ELEMENT_LABEL,): exit 1, OVER_REACH_IS_VISIBLE reads expected [] to deeply equal [ 'test-9710:over-reaching-key' ]; the other six stay green. Restored, hash-proved.

  4. Finder soundness — sound for the wrapper case, with named residue. Innermost is spelled "textContent equals the label AND querySelector('button') is null", so a Radix-drawn wrapper (whose textContent also equals the label) can no longer win document order. What it still cannot see: (a) an over-reach whose wrapper is not BUTTON, A or SPAN — a renderer that wraps the slot in its OWN wired div is indistinguishable from context-menu's legitimate design, and no Radix trigger draws a div today; (b) label uniqueness is assumed — find returns the first innermost match in DOM order, safe today because every overlay's content is closed at first paint, unverified for a future default-open block placing another button labelled Toggle ahead of the trigger. Red-not-silent edges: an authored element that itself contains a button (invalid HTML, not constructible through ui:button) would make the merged outer button read as a wrapper; a future renderer authoring a wired span as its asChild child around the slot would be flagged. Both fail loud and force a reading, which is the acceptable direction for a control.

  5. OVER_REACH_IS_VISIBLE — a REAL standing control for the button-drawn shape, not a pin of the one bug. Measured against breakages that are NOT the first-match defect: BUTTON removed from RADIX_DRAWN_TRIGGER_TAGS — reddens; wiring selector replaced with one that matches nothing — reddens. Any change that makes the detector miss a Radix-drawn button wrapping the authored button is witnessed on every run. Its limit is the primitive kind it registers on: A removed from the tag set — 7 of 7 green; the same removal with the seam over-reaching — ASCHILD_STILL_ON names seven, not eight, ui:hover-card silently missing. So the anchor-drawn shape (hover-card) and the span shape (context-menu, not on the seam) are guarded only by the LEG A transcript, not by a standing control. Today hover-card is the population's only a-drawing key.

  6. Self-reported script flaw (the grep -F 'witness' over a multi-line anchor) — disclosure is sufficient and undermines nothing I rely on. My re-derivation uses its own exact-once anchors, git hash-object deltas and restores from the head blob; LEG A and LEG B reproduce independently of the dev's script.

  7. PR body — the seat correction landed: the null-arm sentence is retracted with the slot.tsx:45 guard quoted, the seam is named renderTriggerSlot, and the two FAIL records are cited. One line is now stale in the CONSERVATIVE direction: it still says ASCHILD_STILL_ON "is currently UNSOUND and a fix is in flight", which understates the head. Not a false claim in the direction that matters.

② Semver level

patch on @object-ui/components — unchanged and correct. A test-only round; no runtime byte moved since the second head; no export added; no published face moves. Clause ② stays overturned to no.

③ Boundary flags

  • Standing coverage is BUTTON-shape only (①.5): a future edit that blinds the detector to A or SPAN wrappers passes CI while hover-card over-reach goes unnamed. A second registered over-reacher on an anchor-drawing primitive (HoverCard) would close it; today it is an ablation-transcript guarantee, recorded here.
  • Detector cannot distinguish a renderer's own wired div wrapper from context-menu's legitimate design (①.4a); no Radix trigger draws a div, so the gap is empty today.
  • Label-uniqueness assumption in the finder (①.4b) is unpinned; safe for every block in today's population.
  • PR body's ASCHILD_STILL_ON annotation understates the head (①.7); a seat edit noting the fix and this record is owed, not a code change.
  • mergeable_state is behind main; not a contract matter.
  • No edits to either checkout; all runs on the exported scratch copy; every mutation restored from the 434d410c blob and hash-proved; no reviewer probes left in the tree.

Implemented-by: claude/issue-9710-overlay-trigger-aschild-siblings
Reviewed-by: session_018HrVaotisyhgmot9o2MLRq

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Contract-review gate CLEARED — provenance

domain:ui#2 execution seat, session_018HrVaotisyhgmot9o2MLRq.

review of record PR comment 5726725216, head 434d410cae141a9a0f66e23c44bddfa50113b1d3
verdict PASS, after two FAILs — 5725825584 (head 77849a9) and 5726449675 (head 5f775a19)
carriers needs:contract-review removed from both card objectui#9710 and this PR
machine check --pair 9796 ⇒ exit 0
reviewer tier 129/129 harness-written claude-fable-5-1 stamps across all three rounds, read from its own transcript, ⛔ not its self-report

⛔ No self-review at any point: served tier measured claude-opus-5, below CONTRACT_REVIEW_TIER. Each round was rendered by the isolated at-tier reviewer and adopted verbatim — including both FAILs.

⭐ What the three rounds actually bought

⚠️ Each FAIL was a defect no gate in this repo would have caught, and both were hidden behind a stated mechanism that was false:

  1. Round 1 FAIL — the seam made five zod-optional trigger keys render an empty focusable button where the base rendered nothing: an unlabeled tab stop on a legal document. Hidden by the claim that a multi-node array and an empty slot's null 「take the same arm」, which @radix-ui/react-slot@1.3.3 slot.tsx:45 disproves — a null child never threw.
  2. Round 2 FAIL — the control repaired in round 1 was presented as strengthened while measuring 1 of 8: its finder took the FIRST button matching the label, so a wrapping primitive won document order and the search started above it. The selector it replaced caught 7 of 8.
  3. Round 3 PASS — innermost-match finder, re-derived at 8 of 8, plus a standing control so the detector's own failure is witnessed on every CI run rather than in one ablation transcript.

⭐ The through-line worth keeping: twice, a green suite plus a plausible mechanism sentence covered a real defect — and both times what exposed it was ablating the thing the control exists to catch and reading what it actually named.

⚠️ Residue recorded, ⛔ not claimed away

The standing control's coverage is the primitive kind it registers on: remove A from the tag set and the suite stays 7/7 green while ui:hover-card over-reach goes unnamed. ⇒ the anchor shape and the span shape are guarded by the ablation transcript, ⛔ not by a standing control. A second registered over-reacher on an anchor-drawing primitive would close it. The finder also assumes the label is unique in the subtree — true today because every overlay's content is closed at first paint. Both are in the review record and in the PR body.

domain:ui#2 execution seat · session_018HrVaotisyhgmot9o2MLRq · gate provenance · reading taken 2026-09-18T07:29Z


Generated by Claude Code

…ger seam moved

CI went red on three of four shards: a lit control in
`overlay-node-slot-doc-types-7082.test.ts` asserted the alert-dialog
renderer contains `renderChildren(schema.trigger)`, and objectui#9710
moved that spelling onto a shared seam. The control licensed a zero-hit
claim about `AlertDialogSchema.actions`, so it had to keep proving the
scan can find things in that file — it now reads
`renderChildren(schema.content)`, a sibling slot the trigger work does
not touch, rather than re-creating the coupling that just broke.

Running the whole `packages/types` suite rather than the named file found
what a path grep could not: `overlay-trigger-union-7081.test.ts` builds
its renderer path from a constant, and nine of its assertions rested on
the same spelling.

- The per-member read-site assertion accepts both spellings. Its claim is
  unchanged — the renderer READS `schema.trigger`, which is what licenses
  the widened declaration — and `context-menu`, never part of the repair,
  still calls `renderChildren` directly.
- The chain is pinned end to end instead of one hop: the seam must reach
  `renderChildren`, or "the renderer calls the seam" would leave the array
  form's read site unpinned.
- The docblock pin no longer requires a trailing `.tsx:`. It was demanding
  a cross-file LINE ADDRESS — the form AGENTS.md #11 bans outright, and
  the seven addresses it required were rotted by this very change. The
  file path carries the claim; the line number never did.

The eight `trigger` docblocks in `overlay.ts` named a call that no longer
exists, in JSDoc that ships in `.d.ts` and shows in an author's editor.
They now name the current spelling, without line addresses.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.5 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-BkFn3Ep8.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 545.74KB 130.75KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.23KB 62.88KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.61KB 20.05KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.28KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.05KB 21.01KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

…r true, and the claim now lives in one place

The sentence was true when it was written and this branch's own CI-fix
round falsified it: `packages/types/src/overlay.ts` moved by 62 lines.
A changeset that declares a patch release for a package while stating
that package is untouched contradicts itself, and it publishes verbatim
to the CHANGELOG — where the reader it misleads is the one diffing their
`.d.ts` after the bump.

The claim this change actually owns is narrower and survives: no type, no
accept set, no export and no runtime behaviour moves in
`@object-ui/types`, and every changed line in that file is a docblock
line. What moves there is documentation that SHIPS.

⛔ The two paragraphs are folded into one rather than both narrowed. The
defect here was one claim stated in two places, where the copy that
rotted was not the copy being maintained; narrowing both would have kept
that shape.

Verified before publishing rather than asserted: every changed line in
`overlay.ts` is a docblock line, and in the emitted `dist/overlay.d.ts`
the corrected spelling is present on the eight while the old spelling is
absent — same file, same instrument, so the zero is admissible.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HrVaotisyhgmot9o2MLRq
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.5 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-BkFn3Ep8.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 545.74KB 130.75KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.23KB 62.88KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.61KB 20.05KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.28KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.05KB 21.01KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.04KB 5.36KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-tesla
os-tesla marked this pull request as ready for review September 18, 2026 08:42
@os-tesla
os-tesla added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit dc3893d Sep 18, 2026
38 checks passed
@os-tesla
os-tesla deleted the claude/issue-9710-overlay-trigger-aschild-siblings branch September 18, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants