feat(workflow-executor): approval requests on Full AI / Automatic trigger actions [PRD-688]#1720
Conversation
10 new issues
|
|
Coverage Impact ⬆️ Merging this pull request will increase total coverage on Modified Files with Diff Coverage (7) 🛟 Help
|
…ction when required by role
PRD-688 (backend / executor half). When a Trigger Action step runs in
Automatic / Full AI mode and the action requires an approval by the triggering
user's role, the executor now files an approval request instead of executing,
reports the step as success (non-blocking — the run continues), and forwards
the created approval id to the front via the StepOutcome so the UI can deep-link
to the approval request.
- agent-client: makeCreateApprovalRequest returns the created approval id
(defensive extraction from the JSON:API response); Action.execute() bubbles it
as { approvalRequested, approvalRequest }.
- workflow-executor: wire forestServer (per-step forestServerToken threaded
runner -> factory -> AgentWithLog -> port) so the agent-client can file the
request; type the executeAction result; branch in the trigger-action executor
(persist pending-approval, no actionResult); carry approvalRequest through the
outcome + update-step mapper; reconstruct it on idempotent replay; add a
dedicated ApprovalRequestCreationError.
fixes PRD-688
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7de1dfc to
ee7a332
Compare
…ction executor
approvalRequest is a Trigger Action concern, but it had leaked into shared
outcome builders: the base buildOutcomeResult abstract (seen by every executor)
and record-step-executor's concrete (shared by read/update/load-related).
Pull it back to where it's used, mirroring the existing selectedOption pattern:
- base abstract stays { status, error? } — no family-specific field.
- record-step-executor's builder stays { status, error? }.
- trigger-record-action-step-executor declares a dedicated buildOutcomeResult
override carrying approvalRequest, delegating to super (which spreads it onto
the record StepOutcome).
No behavior change; the approval id still reaches the outcome. The shared
RecordStepOutcome wire type keeps the optional field (single 'record' outcome
type for all record steps — unchanged).
Refs PRD-688
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
563eb37 to
afb1453
Compare
…proval A global action runs on no record, so the executor must not pass the Trigger Action step's source record when triggering it — otherwise the approval request it files is wrongly linked to that record (shows a Resource that makes no sense). Read the action `type` (forwarded by the orchestrator) and omit the record id for global actions; single/bulk actions keep their record. Refs PRD-688 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lative fallbacks) The Forest server returns the created approval as JSON:API (serializer ref:'id'), so the id is data.id — there is no reference/requestId attribute. Drop the defensive multi-field fallback now that the response shape is known. Refs PRD-688 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the single-use extractApprovalId helper + its type; read data.id at the call site. Refs PRD-688 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the loose `forestServerToken?: string` third arg on
AgentPort.executeAction with a `caller: { user, forestServerToken }`
object. Keeps the token off StepUser (it would leak into the agent JWT
via mintToken's user splat) while keeping it on the only method that
talks to the Forest server.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lobal actions getActionForm was passing the source record id even for global actions, so record-scoped defaults/dynamic-fields/validation were computed for the wrong scope while executeAction (correctly) omits the id. Omit the id in both the form-detection call and the AI-fill re-fetch, matching executeAction. GetActionFormQuery.id is now optional. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Review notes — no correctness blockers, a few things worth hardening🔴 Should fix before mergeSilent loss of the approval deep-link when the server returns no 🟡 Worth addressingGlobal-vs-record misclassification is silent if Test gap — Test gap — adapter-level global-action id omission — |
…d + cover replay/global-form Addresses review on #1720: - Log a Warn (collection/action/rendering) when the approval POST succeeds but no id comes back — on both the live path and the idempotent replay — so a missing deep-link is diagnosable instead of silent. - Add replay tests for the executed and skipped outcomes. - Add an adapter test asserting getActionForm omits recordIds for a global action (parity with executeAction). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @matthv — addressed in 155d199. 🔴 Silent loss of the deep-link when no 🟡 Global-vs-record misclassification if 🟡 Replay test gap — added 🟡 Adapter global-form test gap — added a no- |
Adds the replay case (phase=done + pending-approval without an approval id) that exercises the new Warn branch in checkIdempotency, restoring diff coverage on the modified file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# @forestadmin/agent-client [1.9.0](https://github.com/ForestAdmin/agent-nodejs/compare/@forestadmin/agent-client@1.8.0...@forestadmin/agent-client@1.9.0) (2026-07-01) ### Features * **workflow-executor:** approval requests on Full AI / Automatic trigger actions [PRD-688] ([#1720](#1720)) ([aa3ef58](aa3ef58))
# @forestadmin/workflow-executor [1.11.0](https://github.com/ForestAdmin/agent-nodejs/compare/@forestadmin/workflow-executor@1.10.0...@forestadmin/workflow-executor@1.11.0) (2026-07-01) ### Features * **workflow-executor:** approval requests on Full AI / Automatic trigger actions [PRD-688] ([#1720](#1720)) ([aa3ef58](aa3ef58)) ### Dependencies * **@forestadmin/agent-client:** upgraded to 1.9.0

Context
PRD-688 (backend / executor half, single PR per repo). In the legacy browser engine, triggering an approval-gated action created the approval on the frontend. With the backend migration, Automatic & Full AI Trigger Action steps run on the executor, which didn't create the approval — a regression. This PR makes the executor file the approval request (non-blocking) and handles action scope correctly.
What it does
success(the run continues; the process owner adds an explicit stop if needed). The created approval id is forwarded to the front via theStepOutcome(context.approvalRequest.id) so it can deep-link.typeforwarded by the orchestrator (ForestAdmin/forestadmin-server#8344); degrades safely if absent.errorwith a dedicatedApprovalRequestCreationError.Key changes
makeCreateApprovalRequestreturns the created approval id (defensive extraction);Action.execute()bubbles{ approvalRequested, approvalRequest }.forestServer(per-stepforestServerTokenthreaded runner → factory → AgentWithLog → port); typedExecuteActionResult; the trigger-action executor branches on approval (persistspending-approval, noactionResult), omits the record for global actions, reconstructs the approval id on idempotent replay; outcome + update-step mapper carryapprovalRequest.Architecture note
approvalRequestis contained to the trigger-action executor: a dedicatedbuildOutcomeResultoverride (mirroring condition'sselectedOption), not the shared base/record builders. The action-scope decision stays in the executor and reduces to "id present or not" at the port boundary — lower layers stay generic.Verification
agent-client (340) · mcp-server (547) · workflow-executor (1131) tests pass; build + lint clean.
Frontend half: ForestAdmin/forestadmin#9804. Orchestrator (forwards action
type): ForestAdmin/forestadmin-server#8344.fixes PRD-688
🤖 Generated with Claude Code
Note
Add approval request handling for Full AI and Automatic trigger actions
TriggerRecordActionStepExecutornow persists apending-approvalresult and returns a success outcome instead of blocking, optionally including the approval request id for deep-linking.AgentClientAgentPort.executeActionreturns a normalizedExecuteActionResultunion distinguishing executed results from approval-gated submissions, and wires Forest server connectivity when aforestServerTokenis provided.'global') are now supported: record id is omitted when building forms and executing actions.pending-approvalsteps restores the savedapprovalRequestid without re-triggering execution.forestServerTokenis propagated from each dispatch throughRunner→StepExecutorFactory→AgentWithLog→AgentClientAgentPortto enable approval request creation.Macroscope summarized baa7964.