Skip to content

fix(plugin-grid,core): the selection bar asks the one declared-gate definition, so a whitespace or empty-source visible gate shows there as on rows and toolbars (objectui#11322) - #11350

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11322-bulk-bar-declared-gate
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11322-bulk-bar-declared-gate

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #11322
Clause-②: no

The grid's selection bar now asks the action family's one "is a gate declared?" definition, hasDeclaredVisibilityGate, before it decides anything about a bulk action. A bulk action whose visible is whitespace-only, or an envelope whose source is blank, now shows on the selection bar and runs over every selected record, as it already does on the row menu and the toolbars of the same grid. The built-in Delete is unchanged: a blank userActions.delete.visibleWhen still excludes every selected record on both bulk bars, as on main. Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec (domain:ui seat 2, branch claude/issue-11322-bulk-bar-declared-gate).

This body describes the diff at head 5661606d6. The first revision (88ed87044) also moved core's partitionRowsByPredicate onto the declared-gate definition. Triage withdrew that half on the retriage question (5926391467, "Not A"): the partitioner is shared, and two of its callers pass the built-in Delete's visibleWhen, a field-rule key. b889377c3 reverted it.

What changed, against main

  • packages/plugin-grid/src/bulkEligibility.ts
    • hasVisibilityGate returns hasDeclaredVisibilityGate(def?.visible), imported from the @object-ui/components barrel, instead of visible != null && visible !== ''.
    • partitionBulkRows asks hasVisibilityGate before it calls the fold, and hands an undeclared gate over as undefined. The action family decides "declared?" here; core's fold never decides it for a bulk action.
  • packages/plugin-grid/src/ObjectGrid.tsx: the built-in bulk Delete hands userActions.delete.visibleWhen to core's partitionRowsByPredicate directly, as plugin-list's ListView already does. The options are the ones partitionBulkRows used to pass (warnOnError true, label delete), so its answer is main's. Why this file: with core back at main's opening, partitionBulkRows has to ask "declared?" first, or the button shows while the run refuses every record. If the built-in Delete had stayed on that door, a blank visibleWhen would admit every record, which is the destructive widening triage ruled out. The ablation legs below measure both halves.
  • packages/core/src/evaluator/listConditional.ts and declaredPredicate.ts: comments only. The code is byte-identical to main.
    • partitionRowsByPredicate's docblock now names its callers: the bulk-action fold through partitionBulkRows, and the built-in Delete's visibleWhen from ObjectGrid and from ListView bulkDeleteEligibility. It says the action family decides "declared?" before calling it. Its "absent" bullet used to say "blank string" and now says what the test is: null, undefined or ''.
    • hasDeclaredPredicate's docblock names the one new consumer, plugin-grid's hasVisibilityGate. It names the field-rule tests that stay outside the family (the built-in row item's != null and the partitioner's own opening test). Per AGENTS.md commandment 9, it says that no test or gate re-derives that paragraph.
  • packages/types/src/objectql.ts: the BulkActionDef.visible docblock only. It now says the selection bar reads an ast-only envelope as no gate.
  • No new predicate and no second blank test. The row menu and toolbar paths are unchanged.

Pins

Selection bar: the real BulkActionBar with one selected row, in bulk-action-visible.test.tsx.

authored visible before (main) after
' ' hidden shown
{ dialect: 'cel', source: ' ' } hidden shown
{ dialect: 'cel', source: '' } hidden shown
'' (control) shown shown
no visible key (control) shown shown
false (control, a declared gate) hidden hidden
  • bulkEligibility.test.ts: hasVisibilityGate is false for the three blank shapes. partitionBulkRows passes every record through by reference for them, which is the run path's answer. hasVisibilityGate answers what hasDeclaredVisibilityGate answers over the whole shape list.
  • specExpressionWire-10946.test.tsx: an ast-only bulk visible is no declared gate, and every record qualifies (restated, see below).
  • Built-in Delete, through the real components: in bulkDeleteVisibleWhen.test.tsx (ObjectGrid) and ListView.bulkDeleteVisibleWhen.test.tsx (ListView's non-grid bar), a whitespace or blank-envelope visibleWhen still excludes every selected record. The grid's dialog refuses, the list states the skipped count, and nothing is deleted. The existing no-gate cases in both files are the controls.

Ablation legs, at 5661606d6

Each mutation went through objectstack's scripts/ablation-replace.mjs, which requires the anchor to hit once and checks that the blob hash moved. Each restore was proven by blob hash == HEAD and an empty git diff HEAD. Every leg ran the same 6 files with 69 tests: the four plugin-grid pin files, ListView.bulkDeleteVisibleWhen.test.tsx and RowActionMenu.test.tsx.

leg failed / passed red rows
none (baseline) 0 / 69 none
hasVisibilityGate restored to != null && !== '' 11 / 58 the 3 blank rows on the real BulkActionBar, the 3 blank hasVisibilityGate rows, the 3 partitionBulkRows by-reference rows, the parity row, and the ast-only row
partitionBulkRows hands def.visible to the fold unasked 4 / 65 the 3 partitionBulkRows by-reference rows and the ast-only row; the bar rows stay green
ObjectGrid's built-in Delete routed back through partitionBulkRows 2 / 67 the 2 grid rows for a blank built-in Delete visibleWhen (no skipped notice: the whole selection went through)
  • With core at main's opening, the plugin-grid site alone decides the bar: restoring hasVisibilityGate alone turns the three bar rows red.
  • The second leg is the run: without the door asking first, the button shows but the fold excludes every record.
  • The third leg is the reason ObjectGrid.tsx is in this diff.
  • In every leg these stay green: the controls ('', no key, false, no built-in gate) and RowActionMenu.test.tsx.

Existing pins and pending changesets restated

  • specExpressionWire-10946.test.tsx: "an ast-only envelope qualifies no record (fail closed), and the fault is warned" now reads "is no declared gate: every record qualifies, as on the row menu". The file's docblock is restated too. Triage accepted this as built.
  • BulkActionDef.visible docblock in @object-ui/types: restated, as above.
  • RowActionMenu.test.tsx: the comment "hasVisibilityGate excludes '' for the selection bar" is restated.
  • Still true, left as they are: RowActionMenu.emptyGuard.test.tsx "an empty-string visible is no gate at all, matching hasVisibilityGate", and the two components tests of the same wording.
  • The pending .changeset/10946-expression-wire-slots-by-reference.md says the bulk def "qualifies no record", and that the plugin-grid file pins the ast-only fault "on both". Those sentences would publish false. Following the precedent of PR objectui#11335's note on the 8655 entry, it gets an append-only, dated note naming this change; its frontmatter and every existing sentence are byte-identical. check-changeset-overwrite reports it as case 2, an intentional correction, with the declaration unchanged.
  • The bot's other 27 pending changesets were re-read against this diff (the paragraph the bot quotes, and 6505-… and 8069-… in full), and each stays true as is. They name objectql.ts, ObjectGrid.tsx or declaredPredicate.ts for subjects this diff does not move: the gantt, form, kanban, tree, gallery and alert-dialog keys, i18n keys, export options, sort, page size, relational meta, the bulk-dialog data source, the validator's delegation to hasDeclaredPredicate, and the field-rule blank report. Found by symbol rather than file name, 4420-bulk-delete-visiblewhen and row-predicate-phase2-record-only-5741 (read in full) and the matching sentences of 8730-… and 9572-… are also true as is.

Changesets

  • @object-ui/plugin-grid patch: the bar shows, and runs, a blank-gated bulk action; an ast-only visible is no gate; the built-in Delete is unchanged.
  • @object-ui/core patch, docblock only, no behavior change. check-changeset-presence needs one declaration in the change (an empty frontmatter counts). This entry is there because the docblock ships in core's .d.ts, the same treatment as the types entry.
  • @object-ui/types patch: docblock only.

Acceptance notes

  • (a) hasDeclaredPredicate answers an ast-only envelope "not declared" with no report, while ADR-0137 D4 says a faulting gate predicate is "diagnosed, never a silent true". That holds on the row menu, the toolbars and now the selection bar alike, so it is a question for the definition, not for one surface. carrier: the PM seat. Measured: authoring does admit an ast-only envelope through one door.
    • @objectstack/spec 17.5.0 refuses it on ActionSchema.visible, BulkActionDefSchema.visible and ObjectSchema userActions.delete.visibleWhen.
    • objectui's own safeValidateSchema refuses it on an action:button visible and on a list-view bulkActionDefs[].visible.
    • objectui's own safeValidateSchema accepts it on an object-grid node's bulkActionDefs[].visible. ObjectGridSchema's zod face does not mirror bulkActionDefs at all: it is ledgered as KnownDrift in zod-mirror-parity.test.ts, and the same probe accepts visible: 42 and bulkActionDefs: 'nope'.
    • objectstack main's spec was not measured: the shared objectstack checkout has no built dist.
  • (b) The field-rule family. For the built-in Delete with visibleWhen: '', the bulk bars admit every record while the row item hides; that is already so on main. The spec refuses these shapes at authoring, so no producer reaches them. carrier: none.
  • action-member-visible-gate.test.tsx's docblock still describes hasDeclaredVisibilityGate as != null && !== '', which has been stale since objectui#3850. Separately, the "installed spec admits it" sentences in the BulkActionDef.visible docblock and in the specExpressionWire-10946.test.tsx docblock are stale: spec 17.5.0's BulkActionDefSchema refuses an ast-only visible. This PR touches neither, and nobody carries either.
  • Spec Main Shape Gate was red on every PR head until objectui#11330's fix merged (47e3ce008); its diagnostics were outside this diff. It is green on head 5661606d6.
  • In-flight overlap: packages/types/src/objectql.ts is also edited by open PR objectui#11335, and was edited by PR objectui#11337 (merged as db0beb2aa). The hunks are disjoint: mine is the BulkActionDef.visible docblock. No open PR touches ObjectGrid.tsx, bulkEligibility.ts, listConditional.ts, declaredPredicate.ts or the built-in Delete tests.

Gates, at head 5661606d6

  • pnpm exec vitest run packages/plugin-grid/src packages/core/src/evaluator packages/plugin-list/src/__tests__/ListView.bulkDeleteVisibleWhen.test.tsx --reporter=verbose: 198 files passed; 2153 tests passed, 27 skipped.
  • pnpm exec vitest run scripts/__tests__/ packages/types/: 485 files passed, 2 skipped; 13175 tests passed, 2 skipped.
  • type-check for @object-ui/core, @object-ui/types, @object-ui/plugin-grid and @object-ui/plugin-list: exit 0 each.
    • They ran after turbo built plugin-grid's dependency closure (13 tasks), which covers plugin-list's.
    • --listFilesOnly puts the changed test files in the test programs: plugin-grid 5, plugin-list 1.
  • pnpm exec eslint on the 11 changed TS files: 0 errors. Per-file warning counts equal main's for every file, so the diff adds none.
    • The population is eslint.config.js's **/*.{ts,tsx}.
    • The config has no type-aware linting and no custom rule that reads the disk, so this diff cannot change a verdict on an untouched file.
  • check-changeset-presence ("11 source file(s) of 4 released package(s) changed, and this change declares 3 changeset(s)"), check-control-bytes, check:new-line-citations (0 new) and markdown-test-inputs --audit: exit 0 each.
  • check-changeset-overwrite: report-only, case 2 as above. The governed-queue guard answers NOT GOVERNED.
  • Declared narrowing: the full pnpm test and pnpm lint belong to CI. That includes plugin-list's, components' and app-shell's whole suites.

Generated by Claude Code

claude added 2 commits October 1, 2026 06:14
…efinition

`bulkEligibility.hasVisibilityGate` and core's `partitionRowsByPredicate`
each answered "is a gate declared?" with a test of their own
(`!= null && !== ''`), so a whitespace-only `visible` or an envelope whose
`source` is blank counted as a gate on the grid's selection bar, failed
closed for every selected record, and hid an action the row menu and the
toolbars of the same grid show.

`hasVisibilityGate` now asks `hasDeclaredVisibilityGate` (the
`@object-ui/components` barrel) and `partitionRowsByPredicate` asks core's
`hasDeclaredPredicate`, the same definition under its core name. The
`declaredPredicate.ts` docblock names the two new consumers, and the
fold's docblock says what it now does, including that the built-in
Delete's `userActions.delete.visibleWhen` reaches the same answer on both
selection bars.

Pins: the real `BulkActionBar` with one selected row shows a whitespace,
a blank-source and an empty-source `visible`, with `''`, no gate and
`visible: false` as controls; the fold returns every row by reference for
the blank shapes.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
…n the one definition makes false

objectui#10946 pinned an `ast`-only envelope on a bulk def's `visible` as
evaluated, faulted and failed closed on the selection bar. That was the
bar's own "declared?" scope (`!= null && !== ''`); the row menu and the
toolbars, which ask `hasDeclaredVisibilityGate`, read the same envelope as
no gate. With the bar asking that definition, every selected record
qualifies. The pin, the `BulkActionDef.visible` docblock that described
the old answer, the fold's docblock and the changesets now say so.

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

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

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

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

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

.changeset/4730-retire-dead-locale-key-batch.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    • Superseded twin vocabularies. cellRender.* and rowAction.* duplicated a grid.* vocabulary that won. RowActionMenu.tsx is fully i18n-wired and reads grid.openMenu / grid.edit / grid.delete; ObjectGrid.tsx reads grid.empty / grid.yes / grid.no / grid.systemFields. The twins had no reader on either side. - Labels that outlived their control. calendar.agenda labelled a view mode b55a34647 retired from CalendarViewMode (now 'month' | 'week' | 'day'). home.quickActions.createApp*, layout.systemNav.createApp, actionDialog.defaultActionTitle / .ok and grid.bulk.selectPlaceholder sit in namespaces whose consumers are live and wired but demonstrably read other siblings. - Surfaces that left the product. map.* is the strongest form: @object-ui/plugin-map declares no @object-ui/i18n dependency and contains no t() call at all, so it cannot consume a locale string. home.stats.* and recordDetail.viewersTooltip name surfaces nothing renders.

.changeset/5903-objectgantt-declared-keys.md

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

    Both halves move together. The TS declaration (packages/types/src/objectql.ts) and its zod mirror (src/zod/objectql.zod.ts) gain the same ten keys at the same requiredness — all optional — and no KnownDrift entry is added. navigation is taken from @objectstack/spec's NavigationConfigSchema by reference rather than restated, matching ObjectGridSchema.navigation.

.changeset/6152-object-form-unmirrored-members.md

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

    ObjectFormSchema in @object-ui/types (objectql.ts) declared a set of members that its zod mirror in @object-ui/types/zod had never heard of. Every one of them is read by the object-form renderer (ObjectForm in @object-ui/plugin-form). The two published faces answered differently:

.changeset/6505-predicate-valued-gate-rules.md

  • names evaluator/declaredPredicate.ts → packages/core/src/evaluator/declaredPredicate.ts — edited by this change

    The verdict is delegated to hasDeclaredPredicate (evaluator/declaredPredicate.ts), the repo's single definition of "is a predicate gate declared on this value?" (objectui#3850's ruling), rather than answered a second time in the validator — a hand-rolled twin that agrees today and drifts tomorrow is the defect class this rule was already an instance of. packages/core/src/validation/__tests__/predicate-valued-gate-rules.test.ts pins the delegation behaviourally: the rule's verdict must equal boolean || hasDeclaredPredicate(value) across every probe in the file.

.changeset/6597-retire-fieldmeta-referenceto.md

  • names plugin-grid/src/ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    No authoring story survived the search either. ObjectGrid's own relational-meta pass-through (applyRelationalMeta, plugin-grid/src/ObjectGrid.tsx) copies reference_to / reference / display_field / etc. from the SCHEMA field def only, at all three of its call sites — never from an authored column override. No doc, example, or fixture in this repo shows a table column pinning a lookup's target away from what its schema field already says. Under the maintainer's standing startup-stage rule (2026-08-27: deprecated/alias spellings retire immediately, no transition windows), no measured demand selects withdraw.

.changeset/7322-object-kanban-group-by-limit.md

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

    Breaking for authored metadata: ObjectKanbanSchema.groupField is RETIRED (objectui#7322, ADR-0049 enforce-or-remove), and the two keys the object-kanban renderer actually reads — groupBy and limit — are now DECLARED and validated on both published faces: the TypeScript interface in objectql.ts and the Zod mirror in zod/objectql.zod.ts.

.changeset/7363-objectql-union-arms.md

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

    ObjectGallerySchema and ObjectDataTableSchema are members of ObjectQLComponentSchema on both faces — the TS union in objectql.ts and the zod union in zod/objectql.zod.ts — so AnyComponentSchema, and with it validateSchema / safeValidateSchema / objectui validate, has an arm for object-gallery and object-data-table nodes (objectui#7363).

.changeset/7632-shared-record-source-config.md

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

    That ladder is published contract on both faces — packages/types/src/objectql.ts and its zod mirror both ship .describe() strings naming getDataConfig's order (77cb489b4, maintainer ruling 2026-09-02), pinned by objectql-record-source-refinement-6939.test.ts — and it was hand-copied into five plugin components with no gate holding them together. A change to the ruled order had five edit sites and nothing that noticed a missed one; that is the AGENTS.md #0.1 drift class.

.changeset/7762-object-grid-export-options-bare-array-refusal.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    What was measured, on this branch's base. The mirror declared NO exportOptions member at all, and BaseSchema is .passthrough(), so ObjectGridSchema.safeParse({ type: 'object-grid', objectName: 'accounts', exportOptions: ['csv', 'xlsx'] }) returned success: true with the array back VERBATIM — as did { formats: ['csv', 'pdf'], compression: 'gzip' }. Nothing on the render path parses, and ObjectGrid.tsx reads schema.exportOptions?.formats and only that, so the authored array then lost SILENTLY to the ['csv', 'json'] default: the useEffect that warns about dropped formats reads .formats too and returns early when it is absent, while !!schema.exportOptions kept the export button on screen. An author declared ['csv', 'xlsx'] and got csv/json with no error, no warning and no console line. The two authoring faces disagreed in the direction opposite to objectui#6956's: the TypeScript interface already declared the object form only, so TS refused what zod admitted.

.changeset/7804-objectql-handler-key-arms.md

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

    The four plain objectql.ts node faces declare the nine handler keys their registered renderers read (the objectql.ts slice): ObjectFormSchema.onCancel / .onError / .onOpenChange / .onStepChange / .onSuccess, ObjectGallerySchema.onCardClick / .onRowClick, ObjectGridSchema.onNavigate and ObjectViewSchema.onNavigate.

.changeset/7804-tree-view-handler-slot.md

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

    'runtime-slot' and not 'retired', measured at this key's own channel. 'retired' publishes "no renderer reads this key, so nothing could ever run it" — true of the two siblings already tombstoned on this arm (onSelectChange, onExpandChange) and flatly false here, since the read is live and INVOKED. ⚠️ No in-repo host builds a tree-view node carrying the key: the channel is wired end to end and only the supplier is absent, which is the same shape as ObjectFormSchema.onStepChange in this card's objectql.ts slice and is not evidence of a dead read. The TypeScript declaration is unchanged and still callable, so a programmatic host supplies it exactly as before.

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

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

    Nothing else moves. These spellings are overloaded across the tree and every other owner is a live key on a different declaration — FormSchema.cancelLabel, objectql.ts's confirmLabel, plugin-designer's ConfirmDialog React props, plugin-grid's def.confirmLabel, and plugin-form's ModalForm / DrawerForm, which build a local cancelLabel from schema.cancelText. None is an AlertDialogSchema; none is touched, and a pin asserts it. No fixture, catalog schema, example app or doc fence authored any of the three on an alert-dialog node, so no shipped document is stranded.

.changeset/8069-field-rule-fault-direction-declared.md

  • names evaluator/declaredPredicate.ts → packages/core/src/evaluator/declaredPredicate.ts — edited by this change

    Both spellings now report [blank] the predicate is declared but empty — nothing to evaluate through the same single reporting site as every other fault, on both channels (the built-in console.warn and the onFault passback, so the fault-probing callers that pass warn: false are not silenced either). Every verdict is unchanged, including the envelope spelling: { source: '' } used to reach the engine and come back "AST-only evaluation not yet supported; persist source" and { source: ' ' } "Unexpected token: EOF" — two misleading reasons for one author mistake, both already resolving to the same fallback this change keeps. Blankness is decided by isBlankPredicateText (evaluator/declaredPredicate.ts), the repo's one definition of that question since objectui#3960, now exported for this second consumer rather than copied.

.changeset/8767-object-grid-refuses-string-sort.md

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

    Migration. Write the array: sort: [{ field: 'name', order: 'desc' }]. Both keys are required. SortConfig.order carries no ? in @object-ui/types (packages/types/src/objectql.ts) and no .optional() in its zod mirror, and the protocol's own reusable SortItemSchema requires order as well — measured: that schema refuses [{ field: 'name' }] with invalid_value at 0.order. Do not omit it: this block's array arm interpolates whatever is present, so an omitted order lowers to $orderby: 'name undefined' today. That is pre-existing behaviour on the arm this change does not touch, and it is filed as a successor card rather than widened into here.

.changeset/8801-object-kanban-allow-collapse-retired.md

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

    • the declarations retired here — packages/types/src/objectql.ts and its mirror packages/types/src/zod/objectql.zod.ts; - the pins that assert the retirement — object-kanban-allow-collapse-retired-8801.test.ts and bare-kanban-node-key-retired-8802.test.ts; - a comment in packages/types/src/zod/complex.zod.ts, recording that the deleted retiredZeroReadKanbanKey helper once carried this spelling on the SIBLING arm; - one row of content/docs/api/schema-reference.md; - the .changeset/ release notes that discuss it — this one, the two historical entries covering the sibling arm's own spelling, and objectui#9629's note recording the correction to this paragraph.

.changeset/8885-object-chart-drilldown-title-compareto.md

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

    ObjectChart.tsx reads all three off schema, and until now neither published copy declared any of them: not the TS interface (packages/types/src/objectql.ts) and not the zod mirror (packages/types/src/zod/objectql.zod.ts). They rode BaseSchema's index signature / .passthrough() and arrived unvalidated. drillDown was the sharpest case — this component's registry inputs advertise it to the designer palette, and @objectstack/spec publishes ChartDrillDownSchema for exactly this carrier, so an author was offered a key that neither published shape mentioned.

.changeset/8913-object-kanban-columns-declared.md

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

    What moved. ObjectKanbanSchema gains columns on both halves that move together — the TypeScript interface (objectql.ts) and its Zod mirror (zod/objectql.zod.ts). Retiring the bare kanban node type key (objectui#8802) removed the only face that judged a lane, and object-kanban had never declared the key, so it rode BaseSchema's [key: string]: any / .passthrough(): read by the renderer at three sites, named by no published face.

.changeset/8990-object-kanban-groupby-optional.md

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

    @objectstack/spec declares the key optional — groupBy: z.string().optional() on ObjectKanbanPropsSchema — while this package required it on the TypeScript declaration (packages/types/src/objectql.ts) and on the Zod mirror (packages/types/src/zod/objectql.zod.ts). objectui was therefore narrower than the protocol on a published key: ObjectKanbanSchema.safeParse and safeValidateSchema refused an object-kanban node the protocol accepts, and such a node could not be annotated with its own type.

.changeset/9092-inline-locale-declared-face.md

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

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)

.changeset/9309-object-gallery-filter-destination-typed.md

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

    ObjectGallerySchema.filter is typed as the destination its own docblock names — QueryParams['$filter'] — on both faces, the TS interface in objectql.ts and the zod mirror in zod/objectql.zod.ts (objectui#9309).

.changeset/9511-record-id-is-a-string.md

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

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

.changeset/9549-tree-filter-declared.md

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

    ObjectTreeSchema.filter is declared on both faces, in the shape objectui#9309 settled for ObjectGallerySchema.filter: QueryParams['$filter'] by indexed access on the TS interface in objectql.ts, and the same two-arm union (array first) on the zod mirror in zod/objectql.zod.ts (objectui#9549).

.changeset/9550-object-tree-root-barrel.md

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

    ObjectQLComponentSchema declares the node types an ObjectQL block may be. Every one of its arms was a named export of this package's root barrel except ObjectTreeSchema, which was declared in objectql.ts, applied by the union, and re-exported by the ./zod barrel (objectui#7917) — while no TypeScript consumer could name it. There is no ./objectql subpath to reach around the barrel: the package's exports map is pinned by packages/types/src/__tests__/package-exports-manifest.test.ts, and the root barrel was the only route to this type.

.changeset/9606-object-kanban-card-title.md

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

    Both published faces of the object-kanban arm now name the key: the zod mirror ObjectKanbanSchema in zod/objectql.zod.ts and its TypeScript twin, the ObjectKanbanSchema interface in objectql.ts. Both declare it OPTIONAL, at the same requiredness the other face uses, so the two faces accept and refuse the same documents. (Located and cited by SYMBOL: line addresses in zod/objectql.zod.ts have drifted before, and this change is itself about a drifted mirror.)

.changeset/9628-kanban-column-collapsed-honoured.md

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

    The key was declared on both published faces of the object-kanban arm — the lane element of ObjectKanbanSchema (objectql.ts and its Zod mirror) and the runtime lane KanbanColumn (complex.ts and its mirror) — and read by KanbanEnhanced alone, a module no production source imports. An authored { "id": "todo", "title": "To Do", "collapsed": true } therefore parsed green on both faces and reached a board that did nothing with it: KanbanImpl's only collapse is the SWIMLANE row's, held in viewer state under objectui:kanban-collapsed:ANGLE-BRACKETS(swimlaneField) and never keyed to a lane's declared value. That is the ADR-0049 declared-but-unhonoured shape.

.changeset/9722-bulk-executor-datasource-face.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    • ObjectGrid.tsx → RecordDetailPanel: removing it type-checks GREEN. It was paying for nothing — both sides already declare the same DataSource | undefined from @object-ui/types. Removed outright. - ObjectGrid.tsx → BulkActionDialog: removing it reddens, and it was paying for TWO things at once — the optional-vs-required arm (the grid declares dataSource?: DataSource, the dialog demands one) AND the bulk-door contravariance. Only the first still needs erasing, so it is now a non-null assertion: today's runtime behaviour is preserved exactly, and any future drift of the face reddens at that site instead of passing silently.

.changeset/9853-grid-non-positive-page-size.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    Refuse a non-positive pageSize at all three of ObjectGrid.tsx's read points, instead of giving two different answers for one authored value (objectui#9853).

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

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

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

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

Compared the checked-out tree with 92bcc82d1 (merge-base with origin/main): 11 file(s) changed outside .changeset/, read against 1905 pending declaration(s) that publish a body (2519 pending in total). · run

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3585.6 KB 3607.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-DFAxqFzI.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.55KB 11.41KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.98KB 15.39KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.54KB 63.59KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.91KB 10.00KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

…shared fold; core's opening test returns to main

Triage's ruling on the retriage question (objectui#11322, comment
5926391467): no destructive widening in this PR. A blank built-in Delete
`visibleWhen` stays excluded on both bulk bars, as on main.

- core: `partitionRowsByPredicate` opens with main's
  `pred == null || pred === ''` again; its code is byte-identical to the
  branch point. Its docblock names its callers (the bulk-action fold, and
  the built-in Delete's `visibleWhen` from `ObjectGrid` and `ListView`),
  says the action family decides "declared?" before calling it, and no
  longer calls its test "blank string". `hasDeclaredPredicate`'s docblock
  names only the consumer that asks it now.
- plugin-grid: `partitionBulkRows` asks `hasVisibilityGate` (the action
  family's `hasDeclaredVisibilityGate`) before handing a def's `visible`
  to the fold. Measured with core at main's opening: the button showed,
  but the blank still reached the fold and every record was excluded, so
  the run would refuse what the bar offered.
- plugin-grid: `ObjectGrid`'s built-in Delete hands
  `userActions.delete.visibleWhen` to `partitionRowsByPredicate`
  directly, as `ListView` does, with the options `partitionBulkRows`
  passed (`warnOnError` true, label `delete`). Its answer is main's.
- pins: the core-hunk test file is removed. Through the real
  `ObjectGrid` and `ListView`, a whitespace or blank-envelope built-in
  Delete `visibleWhen` still excludes every record; the no-gate cases are
  the controls. The core changeset becomes docblock-only.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3552.7 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-Zo9f6Exq.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.55KB 11.41KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.98KB 15.39KB
plugin-charts (index.js) 84.07KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 245.45KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.25KB 44.58KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.59KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.91KB 10.00KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

…lk sentence objectui#11322 makes false

The pending `.changeset/10946-expression-wire-slots-by-reference.md`
says an `ast`-only bulk `visible` qualifies no record, warned, and that
`specExpressionWire-10946.test.tsx` pins that fault "on both". With
objectui#11322 (triage 5926391467 point 3) the selection bar asks the
action family's definition first, so that envelope is no gate there.
An append-only dated note says so; the frontmatter and every existing
sentence are unchanged, as in PR objectui#11335's note on the 8655 entry.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3552.7 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-Zo9f6Exq.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.55KB 11.41KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.98KB 15.39KB
plugin-charts (index.js) 84.07KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 245.45KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.25KB 44.58KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.59KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.91KB 10.00KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5661606d6f0ef55dfa5c551c09aa35b3372ea1cd
Local-runs: none

Inputs: card objectui#11322 (body and all six comments, the two os-dev-reports, the retriage question 5926251271 and triage's binding answer 5926391467, "Not A"), PR objectui#11350 (body, 15-file list, net diff against main at 770cc5ba4), and the 43 latest check-runs on the head. Files at the head and at the base were read through contents/PATH?ref=SHA. One disclosure: to locate every file naming partitionRowsByPredicate, hasVisibilityGate, partitionBulkRows and isBuiltinRowActionVisible (code search is closed to this session), the path list came from a grep of the local objectui checkout at 0eb9f36aca (2026-09-29); every hit was then re-read at the head, and nothing below is judged from that checkout.

Check-runs on the head: 43 latest, all completed; 40 success, 3 skipped (Test (coverage), the coverage shard matrix, dependabot). None failed, none in progress. Test and all 8 shards, Test (dist pins), Type Check, Lint, Build & E2E, Spec Main Shape Gate, Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Claim Re-read, Changeset Overwrite Report, Governed Surface Queue Guard, Pre-Install Import Graph Check and Line Citation Gate are green.

① Derived judgments

Each judgment the diff implies, named right or wrong against triage's ruling (5926391467, points 1 to 4).

  1. Core is comments only — right. listConditional.ts and declaredPredicate.ts with block and line comments stripped compare byte-identical between base and head; every changed line in both hunks is a * docblock line. Core's exports, signatures and accept sets are main's. The docblock sentences hold at the head: partitionRowsByPredicate opens with pred == null || pred === '' (head :417); a whitespace string or blank-source envelope passes that test, reaches evalRowPredicate with warnOnError defaulted to true and fallback: false, evalCel reads the two-fallback disagreement as a fault, and the row is excluded — the "evaluated, and fail closed" sentence. Its named callers are exactly the ones at the head: partitionBulkRows (bulkEligibility.ts:127), ObjectGrid's built-in Delete (ObjectGrid.tsx:4945) and ListView's bulkDeleteEligibility (ListView.tsx:1754); the one other non-test file naming the symbol, rowPredicateCanon.ts, names it in a comment only (:47). hasDeclaredPredicate's new paragraph is true: hasDeclaredVisibilityGate is export { hasDeclaredPredicate as hasDeclaredVisibilityGate } (components/src/renderers/action/visibility-gate.ts, barrel line 60); isBuiltinRowActionVisible opens with if (pred == null) return true in both components/.../data-table.tsx and plugin-grid's RowActionMenu.tsx; and the base's hasVisibilityGate was visible != null && visible !== '' (base :75-78). The paragraph's own caveat ("nothing re-derives this") is stated in the docblock, so no gate is claimed that does not exist.

  2. The built-in Delete keeps main's answer on both bulk bars — right (triage point 1). ObjectGrid.tsx moves in two hunks only: partitionRowsByPredicate joins the @object-ui/core import, and the built-in Delete call site goes from partitionBulkRows({ name: 'delete', visible: vw }, expanded, { scope, fields }) to partitionRowsByPredicate(vw, expanded, { scope, fields, label: 'delete' }). On the base, partitionBulkRows forwarded { scope, fields, warnOnError: true, label: def.name } with def.name === 'delete'; at the head the fold itself sets warnOnError: opts.warnOnError ?? true, so the effective options — scope, fields, warnOnError true, label delete, fallback false — are identical, and the value handed in is the same objectDeletePredicates?.visibleWhen. The base's partitionBulkRows had no "declared?" test in front of the fold, so for every value (no key, '', boolean, string, envelope, whitespace, blank envelope, junk) the head's answer equals the base's. It is also the shape ListView already uses with the same key. Nothing else on the grid's built-in Delete path moves: the bar's Delete is the legacy string button (effectiveBulkActions, rendered by BulkActionBar's actions.map), so hasVisibilityGate never sees it, and builtInDeleteDef() is still constructed only for the dialog route. Pinned through the real components and green on the head: bulkDeleteVisibleWhen.test.tsx (grid: whitespace and whitespace-source envelope open the dialog with the skipped notice, Run disabled, nothing deleted) and ListView.bulkDeleteVisibleWhen.test.tsx (the list bar states the skipped count, dispatches nothing), with the existing no-gate cases as controls.

  3. The action fold asks the family's definition first — right (triage point 2), and it is not a second blank test. hasVisibilityGate(def) is return hasDeclaredVisibilityGate(def?.visible); — one call, no copied scope, no trim of its own. partitionBulkRows hands the fold hasVisibilityGate(def) ? def.visible : undefined. That is the action family deciding "declared?" at its own site through the one definition, before the shared partitioner; core's opening test is untouched and serves its field-rule callers. The one side effect is reportBlankGate, which fires from the button's ask and from the fold's ask alike and is deduped once per blank spelling (ADR-0137 D4), as the changeset says ("reported once").

    • A consequence the diff implies beyond the card's table, named here: everything hasDeclaredPredicate calls "not declared" now reaches the fold as undefined and admits every selected record by reference — '' (as on main), whitespace, the blank envelope, the ast-only envelope (ruled, point 3), and junk that is not a predicate at all (0, {}, an array; toPredicateInput returns undefined, silently). On main the junk and ast-only shapes were evaluated, warned and excluded. This is the definition's documented fail-open-on-junk posture arriving with it, which the claim's "no copy of its scope" and triage's "as built" cover; the junk shapes reach a bulk def through the same unmirrored object-grid zod door the dev measured (visible: 42 passes), so they belong with acceptance note (a) and its card, not with this PR. Right, with that flag carried in ③.
  4. The card's table is delivered on the real BulkActionBar — right (triage point 2's deliverable). bulk-action-visible.test.tsx renders BulkActionBar under PredicateScopeProvider with selectedRows={[{ id: '1' }]}: ' ', { dialect: 'cel', source: ' ' } and { dialect: 'cel', source: '' } each show the button; '' and no key show (the card's controls); false hides (a declared gate). The run path admits what the bar offers: bulkEligibility.test.ts has partitionBulkRows return eligible by reference (toBe(ROWS), skipped 0) for the three blanks, and the parity row equates hasVisibilityGate with hasDeclaredVisibilityGate over eight shapes including the booleans. All Test shards are green on the head.

  5. The ast-only restatement — right (triage point 3, accepted as built). At the head, toPredicateInput({ dialect: 'cel', ast: … }) returns undefined (no string source), isBlankPredicateText is false for it, so hasDeclaredPredicate answers "not declared" with no report. specExpressionWire-10946.test.tsx pins exactly that (eligible toBe(BULK_ROWS), skipped 0) and keeps the formatting-rule ast-only fault case (no row painted, warned), which is what the appended changeset note says. The BulkActionDef.visible docblock's added sentences are true at the head; the type member is unchanged. .changeset/10946-expression-wire-slots-by-reference.md: the base's 17 lines are byte-identical to the head's first 17 (frontmatter '@object-ui/types': minor included); the dated note is appended after them. The PR body's two "left as they are" claims hold: RowActionMenu.emptyGuard.test.tsx:576 keeps "no gate at all, matching hasVisibilityGate" and it is still true; action-member-visible-gate.test.tsx:18 still says != null && !== '', stale and untouched, as the body says.

  6. Public surface. plugin-grid's barrel (src/index.tsx) exports BulkActionBar and BulkActionBarProps; bulkEligibility.ts is not exported, so hasVisibilityGate and partitionBulkRows are package-internal and their changed answers are not a published-export change. No export, key, arm or registration is added or removed anywhere in the diff; BulkEligibilityDef, BulkActionDef and core's declarations are unchanged. The one published behavior that moves is the rendered BulkActionBar's verdict for the shapes in item 3, pulled onto the family's declared definition. Adding @object-ui/components as an import of bulkEligibility.ts is within plugin-grid's existing dependency (ObjectGrid.tsx already imports it); Pre-Install Import Graph Check is green.

  7. The row menu and toolbar paths are unchanged — right. No RowActionMenu.tsx, data-table.tsx or action:* renderer is in the diff; only the RowActionMenu.test.tsx comment is restated, truthfully.

② Semver level

Three changesets, each patch: @object-ui/plugin-grid (the bar shows and runs a blank-gated bulk action; ast-only is no gate; the built-in Delete unchanged — matches the diff), @object-ui/core ("docblock only, no behavior change" — true, item ① 1), @object-ui/types ("docblock only" — true, item ① 5). The plugin-list change is a test file only and declares nothing, which Changeset Declaration accepts. Changeset Bump Policy, Changeset Fixed Group Check, Changeset Claim Re-read and Changeset Overwrite Report are green; the 10946 entry's declaration is unchanged, so its edit is the report-only intentional-correction case the PR body names.

Clause-②: no — holds against the built diff. No published type, export or authoring accept set changes; hasVisibilityGate's and partitionBulkRows's answers moved for the blank, ast-only and junk shapes (narrowing "declared", widening "admitted"), but neither is a public export, and the surface they drive — the selection bar — is pulled back onto the declared contract (hasDeclaredPredicate, the objectui#3812 family) rather than widened past it. The one sentence a reader could weigh the other way is the BulkActionDef.visible docblock's ast-only answer, which was a published fail-closed statement and is now "no gate": that is the ruled restatement of a runtime verdict under one definition (triage point 3), the installed spec refuses the shape at every authoring slot the dev measured, and the one door that admits it is the unmirrored object-grid face carried by acceptance note (a). A bug fix in released packages, patch, is the right level.

③ Boundary flags

Dev report 5927286661: open_questions is empty. Its deviations and out-of-scope findings, each answered:

  • File surface widened to ObjectGrid.tsx and ListView.bulkDeleteVisibleWhen.test.tsx (beyond claim 5925703783 and the seat's supplement in 5926251271). Answered: triage point 1 requires a blank built-in Delete visibleWhen to stay excluded on both bars while point 2 makes partitionBulkRows ask "declared?" first; the only way both hold is the grid's built-in Delete leaving that door, which is the ObjectGrid.tsx hunk, and the list pin is point 1's "both bulk bars". The ruling's own consequence; accepted. The dev's ablation leg (built-in Delete routed back through partitionBulkRows, 2 of 67 red) is the dev's measurement, consistent with the code read in ① 2.
  • Point 2's verification done inside plugin-grid, core untouched. Answered by the ruling's own words ("the action family decides 'declared?' before calling it … no second blank test is written anywhere"); verified in ① 3.
  • Core changeset kept as docblock-only. Answered: true to the diff; harmless; Changeset Declaration green.
  • 10946 entry edited append-only. Verified byte-identical leading content in ① 5; Changeset Overwrite Report green.
  • Measurement gap: objectstack main's spec not measured for the ast-only authoring question. Escalated with acceptance note (a): carrier: the PM seat, which the dispatch brief records as filed (objectui#11358). Not this PR's to close.
  • Attribution trailer form. A process note, not a contract question; no finding.
  • Out-of-scope (a), the unmirrored object-grid bulkActionDefs[].visible door admitting an ast-only envelope (and, per the dev's probe, 42 and 'nope'), with hasDeclaredPredicate answering silently against ADR-0137 D4: carried as the PR body says (carrier: the PM seat; objectui#11358 per the brief). This reviewer adds the junk-value consequence from ① 3 to that same card's scope: through that door, 0 / {} / an array on a bulk visible now admit every selected record on the bar silently, where main warned and excluded. Not a blocker here: it is the family definition's ruled posture on every other surface, and no authoring slot the installed spec governs reaches it.
  • Out-of-scope (b), the field-rule family (visibleWhen: '' admits on the bulk bars, hides on the row item, already on main): carrier: none, as triage point 4 ruled under the filing gate.
  • Stale prose untouched (action-member-visible-gate.test.tsx docblock; the "installed spec admits it" sentences in the BulkActionDef.visible and 10946-test docblocks): carrier: none; verified present and untouched at the head.
  • PR body. Every checkable sentence is true at this head. One is true by mechanism rather than by literal reading: "the options are the ones partitionBulkRows used to pass (warnOnError true, label delete)" — warnOnError is not passed at the new call site; the fold's ?? true default supplies it, so the effective options are identical (① 2). Acceptance notes (a) and (b) are as triage named them, with (a) measured. The local-gate sentences are the dev's own account; the head's Test, Type Check and Lint runs corroborate them. The Spec Main Shape Gate sentence is true: green on this head.

Implemented-by: claude/issue-11322-bulk-bar-declared-gate
Reviewed-by: session_011p7ikEivgXefNDaE5S5Uec

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 08:16
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 1, 2026 08:16
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit 5638529 Oct 1, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11322-bulk-bar-declared-gate branch October 1, 2026 08:32
os-litant pushed a commit that referenced this pull request Oct 1, 2026
…jectui#11168 slice 3, round 3, third merge)

origin/main advanced to 5638529 while the round-3 gates ran: #11317
(docs), #11352 (types, fields) and #11350 (plugin-grid, core). No
conflict. #11352 also edits packages/types objectql.ts, objectql.zod.ts
and zod-mirror-parity.test.ts, which this branch edits too; it
auto-merged, and the gates are re-run at this head. The guard is
untouched by this merge, so its census still reads as at 2817b5f:
objectui#11168 5, unjudgedBlocks 2, refusedArms 0.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment