Skip to content

fix(console): a packaged action switched off for this deployment is no longer offered as a live button - #28115

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-1zrim8
Oct 30, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-1zrim8

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

维护者速读

Fixes #27504
Consumer-of: #27842
Clause-②: yes (widening) — four new @object-ui/core exports: ACTION_SWITCHED_OFF_KEY, markSwitchedOffActions, markSwitchedOffActionsByObject, isActionSwitchedOff
Metadata-protocol: none

What changes

The object reads serve, beside the document, the names of the actions this deployment switched off (disabledActions, #27842: a string[] on GET /meta/object/NAME, a map keyed by object name on GET /meta/object). The console reads it and stops offering those actions.

  • @object-ui/core: utils/action-activation.ts stamps a switched-off action entry under a symbol key (markSwitchedOffActions, markSwitchedOffActionsByObject) and answers isActionSwitchedOff. A symbol rides through a spread (localization, a promoted bulk def) and never reaches JSON.stringify or Object.keys, so an action handed back to a metadata write never carries it. ActionEngine.getActionsForLocation and getBulkActions leave such an action out.
  • @object-ui/data-objectstack: the single-object read stamps from its envelope before unwrapping item, beside the existing sortability carry.
  • @object-ui/app-shell: MetadataProvider stamps the whole list and a package-scoped list through one ingestion (ingestObjectList). Its by-name read hands out a structuredClone of the adapter's schema, which drops symbol keys; cloneServedObject carries the stamps onto the copy. Found live: without it the record header kept the button.
  • Every action gate that already hides an action whose requiredPermissions the caller lacks asks isActionSwitchedOff beside it: page:header (containers.tsx), action:bar, the data table's row action defs, RowActionMenu, BulkActionBar, the grid actions column, RelatedList toolbar, DeclaredActionsBar, and the managed-by empty state's list toolbar check.
  • No packages/** file: the server half, including the plain refusal sentence, landed with meta: GET /meta/object serves which packaged actions are switched off beside the item, and the disabled-action refusal reads as a plain sentence #27842.

What to look at (preview)

examples/app-showcase, signed in as the seeded admin, viewport 1440×900.

  1. Switch Mark Done (showcase_mark_done, object showcase_task) off under Setup › Packaged automation › Packaged actions.
  2. Open any task that is not done: the record header and the action bar under the path show Log Time and Archive but no Mark Done.
  3. Switch it back on and reload the task: Mark Done is back in both places.

Tests

  • New: core/src/utils/__tests__/action-activation.test.ts (stamp, spread, JSON, list form, engine location and bulk sets), components/src/__tests__/page-header-switched-off-27504.test.tsx (page:header and action:bar, hidden then offered again), data-objectstack/src/getObjectSchema.disabledActions-27504.test.ts (envelope carried, absent key stamps nothing), app-shell/src/providers/__tests__/MetadataProvider.disabledActions-27504.test.tsx (list envelope, absent key, by-name copy keeps the stamp).
  • 128 test files that name the touched modules, 1056 tests, all passed on c700e99. type-check passed for core, components, data-objectstack, plugin-detail, plugin-grid and app-shell.

Acceptance notes

Verification

Repro on main 561b764 (before)

With Mark Done switched off, the record header and the record:quick_actions bar both offered a live Mark Done, and a click toasted "Action 'showcase_mark_done' is disabled — it is switched off for this installation in the packaged-metadata activation ledger (sys_metadata_activation, ADR-0126 §8). …". GET /meta/object/showcase_task served type, name, sortability, item and no activation state.

Live check against #27842's branch plus this change (after)

Switched off: Mark Done buttons 0, Log Time 2. Re-enabled and reloaded: Mark Done 2, Log Time 2. GET /meta/object/showcase_task served disabledActions: ["showcase_mark_done"]; the list served showcase_task: ["showcase_mark_done"].

Ablations (each red, each restored to HEAD)

The page:header gate, the action:bar gate, the engine's location filter, the adapter's stamp, the provider's list stamp and the by-name copy's stamp carry were each disabled through scripts/ablation-replace.mjs; the matching new test failed every time and the restore matched HEAD's blob.


Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01Utp6CZny6X72Dx4kxxzLm7

… button

The object reads will serve the names of the actions the installation
switched off beside the document (`disabledActions`). The data layer stamps
those action entries under a symbol key (survives a spread, never reaches
JSON), and every action gate that hides a capability-gated action asks
`isActionSwitchedOff` beside it: the record header, action bars, the engine's
location and bulk sets, grid row and bulk actions, related-list toolbars and
the declared-actions bar. No client copy of the ledger.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CZny6X72Dx4kxxzLm7
…hed-off stamps

Measured live against #27842's branch: the record header still offered a
switched-off action, because the provider's by-name object read hands out a
`structuredClone` of the adapter's schema and that drops symbol keys. The stamps
are carried onto the copy by name. The package-scoped list read now runs the
same ingestion as the whole list (`ingestObjectList`).

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CZny6X72Dx4kxxzLm7
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 30, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · os-bill · session_01Utp6CZny6X72Dx4kxxzLm7

Preview review (proxy for epic:#26676#1) · c700e99 · PASS

Read at 2026-10-30T13:30Z. A context-free review agent ran this PR's head (c700e99) as a live showcase console against a local server built from the same head. It checked the work against #27504's Acceptance and the v4 record-page boards. It switched showcase_mark_done off and on again through Setup › Packaged automation, not through the API.

Acceptance

# Acceptance Seen Verdict
1 A switched-off packaged action is not offered as a live button With the action off, a showcase_task record shows no Mark Done in the header, in the record quick-actions bar or in the ⋯ menu. The list doesn't offer it either. PASS
2 The refusal reads as plain words A direct call to the action door answers 409 ACTION_DISABLED with "This action is turned off for this deployment. An administrator can turn it on again under Setup › Packaged automation, or you can use another action." The sentence comes from #27842, which is already on main. PASS
3 Turning it back on restores the button After the action is switched back on in Setup and the record page reloads, Mark Done is back in the header and the ⋯ menu. PASS

Screenshots. GitHub can't host files from the review machine, so the screenshots stay in the seat's card folder under ui-seat4/card-27504/preview/:

  • off_record.png and off_record_header_crop.png show the record page with the action off.
  • off_record_more_menu.png shows the ⋯ menu with the action off.
  • off_list.png shows the list with the action off.
  • on_record.png, on_record_header_crop.png and on_record_more_menu.png show the record page after the action is turned back on.
  • off_setup_dialog.png and on_setup.png show the Setup switch.

Board deviations not caused by this card. These were the same before and after the change, so the review files nothing here. They belong to the other #26676 record-page cards:

  • Archive is disabled with no reason given.
  • The record page is one column with no right rail.
  • The 40px top row and the tab row are missing.
  • Edit sits in the header.
  • The action bar appears twice.

The switch was left ON after the review.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
Contract-review-claim: session_01Utp6CbDAucNyQ4UtzCJKyh · head c700e99 · 2026-10-30T13:33Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

Contract review

Served-tier: TIER_DEFAULT
Head-sha: c700e9995dc0db1d9736c059865db28af8d3eb3f
Local-runs: none

Face: declaration only. The PR body, the changeset .changeset/27504-switched-off-action-not-offered.md and claim 6511284723 each say Clause-②: yes (widening) and Metadata-protocol: none. Read against origin/main: 19 files, +489/−20. The tree touches no packages/** and no ui/packages/components/src/ui/**. Inputs: the card and dev report 6524066946.

① Derived judgments

  • @object-ui/core (published, one . entry). src/index.ts adds export * from './utils/action-activation.js'. The new module exports exactly the four declared names:

    • ACTION_SWITCHED_OFF_KEY, which is Symbol.for('objectui.actionSwitchedOff');
    • markSwitchedOffActions(schema, served), which stamps in place and returns schema. A served that is not a list stamps nothing;
    • markSwitchedOffActionsByObject(objects, served), which returns nothing;
    • isActionSwitchedOff(action).

    The change is additive. ActionEngine.getActionsForLocation and getBulkActions gain a !isActionSwitchedOff filter and keep their signatures. An unstamped action is returned as before, so a caller on an older backend, or with an inline schema, sees no change. The key is a symbol, so Object.keys and JSON.stringify do not see it; utils/__tests__/action-activation.test.ts pins this.

  • @object-ui/data-objectstack (published). getObjectSchema stamps item from the envelope's disabledActions before the existing attachObjectSortability (at about src/index.ts L5541). There is no export change, and the schema's JSON shape is unchanged.

  • @object-ui/app-shell (private).

    • MetadataProvider.tsx gains two module-private helpers. ingestObjectList stamps list reads. cloneServedObject re-stamps by name after structuredClone, which drops symbol keys.
    • DeclaredActionsBar.tsx and managedByEmptyState.ts add the check beside mayInvoke.
  • @object-ui/components, @object-ui/plugin-grid, @object-ui/plugin-detail (published). Each adds && !isActionSwitchedOff(…) beside an existing mayInvoke(requiredPermissions) filter, in containers.tsx (PageHeaderActions), action-bar.tsx, data-table.tsx, RowActionMenu.tsx, BulkActionBar.tsx, objectGridActionsColumn.tsx and RelatedList.tsx. These hide only. There is no prop, export or i18n change.

  • Reads. There is no new request. The names ride the envelope of the object reads that meta: GET /meta/object serves which packaged actions are switched off beside the item, and the disabled-action refusal reads as a plain sentence #27842 (PR feat(spec,metadata-protocol,rest,objectql,runtime): GET /meta/object serves switched-off packaged actions beside the item, and the disabled-action refusal reads as a plain sentence #27964, merged) already serves. When the key is absent nothing is hidden, and the adapter and provider tests pin that.

② Semver level

patch on all six packages. That matches Metadata-protocol: none: AGENTS.md step 3 ships a non-protocol widening as patch, and no existing export changes signature.

③ Boundary flags

  • open_questions is empty.
  • markSwitchedOffActions mutates the served object in place, as its docblock says. The provider's by-name copy carries the stamp onto the clone.
  • Symbol.for puts the symbol in the global registry, so a duplicate @object-ui/core install still reads the stamp. This is deliberate.
  • Switched-off actions are hidden rather than shown disabled with a reason, as the card allowed. This is declared under Acceptance notes.

Implemented-by: claude/project-thread-1zrim8 (card thread)
Reviewed-by: session_01Utp6CbDAucNyQ4UtzCJKyh

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

席位核对

ACCEPT · head c700e9995dc0db1d9736c059865db28af8d3eb3f · 2026-10-30T13:37Z


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

1 participant