Skip to content

fix(console): dispatch flow actions from every surface + cover the screen-flow round trip (framework#3528) - #2833

Merged
os-zhuang merged 1 commit into
mainfrom
claude/screen-flow-submit-resume-v9dqjx
Jul 27, 2026
Merged

fix(console): dispatch flow actions from every surface + cover the screen-flow round trip (framework#3528)#2833
os-zhuang merged 1 commit into
mainfrom
claude/screen-flow-submit-resume-v9dqjx

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Follow-up to #2830 (which fixed the resume half of screen flows). These are the two launch-side holes found while mapping every path that dispatches a type: 'flow' action — on both, a screen flow could not even be started — plus the test coverage the whole seam was missing.

1. Dashboard header actions never dispatched a flow

DashboardRenderer's header click handler allow-listed modal / script. flow, api, form and navigation fell through to:

console.warn(`[DashboardRenderer] Unknown header actionType="${actionType}" for "${label}"`);

…and did nothing at all. Everything that is not a raw url navigation now goes through the ActionRunner, which owns the type registry — there is nothing left for the renderer to second-guess.

2. The console-root <ActionProvider> had no handlers

It exists to give every field widget a modal handler (the lookup "create the referenced record" affordance). But an ActionProvider also decides what a useAction() consumer below it can dispatch, and this one carried no handlers map — so any action:button outside ObjectView / RecordDetailView / PageView / DeclaredActionsBar bound to a runner that could only open modals. A flow action there failed with "Flow handler not registered"; api and script were equally dead.

The root now carries the shared console runtime's api / flow / script handlers plus its confirm / param / result / screen-flow dialogs. modal deliberately stays on the client-side useActionModal handler: registering it in handlers would take precedence over onModal and reroute the inline-create affordance to /api/v1/actions/....

3. Coverage for the screen-flow seam (it had none)

This is how the host-teardown bug in #2830 survived to production: the unit tests stub the runner out of the action runtime, and the runner's own tests feed it a screen directly, so trigger → dialog → resume → refresh was only ever exercised by hand.

  • FlowRunner.suspense.test.tsx — a lazily-loaded screen body must not unwind past the dialog. Reproduces the real shape: a lazy body, a route-level <Suspense> above the host, and host state (mount identity) that must survive. Verified to fail against the pre-boundary runner — the route fallback takes over and the host remounts.
  • e2e/live/screen-flow.spec.ts — the live round trip in the house style: a row flow action triggers the run, the paused screen renders, Submit POSTs to /automation/{flow}/runs/{runId}/resumewith the collected values, and the flow's downstream update_record shows up in the list. Asserts the resume request directly, because the bug class here is a missing request.

Test plan

  • packages/app-shell + packages/plugin-dashboard + apps/console suites: 249 files, 2048 tests passed (1 pre-existing skip).

  • Both new regression tests verified to fail without their fix (dashboard: 3 of 4 fail; suspense: route fallback appears).

  • pnpm --filter @object-ui/app-shell type-check clean; eslint on all changed files: 0 errors.

  • Live E2E run against a real backend (showcase app on a dev server + console dev server):

    ✓ 1 [chromium] › e2e/live/screen-flow.spec.ts:20:1 › a row flow action renders its screen and resumes the run (10.1s)
    1 passed (11.6s)
    

Note the live suite is not part of CI (no CI job runs a backend) — same as every existing action/dialog spec in e2e/live/. The Suspense regression and the dashboard tests do run in CI.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LX9ut3MK3KykE11S9bJmv5


Generated by Claude Code

@vercel

vercelBot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredJul 27, 2026 4:54am

Request Review

…reen-flow round trip (framework#3528)
Follow-up to #2830, which fixed the resume half. These are the two launch-side
holes found while mapping every path that dispatches a `type: 'flow'` action —
on both, a screen flow could not even be started.
**Dashboard header actions never dispatched a flow.** The click handler
allow-listed `modal` / `script`; `flow`, `api`, `form` and `navigation` fell
through to `console.warn("Unknown header actionType")` and did nothing at all.
Everything that is not a raw `url` navigation now goes through the
ActionRunner, which owns the type registry — there is nothing for the renderer
to second-guess.
**The console-root ActionProvider had no handlers.** It exists to give every
field widget a modal handler, but an ActionProvider also decides what a
`useAction()` consumer below it can dispatch, so any `action:button` outside
ObjectView / RecordDetailView / PageView / DeclaredActionsBar bound to a runner
that could only open modals: a flow action there failed with "Flow handler not
registered", and api/script were equally dead. The root now carries the shared
console runtime's api / flow / script handlers plus its confirm / param /
result / screen-flow dialogs. `modal` deliberately stays on the client-side
`useActionModal` handler — putting it in `handlers` would take precedence over
`onModal` and reroute the inline-create affordance to `/api/v1/actions/...`.
Both fixes ship with regression tests that were verified to fail without them.
The screen-flow seam itself had no coverage, which is how the host-teardown bug
survived to production, so this also adds:
- `FlowRunner.suspense.test.tsx` — a lazily-loaded screen body must not unwind
past the dialog. Reproduces the real shape (lazy body, route-level boundary
above the host, host state that must survive) and fails against the
pre-boundary runner.
- `e2e/live/screen-flow.spec.ts` — the live round trip: a row flow action
triggers the run, the paused screen renders, Submit POSTs to
`/automation/{flow}/runs/{runId}/resume` with the collected values, and the
flow's downstream `update_record` shows up in the list. Verified passing
against a real backend (showcase app + console dev server).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LX9ut3MK3KykE11S9bJmv5
@os-zhuang
os-zhuangforce-pushed the claude/screen-flow-submit-resume-v9dqjx branch from 581e61a to b1b9614CompareJuly 27, 2026 04:54
@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

MetricValueBudget
Main entry (gzip)** KB**KB
Entry file``
StatusFAIL

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.20KB2.97KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)21.70KB4.21KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.12KB3.41KB
auth (LoginForm.js)17.86KB5.29KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.43KB2.09KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)33.74KB8.53KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)1.83KB0.79KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.86KB0.85KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)18.38KB4.49KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)3.65KB1.42KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.25KB0.53KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)450.70KB98.15KB
core (index.js)1.86KB0.63KB
create-plugin (index.js)9.28KB2.98KB
data-objectstack (index.js)127.29KB31.96KB
fields (index.js)214.60KB52.67KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.46KB0.96KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)5.37KB1.72KB
i18n (useObjectLabel.js)25.17KB5.80KB
i18n (useSafeTranslation.js)2.87KB1.28KB
layout (index.js)38.45KB10.67KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)4.42KB1.27KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)1.77KB0.77KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)6.84KB2.42KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.00KB1.23KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)45.37KB12.48KB
plugin-charts (index.js)46.90KB13.26KB
plugin-chatbot (index.js)179.53KB42.79KB
plugin-dashboard (index.js)108.55KB27.96KB
plugin-designer (index.js)210.92KB42.69KB
plugin-detail (index.js)214.78KB52.42KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)103.47KB25.10KB
plugin-gantt (index.js)162.33KB39.53KB
plugin-grid (index.js)176.51KB46.38KB
plugin-kanban (index.js)47.82KB13.18KB
plugin-list (index.js)98.71KB23.32KB
plugin-map (index.js)16.80KB5.24KB
plugin-markdown (index.js)13.65KB4.67KB
plugin-report (index.js)37.07KB9.81KB
plugin-timeline (index.js)25.37KB7.20KB
plugin-tree (index.js)8.36KB2.81KB
plugin-view (index.js)85.70KB20.87KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.55KB0.67KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)3.19KB1.38KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)18.70KB6.09KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.00KB0.55KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)2.16KB0.94KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)0.77KB0.41KB
types (disclosure.js)0.20KB0.18KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (index.js)1.97KB0.93KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)0.20KB0.18KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.04KB1.93KB
types (system-fields.js)2.39KB1.17KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)0.75KB0.46KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.0 KB350 KB
Entry fileindex-CGuqjY9Y.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.20KB2.97KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)21.70KB4.21KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.12KB3.41KB
auth (LoginForm.js)17.86KB5.29KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.43KB2.09KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)33.74KB8.53KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)1.83KB0.79KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.86KB0.85KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)18.38KB4.49KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)3.65KB1.42KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.25KB0.53KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)450.70KB98.15KB
core (index.js)1.86KB0.63KB
create-plugin (index.js)9.28KB2.98KB
data-objectstack (index.js)127.29KB31.96KB
fields (index.js)214.60KB52.67KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.46KB0.96KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)5.37KB1.72KB
i18n (useObjectLabel.js)25.17KB5.80KB
i18n (useSafeTranslation.js)2.87KB1.28KB
layout (index.js)38.45KB10.67KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)4.42KB1.27KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)1.77KB0.77KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)6.84KB2.42KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.00KB1.23KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)45.37KB12.48KB
plugin-charts (index.js)46.90KB13.26KB
plugin-chatbot (index.js)179.53KB42.79KB
plugin-dashboard (index.js)108.55KB27.96KB
plugin-designer (index.js)210.92KB42.69KB
plugin-detail (index.js)214.78KB52.42KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)103.47KB25.10KB
plugin-gantt (index.js)162.33KB39.53KB
plugin-grid (index.js)176.51KB46.38KB
plugin-kanban (index.js)47.82KB13.18KB
plugin-list (index.js)98.71KB23.32KB
plugin-map (index.js)16.80KB5.24KB
plugin-markdown (index.js)13.65KB4.67KB
plugin-report (index.js)37.07KB9.81KB
plugin-timeline (index.js)25.37KB7.20KB
plugin-tree (index.js)8.36KB2.81KB
plugin-view (index.js)85.70KB20.87KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.55KB0.67KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)3.19KB1.38KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)18.70KB6.09KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.00KB0.55KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)2.16KB0.94KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)0.77KB0.41KB
types (disclosure.js)0.20KB0.18KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (index.js)1.97KB0.93KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)0.20KB0.18KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.04KB1.93KB
types (system-fields.js)2.39KB1.17KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)0.75KB0.46KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuang
os-zhuang marked this pull request as ready for review July 27, 2026 04:59
@os-zhuang
os-zhuang merged commit 2cb8d78 into mainJul 27, 2026
14 checks passed
@os-zhuang
os-zhuang deleted the claude/screen-flow-submit-resume-v9dqjx branch July 27, 2026 04:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude