Repository navigation
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
Conversation
…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>
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
…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>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
…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>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Inputs: card objectui#11322 (body and all six comments, the two Check-runs on the head: 43 latest, all ① Derived judgmentsEach judgment the diff implies, named right or wrong against triage's ruling (
② Semver levelThree changesets, each Clause-②: no — holds against the built diff. No published type, export or authoring accept set changes; ③ Boundary flagsDev report
Implemented-by: VERDICT: PASS Generated by Claude Code |
…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>
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 whosevisibleis whitespace-only, or an envelope whosesourceis 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 blankuserActions.delete.visibleWhenstill excludes every selected record on both bulk bars, as onmain. Session:https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec(domain:uiseat 2, branchclaude/issue-11322-bulk-bar-declared-gate).This body describes the diff at head
5661606d6. The first revision (88ed87044) also moved core'spartitionRowsByPredicateonto 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'svisibleWhen, a field-rule key.b889377c3reverted it.What changed, against
mainpackages/plugin-grid/src/bulkEligibility.tshasVisibilityGatereturnshasDeclaredVisibilityGate(def?.visible), imported from the@object-ui/componentsbarrel, instead ofvisible != null && visible !== ''.partitionBulkRowsaskshasVisibilityGatebefore it calls the fold, and hands an undeclared gate over asundefined. 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 handsuserActions.delete.visibleWhento core'spartitionRowsByPredicatedirectly, as plugin-list'sListViewalready does. The options are the onespartitionBulkRowsused to pass (warnOnErrortrue, labeldelete), so its answer ismain's. Why this file: with core back atmain's opening,partitionBulkRowshas 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 blankvisibleWhenwould admit every record, which is the destructive widening triage ruled out. The ablation legs below measure both halves.packages/core/src/evaluator/listConditional.tsanddeclaredPredicate.ts: comments only. The code is byte-identical tomain.partitionRowsByPredicate's docblock now names its callers: the bulk-action fold throughpartitionBulkRows, and the built-in Delete'svisibleWhenfromObjectGridand fromListViewbulkDeleteEligibility. 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,undefinedor''.hasDeclaredPredicate's docblock names the one new consumer, plugin-grid'shasVisibilityGate. It names the field-rule tests that stay outside the family (the built-in row item's!= nulland 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: theBulkActionDef.visibledocblock only. It now says the selection bar reads anast-only envelope as no gate.Pins
Selection bar: the real
BulkActionBarwith one selected row, inbulk-action-visible.test.tsx.visible' '{ dialect: 'cel', source: ' ' }{ dialect: 'cel', source: '' }''(control)visiblekey (control)false(control, a declared gate)bulkEligibility.test.ts:hasVisibilityGateis false for the three blank shapes.partitionBulkRowspasses every record through by reference for them, which is the run path's answer.hasVisibilityGateanswers whathasDeclaredVisibilityGateanswers over the whole shape list.specExpressionWire-10946.test.tsx: anast-only bulkvisibleis no declared gate, and every record qualifies (restated, see below).bulkDeleteVisibleWhen.test.tsx(ObjectGrid) andListView.bulkDeleteVisibleWhen.test.tsx(ListView's non-grid bar), a whitespace or blank-envelopevisibleWhenstill 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
5661606d6Each 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 emptygit diff HEAD. Every leg ran the same 6 files with 69 tests: the four plugin-grid pin files,ListView.bulkDeleteVisibleWhen.test.tsxandRowActionMenu.test.tsx.hasVisibilityGaterestored to!= null && !== ''BulkActionBar, the 3 blankhasVisibilityGaterows, the 3partitionBulkRowsby-reference rows, the parity row, and theast-only rowpartitionBulkRowshandsdef.visibleto the fold unaskedpartitionBulkRowsby-reference rows and theast-only row; the bar rows stay greenObjectGrid's built-in Delete routed back throughpartitionBulkRowsvisibleWhen(no skipped notice: the whole selection went through)main's opening, the plugin-grid site alone decides the bar: restoringhasVisibilityGatealone turns the three bar rows red.ObjectGrid.tsxis in this diff.'', no key,false, no built-in gate) andRowActionMenu.test.tsx.Existing pins and pending changesets restated
specExpressionWire-10946.test.tsx: "anast-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.visibledocblock in@object-ui/types: restated, as above.RowActionMenu.test.tsx: the comment "hasVisibilityGateexcludes''for the selection bar" is restated.RowActionMenu.emptyGuard.test.tsx"an empty-stringvisibleis no gate at all, matchinghasVisibilityGate", and the two components tests of the same wording..changeset/10946-expression-wire-slots-by-reference.mdsays the bulk def "qualifies no record", and that the plugin-grid file pins theast-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-overwritereports it as case 2, an intentional correction, with the declaration unchanged.6505-…and8069-…in full), and each stays true as is. They nameobjectql.ts,ObjectGrid.tsxordeclaredPredicate.tsfor 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 tohasDeclaredPredicate, and the field-rule blank report. Found by symbol rather than file name,4420-bulk-delete-visiblewhenandrow-predicate-phase2-record-only-5741(read in full) and the matching sentences of8730-…and9572-…are also true as is.Changesets
@object-ui/plugin-gridpatch: the bar shows, and runs, a blank-gated bulk action; anast-onlyvisibleis no gate; the built-in Delete is unchanged.@object-ui/corepatch, docblock only, no behavior change.check-changeset-presenceneeds 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/typespatch: docblock only.Acceptance notes
hasDeclaredPredicateanswers anast-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 anast-only envelope through one door.@objectstack/spec17.5.0 refuses it onActionSchema.visible,BulkActionDefSchema.visibleandObjectSchemauserActions.delete.visibleWhen.safeValidateSchemarefuses it on anaction:buttonvisibleand on alist-viewbulkActionDefs[].visible.safeValidateSchemaaccepts it on anobject-gridnode'sbulkActionDefs[].visible.ObjectGridSchema's zod face does not mirrorbulkActionDefsat all: it is ledgered asKnownDriftinzod-mirror-parity.test.ts, and the same probe acceptsvisible: 42andbulkActionDefs: 'nope'.main's spec was not measured: the shared objectstack checkout has no builtdist.visibleWhen: '', the bulk bars admit every record while the row item hides; that is already so onmain. The spec refuses these shapes at authoring, so no producer reaches them.carrier: none.action-member-visible-gate.test.tsx's docblock still describeshasDeclaredVisibilityGateas!= null && !== '', which has been stale since objectui#3850. Separately, the "installed spec admits it" sentences in theBulkActionDef.visibledocblock and in thespecExpressionWire-10946.test.tsxdocblock are stale: spec 17.5.0'sBulkActionDefSchemarefuses anast-onlyvisible. This PR touches neither, and nobody carries either.Spec Main Shape Gatewas red on every PR head until objectui#11330's fix merged (47e3ce008); its diagnostics were outside this diff. It is green on head5661606d6.packages/types/src/objectql.tsis also edited by open PR objectui#11335, and was edited by PR objectui#11337 (merged asdb0beb2aa). The hunks are disjoint: mine is theBulkActionDef.visibledocblock. No open PR touchesObjectGrid.tsx,bulkEligibility.ts,listConditional.ts,declaredPredicate.tsor the built-in Delete tests.Gates, at head
5661606d6pnpm 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-checkfor@object-ui/core,@object-ui/types,@object-ui/plugin-gridand@object-ui/plugin-list: exit 0 each.--listFilesOnlyputs the changed test files in the test programs: plugin-grid 5, plugin-list 1.pnpm exec eslinton the 11 changed TS files: 0 errors. Per-file warning counts equalmain's for every file, so the diff adds none.eslint.config.js's**/*.{ts,tsx}.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) andmarkdown-test-inputs --audit: exit 0 each.check-changeset-overwrite: report-only, case 2 as above. The governed-queue guard answers NOT GOVERNED.pnpm testandpnpm lintbelong to CI. That includes plugin-list's, components' and app-shell's whole suites.Generated by Claude Code