fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940) - #7447

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror
Sep 3, 2026
Merged

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940)#7447
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#6940

DataTableSchema.rowActions in packages/types/src/zod/data-display.zod.ts becomes
z.boolean().optional(). Maintainer ruling 2026-09-02 (director seat, summon #8,
verbatim 「7189 A 其他同意」), option A. Option B (a boolean | array union) was
not taken: it would permanently accept a shape the renderer only truthiness-tests.

- rowActions: z.array(z.any()).optional().describe('Row action buttons'),+ rowActions: z.boolean().optional().describe('Show the row actions column (edit/delete) — mirrors the boolean the renderer truthiness-tests (objectui#6940)'),

The mirror was the single outlier and the published one, so safeValidateSchema refused
the exact spelling the component's TS declaration, destructuring default, truthiness
gates, colSpan arithmetic, registered type: 'boolean' authoring input, defaultProps
and docblock example all teach.

⚠️ Two different keys are named rowActions — confirmed distinguished

Only the data-table one moved. The list view's key is untouched, and git diff proves it.

key changed herekey NOT touched
mirrorzod/data-display.zod.tsDataTableSchemazod/objectql.zod.tsObjectGridSchema
was / isz.array(z.any()) to z.boolean()z.array(z.string()), unchanged
TS twindata-display.tsrowActions?: booleanobjectql.tsrowActions?: string[]
ratchetdrift was ledgered in KnownDriftin parity, ledgered in neither

The separation is now pinned, so a later sweep that "harmonises the two rowActions" on
the strength of a shared name turns red here instead of silently retyping a key no ruling
covers. git diff --name-only -- packages/types/src/zod/objectql.zod.ts returns empty.

The rider — the ratchet was NOT blind, and this is the measurement

#5684's mirror-parity ratchet did catch this drift. It measured it and recorded it in
its KnownDrift ledger:

'data-display.zod.ts#DataTableSchema': 'rowActions' | 'onRowEdit' | 'onRowDelete' | 'onSelectionChange' | 'onColumnsReorder';

with an inline note that named the exact question this card later answered — TS declares
rowActions?: boolean, the mirror declared any[], "One of the two is dead; which is a
ruling."
The ratchet's header records the same entry surviving #5927's sweep
("DataTableSchema kept rowActions").

So there is no structural gap, and there is nothing to file. The ratchet's design is
to convert a drift it cannot adjudicate into a ledgered, citable question rather than a
silent pass. It did exactly that; the question sat in the ledger until the maintainer
answered it on 2026-09-02.

⚠️ Deviation from the dispatch brief — one line in a "do not touch" file

The brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as held by open
PR #7432. This PR edits it anyway, and the reason is mechanical rather than
discretionary — flagging it explicitly for the reviewer.

That ledger is bidirectional by design: "the entry fails when new drift appears on that
mirror AND when the recorded drift is fixed — a stale entry cannot rot quietly."

Repairing rowActions therefore turned the package's own gate red:

src/__tests__/zod-mirror-parity.test.ts(1349,14): error TS2322:
Type '"data-display.zod.ts#DataTableSchema"' is not assignable to type 'never'.

Correcting the entry to its measured remaining four keys is the toll the ratchet charges
for every drift repair — it is not "fixing the ratchet" and not a widening of scope.
Without it pnpm --filter @object-ui/types run type-check cannot pass and the PR is
unreviewable.

Collision with #7432 measured, not assumed: zero. Fetched pull/7432/head and
diffed. Every one of its hunks in this file is in the blocks.zod.ts region (lines
~132–148 imports, ~355 MIRRORS, ~522 Declared, ~1484 EXCLUSIONS), removing the nine
block pairs. This PR's edits are the KnownDrift entry near line 747, its docblock, and
the header key census near line 60 — disjoint regions, no textual conflict either way.

Changes are three: drop 'rowActions' from the union, rewrite the docblock paragraph
that would otherwise be a false statement sitting next to the change, and move the header
census from 53 keys to 52.

Tests

Pinned in packages/types/src/__tests__/data-table-declared-keys-6882.test.ts (the home
the ruling named), 7 new cases:

  1. rowActions: true validates.
  2. rowActions: false validates — it is a boolean, not a truthy-only flag.
  3. rowActions: [] is REFUSED — and every issue path is asserted to be
    rowActions, so a document rejected for an unrelated reason cannot read as a passing
    narrowing pin. This is the load-bearing half: a union would have satisfied a
    "true validates" assertion on its own.
  4. The published safeValidateSchema moves in both directions. Measured at this level
    rather than inferred, because it is a z.union and a sibling member accepting the
    document would have left the published surface unchanged.
  5. Both catalog fixtures exist on disk (guard against a vacuous pass).
  6. Both validate unchanged — they author rowActions: true and always did.
  7. The list view's rowActions still takes string[] and rejects true.

Red-then-green ablation

Reverted only the mirror fix, with the pin left in place.

  • Mutation proven on disk before the run: blob d1d427db to de3a6332, plus anchored
    grep counts (boolean spelling 1 to 0, array spelling 0 to 1). de3a6332 is exactly the
    pre-fix blob on origin/main.
  • Result: 5 of 8 failed, including the [] refusal and the safeValidateSchema case.
  • Control: the ObjectGrid separation test stayed green under the mutation, confirming
    the two same-named keys really are independent.
  • Restore proven by state, not by exit code:git checkout HEAD -- ... (absolute
    path, pinned to HEAD rather than the polluted index), then blob equality against the
    HEAD blob and an empty git diff HEAD. Green leg re-run: 8/8.
  • trap ... EXIT INT TERM with absolute paths throughout.
  • No build/dist leg is required or claimed: the pin imports '../zod/data-display.zod.js',
    a relative same-package specifier vitest resolves to TypeScript source, so no dist/
    copy can serve a stale mutation.

Gates, all on the final commit f17e08244

runresult
pnpm --filter @object-ui/types run type-checkgreen (was red before the ledger line)
pnpm exec vitest run packages/types/92 files, 1547 tests passed
pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/ examples/schema-catalog/162 files, 3209 tests passed
pnpm --filter @object-ui/types run lint0 errors (274 pre-existing warnings)
node scripts/check-control-bytes.mjsOK, 6131 files
node scripts/check-changeset-presence.mjs1 changeset declared
objectui check on the catalog✓ All checks passed

objectui check was run with the real built CLI, and the built dist was confirmed
to carry the change (rowActions: z.ZodOptional of ZodBoolean) rather than a stale
copy. user-table.json and full-featured-table.json no longer appear in its failing
bucket; the five entries that remain there are the components-basic-text ones held by a
separate in-flight PR.

Consumer sweep — the narrowing has no casualties

Grepped the repo for rowActions authored as an array. Every hit outside this PR's own
tests is the list-view/ObjectGrid key: p1-spec-alignment.test.ts (type: 'list-view'),
objectViewHostSurface.test.tsx, the plugin-grid legacy bare-name suite and
resolveLegacyRowActions, plugin-grid/README.md, schema-reference.md and
plugin-grid.mdx. Zero documents author an array rowActions on a data-table, which
measures the ruling's stated basis for a patch bump rather than assuming it.

Changeset

@object-ui/types, patch. The body states the reasoning the ruling requires: no
author can have relied on an array value, because the renderer never reads the array — it
only truthiness-tests the key, so even [] rendered identically to true (#6318 measured
42 elements either way, against 39 with the key absent). An array here could never have
carried meaning to any consumer, so the narrowing removes a spelling that was accepted but
inert.

Review status

Clause ② yes. Draft, carries needs:contract-review, not enqueued, not
auto-merged, not self-reviewed. Waits on the project director seat.


🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…#6940)
The hand-written zod mirror declared `rowActions: z.array(z.any()).optional()`
while every other face of the key says boolean — the TS declaration it mirrors,
the renderer's destructuring default, its truthiness gates and colSpan
arithmetic, the registered `type: 'boolean'` authoring input, `defaultProps`,
and the renderer's own docblock example. The mirror was the outlier and the
published one, so `safeValidateSchema` refused the spelling the component's
documentation, defaults and authoring UI all teach, and two shipped
schema-catalog entries failed validation for that and no other reason.
Maintainer ruling 2026-09-02 (director seat, summon #8), option A. Option B (a
`boolean | array` union) was not taken: it would permanently accept a shape the
renderer only truthiness-tests.
Pinned in data-table-declared-keys-6882.test.ts: `true` and `false` validate,
`[]` is REFUSED and refused ON `rowActions` (every issue path asserted, so a
document rejected for an unrelated reason cannot read as a passing narrowing),
the published `safeValidateSchema` surface moves in both directions, and the two
catalog entries validate unchanged. The list view's same-named `rowActions`
(`ObjectGridSchema`, `z.array(z.string())`) is a different key, is in parity with
its own TS twin, and is pinned here as separate rather than touched.
The #5684 mirror-parity ratchet was NOT blind to this drift: it had measured and
ledgered it as `KnownDrift['data-display.zod.ts#DataTableSchema']`, noting "one
of the two is dead; which is a ruling". That ledger fails on a repair exactly as
it fails on new drift, so correcting the entry to its measured four remaining
keys is part of this change, not a widening of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

needs:contract-review applied — Clause ② yes. ⛔ Not reviewed, not enqueued by this seat.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC, which dispatched #6940. Applying the gate label I said I would in the dispatch brief, before the dev's report is in, because a Clause-② PR carrying no gate marker is invisible to the review queue — the exact state I flagged on PR #7432 twenty minutes ago.

This PR narrows what safeValidateSchema accepts on a published package (DataTableSchema.rowActions: z.arrayz.boolean().optional()), per the ruling on #6940 (5509029916, summon #8, maintainer verbatim 「7189 A 其他同意」, option A). It stays draft; this seat runs opus, is not at CONTRACT_REVIEW_TIER, and will not self-review, enqueue or auto-merge it.

Two fences verified by this seat before the report arrived

Both are traps the card names explicitly, and both held:

  • packages/types/src/zod/objectql.zod.ts is byte-identical to main — compared by blob hash. That file carries a different key also spelled rowActions (z.array(z.string()), the list view's legacy bare-name action list), which the ruling says is correct as it stands. Not touched.
  • user-table.json and full-featured-table.json are unchanged, as the ruling required — they "validate again unchanged" rather than being edited into passing.

⚠️ A fence I set was wrong, and the dev was right to cross it

My dispatch brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as ⛔ do-not-touch, held by PR #7432. This PR edits it, and that edit is forced by the fix rather than optional. The dev's own comment in the diff states the mechanism:

this ledger fails on a repair exactly as it fails on new drift, which is why correcting this line was part of that change and not optional.

rowActions was listed in that file's KnownDrift ledger as a disjoint pair. Once the mirror is repaired the pair is in parity, and a ledger still asserting drift on a parity key fails. Leaving the file untouched to honour my fence would have shipped a red PR. ⇒ The fence was mine and it was wrong; nothing here needs reverting.

Conflict risk with PR #7432 — measured, and it is prose only

Both PRs edit that file, so I checked the actual hunks rather than assuming:

regions touched
this PR~L57 (a count comment), ~L732-754 (the DataTableSchemaKnownDrift entry)
#7432~L131, ~L147, ~L354, ~L521, ~L1483 — all blocks.zod.ts imports, MIRRORS/Declared entries and one EXCLUSIONS row

Disjoint. A textual merge should apply cleanly in either order.

The one residue is prose, not a gate: this PR's header line reads "37 entries52 keys", and #7432 removes nine blocks.zod.ts mirrors, so whichever lands second leaves that count stale. Measured that this cannot redden CI — the ledger's entry/key counts are documentation, with no Object.keys(KnownDrift).length assertion anywhere (control: the file contains 13 expect( calls, so the search instrument fires). Only the MIRRORS population is derived and checked, and #7432 removes from both halves so they still agree.

⇒ No action needed from either PR. Recorded so that a stale count found later reads as known prose rot rather than a missed conflict — the class objectui#7433 tracks.

Full review follows once the dev's structured report is in, including the ruling's rider (why #5684's mirror-parity ratchet did not catch a boolean declaration against an array mirror).


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.5 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-Bvw7UhG5.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)515.05KB117.52KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.14KB13.02KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head f17e08244. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6940 ruling (2026-09-02, 「7189 A 其他同意」, option A): DataTableSchema.rowActions is z.boolean().optional(), the one outlier brought to the six faces that already said boolean; option B (union) correctly not taken. The load-bearing pin is the refusal of [] with every issue on rowActions, and it is measured at the published safeValidateSchema union too; the same-named ObjectGridSchema.rowActions (string[]) is pinned as a different key. The KnownDrift edit is the ratchet's toll for a repair, not scope widening, and the rider's finding — the ratchet was not blind, it ledgered the question — is accepted.

Semver / changeset.@object-ui/typespatch, with the ruling's own reasoning: the narrowing removes a spelling that was accepted but inert (the renderer only truthiness-tests the key; [] rendered as true), and the consumer sweep finds zero data-table documents authoring an array. Correct.

Boundary flags. No governed path. Same-line collision with #7468 on the zod-mirror-parity.test.ts header (53 → 52 here, 53 → 56 there); whichever merges second is ejected as dirty and the seat re-derives (expected 39 entries / 55 keys).

Landing. Carrier cleared, ready, auto-merge (SQUASH). All checks green on f17e08244.


Generated by Claude Code

…counting
The merge queue dequeued PR #7447 after #7344 (PR #7468) landed. Both changes
touch the same ledger; the conflict is in two places and neither side was
correct on its own.
1. The KnownDrift docblock region. main added a real new entry from #7344
('crud.zod.ts#DetailSchema': 'onBack') in the same lines where this branch
rewrote the rowActions prose. BOTH are kept: main's entry with its own
docblock, then this branch's rewritten rowActions paragraph documenting
DataTableSchema, whose union is now the measured four keys.
2. The header census — un-enforced prose, so re-derived BY COUNTING the merged
interface rather than by taking a side:
KnownDrift 39 entries / 55 keys (main's 39/56 less the repaired
rowActions; entry count unmoved)
UnmirroredDeclared 17 entries / 98 keys (untouched by this change)
RuntimeOnlyDeclared 7 entries / 24 keys
160 - 39 = 121 pairs with no KnownDrift entry
The "no entry in either" figure was also re-derived: |U u R| = 18, so
160 - 18 = 142, which is what the file already said — checked rather than
assumed, because the same figure was silently stale in the sibling merge.
Verified: pnpm --filter @object-ui/types run type-check exit 0, 0 'error TS'
(script name echoed in the log, so not a zero-match filter); pnpm exec vitest
run packages/types/ -> Test Files 94 passed, Tests 1601 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge conflict resolved — main merged in, census re-derived by counting

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. This PR left the queue without merging after #7468 (#7344) landed; mergeable_state was dirty. Pushed f17e08244..92db9ebc9 — a merge commit, ⛔ no rebase, amend or force-push.

⚠️ Two conflicts, and the second was data, not just prose:

1. The KnownDrift docblock region.#7344 added a real new entry — 'crud.zod.ts#DetailSchema': 'onBack' — in the very lines where this branch rewrote the rowActions paragraph. Both are kept: main's entry with its own docblock, then this branch's rewritten paragraph documenting DataTableSchema, whose union is the measured four keys (onRowEdit | onRowDelete | onSelectionChange | onColumnsReorder). Taking either side alone would have silently dropped a ledger entry or resurrected a repaired one.

2. The header census — un-enforced prose, so counted from the merged interface rather than chosen:

ledgerentrieskeys
KnownDrift3955
UnmirroredDeclared1798
RuntimeOnlyDeclared724

39 / 55 is main's 39 / 56 less the one key this PR repaired — the entry count correctly does not move, which is what this PR's own body predicted. 160 − 39 = 121 was already right.

⭐ The "no entry in either" figure was checked rather than assumed: |U ∪ R| = 18, so 160 − 18 = 142, which is what the file already said. Worth stating because in the sibling merge (#7464) that same figure had gone stale and git flagged nothing — it merged cleanly since only one side touched it. Here it was genuinely still correct.

Verified before pushing

  • pnpm --filter @object-ui/types run type-check → exit 0, 0error TS, with > @object-ui/types@17.6.0 type-check echoed in the log (not the zero-match filter trap).
  • pnpm exec vitest run packages/types/Test Files 94 passed, Tests 1601 passed.

⛔ Not re-enqueued and auto-merge untouched — the landing is the director seat's. Nothing in the contract review changed; only the merge with main.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.9 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-RyaXrnmX.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.24KB117.82KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataTableSchema.rowActions is z.array in the zod mirror and boolean everywhere else — the mirror is the outlier

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940) - #7447

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror
Sep 3, 2026
Merged

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940)#7447
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#6940

DataTableSchema.rowActions in packages/types/src/zod/data-display.zod.ts becomes
z.boolean().optional(). Maintainer ruling 2026-09-02 (director seat, summon #8,
verbatim 「7189 A 其他同意」), option A. Option B (a boolean | array union) was
not taken: it would permanently accept a shape the renderer only truthiness-tests.

- rowActions: z.array(z.any()).optional().describe('Row action buttons'),+ rowActions: z.boolean().optional().describe('Show the row actions column (edit/delete) — mirrors the boolean the renderer truthiness-tests (objectui#6940)'),

The mirror was the single outlier and the published one, so safeValidateSchema refused
the exact spelling the component's TS declaration, destructuring default, truthiness
gates, colSpan arithmetic, registered type: 'boolean' authoring input, defaultProps
and docblock example all teach.

⚠️ Two different keys are named rowActions — confirmed distinguished

Only the data-table one moved. The list view's key is untouched, and git diff proves it.

key changed herekey NOT touched
mirrorzod/data-display.zod.tsDataTableSchemazod/objectql.zod.tsObjectGridSchema
was / isz.array(z.any()) to z.boolean()z.array(z.string()), unchanged
TS twindata-display.tsrowActions?: booleanobjectql.tsrowActions?: string[]
ratchetdrift was ledgered in KnownDriftin parity, ledgered in neither

The separation is now pinned, so a later sweep that "harmonises the two rowActions" on
the strength of a shared name turns red here instead of silently retyping a key no ruling
covers. git diff --name-only -- packages/types/src/zod/objectql.zod.ts returns empty.

The rider — the ratchet was NOT blind, and this is the measurement

#5684's mirror-parity ratchet did catch this drift. It measured it and recorded it in
its KnownDrift ledger:

'data-display.zod.ts#DataTableSchema': 'rowActions' | 'onRowEdit' | 'onRowDelete' | 'onSelectionChange' | 'onColumnsReorder';

with an inline note that named the exact question this card later answered — TS declares
rowActions?: boolean, the mirror declared any[], "One of the two is dead; which is a
ruling."
The ratchet's header records the same entry surviving #5927's sweep
("DataTableSchema kept rowActions").

So there is no structural gap, and there is nothing to file. The ratchet's design is
to convert a drift it cannot adjudicate into a ledgered, citable question rather than a
silent pass. It did exactly that; the question sat in the ledger until the maintainer
answered it on 2026-09-02.

⚠️ Deviation from the dispatch brief — one line in a "do not touch" file

The brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as held by open
PR #7432. This PR edits it anyway, and the reason is mechanical rather than
discretionary — flagging it explicitly for the reviewer.

That ledger is bidirectional by design: "the entry fails when new drift appears on that
mirror AND when the recorded drift is fixed — a stale entry cannot rot quietly."

Repairing rowActions therefore turned the package's own gate red:

src/__tests__/zod-mirror-parity.test.ts(1349,14): error TS2322:
Type '"data-display.zod.ts#DataTableSchema"' is not assignable to type 'never'.

Correcting the entry to its measured remaining four keys is the toll the ratchet charges
for every drift repair — it is not "fixing the ratchet" and not a widening of scope.
Without it pnpm --filter @object-ui/types run type-check cannot pass and the PR is
unreviewable.

Collision with #7432 measured, not assumed: zero. Fetched pull/7432/head and
diffed. Every one of its hunks in this file is in the blocks.zod.ts region (lines
~132–148 imports, ~355 MIRRORS, ~522 Declared, ~1484 EXCLUSIONS), removing the nine
block pairs. This PR's edits are the KnownDrift entry near line 747, its docblock, and
the header key census near line 60 — disjoint regions, no textual conflict either way.

Changes are three: drop 'rowActions' from the union, rewrite the docblock paragraph
that would otherwise be a false statement sitting next to the change, and move the header
census from 53 keys to 52.

Tests

Pinned in packages/types/src/__tests__/data-table-declared-keys-6882.test.ts (the home
the ruling named), 7 new cases:

  1. rowActions: true validates.
  2. rowActions: false validates — it is a boolean, not a truthy-only flag.
  3. rowActions: [] is REFUSED — and every issue path is asserted to be
    rowActions, so a document rejected for an unrelated reason cannot read as a passing
    narrowing pin. This is the load-bearing half: a union would have satisfied a
    "true validates" assertion on its own.
  4. The published safeValidateSchema moves in both directions. Measured at this level
    rather than inferred, because it is a z.union and a sibling member accepting the
    document would have left the published surface unchanged.
  5. Both catalog fixtures exist on disk (guard against a vacuous pass).
  6. Both validate unchanged — they author rowActions: true and always did.
  7. The list view's rowActions still takes string[] and rejects true.

Red-then-green ablation

Reverted only the mirror fix, with the pin left in place.

  • Mutation proven on disk before the run: blob d1d427db to de3a6332, plus anchored
    grep counts (boolean spelling 1 to 0, array spelling 0 to 1). de3a6332 is exactly the
    pre-fix blob on origin/main.
  • Result: 5 of 8 failed, including the [] refusal and the safeValidateSchema case.
  • Control: the ObjectGrid separation test stayed green under the mutation, confirming
    the two same-named keys really are independent.
  • Restore proven by state, not by exit code:git checkout HEAD -- ... (absolute
    path, pinned to HEAD rather than the polluted index), then blob equality against the
    HEAD blob and an empty git diff HEAD. Green leg re-run: 8/8.
  • trap ... EXIT INT TERM with absolute paths throughout.
  • No build/dist leg is required or claimed: the pin imports '../zod/data-display.zod.js',
    a relative same-package specifier vitest resolves to TypeScript source, so no dist/
    copy can serve a stale mutation.

Gates, all on the final commit f17e08244

runresult
pnpm --filter @object-ui/types run type-checkgreen (was red before the ledger line)
pnpm exec vitest run packages/types/92 files, 1547 tests passed
pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/ examples/schema-catalog/162 files, 3209 tests passed
pnpm --filter @object-ui/types run lint0 errors (274 pre-existing warnings)
node scripts/check-control-bytes.mjsOK, 6131 files
node scripts/check-changeset-presence.mjs1 changeset declared
objectui check on the catalog✓ All checks passed

objectui check was run with the real built CLI, and the built dist was confirmed
to carry the change (rowActions: z.ZodOptional of ZodBoolean) rather than a stale
copy. user-table.json and full-featured-table.json no longer appear in its failing
bucket; the five entries that remain there are the components-basic-text ones held by a
separate in-flight PR.

Consumer sweep — the narrowing has no casualties

Grepped the repo for rowActions authored as an array. Every hit outside this PR's own
tests is the list-view/ObjectGrid key: p1-spec-alignment.test.ts (type: 'list-view'),
objectViewHostSurface.test.tsx, the plugin-grid legacy bare-name suite and
resolveLegacyRowActions, plugin-grid/README.md, schema-reference.md and
plugin-grid.mdx. Zero documents author an array rowActions on a data-table, which
measures the ruling's stated basis for a patch bump rather than assuming it.

Changeset

@object-ui/types, patch. The body states the reasoning the ruling requires: no
author can have relied on an array value, because the renderer never reads the array — it
only truthiness-tests the key, so even [] rendered identically to true (#6318 measured
42 elements either way, against 39 with the key absent). An array here could never have
carried meaning to any consumer, so the narrowing removes a spelling that was accepted but
inert.

Review status

Clause ② yes. Draft, carries needs:contract-review, not enqueued, not
auto-merged, not self-reviewed. Waits on the project director seat.


🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…#6940)
The hand-written zod mirror declared `rowActions: z.array(z.any()).optional()`
while every other face of the key says boolean — the TS declaration it mirrors,
the renderer's destructuring default, its truthiness gates and colSpan
arithmetic, the registered `type: 'boolean'` authoring input, `defaultProps`,
and the renderer's own docblock example. The mirror was the outlier and the
published one, so `safeValidateSchema` refused the spelling the component's
documentation, defaults and authoring UI all teach, and two shipped
schema-catalog entries failed validation for that and no other reason.
Maintainer ruling 2026-09-02 (director seat, summon #8), option A. Option B (a
`boolean | array` union) was not taken: it would permanently accept a shape the
renderer only truthiness-tests.
Pinned in data-table-declared-keys-6882.test.ts: `true` and `false` validate,
`[]` is REFUSED and refused ON `rowActions` (every issue path asserted, so a
document rejected for an unrelated reason cannot read as a passing narrowing),
the published `safeValidateSchema` surface moves in both directions, and the two
catalog entries validate unchanged. The list view's same-named `rowActions`
(`ObjectGridSchema`, `z.array(z.string())`) is a different key, is in parity with
its own TS twin, and is pinned here as separate rather than touched.
The #5684 mirror-parity ratchet was NOT blind to this drift: it had measured and
ledgered it as `KnownDrift['data-display.zod.ts#DataTableSchema']`, noting "one
of the two is dead; which is a ruling". That ledger fails on a repair exactly as
it fails on new drift, so correcting the entry to its measured four remaining
keys is part of this change, not a widening of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

needs:contract-review applied — Clause ② yes. ⛔ Not reviewed, not enqueued by this seat.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC, which dispatched #6940. Applying the gate label I said I would in the dispatch brief, before the dev's report is in, because a Clause-② PR carrying no gate marker is invisible to the review queue — the exact state I flagged on PR #7432 twenty minutes ago.

This PR narrows what safeValidateSchema accepts on a published package (DataTableSchema.rowActions: z.arrayz.boolean().optional()), per the ruling on #6940 (5509029916, summon #8, maintainer verbatim 「7189 A 其他同意」, option A). It stays draft; this seat runs opus, is not at CONTRACT_REVIEW_TIER, and will not self-review, enqueue or auto-merge it.

Two fences verified by this seat before the report arrived

Both are traps the card names explicitly, and both held:

  • packages/types/src/zod/objectql.zod.ts is byte-identical to main — compared by blob hash. That file carries a different key also spelled rowActions (z.array(z.string()), the list view's legacy bare-name action list), which the ruling says is correct as it stands. Not touched.
  • user-table.json and full-featured-table.json are unchanged, as the ruling required — they "validate again unchanged" rather than being edited into passing.

⚠️ A fence I set was wrong, and the dev was right to cross it

My dispatch brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as ⛔ do-not-touch, held by PR #7432. This PR edits it, and that edit is forced by the fix rather than optional. The dev's own comment in the diff states the mechanism:

this ledger fails on a repair exactly as it fails on new drift, which is why correcting this line was part of that change and not optional.

rowActions was listed in that file's KnownDrift ledger as a disjoint pair. Once the mirror is repaired the pair is in parity, and a ledger still asserting drift on a parity key fails. Leaving the file untouched to honour my fence would have shipped a red PR. ⇒ The fence was mine and it was wrong; nothing here needs reverting.

Conflict risk with PR #7432 — measured, and it is prose only

Both PRs edit that file, so I checked the actual hunks rather than assuming:

regions touched
this PR~L57 (a count comment), ~L732-754 (the DataTableSchemaKnownDrift entry)
#7432~L131, ~L147, ~L354, ~L521, ~L1483 — all blocks.zod.ts imports, MIRRORS/Declared entries and one EXCLUSIONS row

Disjoint. A textual merge should apply cleanly in either order.

The one residue is prose, not a gate: this PR's header line reads "37 entries52 keys", and #7432 removes nine blocks.zod.ts mirrors, so whichever lands second leaves that count stale. Measured that this cannot redden CI — the ledger's entry/key counts are documentation, with no Object.keys(KnownDrift).length assertion anywhere (control: the file contains 13 expect( calls, so the search instrument fires). Only the MIRRORS population is derived and checked, and #7432 removes from both halves so they still agree.

⇒ No action needed from either PR. Recorded so that a stale count found later reads as known prose rot rather than a missed conflict — the class objectui#7433 tracks.

Full review follows once the dev's structured report is in, including the ruling's rider (why #5684's mirror-parity ratchet did not catch a boolean declaration against an array mirror).


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.5 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-Bvw7UhG5.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)515.05KB117.52KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.14KB13.02KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head f17e08244. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6940 ruling (2026-09-02, 「7189 A 其他同意」, option A): DataTableSchema.rowActions is z.boolean().optional(), the one outlier brought to the six faces that already said boolean; option B (union) correctly not taken. The load-bearing pin is the refusal of [] with every issue on rowActions, and it is measured at the published safeValidateSchema union too; the same-named ObjectGridSchema.rowActions (string[]) is pinned as a different key. The KnownDrift edit is the ratchet's toll for a repair, not scope widening, and the rider's finding — the ratchet was not blind, it ledgered the question — is accepted.

Semver / changeset.@object-ui/typespatch, with the ruling's own reasoning: the narrowing removes a spelling that was accepted but inert (the renderer only truthiness-tests the key; [] rendered as true), and the consumer sweep finds zero data-table documents authoring an array. Correct.

Boundary flags. No governed path. Same-line collision with #7468 on the zod-mirror-parity.test.ts header (53 → 52 here, 53 → 56 there); whichever merges second is ejected as dirty and the seat re-derives (expected 39 entries / 55 keys).

Landing. Carrier cleared, ready, auto-merge (SQUASH). All checks green on f17e08244.


Generated by Claude Code

…counting
The merge queue dequeued PR #7447 after #7344 (PR #7468) landed. Both changes
touch the same ledger; the conflict is in two places and neither side was
correct on its own.
1. The KnownDrift docblock region. main added a real new entry from #7344
('crud.zod.ts#DetailSchema': 'onBack') in the same lines where this branch
rewrote the rowActions prose. BOTH are kept: main's entry with its own
docblock, then this branch's rewritten rowActions paragraph documenting
DataTableSchema, whose union is now the measured four keys.
2. The header census — un-enforced prose, so re-derived BY COUNTING the merged
interface rather than by taking a side:
KnownDrift 39 entries / 55 keys (main's 39/56 less the repaired
rowActions; entry count unmoved)
UnmirroredDeclared 17 entries / 98 keys (untouched by this change)
RuntimeOnlyDeclared 7 entries / 24 keys
160 - 39 = 121 pairs with no KnownDrift entry
The "no entry in either" figure was also re-derived: |U u R| = 18, so
160 - 18 = 142, which is what the file already said — checked rather than
assumed, because the same figure was silently stale in the sibling merge.
Verified: pnpm --filter @object-ui/types run type-check exit 0, 0 'error TS'
(script name echoed in the log, so not a zero-match filter); pnpm exec vitest
run packages/types/ -> Test Files 94 passed, Tests 1601 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge conflict resolved — main merged in, census re-derived by counting

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. This PR left the queue without merging after #7468 (#7344) landed; mergeable_state was dirty. Pushed f17e08244..92db9ebc9 — a merge commit, ⛔ no rebase, amend or force-push.

⚠️ Two conflicts, and the second was data, not just prose:

1. The KnownDrift docblock region.#7344 added a real new entry — 'crud.zod.ts#DetailSchema': 'onBack' — in the very lines where this branch rewrote the rowActions paragraph. Both are kept: main's entry with its own docblock, then this branch's rewritten paragraph documenting DataTableSchema, whose union is the measured four keys (onRowEdit | onRowDelete | onSelectionChange | onColumnsReorder). Taking either side alone would have silently dropped a ledger entry or resurrected a repaired one.

2. The header census — un-enforced prose, so counted from the merged interface rather than chosen:

ledgerentrieskeys
KnownDrift3955
UnmirroredDeclared1798
RuntimeOnlyDeclared724

39 / 55 is main's 39 / 56 less the one key this PR repaired — the entry count correctly does not move, which is what this PR's own body predicted. 160 − 39 = 121 was already right.

⭐ The "no entry in either" figure was checked rather than assumed: |U ∪ R| = 18, so 160 − 18 = 142, which is what the file already said. Worth stating because in the sibling merge (#7464) that same figure had gone stale and git flagged nothing — it merged cleanly since only one side touched it. Here it was genuinely still correct.

Verified before pushing

  • pnpm --filter @object-ui/types run type-check → exit 0, 0error TS, with > @object-ui/types@17.6.0 type-check echoed in the log (not the zero-match filter trap).
  • pnpm exec vitest run packages/types/Test Files 94 passed, Tests 1601 passed.

⛔ Not re-enqueued and auto-merge untouched — the landing is the director seat's. Nothing in the contract review changed; only the merge with main.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.9 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-RyaXrnmX.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.24KB117.82KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataTableSchema.rowActions is z.array in the zod mirror and boolean everywhere else — the mirror is the outlier

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940) - #7447

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror
Sep 3, 2026
Merged

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940)#7447
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#6940

DataTableSchema.rowActions in packages/types/src/zod/data-display.zod.ts becomes
z.boolean().optional(). Maintainer ruling 2026-09-02 (director seat, summon #8,
verbatim 「7189 A 其他同意」), option A. Option B (a boolean | array union) was
not taken: it would permanently accept a shape the renderer only truthiness-tests.

- rowActions: z.array(z.any()).optional().describe('Row action buttons'),+ rowActions: z.boolean().optional().describe('Show the row actions column (edit/delete) — mirrors the boolean the renderer truthiness-tests (objectui#6940)'),

The mirror was the single outlier and the published one, so safeValidateSchema refused
the exact spelling the component's TS declaration, destructuring default, truthiness
gates, colSpan arithmetic, registered type: 'boolean' authoring input, defaultProps
and docblock example all teach.

⚠️ Two different keys are named rowActions — confirmed distinguished

Only the data-table one moved. The list view's key is untouched, and git diff proves it.

key changed herekey NOT touched
mirrorzod/data-display.zod.tsDataTableSchemazod/objectql.zod.tsObjectGridSchema
was / isz.array(z.any()) to z.boolean()z.array(z.string()), unchanged
TS twindata-display.tsrowActions?: booleanobjectql.tsrowActions?: string[]
ratchetdrift was ledgered in KnownDriftin parity, ledgered in neither

The separation is now pinned, so a later sweep that "harmonises the two rowActions" on
the strength of a shared name turns red here instead of silently retyping a key no ruling
covers. git diff --name-only -- packages/types/src/zod/objectql.zod.ts returns empty.

The rider — the ratchet was NOT blind, and this is the measurement

#5684's mirror-parity ratchet did catch this drift. It measured it and recorded it in
its KnownDrift ledger:

'data-display.zod.ts#DataTableSchema': 'rowActions' | 'onRowEdit' | 'onRowDelete' | 'onSelectionChange' | 'onColumnsReorder';

with an inline note that named the exact question this card later answered — TS declares
rowActions?: boolean, the mirror declared any[], "One of the two is dead; which is a
ruling."
The ratchet's header records the same entry surviving #5927's sweep
("DataTableSchema kept rowActions").

So there is no structural gap, and there is nothing to file. The ratchet's design is
to convert a drift it cannot adjudicate into a ledgered, citable question rather than a
silent pass. It did exactly that; the question sat in the ledger until the maintainer
answered it on 2026-09-02.

⚠️ Deviation from the dispatch brief — one line in a "do not touch" file

The brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as held by open
PR #7432. This PR edits it anyway, and the reason is mechanical rather than
discretionary — flagging it explicitly for the reviewer.

That ledger is bidirectional by design: "the entry fails when new drift appears on that
mirror AND when the recorded drift is fixed — a stale entry cannot rot quietly."

Repairing rowActions therefore turned the package's own gate red:

src/__tests__/zod-mirror-parity.test.ts(1349,14): error TS2322:
Type '"data-display.zod.ts#DataTableSchema"' is not assignable to type 'never'.

Correcting the entry to its measured remaining four keys is the toll the ratchet charges
for every drift repair — it is not "fixing the ratchet" and not a widening of scope.
Without it pnpm --filter @object-ui/types run type-check cannot pass and the PR is
unreviewable.

Collision with #7432 measured, not assumed: zero. Fetched pull/7432/head and
diffed. Every one of its hunks in this file is in the blocks.zod.ts region (lines
~132–148 imports, ~355 MIRRORS, ~522 Declared, ~1484 EXCLUSIONS), removing the nine
block pairs. This PR's edits are the KnownDrift entry near line 747, its docblock, and
the header key census near line 60 — disjoint regions, no textual conflict either way.

Changes are three: drop 'rowActions' from the union, rewrite the docblock paragraph
that would otherwise be a false statement sitting next to the change, and move the header
census from 53 keys to 52.

Tests

Pinned in packages/types/src/__tests__/data-table-declared-keys-6882.test.ts (the home
the ruling named), 7 new cases:

  1. rowActions: true validates.
  2. rowActions: false validates — it is a boolean, not a truthy-only flag.
  3. rowActions: [] is REFUSED — and every issue path is asserted to be
    rowActions, so a document rejected for an unrelated reason cannot read as a passing
    narrowing pin. This is the load-bearing half: a union would have satisfied a
    "true validates" assertion on its own.
  4. The published safeValidateSchema moves in both directions. Measured at this level
    rather than inferred, because it is a z.union and a sibling member accepting the
    document would have left the published surface unchanged.
  5. Both catalog fixtures exist on disk (guard against a vacuous pass).
  6. Both validate unchanged — they author rowActions: true and always did.
  7. The list view's rowActions still takes string[] and rejects true.

Red-then-green ablation

Reverted only the mirror fix, with the pin left in place.

  • Mutation proven on disk before the run: blob d1d427db to de3a6332, plus anchored
    grep counts (boolean spelling 1 to 0, array spelling 0 to 1). de3a6332 is exactly the
    pre-fix blob on origin/main.
  • Result: 5 of 8 failed, including the [] refusal and the safeValidateSchema case.
  • Control: the ObjectGrid separation test stayed green under the mutation, confirming
    the two same-named keys really are independent.
  • Restore proven by state, not by exit code:git checkout HEAD -- ... (absolute
    path, pinned to HEAD rather than the polluted index), then blob equality against the
    HEAD blob and an empty git diff HEAD. Green leg re-run: 8/8.
  • trap ... EXIT INT TERM with absolute paths throughout.
  • No build/dist leg is required or claimed: the pin imports '../zod/data-display.zod.js',
    a relative same-package specifier vitest resolves to TypeScript source, so no dist/
    copy can serve a stale mutation.

Gates, all on the final commit f17e08244

runresult
pnpm --filter @object-ui/types run type-checkgreen (was red before the ledger line)
pnpm exec vitest run packages/types/92 files, 1547 tests passed
pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/ examples/schema-catalog/162 files, 3209 tests passed
pnpm --filter @object-ui/types run lint0 errors (274 pre-existing warnings)
node scripts/check-control-bytes.mjsOK, 6131 files
node scripts/check-changeset-presence.mjs1 changeset declared
objectui check on the catalog✓ All checks passed

objectui check was run with the real built CLI, and the built dist was confirmed
to carry the change (rowActions: z.ZodOptional of ZodBoolean) rather than a stale
copy. user-table.json and full-featured-table.json no longer appear in its failing
bucket; the five entries that remain there are the components-basic-text ones held by a
separate in-flight PR.

Consumer sweep — the narrowing has no casualties

Grepped the repo for rowActions authored as an array. Every hit outside this PR's own
tests is the list-view/ObjectGrid key: p1-spec-alignment.test.ts (type: 'list-view'),
objectViewHostSurface.test.tsx, the plugin-grid legacy bare-name suite and
resolveLegacyRowActions, plugin-grid/README.md, schema-reference.md and
plugin-grid.mdx. Zero documents author an array rowActions on a data-table, which
measures the ruling's stated basis for a patch bump rather than assuming it.

Changeset

@object-ui/types, patch. The body states the reasoning the ruling requires: no
author can have relied on an array value, because the renderer never reads the array — it
only truthiness-tests the key, so even [] rendered identically to true (#6318 measured
42 elements either way, against 39 with the key absent). An array here could never have
carried meaning to any consumer, so the narrowing removes a spelling that was accepted but
inert.

Review status

Clause ② yes. Draft, carries needs:contract-review, not enqueued, not
auto-merged, not self-reviewed. Waits on the project director seat.


🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…#6940)
The hand-written zod mirror declared `rowActions: z.array(z.any()).optional()`
while every other face of the key says boolean — the TS declaration it mirrors,
the renderer's destructuring default, its truthiness gates and colSpan
arithmetic, the registered `type: 'boolean'` authoring input, `defaultProps`,
and the renderer's own docblock example. The mirror was the outlier and the
published one, so `safeValidateSchema` refused the spelling the component's
documentation, defaults and authoring UI all teach, and two shipped
schema-catalog entries failed validation for that and no other reason.
Maintainer ruling 2026-09-02 (director seat, summon #8), option A. Option B (a
`boolean | array` union) was not taken: it would permanently accept a shape the
renderer only truthiness-tests.
Pinned in data-table-declared-keys-6882.test.ts: `true` and `false` validate,
`[]` is REFUSED and refused ON `rowActions` (every issue path asserted, so a
document rejected for an unrelated reason cannot read as a passing narrowing),
the published `safeValidateSchema` surface moves in both directions, and the two
catalog entries validate unchanged. The list view's same-named `rowActions`
(`ObjectGridSchema`, `z.array(z.string())`) is a different key, is in parity with
its own TS twin, and is pinned here as separate rather than touched.
The #5684 mirror-parity ratchet was NOT blind to this drift: it had measured and
ledgered it as `KnownDrift['data-display.zod.ts#DataTableSchema']`, noting "one
of the two is dead; which is a ruling". That ledger fails on a repair exactly as
it fails on new drift, so correcting the entry to its measured four remaining
keys is part of this change, not a widening of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

needs:contract-review applied — Clause ② yes. ⛔ Not reviewed, not enqueued by this seat.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC, which dispatched #6940. Applying the gate label I said I would in the dispatch brief, before the dev's report is in, because a Clause-② PR carrying no gate marker is invisible to the review queue — the exact state I flagged on PR #7432 twenty minutes ago.

This PR narrows what safeValidateSchema accepts on a published package (DataTableSchema.rowActions: z.arrayz.boolean().optional()), per the ruling on #6940 (5509029916, summon #8, maintainer verbatim 「7189 A 其他同意」, option A). It stays draft; this seat runs opus, is not at CONTRACT_REVIEW_TIER, and will not self-review, enqueue or auto-merge it.

Two fences verified by this seat before the report arrived

Both are traps the card names explicitly, and both held:

  • packages/types/src/zod/objectql.zod.ts is byte-identical to main — compared by blob hash. That file carries a different key also spelled rowActions (z.array(z.string()), the list view's legacy bare-name action list), which the ruling says is correct as it stands. Not touched.
  • user-table.json and full-featured-table.json are unchanged, as the ruling required — they "validate again unchanged" rather than being edited into passing.

⚠️ A fence I set was wrong, and the dev was right to cross it

My dispatch brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as ⛔ do-not-touch, held by PR #7432. This PR edits it, and that edit is forced by the fix rather than optional. The dev's own comment in the diff states the mechanism:

this ledger fails on a repair exactly as it fails on new drift, which is why correcting this line was part of that change and not optional.

rowActions was listed in that file's KnownDrift ledger as a disjoint pair. Once the mirror is repaired the pair is in parity, and a ledger still asserting drift on a parity key fails. Leaving the file untouched to honour my fence would have shipped a red PR. ⇒ The fence was mine and it was wrong; nothing here needs reverting.

Conflict risk with PR #7432 — measured, and it is prose only

Both PRs edit that file, so I checked the actual hunks rather than assuming:

regions touched
this PR~L57 (a count comment), ~L732-754 (the DataTableSchemaKnownDrift entry)
#7432~L131, ~L147, ~L354, ~L521, ~L1483 — all blocks.zod.ts imports, MIRRORS/Declared entries and one EXCLUSIONS row

Disjoint. A textual merge should apply cleanly in either order.

The one residue is prose, not a gate: this PR's header line reads "37 entries52 keys", and #7432 removes nine blocks.zod.ts mirrors, so whichever lands second leaves that count stale. Measured that this cannot redden CI — the ledger's entry/key counts are documentation, with no Object.keys(KnownDrift).length assertion anywhere (control: the file contains 13 expect( calls, so the search instrument fires). Only the MIRRORS population is derived and checked, and #7432 removes from both halves so they still agree.

⇒ No action needed from either PR. Recorded so that a stale count found later reads as known prose rot rather than a missed conflict — the class objectui#7433 tracks.

Full review follows once the dev's structured report is in, including the ruling's rider (why #5684's mirror-parity ratchet did not catch a boolean declaration against an array mirror).


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.5 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-Bvw7UhG5.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)515.05KB117.52KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.14KB13.02KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head f17e08244. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6940 ruling (2026-09-02, 「7189 A 其他同意」, option A): DataTableSchema.rowActions is z.boolean().optional(), the one outlier brought to the six faces that already said boolean; option B (union) correctly not taken. The load-bearing pin is the refusal of [] with every issue on rowActions, and it is measured at the published safeValidateSchema union too; the same-named ObjectGridSchema.rowActions (string[]) is pinned as a different key. The KnownDrift edit is the ratchet's toll for a repair, not scope widening, and the rider's finding — the ratchet was not blind, it ledgered the question — is accepted.

Semver / changeset.@object-ui/typespatch, with the ruling's own reasoning: the narrowing removes a spelling that was accepted but inert (the renderer only truthiness-tests the key; [] rendered as true), and the consumer sweep finds zero data-table documents authoring an array. Correct.

Boundary flags. No governed path. Same-line collision with #7468 on the zod-mirror-parity.test.ts header (53 → 52 here, 53 → 56 there); whichever merges second is ejected as dirty and the seat re-derives (expected 39 entries / 55 keys).

Landing. Carrier cleared, ready, auto-merge (SQUASH). All checks green on f17e08244.


Generated by Claude Code

…counting
The merge queue dequeued PR #7447 after #7344 (PR #7468) landed. Both changes
touch the same ledger; the conflict is in two places and neither side was
correct on its own.
1. The KnownDrift docblock region. main added a real new entry from #7344
('crud.zod.ts#DetailSchema': 'onBack') in the same lines where this branch
rewrote the rowActions prose. BOTH are kept: main's entry with its own
docblock, then this branch's rewritten rowActions paragraph documenting
DataTableSchema, whose union is now the measured four keys.
2. The header census — un-enforced prose, so re-derived BY COUNTING the merged
interface rather than by taking a side:
KnownDrift 39 entries / 55 keys (main's 39/56 less the repaired
rowActions; entry count unmoved)
UnmirroredDeclared 17 entries / 98 keys (untouched by this change)
RuntimeOnlyDeclared 7 entries / 24 keys
160 - 39 = 121 pairs with no KnownDrift entry
The "no entry in either" figure was also re-derived: |U u R| = 18, so
160 - 18 = 142, which is what the file already said — checked rather than
assumed, because the same figure was silently stale in the sibling merge.
Verified: pnpm --filter @object-ui/types run type-check exit 0, 0 'error TS'
(script name echoed in the log, so not a zero-match filter); pnpm exec vitest
run packages/types/ -> Test Files 94 passed, Tests 1601 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge conflict resolved — main merged in, census re-derived by counting

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. This PR left the queue without merging after #7468 (#7344) landed; mergeable_state was dirty. Pushed f17e08244..92db9ebc9 — a merge commit, ⛔ no rebase, amend or force-push.

⚠️ Two conflicts, and the second was data, not just prose:

1. The KnownDrift docblock region.#7344 added a real new entry — 'crud.zod.ts#DetailSchema': 'onBack' — in the very lines where this branch rewrote the rowActions paragraph. Both are kept: main's entry with its own docblock, then this branch's rewritten paragraph documenting DataTableSchema, whose union is the measured four keys (onRowEdit | onRowDelete | onSelectionChange | onColumnsReorder). Taking either side alone would have silently dropped a ledger entry or resurrected a repaired one.

2. The header census — un-enforced prose, so counted from the merged interface rather than chosen:

ledgerentrieskeys
KnownDrift3955
UnmirroredDeclared1798
RuntimeOnlyDeclared724

39 / 55 is main's 39 / 56 less the one key this PR repaired — the entry count correctly does not move, which is what this PR's own body predicted. 160 − 39 = 121 was already right.

⭐ The "no entry in either" figure was checked rather than assumed: |U ∪ R| = 18, so 160 − 18 = 142, which is what the file already said. Worth stating because in the sibling merge (#7464) that same figure had gone stale and git flagged nothing — it merged cleanly since only one side touched it. Here it was genuinely still correct.

Verified before pushing

  • pnpm --filter @object-ui/types run type-check → exit 0, 0error TS, with > @object-ui/types@17.6.0 type-check echoed in the log (not the zero-match filter trap).
  • pnpm exec vitest run packages/types/Test Files 94 passed, Tests 1601 passed.

⛔ Not re-enqueued and auto-merge untouched — the landing is the director seat's. Nothing in the contract review changed; only the merge with main.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.9 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-RyaXrnmX.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.24KB117.82KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataTableSchema.rowActions is z.array in the zod mirror and boolean everywhere else — the mirror is the outlier

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940) - #7447

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror
Sep 3, 2026
Merged

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940)#7447
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#6940

DataTableSchema.rowActions in packages/types/src/zod/data-display.zod.ts becomes
z.boolean().optional(). Maintainer ruling 2026-09-02 (director seat, summon #8,
verbatim 「7189 A 其他同意」), option A. Option B (a boolean | array union) was
not taken: it would permanently accept a shape the renderer only truthiness-tests.

- rowActions: z.array(z.any()).optional().describe('Row action buttons'),+ rowActions: z.boolean().optional().describe('Show the row actions column (edit/delete) — mirrors the boolean the renderer truthiness-tests (objectui#6940)'),

The mirror was the single outlier and the published one, so safeValidateSchema refused
the exact spelling the component's TS declaration, destructuring default, truthiness
gates, colSpan arithmetic, registered type: 'boolean' authoring input, defaultProps
and docblock example all teach.

⚠️ Two different keys are named rowActions — confirmed distinguished

Only the data-table one moved. The list view's key is untouched, and git diff proves it.

key changed herekey NOT touched
mirrorzod/data-display.zod.tsDataTableSchemazod/objectql.zod.tsObjectGridSchema
was / isz.array(z.any()) to z.boolean()z.array(z.string()), unchanged
TS twindata-display.tsrowActions?: booleanobjectql.tsrowActions?: string[]
ratchetdrift was ledgered in KnownDriftin parity, ledgered in neither

The separation is now pinned, so a later sweep that "harmonises the two rowActions" on
the strength of a shared name turns red here instead of silently retyping a key no ruling
covers. git diff --name-only -- packages/types/src/zod/objectql.zod.ts returns empty.

The rider — the ratchet was NOT blind, and this is the measurement

#5684's mirror-parity ratchet did catch this drift. It measured it and recorded it in
its KnownDrift ledger:

'data-display.zod.ts#DataTableSchema': 'rowActions' | 'onRowEdit' | 'onRowDelete' | 'onSelectionChange' | 'onColumnsReorder';

with an inline note that named the exact question this card later answered — TS declares
rowActions?: boolean, the mirror declared any[], "One of the two is dead; which is a
ruling."
The ratchet's header records the same entry surviving #5927's sweep
("DataTableSchema kept rowActions").

So there is no structural gap, and there is nothing to file. The ratchet's design is
to convert a drift it cannot adjudicate into a ledgered, citable question rather than a
silent pass. It did exactly that; the question sat in the ledger until the maintainer
answered it on 2026-09-02.

⚠️ Deviation from the dispatch brief — one line in a "do not touch" file

The brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as held by open
PR #7432. This PR edits it anyway, and the reason is mechanical rather than
discretionary — flagging it explicitly for the reviewer.

That ledger is bidirectional by design: "the entry fails when new drift appears on that
mirror AND when the recorded drift is fixed — a stale entry cannot rot quietly."

Repairing rowActions therefore turned the package's own gate red:

src/__tests__/zod-mirror-parity.test.ts(1349,14): error TS2322:
Type '"data-display.zod.ts#DataTableSchema"' is not assignable to type 'never'.

Correcting the entry to its measured remaining four keys is the toll the ratchet charges
for every drift repair — it is not "fixing the ratchet" and not a widening of scope.
Without it pnpm --filter @object-ui/types run type-check cannot pass and the PR is
unreviewable.

Collision with #7432 measured, not assumed: zero. Fetched pull/7432/head and
diffed. Every one of its hunks in this file is in the blocks.zod.ts region (lines
~132–148 imports, ~355 MIRRORS, ~522 Declared, ~1484 EXCLUSIONS), removing the nine
block pairs. This PR's edits are the KnownDrift entry near line 747, its docblock, and
the header key census near line 60 — disjoint regions, no textual conflict either way.

Changes are three: drop 'rowActions' from the union, rewrite the docblock paragraph
that would otherwise be a false statement sitting next to the change, and move the header
census from 53 keys to 52.

Tests

Pinned in packages/types/src/__tests__/data-table-declared-keys-6882.test.ts (the home
the ruling named), 7 new cases:

  1. rowActions: true validates.
  2. rowActions: false validates — it is a boolean, not a truthy-only flag.
  3. rowActions: [] is REFUSED — and every issue path is asserted to be
    rowActions, so a document rejected for an unrelated reason cannot read as a passing
    narrowing pin. This is the load-bearing half: a union would have satisfied a
    "true validates" assertion on its own.
  4. The published safeValidateSchema moves in both directions. Measured at this level
    rather than inferred, because it is a z.union and a sibling member accepting the
    document would have left the published surface unchanged.
  5. Both catalog fixtures exist on disk (guard against a vacuous pass).
  6. Both validate unchanged — they author rowActions: true and always did.
  7. The list view's rowActions still takes string[] and rejects true.

Red-then-green ablation

Reverted only the mirror fix, with the pin left in place.

  • Mutation proven on disk before the run: blob d1d427db to de3a6332, plus anchored
    grep counts (boolean spelling 1 to 0, array spelling 0 to 1). de3a6332 is exactly the
    pre-fix blob on origin/main.
  • Result: 5 of 8 failed, including the [] refusal and the safeValidateSchema case.
  • Control: the ObjectGrid separation test stayed green under the mutation, confirming
    the two same-named keys really are independent.
  • Restore proven by state, not by exit code:git checkout HEAD -- ... (absolute
    path, pinned to HEAD rather than the polluted index), then blob equality against the
    HEAD blob and an empty git diff HEAD. Green leg re-run: 8/8.
  • trap ... EXIT INT TERM with absolute paths throughout.
  • No build/dist leg is required or claimed: the pin imports '../zod/data-display.zod.js',
    a relative same-package specifier vitest resolves to TypeScript source, so no dist/
    copy can serve a stale mutation.

Gates, all on the final commit f17e08244

runresult
pnpm --filter @object-ui/types run type-checkgreen (was red before the ledger line)
pnpm exec vitest run packages/types/92 files, 1547 tests passed
pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/ examples/schema-catalog/162 files, 3209 tests passed
pnpm --filter @object-ui/types run lint0 errors (274 pre-existing warnings)
node scripts/check-control-bytes.mjsOK, 6131 files
node scripts/check-changeset-presence.mjs1 changeset declared
objectui check on the catalog✓ All checks passed

objectui check was run with the real built CLI, and the built dist was confirmed
to carry the change (rowActions: z.ZodOptional of ZodBoolean) rather than a stale
copy. user-table.json and full-featured-table.json no longer appear in its failing
bucket; the five entries that remain there are the components-basic-text ones held by a
separate in-flight PR.

Consumer sweep — the narrowing has no casualties

Grepped the repo for rowActions authored as an array. Every hit outside this PR's own
tests is the list-view/ObjectGrid key: p1-spec-alignment.test.ts (type: 'list-view'),
objectViewHostSurface.test.tsx, the plugin-grid legacy bare-name suite and
resolveLegacyRowActions, plugin-grid/README.md, schema-reference.md and
plugin-grid.mdx. Zero documents author an array rowActions on a data-table, which
measures the ruling's stated basis for a patch bump rather than assuming it.

Changeset

@object-ui/types, patch. The body states the reasoning the ruling requires: no
author can have relied on an array value, because the renderer never reads the array — it
only truthiness-tests the key, so even [] rendered identically to true (#6318 measured
42 elements either way, against 39 with the key absent). An array here could never have
carried meaning to any consumer, so the narrowing removes a spelling that was accepted but
inert.

Review status

Clause ② yes. Draft, carries needs:contract-review, not enqueued, not
auto-merged, not self-reviewed. Waits on the project director seat.


🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…#6940)
The hand-written zod mirror declared `rowActions: z.array(z.any()).optional()`
while every other face of the key says boolean — the TS declaration it mirrors,
the renderer's destructuring default, its truthiness gates and colSpan
arithmetic, the registered `type: 'boolean'` authoring input, `defaultProps`,
and the renderer's own docblock example. The mirror was the outlier and the
published one, so `safeValidateSchema` refused the spelling the component's
documentation, defaults and authoring UI all teach, and two shipped
schema-catalog entries failed validation for that and no other reason.
Maintainer ruling 2026-09-02 (director seat, summon #8), option A. Option B (a
`boolean | array` union) was not taken: it would permanently accept a shape the
renderer only truthiness-tests.
Pinned in data-table-declared-keys-6882.test.ts: `true` and `false` validate,
`[]` is REFUSED and refused ON `rowActions` (every issue path asserted, so a
document rejected for an unrelated reason cannot read as a passing narrowing),
the published `safeValidateSchema` surface moves in both directions, and the two
catalog entries validate unchanged. The list view's same-named `rowActions`
(`ObjectGridSchema`, `z.array(z.string())`) is a different key, is in parity with
its own TS twin, and is pinned here as separate rather than touched.
The #5684 mirror-parity ratchet was NOT blind to this drift: it had measured and
ledgered it as `KnownDrift['data-display.zod.ts#DataTableSchema']`, noting "one
of the two is dead; which is a ruling". That ledger fails on a repair exactly as
it fails on new drift, so correcting the entry to its measured four remaining
keys is part of this change, not a widening of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

needs:contract-review applied — Clause ② yes. ⛔ Not reviewed, not enqueued by this seat.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC, which dispatched #6940. Applying the gate label I said I would in the dispatch brief, before the dev's report is in, because a Clause-② PR carrying no gate marker is invisible to the review queue — the exact state I flagged on PR #7432 twenty minutes ago.

This PR narrows what safeValidateSchema accepts on a published package (DataTableSchema.rowActions: z.arrayz.boolean().optional()), per the ruling on #6940 (5509029916, summon #8, maintainer verbatim 「7189 A 其他同意」, option A). It stays draft; this seat runs opus, is not at CONTRACT_REVIEW_TIER, and will not self-review, enqueue or auto-merge it.

Two fences verified by this seat before the report arrived

Both are traps the card names explicitly, and both held:

  • packages/types/src/zod/objectql.zod.ts is byte-identical to main — compared by blob hash. That file carries a different key also spelled rowActions (z.array(z.string()), the list view's legacy bare-name action list), which the ruling says is correct as it stands. Not touched.
  • user-table.json and full-featured-table.json are unchanged, as the ruling required — they "validate again unchanged" rather than being edited into passing.

⚠️ A fence I set was wrong, and the dev was right to cross it

My dispatch brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as ⛔ do-not-touch, held by PR #7432. This PR edits it, and that edit is forced by the fix rather than optional. The dev's own comment in the diff states the mechanism:

this ledger fails on a repair exactly as it fails on new drift, which is why correcting this line was part of that change and not optional.

rowActions was listed in that file's KnownDrift ledger as a disjoint pair. Once the mirror is repaired the pair is in parity, and a ledger still asserting drift on a parity key fails. Leaving the file untouched to honour my fence would have shipped a red PR. ⇒ The fence was mine and it was wrong; nothing here needs reverting.

Conflict risk with PR #7432 — measured, and it is prose only

Both PRs edit that file, so I checked the actual hunks rather than assuming:

regions touched
this PR~L57 (a count comment), ~L732-754 (the DataTableSchemaKnownDrift entry)
#7432~L131, ~L147, ~L354, ~L521, ~L1483 — all blocks.zod.ts imports, MIRRORS/Declared entries and one EXCLUSIONS row

Disjoint. A textual merge should apply cleanly in either order.

The one residue is prose, not a gate: this PR's header line reads "37 entries52 keys", and #7432 removes nine blocks.zod.ts mirrors, so whichever lands second leaves that count stale. Measured that this cannot redden CI — the ledger's entry/key counts are documentation, with no Object.keys(KnownDrift).length assertion anywhere (control: the file contains 13 expect( calls, so the search instrument fires). Only the MIRRORS population is derived and checked, and #7432 removes from both halves so they still agree.

⇒ No action needed from either PR. Recorded so that a stale count found later reads as known prose rot rather than a missed conflict — the class objectui#7433 tracks.

Full review follows once the dev's structured report is in, including the ruling's rider (why #5684's mirror-parity ratchet did not catch a boolean declaration against an array mirror).


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.5 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-Bvw7UhG5.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)515.05KB117.52KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.14KB13.02KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head f17e08244. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6940 ruling (2026-09-02, 「7189 A 其他同意」, option A): DataTableSchema.rowActions is z.boolean().optional(), the one outlier brought to the six faces that already said boolean; option B (union) correctly not taken. The load-bearing pin is the refusal of [] with every issue on rowActions, and it is measured at the published safeValidateSchema union too; the same-named ObjectGridSchema.rowActions (string[]) is pinned as a different key. The KnownDrift edit is the ratchet's toll for a repair, not scope widening, and the rider's finding — the ratchet was not blind, it ledgered the question — is accepted.

Semver / changeset.@object-ui/typespatch, with the ruling's own reasoning: the narrowing removes a spelling that was accepted but inert (the renderer only truthiness-tests the key; [] rendered as true), and the consumer sweep finds zero data-table documents authoring an array. Correct.

Boundary flags. No governed path. Same-line collision with #7468 on the zod-mirror-parity.test.ts header (53 → 52 here, 53 → 56 there); whichever merges second is ejected as dirty and the seat re-derives (expected 39 entries / 55 keys).

Landing. Carrier cleared, ready, auto-merge (SQUASH). All checks green on f17e08244.


Generated by Claude Code

…counting
The merge queue dequeued PR #7447 after #7344 (PR #7468) landed. Both changes
touch the same ledger; the conflict is in two places and neither side was
correct on its own.
1. The KnownDrift docblock region. main added a real new entry from #7344
('crud.zod.ts#DetailSchema': 'onBack') in the same lines where this branch
rewrote the rowActions prose. BOTH are kept: main's entry with its own
docblock, then this branch's rewritten rowActions paragraph documenting
DataTableSchema, whose union is now the measured four keys.
2. The header census — un-enforced prose, so re-derived BY COUNTING the merged
interface rather than by taking a side:
KnownDrift 39 entries / 55 keys (main's 39/56 less the repaired
rowActions; entry count unmoved)
UnmirroredDeclared 17 entries / 98 keys (untouched by this change)
RuntimeOnlyDeclared 7 entries / 24 keys
160 - 39 = 121 pairs with no KnownDrift entry
The "no entry in either" figure was also re-derived: |U u R| = 18, so
160 - 18 = 142, which is what the file already said — checked rather than
assumed, because the same figure was silently stale in the sibling merge.
Verified: pnpm --filter @object-ui/types run type-check exit 0, 0 'error TS'
(script name echoed in the log, so not a zero-match filter); pnpm exec vitest
run packages/types/ -> Test Files 94 passed, Tests 1601 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge conflict resolved — main merged in, census re-derived by counting

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. This PR left the queue without merging after #7468 (#7344) landed; mergeable_state was dirty. Pushed f17e08244..92db9ebc9 — a merge commit, ⛔ no rebase, amend or force-push.

⚠️ Two conflicts, and the second was data, not just prose:

1. The KnownDrift docblock region.#7344 added a real new entry — 'crud.zod.ts#DetailSchema': 'onBack' — in the very lines where this branch rewrote the rowActions paragraph. Both are kept: main's entry with its own docblock, then this branch's rewritten paragraph documenting DataTableSchema, whose union is the measured four keys (onRowEdit | onRowDelete | onSelectionChange | onColumnsReorder). Taking either side alone would have silently dropped a ledger entry or resurrected a repaired one.

2. The header census — un-enforced prose, so counted from the merged interface rather than chosen:

ledgerentrieskeys
KnownDrift3955
UnmirroredDeclared1798
RuntimeOnlyDeclared724

39 / 55 is main's 39 / 56 less the one key this PR repaired — the entry count correctly does not move, which is what this PR's own body predicted. 160 − 39 = 121 was already right.

⭐ The "no entry in either" figure was checked rather than assumed: |U ∪ R| = 18, so 160 − 18 = 142, which is what the file already said. Worth stating because in the sibling merge (#7464) that same figure had gone stale and git flagged nothing — it merged cleanly since only one side touched it. Here it was genuinely still correct.

Verified before pushing

  • pnpm --filter @object-ui/types run type-check → exit 0, 0error TS, with > @object-ui/types@17.6.0 type-check echoed in the log (not the zero-match filter trap).
  • pnpm exec vitest run packages/types/Test Files 94 passed, Tests 1601 passed.

⛔ Not re-enqueued and auto-merge untouched — the landing is the director seat's. Nothing in the contract review changed; only the merge with main.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.9 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-RyaXrnmX.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.24KB117.82KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataTableSchema.rowActions is z.array in the zod mirror and boolean everywhere else — the mirror is the outlier

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940) - #7447

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror
Sep 3, 2026
Merged

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940)#7447
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#6940

DataTableSchema.rowActions in packages/types/src/zod/data-display.zod.ts becomes
z.boolean().optional(). Maintainer ruling 2026-09-02 (director seat, summon #8,
verbatim 「7189 A 其他同意」), option A. Option B (a boolean | array union) was
not taken: it would permanently accept a shape the renderer only truthiness-tests.

- rowActions: z.array(z.any()).optional().describe('Row action buttons'),+ rowActions: z.boolean().optional().describe('Show the row actions column (edit/delete) — mirrors the boolean the renderer truthiness-tests (objectui#6940)'),

The mirror was the single outlier and the published one, so safeValidateSchema refused
the exact spelling the component's TS declaration, destructuring default, truthiness
gates, colSpan arithmetic, registered type: 'boolean' authoring input, defaultProps
and docblock example all teach.

⚠️ Two different keys are named rowActions — confirmed distinguished

Only the data-table one moved. The list view's key is untouched, and git diff proves it.

key changed herekey NOT touched
mirrorzod/data-display.zod.tsDataTableSchemazod/objectql.zod.tsObjectGridSchema
was / isz.array(z.any()) to z.boolean()z.array(z.string()), unchanged
TS twindata-display.tsrowActions?: booleanobjectql.tsrowActions?: string[]
ratchetdrift was ledgered in KnownDriftin parity, ledgered in neither

The separation is now pinned, so a later sweep that "harmonises the two rowActions" on
the strength of a shared name turns red here instead of silently retyping a key no ruling
covers. git diff --name-only -- packages/types/src/zod/objectql.zod.ts returns empty.

The rider — the ratchet was NOT blind, and this is the measurement

#5684's mirror-parity ratchet did catch this drift. It measured it and recorded it in
its KnownDrift ledger:

'data-display.zod.ts#DataTableSchema': 'rowActions' | 'onRowEdit' | 'onRowDelete' | 'onSelectionChange' | 'onColumnsReorder';

with an inline note that named the exact question this card later answered — TS declares
rowActions?: boolean, the mirror declared any[], "One of the two is dead; which is a
ruling."
The ratchet's header records the same entry surviving #5927's sweep
("DataTableSchema kept rowActions").

So there is no structural gap, and there is nothing to file. The ratchet's design is
to convert a drift it cannot adjudicate into a ledgered, citable question rather than a
silent pass. It did exactly that; the question sat in the ledger until the maintainer
answered it on 2026-09-02.

⚠️ Deviation from the dispatch brief — one line in a "do not touch" file

The brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as held by open
PR #7432. This PR edits it anyway, and the reason is mechanical rather than
discretionary — flagging it explicitly for the reviewer.

That ledger is bidirectional by design: "the entry fails when new drift appears on that
mirror AND when the recorded drift is fixed — a stale entry cannot rot quietly."

Repairing rowActions therefore turned the package's own gate red:

src/__tests__/zod-mirror-parity.test.ts(1349,14): error TS2322:
Type '"data-display.zod.ts#DataTableSchema"' is not assignable to type 'never'.

Correcting the entry to its measured remaining four keys is the toll the ratchet charges
for every drift repair — it is not "fixing the ratchet" and not a widening of scope.
Without it pnpm --filter @object-ui/types run type-check cannot pass and the PR is
unreviewable.

Collision with #7432 measured, not assumed: zero. Fetched pull/7432/head and
diffed. Every one of its hunks in this file is in the blocks.zod.ts region (lines
~132–148 imports, ~355 MIRRORS, ~522 Declared, ~1484 EXCLUSIONS), removing the nine
block pairs. This PR's edits are the KnownDrift entry near line 747, its docblock, and
the header key census near line 60 — disjoint regions, no textual conflict either way.

Changes are three: drop 'rowActions' from the union, rewrite the docblock paragraph
that would otherwise be a false statement sitting next to the change, and move the header
census from 53 keys to 52.

Tests

Pinned in packages/types/src/__tests__/data-table-declared-keys-6882.test.ts (the home
the ruling named), 7 new cases:

  1. rowActions: true validates.
  2. rowActions: false validates — it is a boolean, not a truthy-only flag.
  3. rowActions: [] is REFUSED — and every issue path is asserted to be
    rowActions, so a document rejected for an unrelated reason cannot read as a passing
    narrowing pin. This is the load-bearing half: a union would have satisfied a
    "true validates" assertion on its own.
  4. The published safeValidateSchema moves in both directions. Measured at this level
    rather than inferred, because it is a z.union and a sibling member accepting the
    document would have left the published surface unchanged.
  5. Both catalog fixtures exist on disk (guard against a vacuous pass).
  6. Both validate unchanged — they author rowActions: true and always did.
  7. The list view's rowActions still takes string[] and rejects true.

Red-then-green ablation

Reverted only the mirror fix, with the pin left in place.

  • Mutation proven on disk before the run: blob d1d427db to de3a6332, plus anchored
    grep counts (boolean spelling 1 to 0, array spelling 0 to 1). de3a6332 is exactly the
    pre-fix blob on origin/main.
  • Result: 5 of 8 failed, including the [] refusal and the safeValidateSchema case.
  • Control: the ObjectGrid separation test stayed green under the mutation, confirming
    the two same-named keys really are independent.
  • Restore proven by state, not by exit code:git checkout HEAD -- ... (absolute
    path, pinned to HEAD rather than the polluted index), then blob equality against the
    HEAD blob and an empty git diff HEAD. Green leg re-run: 8/8.
  • trap ... EXIT INT TERM with absolute paths throughout.
  • No build/dist leg is required or claimed: the pin imports '../zod/data-display.zod.js',
    a relative same-package specifier vitest resolves to TypeScript source, so no dist/
    copy can serve a stale mutation.

Gates, all on the final commit f17e08244

runresult
pnpm --filter @object-ui/types run type-checkgreen (was red before the ledger line)
pnpm exec vitest run packages/types/92 files, 1547 tests passed
pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/ examples/schema-catalog/162 files, 3209 tests passed
pnpm --filter @object-ui/types run lint0 errors (274 pre-existing warnings)
node scripts/check-control-bytes.mjsOK, 6131 files
node scripts/check-changeset-presence.mjs1 changeset declared
objectui check on the catalog✓ All checks passed

objectui check was run with the real built CLI, and the built dist was confirmed
to carry the change (rowActions: z.ZodOptional of ZodBoolean) rather than a stale
copy. user-table.json and full-featured-table.json no longer appear in its failing
bucket; the five entries that remain there are the components-basic-text ones held by a
separate in-flight PR.

Consumer sweep — the narrowing has no casualties

Grepped the repo for rowActions authored as an array. Every hit outside this PR's own
tests is the list-view/ObjectGrid key: p1-spec-alignment.test.ts (type: 'list-view'),
objectViewHostSurface.test.tsx, the plugin-grid legacy bare-name suite and
resolveLegacyRowActions, plugin-grid/README.md, schema-reference.md and
plugin-grid.mdx. Zero documents author an array rowActions on a data-table, which
measures the ruling's stated basis for a patch bump rather than assuming it.

Changeset

@object-ui/types, patch. The body states the reasoning the ruling requires: no
author can have relied on an array value, because the renderer never reads the array — it
only truthiness-tests the key, so even [] rendered identically to true (#6318 measured
42 elements either way, against 39 with the key absent). An array here could never have
carried meaning to any consumer, so the narrowing removes a spelling that was accepted but
inert.

Review status

Clause ② yes. Draft, carries needs:contract-review, not enqueued, not
auto-merged, not self-reviewed. Waits on the project director seat.


🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…#6940)
The hand-written zod mirror declared `rowActions: z.array(z.any()).optional()`
while every other face of the key says boolean — the TS declaration it mirrors,
the renderer's destructuring default, its truthiness gates and colSpan
arithmetic, the registered `type: 'boolean'` authoring input, `defaultProps`,
and the renderer's own docblock example. The mirror was the outlier and the
published one, so `safeValidateSchema` refused the spelling the component's
documentation, defaults and authoring UI all teach, and two shipped
schema-catalog entries failed validation for that and no other reason.
Maintainer ruling 2026-09-02 (director seat, summon #8), option A. Option B (a
`boolean | array` union) was not taken: it would permanently accept a shape the
renderer only truthiness-tests.
Pinned in data-table-declared-keys-6882.test.ts: `true` and `false` validate,
`[]` is REFUSED and refused ON `rowActions` (every issue path asserted, so a
document rejected for an unrelated reason cannot read as a passing narrowing),
the published `safeValidateSchema` surface moves in both directions, and the two
catalog entries validate unchanged. The list view's same-named `rowActions`
(`ObjectGridSchema`, `z.array(z.string())`) is a different key, is in parity with
its own TS twin, and is pinned here as separate rather than touched.
The #5684 mirror-parity ratchet was NOT blind to this drift: it had measured and
ledgered it as `KnownDrift['data-display.zod.ts#DataTableSchema']`, noting "one
of the two is dead; which is a ruling". That ledger fails on a repair exactly as
it fails on new drift, so correcting the entry to its measured four remaining
keys is part of this change, not a widening of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

needs:contract-review applied — Clause ② yes. ⛔ Not reviewed, not enqueued by this seat.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC, which dispatched #6940. Applying the gate label I said I would in the dispatch brief, before the dev's report is in, because a Clause-② PR carrying no gate marker is invisible to the review queue — the exact state I flagged on PR #7432 twenty minutes ago.

This PR narrows what safeValidateSchema accepts on a published package (DataTableSchema.rowActions: z.arrayz.boolean().optional()), per the ruling on #6940 (5509029916, summon #8, maintainer verbatim 「7189 A 其他同意」, option A). It stays draft; this seat runs opus, is not at CONTRACT_REVIEW_TIER, and will not self-review, enqueue or auto-merge it.

Two fences verified by this seat before the report arrived

Both are traps the card names explicitly, and both held:

  • packages/types/src/zod/objectql.zod.ts is byte-identical to main — compared by blob hash. That file carries a different key also spelled rowActions (z.array(z.string()), the list view's legacy bare-name action list), which the ruling says is correct as it stands. Not touched.
  • user-table.json and full-featured-table.json are unchanged, as the ruling required — they "validate again unchanged" rather than being edited into passing.

⚠️ A fence I set was wrong, and the dev was right to cross it

My dispatch brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as ⛔ do-not-touch, held by PR #7432. This PR edits it, and that edit is forced by the fix rather than optional. The dev's own comment in the diff states the mechanism:

this ledger fails on a repair exactly as it fails on new drift, which is why correcting this line was part of that change and not optional.

rowActions was listed in that file's KnownDrift ledger as a disjoint pair. Once the mirror is repaired the pair is in parity, and a ledger still asserting drift on a parity key fails. Leaving the file untouched to honour my fence would have shipped a red PR. ⇒ The fence was mine and it was wrong; nothing here needs reverting.

Conflict risk with PR #7432 — measured, and it is prose only

Both PRs edit that file, so I checked the actual hunks rather than assuming:

regions touched
this PR~L57 (a count comment), ~L732-754 (the DataTableSchemaKnownDrift entry)
#7432~L131, ~L147, ~L354, ~L521, ~L1483 — all blocks.zod.ts imports, MIRRORS/Declared entries and one EXCLUSIONS row

Disjoint. A textual merge should apply cleanly in either order.

The one residue is prose, not a gate: this PR's header line reads "37 entries52 keys", and #7432 removes nine blocks.zod.ts mirrors, so whichever lands second leaves that count stale. Measured that this cannot redden CI — the ledger's entry/key counts are documentation, with no Object.keys(KnownDrift).length assertion anywhere (control: the file contains 13 expect( calls, so the search instrument fires). Only the MIRRORS population is derived and checked, and #7432 removes from both halves so they still agree.

⇒ No action needed from either PR. Recorded so that a stale count found later reads as known prose rot rather than a missed conflict — the class objectui#7433 tracks.

Full review follows once the dev's structured report is in, including the ruling's rider (why #5684's mirror-parity ratchet did not catch a boolean declaration against an array mirror).


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.5 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-Bvw7UhG5.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)515.05KB117.52KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.14KB13.02KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head f17e08244. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6940 ruling (2026-09-02, 「7189 A 其他同意」, option A): DataTableSchema.rowActions is z.boolean().optional(), the one outlier brought to the six faces that already said boolean; option B (union) correctly not taken. The load-bearing pin is the refusal of [] with every issue on rowActions, and it is measured at the published safeValidateSchema union too; the same-named ObjectGridSchema.rowActions (string[]) is pinned as a different key. The KnownDrift edit is the ratchet's toll for a repair, not scope widening, and the rider's finding — the ratchet was not blind, it ledgered the question — is accepted.

Semver / changeset.@object-ui/typespatch, with the ruling's own reasoning: the narrowing removes a spelling that was accepted but inert (the renderer only truthiness-tests the key; [] rendered as true), and the consumer sweep finds zero data-table documents authoring an array. Correct.

Boundary flags. No governed path. Same-line collision with #7468 on the zod-mirror-parity.test.ts header (53 → 52 here, 53 → 56 there); whichever merges second is ejected as dirty and the seat re-derives (expected 39 entries / 55 keys).

Landing. Carrier cleared, ready, auto-merge (SQUASH). All checks green on f17e08244.


Generated by Claude Code

…counting
The merge queue dequeued PR #7447 after #7344 (PR #7468) landed. Both changes
touch the same ledger; the conflict is in two places and neither side was
correct on its own.
1. The KnownDrift docblock region. main added a real new entry from #7344
('crud.zod.ts#DetailSchema': 'onBack') in the same lines where this branch
rewrote the rowActions prose. BOTH are kept: main's entry with its own
docblock, then this branch's rewritten rowActions paragraph documenting
DataTableSchema, whose union is now the measured four keys.
2. The header census — un-enforced prose, so re-derived BY COUNTING the merged
interface rather than by taking a side:
KnownDrift 39 entries / 55 keys (main's 39/56 less the repaired
rowActions; entry count unmoved)
UnmirroredDeclared 17 entries / 98 keys (untouched by this change)
RuntimeOnlyDeclared 7 entries / 24 keys
160 - 39 = 121 pairs with no KnownDrift entry
The "no entry in either" figure was also re-derived: |U u R| = 18, so
160 - 18 = 142, which is what the file already said — checked rather than
assumed, because the same figure was silently stale in the sibling merge.
Verified: pnpm --filter @object-ui/types run type-check exit 0, 0 'error TS'
(script name echoed in the log, so not a zero-match filter); pnpm exec vitest
run packages/types/ -> Test Files 94 passed, Tests 1601 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge conflict resolved — main merged in, census re-derived by counting

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. This PR left the queue without merging after #7468 (#7344) landed; mergeable_state was dirty. Pushed f17e08244..92db9ebc9 — a merge commit, ⛔ no rebase, amend or force-push.

⚠️ Two conflicts, and the second was data, not just prose:

1. The KnownDrift docblock region.#7344 added a real new entry — 'crud.zod.ts#DetailSchema': 'onBack' — in the very lines where this branch rewrote the rowActions paragraph. Both are kept: main's entry with its own docblock, then this branch's rewritten paragraph documenting DataTableSchema, whose union is the measured four keys (onRowEdit | onRowDelete | onSelectionChange | onColumnsReorder). Taking either side alone would have silently dropped a ledger entry or resurrected a repaired one.

2. The header census — un-enforced prose, so counted from the merged interface rather than chosen:

ledgerentrieskeys
KnownDrift3955
UnmirroredDeclared1798
RuntimeOnlyDeclared724

39 / 55 is main's 39 / 56 less the one key this PR repaired — the entry count correctly does not move, which is what this PR's own body predicted. 160 − 39 = 121 was already right.

⭐ The "no entry in either" figure was checked rather than assumed: |U ∪ R| = 18, so 160 − 18 = 142, which is what the file already said. Worth stating because in the sibling merge (#7464) that same figure had gone stale and git flagged nothing — it merged cleanly since only one side touched it. Here it was genuinely still correct.

Verified before pushing

  • pnpm --filter @object-ui/types run type-check → exit 0, 0error TS, with > @object-ui/types@17.6.0 type-check echoed in the log (not the zero-match filter trap).
  • pnpm exec vitest run packages/types/Test Files 94 passed, Tests 1601 passed.

⛔ Not re-enqueued and auto-merge untouched — the landing is the director seat's. Nothing in the contract review changed; only the merge with main.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.9 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-RyaXrnmX.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.24KB117.82KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataTableSchema.rowActions is z.array in the zod mirror and boolean everywhere else — the mirror is the outlier

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940) - #7447

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror
Sep 3, 2026
Merged

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940)#7447
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#6940

DataTableSchema.rowActions in packages/types/src/zod/data-display.zod.ts becomes
z.boolean().optional(). Maintainer ruling 2026-09-02 (director seat, summon #8,
verbatim 「7189 A 其他同意」), option A. Option B (a boolean | array union) was
not taken: it would permanently accept a shape the renderer only truthiness-tests.

- rowActions: z.array(z.any()).optional().describe('Row action buttons'),+ rowActions: z.boolean().optional().describe('Show the row actions column (edit/delete) — mirrors the boolean the renderer truthiness-tests (objectui#6940)'),

The mirror was the single outlier and the published one, so safeValidateSchema refused
the exact spelling the component's TS declaration, destructuring default, truthiness
gates, colSpan arithmetic, registered type: 'boolean' authoring input, defaultProps
and docblock example all teach.

⚠️ Two different keys are named rowActions — confirmed distinguished

Only the data-table one moved. The list view's key is untouched, and git diff proves it.

key changed herekey NOT touched
mirrorzod/data-display.zod.tsDataTableSchemazod/objectql.zod.tsObjectGridSchema
was / isz.array(z.any()) to z.boolean()z.array(z.string()), unchanged
TS twindata-display.tsrowActions?: booleanobjectql.tsrowActions?: string[]
ratchetdrift was ledgered in KnownDriftin parity, ledgered in neither

The separation is now pinned, so a later sweep that "harmonises the two rowActions" on
the strength of a shared name turns red here instead of silently retyping a key no ruling
covers. git diff --name-only -- packages/types/src/zod/objectql.zod.ts returns empty.

The rider — the ratchet was NOT blind, and this is the measurement

#5684's mirror-parity ratchet did catch this drift. It measured it and recorded it in
its KnownDrift ledger:

'data-display.zod.ts#DataTableSchema': 'rowActions' | 'onRowEdit' | 'onRowDelete' | 'onSelectionChange' | 'onColumnsReorder';

with an inline note that named the exact question this card later answered — TS declares
rowActions?: boolean, the mirror declared any[], "One of the two is dead; which is a
ruling."
The ratchet's header records the same entry surviving #5927's sweep
("DataTableSchema kept rowActions").

So there is no structural gap, and there is nothing to file. The ratchet's design is
to convert a drift it cannot adjudicate into a ledgered, citable question rather than a
silent pass. It did exactly that; the question sat in the ledger until the maintainer
answered it on 2026-09-02.

⚠️ Deviation from the dispatch brief — one line in a "do not touch" file

The brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as held by open
PR #7432. This PR edits it anyway, and the reason is mechanical rather than
discretionary — flagging it explicitly for the reviewer.

That ledger is bidirectional by design: "the entry fails when new drift appears on that
mirror AND when the recorded drift is fixed — a stale entry cannot rot quietly."

Repairing rowActions therefore turned the package's own gate red:

src/__tests__/zod-mirror-parity.test.ts(1349,14): error TS2322:
Type '"data-display.zod.ts#DataTableSchema"' is not assignable to type 'never'.

Correcting the entry to its measured remaining four keys is the toll the ratchet charges
for every drift repair — it is not "fixing the ratchet" and not a widening of scope.
Without it pnpm --filter @object-ui/types run type-check cannot pass and the PR is
unreviewable.

Collision with #7432 measured, not assumed: zero. Fetched pull/7432/head and
diffed. Every one of its hunks in this file is in the blocks.zod.ts region (lines
~132–148 imports, ~355 MIRRORS, ~522 Declared, ~1484 EXCLUSIONS), removing the nine
block pairs. This PR's edits are the KnownDrift entry near line 747, its docblock, and
the header key census near line 60 — disjoint regions, no textual conflict either way.

Changes are three: drop 'rowActions' from the union, rewrite the docblock paragraph
that would otherwise be a false statement sitting next to the change, and move the header
census from 53 keys to 52.

Tests

Pinned in packages/types/src/__tests__/data-table-declared-keys-6882.test.ts (the home
the ruling named), 7 new cases:

  1. rowActions: true validates.
  2. rowActions: false validates — it is a boolean, not a truthy-only flag.
  3. rowActions: [] is REFUSED — and every issue path is asserted to be
    rowActions, so a document rejected for an unrelated reason cannot read as a passing
    narrowing pin. This is the load-bearing half: a union would have satisfied a
    "true validates" assertion on its own.
  4. The published safeValidateSchema moves in both directions. Measured at this level
    rather than inferred, because it is a z.union and a sibling member accepting the
    document would have left the published surface unchanged.
  5. Both catalog fixtures exist on disk (guard against a vacuous pass).
  6. Both validate unchanged — they author rowActions: true and always did.
  7. The list view's rowActions still takes string[] and rejects true.

Red-then-green ablation

Reverted only the mirror fix, with the pin left in place.

  • Mutation proven on disk before the run: blob d1d427db to de3a6332, plus anchored
    grep counts (boolean spelling 1 to 0, array spelling 0 to 1). de3a6332 is exactly the
    pre-fix blob on origin/main.
  • Result: 5 of 8 failed, including the [] refusal and the safeValidateSchema case.
  • Control: the ObjectGrid separation test stayed green under the mutation, confirming
    the two same-named keys really are independent.
  • Restore proven by state, not by exit code:git checkout HEAD -- ... (absolute
    path, pinned to HEAD rather than the polluted index), then blob equality against the
    HEAD blob and an empty git diff HEAD. Green leg re-run: 8/8.
  • trap ... EXIT INT TERM with absolute paths throughout.
  • No build/dist leg is required or claimed: the pin imports '../zod/data-display.zod.js',
    a relative same-package specifier vitest resolves to TypeScript source, so no dist/
    copy can serve a stale mutation.

Gates, all on the final commit f17e08244

runresult
pnpm --filter @object-ui/types run type-checkgreen (was red before the ledger line)
pnpm exec vitest run packages/types/92 files, 1547 tests passed
pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/ examples/schema-catalog/162 files, 3209 tests passed
pnpm --filter @object-ui/types run lint0 errors (274 pre-existing warnings)
node scripts/check-control-bytes.mjsOK, 6131 files
node scripts/check-changeset-presence.mjs1 changeset declared
objectui check on the catalog✓ All checks passed

objectui check was run with the real built CLI, and the built dist was confirmed
to carry the change (rowActions: z.ZodOptional of ZodBoolean) rather than a stale
copy. user-table.json and full-featured-table.json no longer appear in its failing
bucket; the five entries that remain there are the components-basic-text ones held by a
separate in-flight PR.

Consumer sweep — the narrowing has no casualties

Grepped the repo for rowActions authored as an array. Every hit outside this PR's own
tests is the list-view/ObjectGrid key: p1-spec-alignment.test.ts (type: 'list-view'),
objectViewHostSurface.test.tsx, the plugin-grid legacy bare-name suite and
resolveLegacyRowActions, plugin-grid/README.md, schema-reference.md and
plugin-grid.mdx. Zero documents author an array rowActions on a data-table, which
measures the ruling's stated basis for a patch bump rather than assuming it.

Changeset

@object-ui/types, patch. The body states the reasoning the ruling requires: no
author can have relied on an array value, because the renderer never reads the array — it
only truthiness-tests the key, so even [] rendered identically to true (#6318 measured
42 elements either way, against 39 with the key absent). An array here could never have
carried meaning to any consumer, so the narrowing removes a spelling that was accepted but
inert.

Review status

Clause ② yes. Draft, carries needs:contract-review, not enqueued, not
auto-merged, not self-reviewed. Waits on the project director seat.


🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…#6940)
The hand-written zod mirror declared `rowActions: z.array(z.any()).optional()`
while every other face of the key says boolean — the TS declaration it mirrors,
the renderer's destructuring default, its truthiness gates and colSpan
arithmetic, the registered `type: 'boolean'` authoring input, `defaultProps`,
and the renderer's own docblock example. The mirror was the outlier and the
published one, so `safeValidateSchema` refused the spelling the component's
documentation, defaults and authoring UI all teach, and two shipped
schema-catalog entries failed validation for that and no other reason.
Maintainer ruling 2026-09-02 (director seat, summon #8), option A. Option B (a
`boolean | array` union) was not taken: it would permanently accept a shape the
renderer only truthiness-tests.
Pinned in data-table-declared-keys-6882.test.ts: `true` and `false` validate,
`[]` is REFUSED and refused ON `rowActions` (every issue path asserted, so a
document rejected for an unrelated reason cannot read as a passing narrowing),
the published `safeValidateSchema` surface moves in both directions, and the two
catalog entries validate unchanged. The list view's same-named `rowActions`
(`ObjectGridSchema`, `z.array(z.string())`) is a different key, is in parity with
its own TS twin, and is pinned here as separate rather than touched.
The #5684 mirror-parity ratchet was NOT blind to this drift: it had measured and
ledgered it as `KnownDrift['data-display.zod.ts#DataTableSchema']`, noting "one
of the two is dead; which is a ruling". That ledger fails on a repair exactly as
it fails on new drift, so correcting the entry to its measured four remaining
keys is part of this change, not a widening of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

needs:contract-review applied — Clause ② yes. ⛔ Not reviewed, not enqueued by this seat.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC, which dispatched #6940. Applying the gate label I said I would in the dispatch brief, before the dev's report is in, because a Clause-② PR carrying no gate marker is invisible to the review queue — the exact state I flagged on PR #7432 twenty minutes ago.

This PR narrows what safeValidateSchema accepts on a published package (DataTableSchema.rowActions: z.arrayz.boolean().optional()), per the ruling on #6940 (5509029916, summon #8, maintainer verbatim 「7189 A 其他同意」, option A). It stays draft; this seat runs opus, is not at CONTRACT_REVIEW_TIER, and will not self-review, enqueue or auto-merge it.

Two fences verified by this seat before the report arrived

Both are traps the card names explicitly, and both held:

  • packages/types/src/zod/objectql.zod.ts is byte-identical to main — compared by blob hash. That file carries a different key also spelled rowActions (z.array(z.string()), the list view's legacy bare-name action list), which the ruling says is correct as it stands. Not touched.
  • user-table.json and full-featured-table.json are unchanged, as the ruling required — they "validate again unchanged" rather than being edited into passing.

⚠️ A fence I set was wrong, and the dev was right to cross it

My dispatch brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as ⛔ do-not-touch, held by PR #7432. This PR edits it, and that edit is forced by the fix rather than optional. The dev's own comment in the diff states the mechanism:

this ledger fails on a repair exactly as it fails on new drift, which is why correcting this line was part of that change and not optional.

rowActions was listed in that file's KnownDrift ledger as a disjoint pair. Once the mirror is repaired the pair is in parity, and a ledger still asserting drift on a parity key fails. Leaving the file untouched to honour my fence would have shipped a red PR. ⇒ The fence was mine and it was wrong; nothing here needs reverting.

Conflict risk with PR #7432 — measured, and it is prose only

Both PRs edit that file, so I checked the actual hunks rather than assuming:

regions touched
this PR~L57 (a count comment), ~L732-754 (the DataTableSchemaKnownDrift entry)
#7432~L131, ~L147, ~L354, ~L521, ~L1483 — all blocks.zod.ts imports, MIRRORS/Declared entries and one EXCLUSIONS row

Disjoint. A textual merge should apply cleanly in either order.

The one residue is prose, not a gate: this PR's header line reads "37 entries52 keys", and #7432 removes nine blocks.zod.ts mirrors, so whichever lands second leaves that count stale. Measured that this cannot redden CI — the ledger's entry/key counts are documentation, with no Object.keys(KnownDrift).length assertion anywhere (control: the file contains 13 expect( calls, so the search instrument fires). Only the MIRRORS population is derived and checked, and #7432 removes from both halves so they still agree.

⇒ No action needed from either PR. Recorded so that a stale count found later reads as known prose rot rather than a missed conflict — the class objectui#7433 tracks.

Full review follows once the dev's structured report is in, including the ruling's rider (why #5684's mirror-parity ratchet did not catch a boolean declaration against an array mirror).


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.5 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-Bvw7UhG5.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)515.05KB117.52KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.14KB13.02KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head f17e08244. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6940 ruling (2026-09-02, 「7189 A 其他同意」, option A): DataTableSchema.rowActions is z.boolean().optional(), the one outlier brought to the six faces that already said boolean; option B (union) correctly not taken. The load-bearing pin is the refusal of [] with every issue on rowActions, and it is measured at the published safeValidateSchema union too; the same-named ObjectGridSchema.rowActions (string[]) is pinned as a different key. The KnownDrift edit is the ratchet's toll for a repair, not scope widening, and the rider's finding — the ratchet was not blind, it ledgered the question — is accepted.

Semver / changeset.@object-ui/typespatch, with the ruling's own reasoning: the narrowing removes a spelling that was accepted but inert (the renderer only truthiness-tests the key; [] rendered as true), and the consumer sweep finds zero data-table documents authoring an array. Correct.

Boundary flags. No governed path. Same-line collision with #7468 on the zod-mirror-parity.test.ts header (53 → 52 here, 53 → 56 there); whichever merges second is ejected as dirty and the seat re-derives (expected 39 entries / 55 keys).

Landing. Carrier cleared, ready, auto-merge (SQUASH). All checks green on f17e08244.


Generated by Claude Code

…counting
The merge queue dequeued PR #7447 after #7344 (PR #7468) landed. Both changes
touch the same ledger; the conflict is in two places and neither side was
correct on its own.
1. The KnownDrift docblock region. main added a real new entry from #7344
('crud.zod.ts#DetailSchema': 'onBack') in the same lines where this branch
rewrote the rowActions prose. BOTH are kept: main's entry with its own
docblock, then this branch's rewritten rowActions paragraph documenting
DataTableSchema, whose union is now the measured four keys.
2. The header census — un-enforced prose, so re-derived BY COUNTING the merged
interface rather than by taking a side:
KnownDrift 39 entries / 55 keys (main's 39/56 less the repaired
rowActions; entry count unmoved)
UnmirroredDeclared 17 entries / 98 keys (untouched by this change)
RuntimeOnlyDeclared 7 entries / 24 keys
160 - 39 = 121 pairs with no KnownDrift entry
The "no entry in either" figure was also re-derived: |U u R| = 18, so
160 - 18 = 142, which is what the file already said — checked rather than
assumed, because the same figure was silently stale in the sibling merge.
Verified: pnpm --filter @object-ui/types run type-check exit 0, 0 'error TS'
(script name echoed in the log, so not a zero-match filter); pnpm exec vitest
run packages/types/ -> Test Files 94 passed, Tests 1601 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge conflict resolved — main merged in, census re-derived by counting

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. This PR left the queue without merging after #7468 (#7344) landed; mergeable_state was dirty. Pushed f17e08244..92db9ebc9 — a merge commit, ⛔ no rebase, amend or force-push.

⚠️ Two conflicts, and the second was data, not just prose:

1. The KnownDrift docblock region.#7344 added a real new entry — 'crud.zod.ts#DetailSchema': 'onBack' — in the very lines where this branch rewrote the rowActions paragraph. Both are kept: main's entry with its own docblock, then this branch's rewritten paragraph documenting DataTableSchema, whose union is the measured four keys (onRowEdit | onRowDelete | onSelectionChange | onColumnsReorder). Taking either side alone would have silently dropped a ledger entry or resurrected a repaired one.

2. The header census — un-enforced prose, so counted from the merged interface rather than chosen:

ledgerentrieskeys
KnownDrift3955
UnmirroredDeclared1798
RuntimeOnlyDeclared724

39 / 55 is main's 39 / 56 less the one key this PR repaired — the entry count correctly does not move, which is what this PR's own body predicted. 160 − 39 = 121 was already right.

⭐ The "no entry in either" figure was checked rather than assumed: |U ∪ R| = 18, so 160 − 18 = 142, which is what the file already said. Worth stating because in the sibling merge (#7464) that same figure had gone stale and git flagged nothing — it merged cleanly since only one side touched it. Here it was genuinely still correct.

Verified before pushing

  • pnpm --filter @object-ui/types run type-check → exit 0, 0error TS, with > @object-ui/types@17.6.0 type-check echoed in the log (not the zero-match filter trap).
  • pnpm exec vitest run packages/types/Test Files 94 passed, Tests 1601 passed.

⛔ Not re-enqueued and auto-merge untouched — the landing is the director seat's. Nothing in the contract review changed; only the merge with main.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.9 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-RyaXrnmX.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.24KB117.82KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataTableSchema.rowActions is z.array in the zod mirror and boolean everywhere else — the mirror is the outlier

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940) - #7447

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror
Sep 3, 2026
Merged

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940)#7447
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#6940

DataTableSchema.rowActions in packages/types/src/zod/data-display.zod.ts becomes
z.boolean().optional(). Maintainer ruling 2026-09-02 (director seat, summon #8,
verbatim 「7189 A 其他同意」), option A. Option B (a boolean | array union) was
not taken: it would permanently accept a shape the renderer only truthiness-tests.

- rowActions: z.array(z.any()).optional().describe('Row action buttons'),+ rowActions: z.boolean().optional().describe('Show the row actions column (edit/delete) — mirrors the boolean the renderer truthiness-tests (objectui#6940)'),

The mirror was the single outlier and the published one, so safeValidateSchema refused
the exact spelling the component's TS declaration, destructuring default, truthiness
gates, colSpan arithmetic, registered type: 'boolean' authoring input, defaultProps
and docblock example all teach.

⚠️ Two different keys are named rowActions — confirmed distinguished

Only the data-table one moved. The list view's key is untouched, and git diff proves it.

key changed herekey NOT touched
mirrorzod/data-display.zod.tsDataTableSchemazod/objectql.zod.tsObjectGridSchema
was / isz.array(z.any()) to z.boolean()z.array(z.string()), unchanged
TS twindata-display.tsrowActions?: booleanobjectql.tsrowActions?: string[]
ratchetdrift was ledgered in KnownDriftin parity, ledgered in neither

The separation is now pinned, so a later sweep that "harmonises the two rowActions" on
the strength of a shared name turns red here instead of silently retyping a key no ruling
covers. git diff --name-only -- packages/types/src/zod/objectql.zod.ts returns empty.

The rider — the ratchet was NOT blind, and this is the measurement

#5684's mirror-parity ratchet did catch this drift. It measured it and recorded it in
its KnownDrift ledger:

'data-display.zod.ts#DataTableSchema': 'rowActions' | 'onRowEdit' | 'onRowDelete' | 'onSelectionChange' | 'onColumnsReorder';

with an inline note that named the exact question this card later answered — TS declares
rowActions?: boolean, the mirror declared any[], "One of the two is dead; which is a
ruling."
The ratchet's header records the same entry surviving #5927's sweep
("DataTableSchema kept rowActions").

So there is no structural gap, and there is nothing to file. The ratchet's design is
to convert a drift it cannot adjudicate into a ledgered, citable question rather than a
silent pass. It did exactly that; the question sat in the ledger until the maintainer
answered it on 2026-09-02.

⚠️ Deviation from the dispatch brief — one line in a "do not touch" file

The brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as held by open
PR #7432. This PR edits it anyway, and the reason is mechanical rather than
discretionary — flagging it explicitly for the reviewer.

That ledger is bidirectional by design: "the entry fails when new drift appears on that
mirror AND when the recorded drift is fixed — a stale entry cannot rot quietly."

Repairing rowActions therefore turned the package's own gate red:

src/__tests__/zod-mirror-parity.test.ts(1349,14): error TS2322:
Type '"data-display.zod.ts#DataTableSchema"' is not assignable to type 'never'.

Correcting the entry to its measured remaining four keys is the toll the ratchet charges
for every drift repair — it is not "fixing the ratchet" and not a widening of scope.
Without it pnpm --filter @object-ui/types run type-check cannot pass and the PR is
unreviewable.

Collision with #7432 measured, not assumed: zero. Fetched pull/7432/head and
diffed. Every one of its hunks in this file is in the blocks.zod.ts region (lines
~132–148 imports, ~355 MIRRORS, ~522 Declared, ~1484 EXCLUSIONS), removing the nine
block pairs. This PR's edits are the KnownDrift entry near line 747, its docblock, and
the header key census near line 60 — disjoint regions, no textual conflict either way.

Changes are three: drop 'rowActions' from the union, rewrite the docblock paragraph
that would otherwise be a false statement sitting next to the change, and move the header
census from 53 keys to 52.

Tests

Pinned in packages/types/src/__tests__/data-table-declared-keys-6882.test.ts (the home
the ruling named), 7 new cases:

  1. rowActions: true validates.
  2. rowActions: false validates — it is a boolean, not a truthy-only flag.
  3. rowActions: [] is REFUSED — and every issue path is asserted to be
    rowActions, so a document rejected for an unrelated reason cannot read as a passing
    narrowing pin. This is the load-bearing half: a union would have satisfied a
    "true validates" assertion on its own.
  4. The published safeValidateSchema moves in both directions. Measured at this level
    rather than inferred, because it is a z.union and a sibling member accepting the
    document would have left the published surface unchanged.
  5. Both catalog fixtures exist on disk (guard against a vacuous pass).
  6. Both validate unchanged — they author rowActions: true and always did.
  7. The list view's rowActions still takes string[] and rejects true.

Red-then-green ablation

Reverted only the mirror fix, with the pin left in place.

  • Mutation proven on disk before the run: blob d1d427db to de3a6332, plus anchored
    grep counts (boolean spelling 1 to 0, array spelling 0 to 1). de3a6332 is exactly the
    pre-fix blob on origin/main.
  • Result: 5 of 8 failed, including the [] refusal and the safeValidateSchema case.
  • Control: the ObjectGrid separation test stayed green under the mutation, confirming
    the two same-named keys really are independent.
  • Restore proven by state, not by exit code:git checkout HEAD -- ... (absolute
    path, pinned to HEAD rather than the polluted index), then blob equality against the
    HEAD blob and an empty git diff HEAD. Green leg re-run: 8/8.
  • trap ... EXIT INT TERM with absolute paths throughout.
  • No build/dist leg is required or claimed: the pin imports '../zod/data-display.zod.js',
    a relative same-package specifier vitest resolves to TypeScript source, so no dist/
    copy can serve a stale mutation.

Gates, all on the final commit f17e08244

runresult
pnpm --filter @object-ui/types run type-checkgreen (was red before the ledger line)
pnpm exec vitest run packages/types/92 files, 1547 tests passed
pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/ examples/schema-catalog/162 files, 3209 tests passed
pnpm --filter @object-ui/types run lint0 errors (274 pre-existing warnings)
node scripts/check-control-bytes.mjsOK, 6131 files
node scripts/check-changeset-presence.mjs1 changeset declared
objectui check on the catalog✓ All checks passed

objectui check was run with the real built CLI, and the built dist was confirmed
to carry the change (rowActions: z.ZodOptional of ZodBoolean) rather than a stale
copy. user-table.json and full-featured-table.json no longer appear in its failing
bucket; the five entries that remain there are the components-basic-text ones held by a
separate in-flight PR.

Consumer sweep — the narrowing has no casualties

Grepped the repo for rowActions authored as an array. Every hit outside this PR's own
tests is the list-view/ObjectGrid key: p1-spec-alignment.test.ts (type: 'list-view'),
objectViewHostSurface.test.tsx, the plugin-grid legacy bare-name suite and
resolveLegacyRowActions, plugin-grid/README.md, schema-reference.md and
plugin-grid.mdx. Zero documents author an array rowActions on a data-table, which
measures the ruling's stated basis for a patch bump rather than assuming it.

Changeset

@object-ui/types, patch. The body states the reasoning the ruling requires: no
author can have relied on an array value, because the renderer never reads the array — it
only truthiness-tests the key, so even [] rendered identically to true (#6318 measured
42 elements either way, against 39 with the key absent). An array here could never have
carried meaning to any consumer, so the narrowing removes a spelling that was accepted but
inert.

Review status

Clause ② yes. Draft, carries needs:contract-review, not enqueued, not
auto-merged, not self-reviewed. Waits on the project director seat.


🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…#6940)
The hand-written zod mirror declared `rowActions: z.array(z.any()).optional()`
while every other face of the key says boolean — the TS declaration it mirrors,
the renderer's destructuring default, its truthiness gates and colSpan
arithmetic, the registered `type: 'boolean'` authoring input, `defaultProps`,
and the renderer's own docblock example. The mirror was the outlier and the
published one, so `safeValidateSchema` refused the spelling the component's
documentation, defaults and authoring UI all teach, and two shipped
schema-catalog entries failed validation for that and no other reason.
Maintainer ruling 2026-09-02 (director seat, summon #8), option A. Option B (a
`boolean | array` union) was not taken: it would permanently accept a shape the
renderer only truthiness-tests.
Pinned in data-table-declared-keys-6882.test.ts: `true` and `false` validate,
`[]` is REFUSED and refused ON `rowActions` (every issue path asserted, so a
document rejected for an unrelated reason cannot read as a passing narrowing),
the published `safeValidateSchema` surface moves in both directions, and the two
catalog entries validate unchanged. The list view's same-named `rowActions`
(`ObjectGridSchema`, `z.array(z.string())`) is a different key, is in parity with
its own TS twin, and is pinned here as separate rather than touched.
The #5684 mirror-parity ratchet was NOT blind to this drift: it had measured and
ledgered it as `KnownDrift['data-display.zod.ts#DataTableSchema']`, noting "one
of the two is dead; which is a ruling". That ledger fails on a repair exactly as
it fails on new drift, so correcting the entry to its measured four remaining
keys is part of this change, not a widening of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

needs:contract-review applied — Clause ② yes. ⛔ Not reviewed, not enqueued by this seat.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC, which dispatched #6940. Applying the gate label I said I would in the dispatch brief, before the dev's report is in, because a Clause-② PR carrying no gate marker is invisible to the review queue — the exact state I flagged on PR #7432 twenty minutes ago.

This PR narrows what safeValidateSchema accepts on a published package (DataTableSchema.rowActions: z.arrayz.boolean().optional()), per the ruling on #6940 (5509029916, summon #8, maintainer verbatim 「7189 A 其他同意」, option A). It stays draft; this seat runs opus, is not at CONTRACT_REVIEW_TIER, and will not self-review, enqueue or auto-merge it.

Two fences verified by this seat before the report arrived

Both are traps the card names explicitly, and both held:

  • packages/types/src/zod/objectql.zod.ts is byte-identical to main — compared by blob hash. That file carries a different key also spelled rowActions (z.array(z.string()), the list view's legacy bare-name action list), which the ruling says is correct as it stands. Not touched.
  • user-table.json and full-featured-table.json are unchanged, as the ruling required — they "validate again unchanged" rather than being edited into passing.

⚠️ A fence I set was wrong, and the dev was right to cross it

My dispatch brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as ⛔ do-not-touch, held by PR #7432. This PR edits it, and that edit is forced by the fix rather than optional. The dev's own comment in the diff states the mechanism:

this ledger fails on a repair exactly as it fails on new drift, which is why correcting this line was part of that change and not optional.

rowActions was listed in that file's KnownDrift ledger as a disjoint pair. Once the mirror is repaired the pair is in parity, and a ledger still asserting drift on a parity key fails. Leaving the file untouched to honour my fence would have shipped a red PR. ⇒ The fence was mine and it was wrong; nothing here needs reverting.

Conflict risk with PR #7432 — measured, and it is prose only

Both PRs edit that file, so I checked the actual hunks rather than assuming:

regions touched
this PR~L57 (a count comment), ~L732-754 (the DataTableSchemaKnownDrift entry)
#7432~L131, ~L147, ~L354, ~L521, ~L1483 — all blocks.zod.ts imports, MIRRORS/Declared entries and one EXCLUSIONS row

Disjoint. A textual merge should apply cleanly in either order.

The one residue is prose, not a gate: this PR's header line reads "37 entries52 keys", and #7432 removes nine blocks.zod.ts mirrors, so whichever lands second leaves that count stale. Measured that this cannot redden CI — the ledger's entry/key counts are documentation, with no Object.keys(KnownDrift).length assertion anywhere (control: the file contains 13 expect( calls, so the search instrument fires). Only the MIRRORS population is derived and checked, and #7432 removes from both halves so they still agree.

⇒ No action needed from either PR. Recorded so that a stale count found later reads as known prose rot rather than a missed conflict — the class objectui#7433 tracks.

Full review follows once the dev's structured report is in, including the ruling's rider (why #5684's mirror-parity ratchet did not catch a boolean declaration against an array mirror).


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.5 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-Bvw7UhG5.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)515.05KB117.52KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.14KB13.02KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head f17e08244. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6940 ruling (2026-09-02, 「7189 A 其他同意」, option A): DataTableSchema.rowActions is z.boolean().optional(), the one outlier brought to the six faces that already said boolean; option B (union) correctly not taken. The load-bearing pin is the refusal of [] with every issue on rowActions, and it is measured at the published safeValidateSchema union too; the same-named ObjectGridSchema.rowActions (string[]) is pinned as a different key. The KnownDrift edit is the ratchet's toll for a repair, not scope widening, and the rider's finding — the ratchet was not blind, it ledgered the question — is accepted.

Semver / changeset.@object-ui/typespatch, with the ruling's own reasoning: the narrowing removes a spelling that was accepted but inert (the renderer only truthiness-tests the key; [] rendered as true), and the consumer sweep finds zero data-table documents authoring an array. Correct.

Boundary flags. No governed path. Same-line collision with #7468 on the zod-mirror-parity.test.ts header (53 → 52 here, 53 → 56 there); whichever merges second is ejected as dirty and the seat re-derives (expected 39 entries / 55 keys).

Landing. Carrier cleared, ready, auto-merge (SQUASH). All checks green on f17e08244.


Generated by Claude Code

…counting
The merge queue dequeued PR #7447 after #7344 (PR #7468) landed. Both changes
touch the same ledger; the conflict is in two places and neither side was
correct on its own.
1. The KnownDrift docblock region. main added a real new entry from #7344
('crud.zod.ts#DetailSchema': 'onBack') in the same lines where this branch
rewrote the rowActions prose. BOTH are kept: main's entry with its own
docblock, then this branch's rewritten rowActions paragraph documenting
DataTableSchema, whose union is now the measured four keys.
2. The header census — un-enforced prose, so re-derived BY COUNTING the merged
interface rather than by taking a side:
KnownDrift 39 entries / 55 keys (main's 39/56 less the repaired
rowActions; entry count unmoved)
UnmirroredDeclared 17 entries / 98 keys (untouched by this change)
RuntimeOnlyDeclared 7 entries / 24 keys
160 - 39 = 121 pairs with no KnownDrift entry
The "no entry in either" figure was also re-derived: |U u R| = 18, so
160 - 18 = 142, which is what the file already said — checked rather than
assumed, because the same figure was silently stale in the sibling merge.
Verified: pnpm --filter @object-ui/types run type-check exit 0, 0 'error TS'
(script name echoed in the log, so not a zero-match filter); pnpm exec vitest
run packages/types/ -> Test Files 94 passed, Tests 1601 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge conflict resolved — main merged in, census re-derived by counting

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. This PR left the queue without merging after #7468 (#7344) landed; mergeable_state was dirty. Pushed f17e08244..92db9ebc9 — a merge commit, ⛔ no rebase, amend or force-push.

⚠️ Two conflicts, and the second was data, not just prose:

1. The KnownDrift docblock region.#7344 added a real new entry — 'crud.zod.ts#DetailSchema': 'onBack' — in the very lines where this branch rewrote the rowActions paragraph. Both are kept: main's entry with its own docblock, then this branch's rewritten paragraph documenting DataTableSchema, whose union is the measured four keys (onRowEdit | onRowDelete | onSelectionChange | onColumnsReorder). Taking either side alone would have silently dropped a ledger entry or resurrected a repaired one.

2. The header census — un-enforced prose, so counted from the merged interface rather than chosen:

ledgerentrieskeys
KnownDrift3955
UnmirroredDeclared1798
RuntimeOnlyDeclared724

39 / 55 is main's 39 / 56 less the one key this PR repaired — the entry count correctly does not move, which is what this PR's own body predicted. 160 − 39 = 121 was already right.

⭐ The "no entry in either" figure was checked rather than assumed: |U ∪ R| = 18, so 160 − 18 = 142, which is what the file already said. Worth stating because in the sibling merge (#7464) that same figure had gone stale and git flagged nothing — it merged cleanly since only one side touched it. Here it was genuinely still correct.

Verified before pushing

  • pnpm --filter @object-ui/types run type-check → exit 0, 0error TS, with > @object-ui/types@17.6.0 type-check echoed in the log (not the zero-match filter trap).
  • pnpm exec vitest run packages/types/Test Files 94 passed, Tests 1601 passed.

⛔ Not re-enqueued and auto-merge untouched — the landing is the director seat's. Nothing in the contract review changed; only the merge with main.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.9 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-RyaXrnmX.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.24KB117.82KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataTableSchema.rowActions is z.array in the zod mirror and boolean everywhere else — the mirror is the outlier

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940) - #7447

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror
Sep 3, 2026
Merged

fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940)#7447
os-project-manager merged 2 commits into
mainfrom
claude/issue-6940-rowactions-boolean-mirror

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#6940

DataTableSchema.rowActions in packages/types/src/zod/data-display.zod.ts becomes
z.boolean().optional(). Maintainer ruling 2026-09-02 (director seat, summon #8,
verbatim 「7189 A 其他同意」), option A. Option B (a boolean | array union) was
not taken: it would permanently accept a shape the renderer only truthiness-tests.

- rowActions: z.array(z.any()).optional().describe('Row action buttons'),+ rowActions: z.boolean().optional().describe('Show the row actions column (edit/delete) — mirrors the boolean the renderer truthiness-tests (objectui#6940)'),

The mirror was the single outlier and the published one, so safeValidateSchema refused
the exact spelling the component's TS declaration, destructuring default, truthiness
gates, colSpan arithmetic, registered type: 'boolean' authoring input, defaultProps
and docblock example all teach.

⚠️ Two different keys are named rowActions — confirmed distinguished

Only the data-table one moved. The list view's key is untouched, and git diff proves it.

key changed herekey NOT touched
mirrorzod/data-display.zod.tsDataTableSchemazod/objectql.zod.tsObjectGridSchema
was / isz.array(z.any()) to z.boolean()z.array(z.string()), unchanged
TS twindata-display.tsrowActions?: booleanobjectql.tsrowActions?: string[]
ratchetdrift was ledgered in KnownDriftin parity, ledgered in neither

The separation is now pinned, so a later sweep that "harmonises the two rowActions" on
the strength of a shared name turns red here instead of silently retyping a key no ruling
covers. git diff --name-only -- packages/types/src/zod/objectql.zod.ts returns empty.

The rider — the ratchet was NOT blind, and this is the measurement

#5684's mirror-parity ratchet did catch this drift. It measured it and recorded it in
its KnownDrift ledger:

'data-display.zod.ts#DataTableSchema': 'rowActions' | 'onRowEdit' | 'onRowDelete' | 'onSelectionChange' | 'onColumnsReorder';

with an inline note that named the exact question this card later answered — TS declares
rowActions?: boolean, the mirror declared any[], "One of the two is dead; which is a
ruling."
The ratchet's header records the same entry surviving #5927's sweep
("DataTableSchema kept rowActions").

So there is no structural gap, and there is nothing to file. The ratchet's design is
to convert a drift it cannot adjudicate into a ledgered, citable question rather than a
silent pass. It did exactly that; the question sat in the ledger until the maintainer
answered it on 2026-09-02.

⚠️ Deviation from the dispatch brief — one line in a "do not touch" file

The brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as held by open
PR #7432. This PR edits it anyway, and the reason is mechanical rather than
discretionary — flagging it explicitly for the reviewer.

That ledger is bidirectional by design: "the entry fails when new drift appears on that
mirror AND when the recorded drift is fixed — a stale entry cannot rot quietly."

Repairing rowActions therefore turned the package's own gate red:

src/__tests__/zod-mirror-parity.test.ts(1349,14): error TS2322:
Type '"data-display.zod.ts#DataTableSchema"' is not assignable to type 'never'.

Correcting the entry to its measured remaining four keys is the toll the ratchet charges
for every drift repair — it is not "fixing the ratchet" and not a widening of scope.
Without it pnpm --filter @object-ui/types run type-check cannot pass and the PR is
unreviewable.

Collision with #7432 measured, not assumed: zero. Fetched pull/7432/head and
diffed. Every one of its hunks in this file is in the blocks.zod.ts region (lines
~132–148 imports, ~355 MIRRORS, ~522 Declared, ~1484 EXCLUSIONS), removing the nine
block pairs. This PR's edits are the KnownDrift entry near line 747, its docblock, and
the header key census near line 60 — disjoint regions, no textual conflict either way.

Changes are three: drop 'rowActions' from the union, rewrite the docblock paragraph
that would otherwise be a false statement sitting next to the change, and move the header
census from 53 keys to 52.

Tests

Pinned in packages/types/src/__tests__/data-table-declared-keys-6882.test.ts (the home
the ruling named), 7 new cases:

  1. rowActions: true validates.
  2. rowActions: false validates — it is a boolean, not a truthy-only flag.
  3. rowActions: [] is REFUSED — and every issue path is asserted to be
    rowActions, so a document rejected for an unrelated reason cannot read as a passing
    narrowing pin. This is the load-bearing half: a union would have satisfied a
    "true validates" assertion on its own.
  4. The published safeValidateSchema moves in both directions. Measured at this level
    rather than inferred, because it is a z.union and a sibling member accepting the
    document would have left the published surface unchanged.
  5. Both catalog fixtures exist on disk (guard against a vacuous pass).
  6. Both validate unchanged — they author rowActions: true and always did.
  7. The list view's rowActions still takes string[] and rejects true.

Red-then-green ablation

Reverted only the mirror fix, with the pin left in place.

  • Mutation proven on disk before the run: blob d1d427db to de3a6332, plus anchored
    grep counts (boolean spelling 1 to 0, array spelling 0 to 1). de3a6332 is exactly the
    pre-fix blob on origin/main.
  • Result: 5 of 8 failed, including the [] refusal and the safeValidateSchema case.
  • Control: the ObjectGrid separation test stayed green under the mutation, confirming
    the two same-named keys really are independent.
  • Restore proven by state, not by exit code:git checkout HEAD -- ... (absolute
    path, pinned to HEAD rather than the polluted index), then blob equality against the
    HEAD blob and an empty git diff HEAD. Green leg re-run: 8/8.
  • trap ... EXIT INT TERM with absolute paths throughout.
  • No build/dist leg is required or claimed: the pin imports '../zod/data-display.zod.js',
    a relative same-package specifier vitest resolves to TypeScript source, so no dist/
    copy can serve a stale mutation.

Gates, all on the final commit f17e08244

runresult
pnpm --filter @object-ui/types run type-checkgreen (was red before the ledger line)
pnpm exec vitest run packages/types/92 files, 1547 tests passed
pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/ examples/schema-catalog/162 files, 3209 tests passed
pnpm --filter @object-ui/types run lint0 errors (274 pre-existing warnings)
node scripts/check-control-bytes.mjsOK, 6131 files
node scripts/check-changeset-presence.mjs1 changeset declared
objectui check on the catalog✓ All checks passed

objectui check was run with the real built CLI, and the built dist was confirmed
to carry the change (rowActions: z.ZodOptional of ZodBoolean) rather than a stale
copy. user-table.json and full-featured-table.json no longer appear in its failing
bucket; the five entries that remain there are the components-basic-text ones held by a
separate in-flight PR.

Consumer sweep — the narrowing has no casualties

Grepped the repo for rowActions authored as an array. Every hit outside this PR's own
tests is the list-view/ObjectGrid key: p1-spec-alignment.test.ts (type: 'list-view'),
objectViewHostSurface.test.tsx, the plugin-grid legacy bare-name suite and
resolveLegacyRowActions, plugin-grid/README.md, schema-reference.md and
plugin-grid.mdx. Zero documents author an array rowActions on a data-table, which
measures the ruling's stated basis for a patch bump rather than assuming it.

Changeset

@object-ui/types, patch. The body states the reasoning the ruling requires: no
author can have relied on an array value, because the renderer never reads the array — it
only truthiness-tests the key, so even [] rendered identically to true (#6318 measured
42 elements either way, against 39 with the key absent). An array here could never have
carried meaning to any consumer, so the narrowing removes a spelling that was accepted but
inert.

Review status

Clause ② yes. Draft, carries needs:contract-review, not enqueued, not
auto-merged, not self-reviewed. Waits on the project director seat.


🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…#6940)
The hand-written zod mirror declared `rowActions: z.array(z.any()).optional()`
while every other face of the key says boolean — the TS declaration it mirrors,
the renderer's destructuring default, its truthiness gates and colSpan
arithmetic, the registered `type: 'boolean'` authoring input, `defaultProps`,
and the renderer's own docblock example. The mirror was the outlier and the
published one, so `safeValidateSchema` refused the spelling the component's
documentation, defaults and authoring UI all teach, and two shipped
schema-catalog entries failed validation for that and no other reason.
Maintainer ruling 2026-09-02 (director seat, summon #8), option A. Option B (a
`boolean | array` union) was not taken: it would permanently accept a shape the
renderer only truthiness-tests.
Pinned in data-table-declared-keys-6882.test.ts: `true` and `false` validate,
`[]` is REFUSED and refused ON `rowActions` (every issue path asserted, so a
document rejected for an unrelated reason cannot read as a passing narrowing),
the published `safeValidateSchema` surface moves in both directions, and the two
catalog entries validate unchanged. The list view's same-named `rowActions`
(`ObjectGridSchema`, `z.array(z.string())`) is a different key, is in parity with
its own TS twin, and is pinned here as separate rather than touched.
The #5684 mirror-parity ratchet was NOT blind to this drift: it had measured and
ledgered it as `KnownDrift['data-display.zod.ts#DataTableSchema']`, noting "one
of the two is dead; which is a ruling". That ledger fails on a repair exactly as
it fails on new drift, so correcting the entry to its measured four remaining
keys is part of this change, not a widening of it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

needs:contract-review applied — Clause ② yes. ⛔ Not reviewed, not enqueued by this seat.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC, which dispatched #6940. Applying the gate label I said I would in the dispatch brief, before the dev's report is in, because a Clause-② PR carrying no gate marker is invisible to the review queue — the exact state I flagged on PR #7432 twenty minutes ago.

This PR narrows what safeValidateSchema accepts on a published package (DataTableSchema.rowActions: z.arrayz.boolean().optional()), per the ruling on #6940 (5509029916, summon #8, maintainer verbatim 「7189 A 其他同意」, option A). It stays draft; this seat runs opus, is not at CONTRACT_REVIEW_TIER, and will not self-review, enqueue or auto-merge it.

Two fences verified by this seat before the report arrived

Both are traps the card names explicitly, and both held:

  • packages/types/src/zod/objectql.zod.ts is byte-identical to main — compared by blob hash. That file carries a different key also spelled rowActions (z.array(z.string()), the list view's legacy bare-name action list), which the ruling says is correct as it stands. Not touched.
  • user-table.json and full-featured-table.json are unchanged, as the ruling required — they "validate again unchanged" rather than being edited into passing.

⚠️ A fence I set was wrong, and the dev was right to cross it

My dispatch brief listed packages/types/src/__tests__/zod-mirror-parity.test.ts as ⛔ do-not-touch, held by PR #7432. This PR edits it, and that edit is forced by the fix rather than optional. The dev's own comment in the diff states the mechanism:

this ledger fails on a repair exactly as it fails on new drift, which is why correcting this line was part of that change and not optional.

rowActions was listed in that file's KnownDrift ledger as a disjoint pair. Once the mirror is repaired the pair is in parity, and a ledger still asserting drift on a parity key fails. Leaving the file untouched to honour my fence would have shipped a red PR. ⇒ The fence was mine and it was wrong; nothing here needs reverting.

Conflict risk with PR #7432 — measured, and it is prose only

Both PRs edit that file, so I checked the actual hunks rather than assuming:

regions touched
this PR~L57 (a count comment), ~L732-754 (the DataTableSchemaKnownDrift entry)
#7432~L131, ~L147, ~L354, ~L521, ~L1483 — all blocks.zod.ts imports, MIRRORS/Declared entries and one EXCLUSIONS row

Disjoint. A textual merge should apply cleanly in either order.

The one residue is prose, not a gate: this PR's header line reads "37 entries52 keys", and #7432 removes nine blocks.zod.ts mirrors, so whichever lands second leaves that count stale. Measured that this cannot redden CI — the ledger's entry/key counts are documentation, with no Object.keys(KnownDrift).length assertion anywhere (control: the file contains 13 expect( calls, so the search instrument fires). Only the MIRRORS population is derived and checked, and #7432 removes from both halves so they still agree.

⇒ No action needed from either PR. Recorded so that a stale count found later reads as known prose rot rather than a missed conflict — the class objectui#7433 tracks.

Full review follows once the dev's structured report is in, including the ruling's rider (why #5684's mirror-parity ratchet did not catch a boolean declaration against an array mirror).


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.5 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-Bvw7UhG5.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)515.05KB117.52KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.14KB13.02KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head f17e08244. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6940 ruling (2026-09-02, 「7189 A 其他同意」, option A): DataTableSchema.rowActions is z.boolean().optional(), the one outlier brought to the six faces that already said boolean; option B (union) correctly not taken. The load-bearing pin is the refusal of [] with every issue on rowActions, and it is measured at the published safeValidateSchema union too; the same-named ObjectGridSchema.rowActions (string[]) is pinned as a different key. The KnownDrift edit is the ratchet's toll for a repair, not scope widening, and the rider's finding — the ratchet was not blind, it ledgered the question — is accepted.

Semver / changeset.@object-ui/typespatch, with the ruling's own reasoning: the narrowing removes a spelling that was accepted but inert (the renderer only truthiness-tests the key; [] rendered as true), and the consumer sweep finds zero data-table documents authoring an array. Correct.

Boundary flags. No governed path. Same-line collision with #7468 on the zod-mirror-parity.test.ts header (53 → 52 here, 53 → 56 there); whichever merges second is ejected as dirty and the seat re-derives (expected 39 entries / 55 keys).

Landing. Carrier cleared, ready, auto-merge (SQUASH). All checks green on f17e08244.


Generated by Claude Code

…counting
The merge queue dequeued PR #7447 after #7344 (PR #7468) landed. Both changes
touch the same ledger; the conflict is in two places and neither side was
correct on its own.
1. The KnownDrift docblock region. main added a real new entry from #7344
('crud.zod.ts#DetailSchema': 'onBack') in the same lines where this branch
rewrote the rowActions prose. BOTH are kept: main's entry with its own
docblock, then this branch's rewritten rowActions paragraph documenting
DataTableSchema, whose union is now the measured four keys.
2. The header census — un-enforced prose, so re-derived BY COUNTING the merged
interface rather than by taking a side:
KnownDrift 39 entries / 55 keys (main's 39/56 less the repaired
rowActions; entry count unmoved)
UnmirroredDeclared 17 entries / 98 keys (untouched by this change)
RuntimeOnlyDeclared 7 entries / 24 keys
160 - 39 = 121 pairs with no KnownDrift entry
The "no entry in either" figure was also re-derived: |U u R| = 18, so
160 - 18 = 142, which is what the file already said — checked rather than
assumed, because the same figure was silently stale in the sibling merge.
Verified: pnpm --filter @object-ui/types run type-check exit 0, 0 'error TS'
(script name echoed in the log, so not a zero-match filter); pnpm exec vitest
run packages/types/ -> Test Files 94 passed, Tests 1601 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Merge conflict resolved — main merged in, census re-derived by counting

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. This PR left the queue without merging after #7468 (#7344) landed; mergeable_state was dirty. Pushed f17e08244..92db9ebc9 — a merge commit, ⛔ no rebase, amend or force-push.

⚠️ Two conflicts, and the second was data, not just prose:

1. The KnownDrift docblock region.#7344 added a real new entry — 'crud.zod.ts#DetailSchema': 'onBack' — in the very lines where this branch rewrote the rowActions paragraph. Both are kept: main's entry with its own docblock, then this branch's rewritten paragraph documenting DataTableSchema, whose union is the measured four keys (onRowEdit | onRowDelete | onSelectionChange | onColumnsReorder). Taking either side alone would have silently dropped a ledger entry or resurrected a repaired one.

2. The header census — un-enforced prose, so counted from the merged interface rather than chosen:

ledgerentrieskeys
KnownDrift3955
UnmirroredDeclared1798
RuntimeOnlyDeclared724

39 / 55 is main's 39 / 56 less the one key this PR repaired — the entry count correctly does not move, which is what this PR's own body predicted. 160 − 39 = 121 was already right.

⭐ The "no entry in either" figure was checked rather than assumed: |U ∪ R| = 18, so 160 − 18 = 142, which is what the file already said. Worth stating because in the sibling merge (#7464) that same figure had gone stale and git flagged nothing — it merged cleanly since only one side touched it. Here it was genuinely still correct.

Verified before pushing

  • pnpm --filter @object-ui/types run type-check → exit 0, 0error TS, with > @object-ui/types@17.6.0 type-check echoed in the log (not the zero-match filter trap).
  • pnpm exec vitest run packages/types/Test Files 94 passed, Tests 1601 passed.

⛔ Not re-enqueued and auto-merge untouched — the landing is the director seat's. Nothing in the contract review changed; only the merge with main.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.9 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-RyaXrnmX.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)516.24KB117.82KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)34.34KB9.17KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)48.27KB13.32KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.79KB34.62KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.27KB64.20KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)167.18KB40.94KB
plugin-grid (index.js)209.25KB56.71KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.53KB27.66KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DataTableSchema.rowActions is z.array in the zod mirror and boolean everywhere else — the mirror is the outlier

2 participants

@os-project-manager@claude