|
1362 | 1362 | "title": "Action buttons surface at exactly their declared locations — list toolbar, list row, record header/more/related/section — and each dispatches for real", |
1363 | 1363 | "since": "v15", |
1364 | 1364 | "status": "active", |
1365 | | - "revision": 3, |
| 1365 | + "revision": 4, |
1366 | 1366 | "priority": "P1", |
1367 | 1367 | "surface": "browser", |
1368 | 1368 | "personas": [ |
|
1371 | 1371 | "fixtures": { |
1372 | 1372 | "app": "showcase", |
1373 | 1373 | "requires": [ |
1374 | | - "the per-location action fleet on showcase_task (examples/app-showcase/src/ui/actions/index.ts): showcase_bulk_reassign (list_item+list_toolbar, flow), showcase_quick_view (list_item, modal), showcase_mark_done (list_item+record_header+record_section, script, visible '!record.done'), showcase_log_time (record_header+record_related+record_section, form), showcase_open_docs (record_more, url), showcase_recalc_selection (record_more, api — deliberately kept OFF the toolbar, objectui#3142)", |
| 1374 | + "the per-location action fleet on showcase_task (examples/app-showcase/src/ui/actions/index.ts): showcase_bulk_reassign (list_item+list_toolbar, flow), showcase_quick_view (list_item, modal), showcase_mark_done (list_item+record_header+record_section, script, visible '!record.done'), showcase_log_time (record_header+record_related+record_section, form), showcase_open_docs (record_more, url), showcase_recalc_selection (record_more, api — declares record_more ONLY, which is what keeps it off the toolbar; objectui#3142), showcase_new_task (locations: [] — the fleet's headless specimen, read by the empty-locations probe)", |
1375 | 1375 | "seeded tasks in both done and not-done states for the CEL visibility both-sides check" |
1376 | 1376 | ] |
1377 | 1377 | }, |
|
1382 | 1382 | "record_more — showcase_open_docs / showcase_recalc_selection under the ⋯ overflow", |
1383 | 1383 | "record_related — showcase_log_time on the related-list section", |
1384 | 1384 | "record_section — showcase_mark_done / showcase_log_time in the Task Detail quick-actions bar (record:quick_actions resolves through the location filter)", |
1385 | | - "empty-locations semantics probe — a locations-less action lands on EVERY location including the toolbar (objectui action-bar.tsx documented behavior; the reason recalc_selection must declare record_more)" |
| 1385 | + "empty-locations semantics probe — an action with NO locations key renders on NO surface, and `locations: []` likewise (objectui#3142 collapsed the renderers onto one membership predicate, `actionRendersAt`; placement is a declaration, which is why recalc_selection has to name record_more)" |
1386 | 1386 | ], |
1387 | 1387 | "steps": [ |
1388 | 1388 | "boot the showcase isolated; sign in as admin; open the showcase_task list view", |
|
1391 | 1391 | "dispatch one action per location with a ref-targeted click: bulk_reassign (screen-flow wizard opens), quick_view (modal opens), mark_done (script executes), log_time (form dialog opens on showcase_task.edit), open_docs (url navigation), recalc_selection from the ⋯ menu (api POST); capture each network trace", |
1392 | 1392 | "verify the state-changing dispatches server-side: mark_done flips the task's done flag (API re-read), recalc_selection's per-record branch recomputes the estimate", |
1393 | 1393 | "CEL visibility both sides: locate a done task and a not-done task; read the row menu and record header of each for showcase_mark_done", |
1394 | | - "empty-locations probe: in a scratch/writable package author a copy of an api action with NO locations key; reload and record every surface it appears on (including the toolbar), then delete the probe" |
| 1394 | + "empty-locations probe, both halves: (a) in a scratch/writable package author a copy of an api action with NO locations key; reload and sweep all six surfaces (list toolbar, row menu, record header, ⋯ overflow, related-list section, quick-actions bar) recording every surface it appears on — the expected list is EMPTY — then delete the probe; (b) sweep the same six surfaces for showcase_new_task, the fixture fleet's headless `locations: []` declaration, and record that its list is empty too" |
1395 | 1395 | ], |
1396 | 1396 | "acceptance": [ |
1397 | 1397 | { |
|
1419 | 1419 | "evidence": "the four reads + screenshots" |
1420 | 1420 | }, |
1421 | 1421 | { |
1422 | | - "clause": "empty/missing locations means EVERY location — the probe action appears on all surfaced slots including the list toolbar (the objectui#3142 semantics that forces recalc_selection to declare record_more, because a toolbar dispatch has no selection and the endpoint rejects it)", |
| 1422 | + "clause": "empty/missing locations means NO location — the probe action with no `locations` key renders on none of the six surfaces, and the headless `locations: []` declaration (showcase_new_task) renders on none either. Placement is a declaration, not a default: objectui#3142 collapsed four disagreeing renderers onto the single `actionRendersAt` predicate, a plain membership test, so an undeclared or empty list matches nothing. recalc_selection names record_more for that reason — an explicit single-record placement that also keeps it off the toolbar, where a dispatch would carry no selection and the endpoint would reject it — NOT to opt out of an everywhere-default", |
1423 | 1423 | "oracle": "dom", |
1424 | | - "verify": "the locations-less probe's placement list covers all applicable surfaces; recalc_selection itself stays OFF the toolbar", |
1425 | | - "evidence": "probe placement list + toolbar DOM read" |
| 1424 | + "verify": "both six-surface DOM sweeps (the locations-less probe, and showcase_new_task) come back with an empty placement list; recalc_selection is present under ⋯ and absent from the toolbar", |
| 1425 | + "evidence": "the two empty placement lists + the toolbar DOM read" |
1426 | 1426 | } |
1427 | 1427 | ], |
1428 | 1428 | "negative": [ |
1429 | | - "any action rendering at a location it did not declare (and did not inherit via the empty-locations rule) is a FAIL — placement is a contract, not a hint", |
| 1429 | + "any action rendering at a location it did not declare is a FAIL — placement is a contract, not a hint, and since objectui#3142 there is no everywhere-default left to inherit", |
1430 | 1430 | "showcase_recalc_selection appearing on the list toolbar is the objectui#3142 regression shape — FAIL even though clicking it would merely error", |
1431 | 1431 | "a dispatch that opens the wrong target (e.g. log_time opening a list view instead of the showcase_task.edit form — the #2554 build-gate class) is a FAIL of the dispatch clause, not a cosmetic note" |
1432 | 1432 | ], |
|
1441 | 1441 | }, |
1442 | 1442 | "source": [ |
1443 | 1443 | "packages/spec/src/ui/action.zod.ts:565 (ACTION_LOCATIONS — the canonical 6-value enum, single source of truth; `global_nav` was the 7th until #6888 retired it — no product surface rendered it)", |
1444 | | - "examples/app-showcase/src/ui/actions/index.ts (per-location fixture fleet + the record:quick_actions filter note + the objectui#3142 empty-locations commentary)", |
1445 | | - "objectui: packages/.../action-bar.tsx (missing/empty locations → every location)", |
| 1444 | + "examples/app-showcase/src/ui/actions/index.ts (per-location fixture fleet + the record:quick_actions filter note + the two headless `locations: []` declarations). NOTE: RecalcSelectionAction's TSDoc there still narrates the pre-objectui#3142 'missing/empty locations → every location' rule — stale prose, filed separately; the declaration it explains (record_more) is correct either way", |
| 1445 | + "packages/lint/src/validate-action-locations.ts (the `action-no-placement` rule — this repo's codification of the current contract: an action with no `locations` renders nowhere and is therefore inert, while `locations: []` is the deliberate headless declaration and is NOT flagged)", |
| 1446 | + "objectui: packages/types/src/ui-action.ts `actionRendersAt` (the single placement predicate — a membership test, so an undeclared or empty `locations` matches nothing) + packages/.../action-bar.tsx, its consumer since objectui#3142", |
1446 | 1447 | "cross-ref: bulk dispatch-count semantics live in records-forms.list-view-capabilities (bulk-actions variant); param dialogs in records-forms.action-param-widgets" |
1447 | 1448 | ], |
1448 | 1449 | "history": [ |
|
1463 | 1464 | "date": "2026-08-10", |
1464 | 1465 | "change": "dropped the global_nav variant — the vocabulary lost that member (#6888). The variant was UNRUNNABLE as written, not merely obsolete: its step 'new_task from the palette' could never pass, because the console ⌘K palette builds its groups from nav items, objects, dashboards, pages, reports, recent items and record search and reads no action metadata at all. The fixture fleet loses showcase_new_task with it (now headless, locations: []); the dispatch step drops from seven traces to six; enumSource.expect 7 → 6", |
1465 | 1466 | "ref": "claude/issue-6888-retire-global-nav" |
| 1467 | + }, |
| 1468 | + { |
| 1469 | + "revision": 4, |
| 1470 | + "date": "2026-08-10", |
| 1471 | + "change": "inverted the empty-locations probe to the post-objectui#3142 contract (#7323). The variant, its step and its acceptance clause all asserted that a locations-less action lands on EVERY location; objectui#3142 collapsed four disagreeing renderers onto one membership predicate (actionRendersAt), so such an action now lands on NONE. The probe was therefore grading correct behavior as a FAIL, and contradicted this same item's negative list, which already encoded the post-#3142 contract. The probe is kept rather than deleted — both halves stay observable end-to-end: no `locations` key → nowhere (the inert shape packages/lint's action-no-placement warns about), and `locations: []` → nowhere deliberately (read off the fleet's showcase_new_task, which revision 3 made headless). Re-anchored the recalc_selection note — record_more is an explicit placement, not an opt-out of an everywhere-default — reworded the objectui source line to name the predicate, and added the in-repo codification as a source", |
| 1472 | + "ref": "claude/issue-7323-action-location-matrix-probe" |
1466 | 1473 | } |
1467 | 1474 | ], |
1468 | 1475 | "enumSource": { |
|
0 commit comments