Advanced Workflow Patterns
Use these recipes to build autonomous workflows: chain AI commands behind approval gates, verify agent work with deterministic checks, branch, recover stopped runs, and run workflows in CI. The first recipe is a complete workflow; the rest are fragments. Run frg workflow validate <name> after every change.
Prerequisites
- A working workflow file. If you have not written one, start with Creating Your First Workflow.
- For AI command states: a configured agent (
frg config init) and the context your workflow requires (frg config set-issue <ID>).
Chain AI commands behind approval gates
This workflow checks the repository, drafts an issue with the AI agent, retries once if the draft is incomplete, pauses for review, and publishes.
workflow_schema_version = "1.0.0"
[workflow]
name = "draft-review-publish"
description = "Draft an issue, gate it on approval, then publish it"
version = "1.0.0"
required_context = ["issue_id"]
[workflow.initial_state]
name = "preflight"
[[workflow.states]]
name = "preflight"
description = "Confirm the repository is ready"
operations = [
{ id = "git-available", type = "command_exists", command = "git", on_false = "fail" },
{ id = "clean-tree", type = "command_success", command = "git diff --quiet", on_false = "fail" },
]
on_success = "draft_issue"
on_failure = "preflight_failed"
[[workflow.states]]
name = "draft_issue"
description = "Drafting issue description"
command = { target = "issue", operation = "draft" }
on_success = "await_draft_approval"
on_failure = "retry_draft"
[[workflow.states]]
name = "retry_draft"
description = "Retrying the draft in the same agent session"
command = { target = "issue", operation = "draft", modifiers = ["retry"] }
on_success = "await_draft_approval"
on_failure = "draft_failed"
[[workflow.states]]
name = "await_draft_approval"
description = "Review and edit the draft before publishing"
on_success = "publish_issue"
on_failure = "draft_rejected"
[workflow.states.approval]
gate_id = "draft_approval"
message = "Issue draft is ready. Review and edit it, then approve to publish."
timeout_seconds = 86400
[[workflow.states]]
name = "publish_issue"
description = "Publishing the issue"
command = { target = "issue", operation = "publish" }
on_success = "completed"
on_failure = "publish_failed"
[workflow.terminal_states]
success = ["completed"]
failed = ["preflight_failed", "draft_failed", "publish_failed"]
cancelled = ["draft_rejected"]
[[workflow.states]]
name = "completed"
description = "Issue drafted, approved, and published"
[[workflow.states]]
name = "preflight_failed"
description = "Repository is not ready"
[[workflow.states]]
name = "draft_failed"
description = "Draft failed after retry"
[[workflow.states]]
name = "publish_failed"
description = "Publishing failed"
[[workflow.states]]
name = "draft_rejected"
description = "Draft was rejected at review"
Run it:
frg config set-issue 123
frg workflow validate draft-review-publish
frg workflow run draft-review-publish
Pass flags to a command state
modifiers lists the command's flags without the leading --. A flag's value is the next element. The built-in workflows use:
| Modifiers | Equivalent command |
|---|---|
["retry"] | frg code create --retry |
["clear"] | frg code create --clear |
["lite"] | frg issue plan --lite |
["auto-fix", "issues"] | frg code review --auto-fix issues |
Require a confirmation code or a timeout route
Harden a gate for risky transitions:
[workflow.states.approval]
gate_id = "merge_approval"
message = "PR is ready to merge. Type the confirmation code to approve."
required_code = "MERGE"
timeout_seconds = 3600
on_timeout = "merge_timed_out"
The approver passes the code with frg workflow approve --code MERGE --email <EMAIL>. Codes are case-sensitive.
Warning: Approval codes are written verbatim to the transaction log and uploaded to your OpenSearch/OTEL backend when one is configured. Do not use secrets as codes.
Add an AI agent step and reuse its session
An operation with type = "agent" sends a free-form prompt to the AI agent. context injects Forge context (issue, code, pr, security, ticket). share_session_with continues the agent session of an earlier state.
[[workflow.states]]
name = "summarize_changes"
description = "Summarize the branch for reviewers"
operation = { id = "summarize", type = "agent", prompt = "Summarize the changes on the current branch in CHANGES.md. List changed modules and risks.", context = ["issue"] }
on_success = "verify_summary"
on_failure = "await_intervention"
Verify agent work with a deterministic check
Agent steps can report success without doing the work. Follow them with a check, retry once in the same session, and then fall back to a human. The built-in pr-to-merge workflow uses this pattern after the agent creates a PR.
[[workflow.states]]
name = "verify_summary"
description = "Verify the summary file was written"
operation = { id = "summary-exists", type = "file_contains", path = "CHANGES.md", content = "Risks", on_false = "fail" }
on_success = "pr_draft"
on_failure = "retry_summary"
[[workflow.states]]
name = "retry_summary"
description = "Ask the agent to finish the summary"
operation = { id = "retry-summary", type = "agent", prompt = "CHANGES.md is missing or has no Risks section. Complete it.", context = ["issue"], share_session_with = "summarize_changes" }
on_success = "verify_summary_retry"
on_failure = "await_intervention"
[[workflow.states]]
name = "verify_summary_retry"
description = "Verify the summary after retry"
operation = { id = "summary-exists-retry", type = "file_contains", path = "CHANGES.md", content = "Risks", on_false = "fail" }
on_success = "pr_draft"
on_failure = "await_intervention"
[[workflow.states]]
name = "await_intervention"
description = "Summary failed - manual intervention required"
on_success = "pr_draft"
on_failure = "summary_failed"
[workflow.states.approval]
gate_id = "summary_intervention"
message = "Write CHANGES.md manually, then approve to continue."
timeout_seconds = 86400
Branch on a condition
Any check can act as a router: on_success and on_failure lead to different states.
[[workflow.states]]
name = "has_plan"
description = "Skip planning when a plan is already committed"
operation = { id = "plan-exists", type = "file_exists", path = "docs/plan.md", on_false = "fail" }
on_success = "create_code"
on_failure = "plan_implementation"
[[workflow.states]]
name = "is_ci"
description = "Use the lite review in CI"
operation = { id = "ci", type = "env_equals", env_name = "CI", env_value = "true", on_false = "fail" }
on_success = "code_review_lite"
on_failure = "code_review"
Inside a multi-operation state, on_false = "skip" logs the failed check and continues with the next operation instead of failing the state.
Recover a stopped workflow
- Find the state to resume from:
frg workflow status
frg workflow run issue-to-pr --list-states
- If the run was interrupted (Ctrl+C or crash), release the lock:
frg workflow unlock
Use --force only if you are sure no other workflow is running.
- Resume:
frg workflow run issue-to-pr --start-from create_code
To redirect a run while approving or rejecting a gate, pass --resume-at:
frg workflow approve --code <CODE> --email dev@example.com --resume-at create_code
frg workflow reject --code <CODE> --email dev@example.com --resume-at plan_implementation
To inspect a specific job's approvals, pass its directory: frg workflow approve --list-all --work-dir job_744.
Run workflows unattended in CI
export FORGE_ISSUE_ID=123
frg workflow run issue-to-pr-lite --bypass-approvals
--bypass-approvals skips approval gates. Bypassed approvals are recorded as Skipped via --bypass-approvals. Keep human gates in interactive runs of workflows that merge or publish.
Run several workflows at once
Only one workflow runs per repository folder. To run workflows concurrently, give each one its own Git worktree:
git worktree add ../app-issue-124 -b issue-124
cd ../app-issue-124
FORGE_ISSUE_ID=124 frg workflow run issue-to-pr
Customize a built-in workflow
frg config export --workflows # copies all 10 built-in workflows to .forge/config/workflows/
frg workflow validate issue-to-pr # re-validate after editing
frg workflow graph issue-to-pr # regenerate .forge/config/workflows/issue-to-pr.md