Approval Workflows
Human approval gates for autonomous task execution, PR merging, and failure recovery.
Approval workflows are designed for the autopilot prod environment where human oversight is required before code ships.
Overview
Pilot supports optional approval checkpoints at three stages of the autonomous pipeline. When enabled, Pilot pauses execution and sends an approval request through your configured channel (Telegram, Slack, or GitHub PR reviews).
Task Received
│
├─ pre_execution ──→ Approve? ──→ Execute Task
│ ↘ Reject → Skip Task
│
├─ Task Executes → PR Created → CI Passes
│
├─ pre_merge ──────→ Approve? ──→ Merge PR
│ ↘ Reject → Mark Failed
│
└─ (On Failure)
post_failure ───→ Approve? ──→ Retry
↘ Reject → AbortStages
| Stage | When | Purpose |
|---|---|---|
pre_execution | Before Pilot starts working on a task | Gate sensitive or high-risk work |
pre_merge | After CI passes, before auto-merge | Final human review before shipping |
post_failure | After task execution or CI fails | Approve retry or escalate |
Decisions
Each approval request resolves to one of three outcomes:
| Decision | Effect |
|---|---|
approved | Proceed to the next stage |
rejected | Stop execution, mark task as failed |
timeout | Apply default_action (configurable per stage) |
Configuration
# ~/.pilot/config.yaml
approval:
enabled: true
default_timeout: 1h
default_action: rejected # What happens on timeout: approved | rejected
pre_execution:
enabled: true
timeout: 1h
default_action: rejected
approvers:
- "@alice"
- "@bob"
require_all: false # Any one approver is sufficient
pre_merge:
enabled: true
timeout: 24h # Longer window for code review
default_action: rejected
approvers: [] # Empty = any configured channel user
post_failure:
enabled: false # Disabled by default
timeout: 1h
default_action: rejected
approvers: []Per-Stage Overrides
Each stage inherits from the top-level defaults and can override:
| Field | Default | Description |
|---|---|---|
enabled | true | Enable this approval stage |
timeout | default_timeout | How long to wait for a response |
default_action | default_action | Action on timeout (approved or rejected) |
approvers | [] | List of authorized approver handles |
require_all | false | Require all approvers vs any one |
Setting default_action: approved means tasks will auto-proceed if no one responds within the timeout. Use with caution.
Timeout Behavior
When an approval request expires:
- Pilot logs a warning with the request ID and task ID
- The pending request is cancelled on the notification channel
- The
default_actionfor that stage is applied - If
default_action: rejected, the task is marked as failed
Timeout precedence (highest to lowest):
- Stage-specific
timeout(e.g.,pre_merge.timeout: 24h) - Global
default_timeout(e.g.,approval.default_timeout: 1h) - Built-in defaults: 1h for
pre_execution/post_failure, 24h forpre_merge
require_all Semantics
| Setting | Behavior |
|---|---|
require_all: false (default) | First approver to respond decides |
require_all: true | All listed approvers must approve |
When require_all: true, a single rejection from any approver rejects the request immediately. All approvers must explicitly approve for the request to proceed.
Channels
Pilot sends approval requests through whichever channel is configured. If multiple channels are available, the first registered channel is used.
Telegram Inline Keyboard
Approval requests appear as messages with inline buttons:
🔔 Approval Required: pre_merge
Task: GH-123 — Add rate limiting
PR: #456
[✅ Merge] [❌ Reject]Button labels are stage-specific:
| Stage | Approve Button | Reject Button |
|---|---|---|
pre_execution | ✅ Execute | ❌ Cancel |
pre_merge | ✅ Merge | ❌ Reject |
post_failure | 🔄 Retry | ⏹ Abort |
After a decision, the message is edited to show the result. No setup beyond the standard Telegram Bot configuration is needed.
Slack Interactive Blocks
Approval requests are posted as rich messages with action buttons:
🔔 Approval Required: pre_merge
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Task: GH-123 — Add rate limiting
PR: https://github.com/you/repo/pull/456
[Approve] [Reject]Slack blocks include:
- Section block with task metadata and PR link
- Actions block with approve/reject buttons
Requires Slack interactive messages to be configured (see your Slack app’s Interactivity & Shortcuts settings).
GitHub PR Reviews
For the pre_merge stage, Pilot can use native GitHub PR reviews as the approval mechanism.
- Approve: Submit a PR review with “Approve”
- Reject: Submit a PR review with “Request changes”
Pilot polls for review status at a configurable interval (default: 30s). Webhook events (pull_request_review.submitted) provide instant response when configured.
# GitHub approval is automatic when using --github flag
pilot start --github --autopilot=prodNo additional configuration needed — Pilot monitors PR reviews on any PR it creates.
Autopilot Integration
Approval workflows integrate directly with the autopilot environment system:
| Environment | Approval Required | Behavior |
|---|---|---|
dev | No | Auto-merge after CI passes |
stage | No | Auto-merge after CI + delay |
prod | Yes (pre_merge) | Waits for human approval before merge |
Production Flow
Task → Execute → PR → CI Passes → Await Approval → Merge
↓
(timeout)
↓
default_actionStart Pilot with approval-gated production mode:
pilot start --github --autopilot=prodWhen CI passes on a PR in prod mode:
- Pilot transitions the PR to Await Approval state
- An approval request is sent via your configured channel
- Pilot blocks until approved, rejected, or timed out
- On approval, the PR is merged. On rejection, the PR is marked as failed.
Disabling Approval
Approval is disabled by default. To explicitly skip it:
approval:
enabled: falseIndividual stages can also be disabled while keeping the system active:
approval:
enabled: true
pre_execution:
enabled: false # Skip pre-execution gate
pre_merge:
enabled: true # Keep pre-merge gate
post_failure:
enabled: false # Skip post-failure gateWhen a stage is disabled or the approval system is off, Pilot auto-approves and proceeds without pausing.