Skip to content

[finding] No test pins that a record:quick_actions action's resultDialog actually opens — the handler reaches it only via the shared runner #5711

Description

@os-warren

Found while implementing objectstack#10681 (adding a one-shot resultDialog to sys_user.generate_backup_codes, which renders at record:quick_actions { location: 'record_section' }). Filed unassigned; no fix attempted — the framework card's scope was the declaration, and this is a gap in objectui's own coverage.

The observation

resultDialog is honoured centrally in packages/core/src/actions/ActionRunner.ts. On success it suppresses the successMessage toast and calls the registered handler, and it is explicit about what happens when none is registered:

[ActionRunner] action.resultDialog set but no resultDialogHandler registered — the response value will not be shown to the user.

That is a console.warn and nothing else. The action still reports success; the value is simply discarded. For a one-shot reveal (backup codes, a freshly minted client_secret, a temporary password) that silent discard is the whole defect class — objectstack#10681 is exactly it, one layer up.

Why the quick-actions surface is the unpinned one

Counted, not recalled:

  • packages/plugin-detail/src contains zeroresultDialog references. That is correct by design — record-quick-actions.tsx never touches the key; it resolves actions through useActionEngine and calls executeAction, and the runner does the rest.
  • useActionEngine reuses the surrounding provider's runner when there is one, and constructs a local one otherwise (packages/react/src/hooks/useActionEngine.ts). Its own comment names the failure mode: "because its local runner has no handlers installed".
  • packages/react/src/hooks/__tests__/useActionEngine.sharedRunner.test.tsx pins runner sharing, but contains noresultDialog reference — it does not pin that a dialog handler registered on the provider is reachable from an engine created by a child component.

So the chain RecordDetailViewActionProvider onResultDialogDeclaredActionsBar / record:quick_actionsuseActionEngine → shared runner → handler is load-bearing for every one-shot reveal on a record page, and no test covers its last two links. ActionRunner.resultDialog.test.ts pins the runner in isolation, where the handler is registered directly.

Why it is worth pinning rather than trusting

The failure is silent in the direction that matters. If a refactor caused a quick-actions bar to fall back to a local runner, every affected action would keep reporting success, the toast would stay suppressed (the runner suppresses it whenever resultDialog is set, independently of whether a handler exists), and the user would see nothing at all — a strictly worse outcome than before the reveal was declared. The only trace is a console.warn in a browser nobody is reading.

This finding does not claim the wiring is currently broken — reading the source, it is correct today, and the framework-side pin in objectstack#10681 depends on that reading. The claim is that nothing would notice if it stopped being correct.

Suggested shape

A component-level test that mounts a record:quick_actions bar inside an ActionProvider carrying an onResultDialog spy, runs an action whose spec declares a resultDialog, and asserts the spy fired with the response payload — plus the negative leg (no provider ⇒ the documented warn path), so the two are distinguishable. The DeclaredActionsBar tests already establish the mounting pattern at location="record_section".

Worth checking in the same sweep whether record:alert's CTA path (whose header claims "the same pipeline that record:quick_actions uses") shares the gap.

Generated by Claude Code

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:queue

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions