Repository navigation
fix(plugin-grid)!: a list's selection bar and row menu draw only actions the object declares (#30169) - #30387
objectstack-fleet[bot] wants to merge 9 commits into
Conversation
…ot declare draw nothing
…ckage trigger, repair fixtures
…angeset for the undeclared-name narrowing Claude-Session: https://claude.ai/code/session_01Utp6CZ1rxZPzain2p5bv5x Co-authored-by: Claude <noreply@anthropic.com>
…as the runner's Claude-Session: https://claude.ai/code/session_01Utp6CZ1rxZPzain2p5bv5x Co-authored-by: Claude <noreply@anthropic.com>
…ithout an any cast Claude-Session: https://claude.ai/code/session_01Utp6CZ1rxZPzain2p5bv5x Co-authored-by: Claude <noreply@anthropic.com>
… rewrite derives it Claude-Session: https://claude.ai/code/session_01Utp6CZ1rxZPzain2p5bv5x Co-authored-by: Claude <noreply@anthropic.com>
|
domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2 Waits-on: main-red #30367
|
|
domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2 Contract reviewServed-tier: Inputs: card #30169 (body and every comment; ruling 6651753071 = option C, read verbatim), PR #30387 body, file list, net diff ① Derived judgmentsAccept-set and surface changes the diff implies, each judged against the ruling and the cited code:
② Semver level
③ Boundary flagsEvery flag in the os-dev-report (card comment 6654001061) and the earlier report (6642981552):
④ Design judgmentPer scope file, against the ruling and the cited code; each break attempt named.
No must-fix. Should-fixes (a)-(d) are each a few lines in Check-runs on this head (their conclusions are the gate verdicts, read after every run completed): Implemented-by: VERDICT: PASS |
…ect-thread-8y2k2m Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CZ1rxZPzain2p5bv5x
…bject, reports an unread schema, gates in a production build, and says it once
Contract review 6654355327 should-fixes (a)-(d), objectstack#30169:
(a) Readiness and the message's address use the resolved objectName
(resolveRecordSourceObjectName), so a grid naming its object only through
data.object no longer reports declared names before the schema lands.
(b) Mirrors objectTypesPending: a schema is awaited only with an object and a
getObjectSchema; a failed read (inline: objectSchemaReadFailed; row load:
error) is not awaited. Names reported with no schema in hand are said to be
unread, not undeclared.
(c) The dev gate is the bundler-replaced literal process.env.NODE_ENV with a
module-scoped declare (the resolveActionParams idiom); the globalThis chain
survived the Console build and read its window.process shim. Pinned by
transforming the module with the Console's define.
(d) A module-level Set keyed by the message makes the warning once under
StrictMode and across a remount; reset exported for tests.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CZ1rxZPzain2p5bv5x
|
domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2 Contract reviewServed-tier: Delta review of this seat's record on head ① Derived judgmentsEach should-fix against the tree:
② Semver levelUnchanged from the reviewed head and still matching the tree: ③ Boundary flags
④ Design judgment
No must-fix, no new should-fix. Check-runs on this head (their conclusions are the gate verdicts, read after every run completed): every non-skipped check-run is Implemented-by: VERDICT: PASS |
|
domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2 维护者速读
席位核对 — ACCEPT · domain:ui#2 · head
|
Requested by Bill · project thread
domain:ui#2 · os-bill · session_01Utp6Cb5R6JDdfyvtj3dPR2
Fixes #30169
Clause-②: no (narrowing) — the grid stops drawing a bare
bulkActionsor legacyrowActionsname that matches no action the object declares.packages/spec/src/ui/bulk-action.zod.tsalready says the member "names an action the object declares", and lintaction-name-undefined(ADR-0049) refuses any other at authoring. Owes aCONTRACT_REVIEW_TIERreview before the queue.Metadata-protocol: narrowed (an undeclared bare
bulkActions/rowActionsname no longer draws)Skills-docs: none owed —
skills/**teaches no handler-only bulk or row action name (searchedskills/forbulkActions/rowActionsexamples and runner-handler text: no hit);content/docs/ui/**already names only declared actions.维护者速读
ui/packages/plugin-grid/src/ObjectGrid.tsx(effectiveBulkActions不再并入未声明名字、两次警告调用)→ui/packages/plugin-grid/src/undeclaredActionNames.ts→ 测试objectBulkActionDispatch.test.tsx新 describe「a bulk action a disabled package withholds」。pnpm --dir ui exec vitest run packages/plugin-grid/src/__tests__/objectBulkActionDispatch.test.tsx,预期全部通过;其中「withheld」用例在旧逻辑下转红(选择栏画出了被扣下的按钮)。What changes
resolveBulkActionsandresolveLegacyRowActionskept a bare name that matched no action inobjectDef.actions, andObjectGriddrew it by humanized name and dispatched it to the runner by name, on the reasoning that a host might have registered a runner handler under it. That is how a list whose app has a package disabled kept a ghost button: the server withholds the package's actions from the served object, so their names match nothing, and the click toastedAction "create_campaign" is not executable. Ruling 6651753071 (option C) retires the tolerance.resolveBulkActions/resolveLegacyRowActions. A name with no declared match produces no def. It comes back asundeclared(renamed fromunresolved; neither function is a package export), used only for the warning below. Both module headers now state the rule and its source (the spec sentence andaction-name-undefined), replacing the "that path must keep working" paragraph.ObjectGrid. The selection bar's string list is now only the canonical'delete'(author-declared or the implicit bulk delete), unchanged in its routing (onBulkDelete, or the runner by name when noonBulkDeleteis wired). The row menu no longer receives a by-name list:objectGridActionsColumn.tsxdrops thecustomRowActionsinput, so the Actions column and the kebab read only resolved defs plus the canonical Edit / Delete.undeclaredActionNames.ts(new).describeUndeclaredActionNamesbuilds one message per fold naming the key, the block address and every dropped name;useUndeclaredActionNamesWarninglogs it once per distinct list, only whenNODE_ENVis notproduction, and only once the object's schema has loaded (or the grid names no object), so a list still loading its object is never reported.ObjectGridcalls it twice, forbulkActionsandrowActions, in hook order before the early returns.demo/bulk-actions.tsxswapslegacy_only_handler(a handler-only name) fornot_declared, drops the handler, and says the bar draws no button for it.README.mdreplacesbulkActions: ['delete', 'export']androwActions: ['view', …](exportandvieware handler-only) with a declareddeactivate, and states the rule under Row Actions.Unchanged: a name that matches a declared action is promoted exactly as before (label, icon, params,
visible,requiredPermissions);bulkActionDefsare untouched;deletekeeps its own path (keptDeleteAction/onBulkDelete).Consumers measured (this tree, every non-test
bulkActions/batchActions/rowActionslist underexamples/,ui/apps/,ui/packages/,packages/):examples/app-showcase(task.view.ts,field-zoo.view.ts) names only actions declared insrc/ui/actions/index.ts;packages/lintfixtures mirror the showcase. The one handler-only name was the plugin-grid demo, changed here. The Console's runner handlers (useObjectActions.ts:create,delete,navigate,refresh;AppContent.tsx:crud_success,dialog_cancel,navigate_create,navigate_edit) are action TYPES for other surfaces, not bulk or row names. No fallback is kept for an out-of-repo host; its remedy is in the changeset's FROM → TO.Tests
Pins of the old tolerance, each flipped to the new behaviour with the trigger named in the test:
bulkActionMembers-8071.test.tsxrow 3:bulkActions: ['approve', 'no_such_action']draws exactlybulk-action-approve, no "No Such Action", and oneObjectGrid bulkActionswarning naming'no_such_action'.objectBulkActionDispatch.test.tsx("keeps an unresolvable name alongside the promoted defs" → "draws no button for a name the object does not declare, even with a runner handler under it"): a handler registered undercrm_only_handlerneither draws the button nor runs.legacyRowActionDispatch.test.tsx("leaves a name with no declared action to the by-name handler path" → "draws no entry … and warns once naming it"): the declaredconvert_leadentry draws,crm_only_handlerdoes not, oneObjectGrid rowActionswarning.resolveBulkActions.test.ts,resolveLegacyRowActions.test.ts: the by-name cases flip to "no def, reported as undeclared"; a new row pins that a repeated undeclared row name is reported once.Trigger test (new top-level describe in
objectBulkActionDispatch.test.tsx, the shared harness): the view namespush_downandcreate_campaign. Withheld (the served object lackscreate_campaign): nobulk-action-create_campaign, no "Create Campaign", one warning naming it and notpush_down. Served: the button reads the action's own label "Add to Campaign", and no warning.Fixtures that used an undeclared name as a stand-in, repaired (behaviour under test unchanged):
bulkActionsSpecKey.test.tsx,rowCrudEffectiveOps.test.tsx,gridPagerSelectionMembers-8071.test.tsx,actionsColumnAndChips-12708.test.tsx: the object now declares the named action (approve/reject,notify,approve,archive).bulkActionRefresh.test.tsx: the by-name dispatch path these rows pin (refresh and selection reset after a string action) is now reached by the canonical'delete'with noonBulkDelete, the handler registered underdelete.Runs (all
pnpm --dir ui exec vitest run --maxWorkers=2 …underscripts/pm/os-verify-lock.sh):9afcd6a6, the five resolver / dispatch files before any edit:Test Files 5 passed (5),Tests 54 passed (54)(the old pins green, encoding the tolerance).packages/plugin-grid/after the source change, before the test changes:Test Files 10 failed | 267 passed (277),Tests 27 failed | 2350 passed (2377), the 27 being exactly the pins and fixtures listed above.Test Files 11 passed (11),Tests 124 passed (124).Test Files 14 passed (14),Tests 542 passed (542)andTest Files 10 passed (10),Tests 237 passed (237).Ablation, two legs through
scripts/ablation-replace.mjs(tests importsrc, no build involved), each landing verified and restored to the HEAD blob withgit diff HEADempty:effectiveBulkActionsre-includes the undeclared names (? keptDeleteAction→? [...undeclaredBulkActions, ...keptDeleteAction]):Tests 3 failed | 17 passed (20)— 8071 row 3, the withheld trigger, the handler-name row.console.warndisabled:Tests 3 failed | 24 passed (27)— 8071 row 3, the row-menu warning row, the withheld trigger.Type-check:
pnpm exec turbo run type-check --filter=@object-ui/plugin-grid --concurrency=2(tsc --noEmit && tsc -p tsconfig.test.json):Tasks: 53 successful, 53 total. Lint:eslinton every touched plugin-grid file, 0 errors (warnings are pre-existingno-explicit-anyin the touched files; the new module has none).Acceptance notes
ui/packages/plugin-list/src/ListView.tsxdraws its own bulk bar for non-grid views (currentView !== 'grid', thebulk-actions-barregion mappingpermittedBulkActions), and it renders every bare name by label with no resolution against the object's actions. Under this ruling's rule ("the selection bar draws only actions the object declares") that bar still draws an undeclared or withheld name. Read-only inference, not reproduced in a browser; outside this card's file surface, so not touched.ui/AGENTS.md✨ Set up Copilot instructions #2 (docs-driven) also names the matching page undercontent/docs/frontend/; that tree is outside the claimed surface.content/docs/ui/actions.mdxandcontent/docs/ui/views.mdxalready show only declared names.Verification
Derived gates (dispatch-gates --commands on the final head 2f621a5): 36 run, all exit 0
node scripts/check-adr-0087-registration.mjs --base origin/main:: exit 0node scripts/check-adr-0087-registration.mjs --self-test:: exit 0node scripts/check-changeset-no-major.mjs --base origin/main:: exit 0node scripts/check-closing-keyword-parity.mjs:: exit 0node scripts/check-closing-keyword-parity.mjs --self-test:: exit 0node scripts/check-comment-mask-corpus.mjs:: exit 0node scripts/check-empty-changeset.mjs --base origin/main:: exit 0node scripts/check-empty-changeset.mjs --self-test:: exit 0node scripts/check-sdui-manifest.mjs:: exit 0node scripts/check-sdui-manifest.mjs --self-test:: exit 0node scripts/pm/release-rehearsal-clone.mjs --self-test:: exit 0node scripts/release-pending-publish.mjs --self-test:: exit 0pnpm --dir ui exec vitest run scripts/:: exit 0pnpm --filter @objectstack/spec run check:duration-unit-keys:: exit 0pnpm check:changeset-gate-self-tests:: exit 0pnpm check:closed-ceremony-text:: exit 0pnpm check:cross-package-test-inputs:: exit 0pnpm check:dispatcher-error-vocabulary:: exit 0pnpm check:driver-memory-census:: exit 0pnpm check:engine-double-contract:: exit 0pnpm check:error-status-conformance:: exit 0pnpm check:gitlink-declared:: exit 0pnpm check:nul-bytes:: exit 0pnpm check:published-files:: exit 0pnpm check:query-options-erasure:: exit 0pnpm check:refd-timer-probe:: exit 0pnpm check:registry-log-declared:: exit 0pnpm check:rest-log-spy-declared:: exit 0pnpm check:skill-identifier-liveness:: exit 0pnpm check:test-source-alias:: exit 0pnpm check:tier-file-adoption:: exit 0pnpm check:type-check-coverage:: exit 0pnpm check:undeclared-dep-imports:: exit 0pnpm check:watch-hint-literal:: exit 0pnpm check:where-matcher:: exit 0pnpm check:type-check-debt:: exit 0pnpm check:type-check-debtwas re-run alone under the verify lock (VERDICT command-exit 0); its first run in the loop exited 3 queued behind this card's own dogfood hold.node scripts/pm/dispatch-gates.mjs --ran:36 derived famil(ies) accounted for — 36 run, 0 NOT-MEASURED.Standing ui gates
node scripts/check-sdui-manifest.mjs(and--self-test): in the derived list above, exit 0.pnpm --dir ui check:new-line-citations:VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0.Dogfood battery (final head,
(narrowing)arm):pnpm --filter @objectstack/dogfood testunder the verify lock on899d981b(closure built first):Test Files 419 passed | 3 skipped (422),Tests 3492 passed | 36 skipped (3528),VERDICT command-exit 0. The final head2f621a58differs from899d981bonly in the changeset's remedy paragraph.Generated by Claude Code