Repository navigation
feat(api): run a pinned deployment version and expose paused run points in v2 - #9854
Conversation
executeWorkflowService loaded the pinned deployment version only to read its variables and blocks; the execution metadata carried no pin, so the core loaded and ran the active deployment. Carry the pin as pinnedDeploymentVersionId on the sync, stream, and async paths and load that version in the core. MCP serve, which already passed a pin, now runs the version it selected.
run: { source: 'deployment', version } runs that deployment version
instead of the active one. The pin is its own write-role operation,
workflows.execute_version, selected after parsing; the use case resolves
the number to the version row and answers an unknown version with 404.
Anonymous public-API callers cannot pin. The run resource reports the
deployment version each run executed.
GET /api/v2/workflows/{id}/runs/{runId} lists every point a paused run
waits at under paused.points: block, kind, resume state, parallel and
loop scope, queue position, the fields a human pause asks for, and its
reviewer content. A pause's stored response can carry resolved secrets,
so the content and fields are scrubbed through the run's resolved-secret
registry when the pause is captured and marked displayScrubbed; points
stored before this expose neither. Resume links, API-mode status and
headers, and the execution snapshot are never read into the response.
…s field optional The shared executeDeployedWorkflow helper took a broad Principal, which no longer satisfies executeWorkflowService's WorkflowExecutionPrincipal once the use case's narrowed principal is widened by the extraction. The internal run status response's new deploymentVersion field is optional so a browser bundle deployed ahead of its server still parses the response.
Runs a workflow deployed twice through the execute-version use case: a sync and an async run pinned to version 1 execute version 1 while version 2 is active and record it on the log, the queued payload carries the pin, and an unknown version is not_found before any run starts. Both pinned cases fail with the executor change reverted.
An unpinned queued run seeded its execution snapshot with the workflow row's variables, which are the editor's draft, while the sync and stream paths use the variables of the deployed state they run. Load the active deployment's variables in the worker as the sync path does, so a queued run of the deployment no longer sees unsaved or undeployed variable edits.
…lay gate The core loads a resumed run's admitted version and a pinned run's version through one branch. The scrubbed-display rule that persisting and mapping a pause point both apply is one helper, and mapping is the single gate the run detail relies on. The pin rides the queue payload as a plain optional field, and a whitespace-only input field name is no longer a field.
Adds the test:workflow-pinned-execute:e2e package script and names the environment the script needs against a dev stack.
Scheduled and webhook runs executed their deployed version's blocks with
the workflow row's saved variables, the editor's draft. They now use the
variables the version was deployed with, as queued API runs do.
A version deployed before deployments snapshotted variables has no
variables in its snapshot, and the loader defaults them to {}. Those runs
have always used the saved variables, so the loader now reports whether the
snapshot recorded variables and resolveDeployedRunVariables keeps the saved
ones only for such a version, on the queued API, schedule, and webhook
paths. The sync path is unchanged. Proven against real Postgres by running a
schedule and a generic webhook delivery in-process; both read the saved
variables before this change, and the pre-snapshot case reads {} without
the fallback.
The worker loaded the active deployment for its variables and the core then loaded the active deployment again for its blocks, so a deploy landing between the two could pair one version's variables with another's graph. The worker now pins the core to the version it loaded, and retries that read on a transient database error as the core's own load does. Schedules and webhooks already hand the core the blocks of the version they loaded, so they need no change.
A paused run's pause points carried each point's reviewer display whatever the caller asked for or the viewer's permission group withheld, though the display is rendered from upstream block output. The run detail now returns display only with includeOutput and when logs.trace_spans is not denied, as it does finalOutput, blockOutputs, and files; input fields are block configuration and stay. The display and the input fields are captured as separate projections, so content too large to prove free of secrets withholds only itself. A point recorded without a registration time lists with registeredAt null instead of failing the response. A real human-in-the-loop pause run in-process through the engine, with a workspace secret in its content, proves the stored point and the run detail carry the redacted reference, and that both display gates hold.
Covers run.version on v2 execute (write access, keyed callers, 404 for an unknown version, version on the run status) and paused.points on the run detail, including when display is withheld.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
[High impact] The PR appears safe to merge; no new actionable issues remain. SummaryAdds pinned deployment runs and pause details to v2.
|
…he content compatibility baseline
…y once A resume still starting or queued has no log row of its own, so its status reported no deployment version until it settled and then reported the parent's. It now reports the version its parent run recorded throughout. The pause context detail strips each listed point's response data because the selected point already carries it; it now strips the listed points' display for the same reason instead of returning every point's content.
…hten waits The webhook delivery case now runs a version that a later deployment superseded, so it shows the run uses its admitted version's variables rather than the active one's. Run waits end below the integration test timeout so a failed run reports its status instead of a bare timeout. The pinned execute e2e script logs static messages with metadata, and its approval declares the field whose capture it asserts.
Adds the API deployment page's new code example to the compatibility baseline without renumbering the page's existing examples.
The canonical-terminal-status test parses a v2 run body against the route contract, which now requires version on the run resource.
…rsion-pin-paused-detail
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 39 files
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
…up hides execution data
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 39 files
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
A settled resume attempt has no log row of its own, so the status read answers from the parent run it continued and can report that run paused. The run detail then read pause points by the attempt id, which has no paused record, and returned none. The status read now reports which execution's state answered it, and the run detail reads the points from that execution.
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 39 files
Reply with feedback, questions, or to request a fix.
Turn on auto-fix | Re-trigger cubic
Summary
executeWorkflowServiceloaded the pinned version only for its variables while the core ran the active graph. The pin now reachesloadWorkflowStateon the sync, stream, and queued paths (this also makes MCP serve run the version it advertised)run: { source: 'deployment', version }toPOST /api/v2/workflows/{id}/executeto run an earlier deployment version without activating it. It requires write access (operationworkflows.execute_version); an unknown version is a 404 and anonymous public-API callers can't pinGET /api/v2/workflows/{id}/runs/{runId}now reports theversionthat ran andpaused.points: every pause point with block, kind, status, parallel/loop position, queue position, input fields, and display contentDeploy note: a background worker on the previous release ignores
pinnedDeploymentVersionIdand runs the active version, so deploy workers with or before the web tier.Type of Change
Testing
execute-workflow-version.integration.ts(pinned sync/async runs execute v1 while v2 is active; red with the executor fix reverted),deployed-run-variables.integration.ts(queued/schedule/webhook variables, legacy fallback),read-workflow-run-pause.integration.ts(display withheld for restricted viewers),pause-persistence.integration.ts(multi-branch points, redaction through the real engine with an encrypted secret, oversized display keeps input fields, legacy points)tscon apps/sim;check:openapi,cli-api,mcp-operations,v2-route-table,api-validation:strict,permission-group-enforcement,actorless-executor-operations,capability-subject,application-graph,boundaries; root scripts and sim-cli suites; biomeapps/sim/scripts/test-workflow-pinned-execute-e2e.tscovers the HTTP-only paths against a dev stackChecklist
test-auditauthoring gate)