Skip to content

feat(client): resume + getScreen on the automation SDK surface (#3528) - #3552

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

feat(client): resume + getScreen on the automation SDK surface (#3528)#3552
os-zhuang merged 2 commits into
mainfrom
claude/screen-flow-submit-resume-v9dqjx

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes the SDK half of #3528. The Console half is objectui#(companion PR on the same branch name).

What was actually wrong

The issue reports that Submit on a screen-flow step never calls the resume endpoint. I reproduced the whole path against a real server with the published @objectstack/console 16.1.0 build (dropped into packages/console/dist, .objectui-sha cf2d56e), driving it in Chromium:

SurfaceResult
List row action (showcase_bulk_reassign → flat-field screen)trigger → screen → resume 200, record updated
Record page action (same flow, record_section)trigger → screen → resume 200
Record page action → crm_convert_lead_wizard (two object-form steps, the lead-conversion shape)trigger → form → account created → resume 200 → step 2
Developer → Flow Runs → Test Runtrigger only. Paused envelope rendered as JSON. No screen, no Submit, no resume — the run is orphaned.

So the resume call does exist on the record/list surfaces, but the reporter's diagnosis — "@objectstack/client's automation surface exposes getFlow, execute, listRuns, getRun but no resume … the SDK the Console builds on has no way to express the call" — is correct, and it is exactly what dead-ends the fourth row. Every test run of a screen flow there leaves a paused row behind with no way to finish it.

This PR

  • automation.resume(flowName, runId, signal?) — posts the collected screen values as inputs (applied as bare flow variables), plus the approval-style output / branchLabel the dispatcher already accepts. Returns the next { status: 'paused', screen } of a multi-step wizard, or the terminal AutomationResult.
  • automation.getScreen(flowName, runId) — the screen a paused run is waiting on, so a client that did not launch the run (a reload, another tab, an inbox) can render the pending step before resuming.
  • Both on the environment-scoped client (client.project(id).automation.*) as well as the unscoped one.
  • Dispatcher route testsPOST /:name/runs/:runId/resume and GET /:name/runs/:runId/screen had zero coverage. Now covered: inputs / variables aliasing, output + branchLabel, the empty-signal body, the pause-again envelope, the 501 when the service cannot resume, the 404 when no screen is pending, and the ordering guard that keeps /runs/:runId/screen from being swallowed by the /runs/:runId run lookup.

No route or engine changes — the server side of ADR-0019 already worked, confirmed by hand over HTTP before touching anything.

Test plan

  • pnpm --filter @objectstack/client test — 116 passed (11 new automation assertions incl. the scoped client's first automation tests).
  • pnpm --filter @objectstack/runtime exec vitest run src/http-dispatcher.test.ts — 193 passed (9 new).
  • Manual HTTP round-trip against examples/app-showcase on a live dev server: trigger → status: paused → resume → success: true, record updated.

Not in this PR

Two adjacent gaps found while tracing every UI path that can dispatch a type: 'flow' action — both are launch-side, not resume-side, and want their own change:

  • DashboardRenderer header actions only branch on url / modal / script; a type: 'flow' header action falls through to console.warn("Unknown header actionType") and never dispatches. It also has no ActionProvider in its subtree, so even routed it would report "Flow handler not registered".
  • The console-root <ActionProvider> (ConsoleShell) is mounted with no handlers map, so any action:button outside ObjectView / RecordDetailView / PageView / DeclaredActionsBar binds to a runner with no flow handler.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LX9ut3MK3KykE11S9bJmv5


Generated by Claude Code

A `type: 'screen'` flow suspends at its `screen` node — `execute()` returns
`{ status: 'paused', runId, screen }` and the run waits for input. The
dispatcher has served the other half of that contract since ADR-0019
(`POST /automation/:flow/runs/:runId/resume`, `GET .../screen`), but the client
SDK's automation surface stopped at getFlow / execute / listRuns / getRun. Any
consumer built on the SDK could therefore start a screen flow and never finish
it: the run stayed suspended and the only way out was hand-rolling the HTTP
call. That is what dead-ended the Console's developer "Flow Runs" test runner,
where every test run of a screen flow orphaned a `paused` row.
- `automation.resume(flowName, runId, signal?)` posts the collected screen
values as `inputs`, plus the approval-style `output` / `branchLabel` the
dispatcher already accepts, and returns either the next paused screen of a
multi-step wizard or the terminal AutomationResult.
- `automation.getScreen(flowName, runId)` returns the screen a paused run is
waiting on, so a client that did not launch the run (a reload, another tab,
an inbox) can render the pending step before resuming.
- Both land on the environment-scoped client as well as the unscoped one.
The two dispatcher routes had no test coverage at all; they now cover the
`inputs` / `variables` aliasing, approval-style output + branchLabel, the
pause-again envelope, the 501 when the service cannot resume, and the ordering
guard that keeps `/runs/:runId/screen` from being swallowed by the run lookup.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LX9ut3MK3KykE11S9bJmv5
@vercel

vercelBot commented Jul 27, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specBuildingBuildingPreview, CommentJul 27, 2026 4:23am
1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredJul 27, 2026 4:23am

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/client, @objectstack/runtime.

26 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx(via packages/client)
  • content/docs/api/client-sdk.mdx(via @objectstack/client)
  • content/docs/api/data-flow.mdx(via @objectstack/client)
  • content/docs/api/environment-routing.mdx(via @objectstack/client)
  • content/docs/api/error-catalog.mdx(via @objectstack/client)
  • content/docs/api/index.mdx(via @objectstack/runtime)
  • content/docs/api/wire-format.mdx(via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx(via @objectstack/runtime)
  • content/docs/concepts/north-star.mdx(via packages/runtime)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/runtime)
  • content/docs/deployment/index.mdx(via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx(via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx(via @objectstack/runtime)
  • content/docs/deployment/vercel.mdx(via @objectstack/runtime)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/client, @objectstack/runtime)
  • content/docs/kernel/runtime-services/data-service.mdx(via packages/client)
  • content/docs/kernel/runtime-services/index.mdx(via packages/client)
  • content/docs/permissions/authentication.mdx(via @objectstack/client, @objectstack/runtime)
  • content/docs/permissions/authorization.mdx(via packages/runtime)
  • content/docs/plugins/packages.mdx(via @objectstack/client, @objectstack/runtime)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/runtime)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/runtime)
  • content/docs/protocol/kernel/realtime-protocol.mdx(via @objectstack/client)
  • content/docs/releases/implementation-status.mdx(via @objectstack/client, @objectstack/runtime)
  • content/docs/releases/v16.mdx(via @objectstack/client)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…uide (#3528)
The SDK page's automation example stopped at `trigger()`, which is exactly the
gap the issue reports: a reader had no way to learn that `execute()` on a
screen flow returns a paused run, or how to finish it. Adds the
execute → paused → resume round-trip and `getScreen()` for a client that did
not launch the run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LX9ut3MK3KykE11S9bJmv5
@os-zhuang
os-zhuang marked this pull request as ready for review July 27, 2026 04:51
@os-zhuang
os-zhuang merged commit 0cdb57a into mainJul 27, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/screen-flow-submit-resume-v9dqjx branch July 27, 2026 04:51
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@zhuangjianguo