Approval 노드
DAG workflow node는 사람이 게이트를 승인하거나 거절할 때까지 워크플로 실행을 일시 중지하는 approval 필드를 지원합니다. approval node를 사용하면 AI 기반 노드 사이에 사람 검토 단계를 넣을 수 있습니다. 예를 들어 비용이 큰 구현 작업을 시작하기 전에 생성된 계획을 검토할 수 있습니다.
Web UI 사용자: workflow 수준에
interactive: true를 추가하세요. 이 값이 없으면 workflow가 background worker로 dispatch되어 approval gate 메시지가 chat window에 표시되지 않습니다. Web Execution Mode를 참고하세요.
name: plan-approve-implementdescription: Plan, get approval, then implementinteractive: true # Required for Web UI: ensures approval gates appear in chat
nodes: - id: plan prompt: | Analyze the codebase and create a detailed implementation plan. $USER_MESSAGE
- id: review-gate approval: message: "Review the plan above before proceeding with implementation." depends_on: [plan]
- id: implement command: implement depends_on: [review-gate]실행이 review-gate에 도달하면 workflow가 일시 중지되고 사용 중인 플랫폼(CLI, Slack, GitHub 등)으로 사용자에게 메시지를 보냅니다. Web UI에서는 메시지가 chat에 표시되려면 interactive: true가 필요합니다.
- Pause: executor가 workflow run status를
paused로 설정하고 approval context(node ID와 message)를 run metadata에 저장합니다. - Notify: approval prompt와 승인/거절 방법이 담긴 메시지를 사용자에게 보냅니다.
- Wait: 사용자가 조치를 취할 때까지 workflow는 paused 상태로 유지됩니다. paused run은 worktree path guard를 막으므로 같은 경로에서 다른 workflow가 시작될 수 없습니다.
- Approve: 사용자가 승인하면 approval node에 대한
node_completedevent가 기록되고 run이 재개 가능한 상태로 전환됩니다. 자연어 메시지(권장)와 CLI는 즉시 auto-resume됩니다. 명시적인/workflow approvecommand는 승인을 기록합니다. 재개하려면 후속 메시지를 보내세요. - Reject: 사용자가 거절합니다.
on_reject가 없을 때: workflow가 즉시 취소됩니다.on_reject가 있을 때: executor가$REJECTION_REASON을 치환한 뒤on_reject.prompt를 AI로 실행하고, 같은 gate에서 다시 일시 중지합니다. 사용자가 승인하거나on_reject.max_attempts에 도달할 때까지 반복되며, 최대 횟수에 도달하면 workflow가 취소됩니다.
YAML Schema
Section titled “YAML Schema”- id: gate-name approval: message: "Human-readable prompt shown to the user" capture_response: true # optional: store comment as $gate-name.output on_reject: # optional: AI rework on rejection instead of cancel prompt: "Fix based on feedback: $REJECTION_REASON" max_attempts: 3 # optional: default 3, range 1–10 depends_on: [upstream-node] # optional when: "$plan.output != ''" # optional condition trigger_rule: all_success # optional (default: all_success)| Field | Type | Required | Description |
|---|---|---|---|
approval.message | string | Yes | workflow가 일시 중지될 때 사용자에게 표시되는 메시지 |
approval.capture_response | boolean | No | true이면 사용자의 approval comment가 downstream node에서 사용할 수 있도록 $<node-id>.output에 저장됩니다. 기본값: false |
approval.on_reject.prompt | string | No | 사용자가 거절했을 때 AI로 실행할 prompt template입니다. $REJECTION_REASON은 reject reason으로 치환됩니다. 실행 후 workflow는 같은 gate에서 다시 일시 중지됩니다 |
approval.on_reject.max_attempts | integer | No | workflow가 취소되기 전 on_reject prompt가 실행될 수 있는 최대 횟수입니다. 범위: 1–10. 기본값: 3 |
Approval node는 AI agent를 호출하지 않으므로 AI 전용 필드(model, provider, context, output_format, allowed_tools, denied_tools, hooks, mcp, skills, idle_timeout)를 지원하지 않습니다. (on_reject.prompt는 workflow의 default provider를 사용하는 별도 AI node로 실행됩니다.)
표준 DAG 필드(id, depends_on, when, trigger_rule, retry)는 예상대로 동작합니다.
승인과 거절
Section titled “승인과 거절”자연어(권장)
Section titled “자연어(권장)”같은 대화에 답변을 입력하기만 하면 됩니다. 시스템은 paused workflow를 감지하고 메시지를 approval response로 처리합니다.
User: "Looks good, but add error handling for the edge cases"→ System auto-approves, resumes workflow with your message as $gate.output (only if capture_response: true is set)이 방식은 모든 플랫폼(Web, Slack, Telegram, Discord, GitHub)에서 동작합니다.
거절하려면 대신 /workflow reject <run-id>를 사용하세요.
CLI는 non-interactive 방식이므로 명시적인 command를 사용합니다.
# Approve (resumes the workflow immediately)bun run cli workflow approve <run-id>bun run cli workflow approve <run-id> --comment "Looks good, proceed"
# Reject# Without on_reject: cancels the workflow# With on_reject: records feedback, triggers AI rework, re-pausesbun run cli workflow reject <run-id>bun run cli workflow reject <run-id> --reason "Plan needs more test coverage"명시적 Commands(모든 플랫폼)
Section titled “명시적 Commands(모든 플랫폼)”/workflow approve <run-id> looks good/workflow reject <run-id> needs changesWeb UI
Section titled “Web UI”Paused workflow는 dashboard에 amber pulsing badge로 표시됩니다. workflow card에서 Approve 또는 Reject를 직접 클릭하세요.
REST API
Section titled “REST API”# Approvecurl -X POST http://localhost:3090/api/workflows/runs/<run-id>/approve \ -H "Content-Type: application/json" \ -d '{"comment": "Approved"}'
# Rejectcurl -X POST http://localhost:3090/api/workflows/runs/<run-id>/reject \ -H "Content-Type: application/json" \ -d '{"reason": "Needs revision"}'Downstream Output
Section titled “Downstream Output”기본적으로 사용자의 approval comment는 downstream에서 사용할 수 없습니다. $<node-id>.output은 빈 문자열입니다. comment를 node output으로 캡처하려면 capture_response: true를 설정하세요.
nodes: - id: gate approval: message: "Any special instructions for implementation?" capture_response: true # Makes the user's comment available as $gate.output depends_on: [plan]
- id: implement prompt: | Implement the plan. User instructions: $gate.output depends_on: [gate]capture_response: true가 없으면 downstream node는 $gate.output을 참조하지 않아야 합니다. 값이 빈 문자열이기 때문입니다.
AI 재작업을 포함한 거절(on_reject)
Section titled “AI 재작업을 포함한 거절(on_reject)”on_reject가 설정되어 있으면 거절이 workflow를 취소하지 않습니다. 대신 executor가 rejection reason을 포함한 AI prompt를 실행하고 같은 gate에서 다시 일시 중지합니다.
- id: review-gate approval: message: "Review the implementation plan." capture_response: true on_reject: prompt: | The reviewer rejected the plan with this feedback: $REJECTION_REASON
Revise the plan to address the feedback, then summarize the changes. max_attempts: 3 # After 3 rejections, the workflow is cancelled. Default: 3. depends_on: [plan]$REJECTION_REASON 변수는 거절한 사용자가 제공한 --reason text로 치환됩니다. AI 재작업 이후 workflow는 다시 일시 중지되어 reviewer가 다시 승인하거나 거절할 수 있습니다.
on_reject 사용 시 lifecycle
Section titled “on_reject 사용 시 lifecycle”- Workflow가 approval gate에서 일시 중지됩니다
- Reviewer가 거절합니다.
rejection_count가 증가하고rejection_reason이 저장됩니다 rejection_count < max_attempts이면on_reject.prompt가 AI로 실행되고 workflow가 다시 일시 중지됩니다rejection_count >= max_attempts이면 workflow가 취소됩니다
Edge Cases
Section titled “Edge Cases”- 여러 approval node: 지원됩니다. 각 node가 workflow를 독립적으로 일시 중지합니다.
- 병렬 layer의 approval: 같은 layer의 다른 node는 정상적으로 완료됩니다. workflow는 layer boundary에서 일시 중지됩니다.
- paused 상태에서 server restart: run은 database에 유지됩니다. restart 후에도 사용자가 승인하거나 거절할 수 있습니다.
- paused run 포기:
/workflow abandon <id>또는 dashboard의 Abandon button을 사용하세요.
Approval node는 기존 resume infrastructure(workflow lifecycle PR #871)를 재사용합니다. 승인되면 run이 잠시 failed status를 거쳐 findResumableRun이 이를 잡을 수 있게 합니다. 이렇게 해서 resume logic 중복을 피합니다. metadata.approval_response 필드는 approved-then-resumed run과 실제 failed run을 구분합니다.