Autopilot Mode
Fully autonomous operation with automatic PR merging. Autopilot is the post-execution tail of Pilot’s Execution Pipeline — it picks up once the PR is opened and runs CI monitoring, conflict resolution, auto-merge, and release.
Autopilot can merge code without human review. Use with caution.
Environments
| Environment | Auto-merge | CI Required | Use Case |
|---|---|---|---|
dev | After CI passes | Yes | Personal projects |
stage | After CI + delay | Yes | Team staging |
prod | Never | Yes | Production (review required) |
See Autopilot Environments for detailed behavior differences, recommended configurations, and migration guides.
Configuration
# ~/.pilot/config.yaml
orchestrator:
autopilot:
enabled: true
environment: stage
auto_merge: trueUsage
# Development - fast iteration
pilot start --github --env=dev
# Staging - with safety delay
pilot start --github --env=stage
# Production - no auto-merge
pilot start --github --env=prodSafety Features
CI Requirement
Autopilot always waits for CI to pass:
Task → PR → CI Running → CI Passes → Merge
↘ CI Fails → Notify, No MergeProtected Branches
Direct pushes to protected branches are blocked. Autopilot always creates PRs.
Monitoring
Track autopilot activity:
# View recent autopilot actions
pilot logs --autopilot
# Dashboard with real-time updates
pilot start --github --env=dev --dashboardRollback
If autopilot merges something problematic:
# Revert the last autopilot merge
pilot rollback --last
# Revert a specific PR
pilot rollback --pr 123Conflict Resolution
When a Pilot PR has a merge conflict, autopilot automatically attempts to resolve it via rebase before falling back to full re-execution. This is handled by handleMergeConflict() in the autopilot controller.
Auto-Rebase Flow
Merge Conflict Detected
│
▼
┌──────────────────────────┐
│ handleMergeConflict() │
│ Detect DIRTY/CONFLICTING │
└────────┬─────────────────┘
│
▼
┌──────────────────────────┐
│ UpdatePullRequestBranch │ ← GitHub API: merge base into head
│ (auto-rebase) │
└────────┬─────────────────┘
│
┌────┴────┐
│ │
Success Failure
│ │
▼ ▼
WaitingCI ┌──────────────────────────┐
(re-run CI) │ Close PR with comment │
│ Re-queue original issue │
│ New branch from latest │
│ main → full re-execution │
└──────────────────────────┘How Conflict Detection Works
Autopilot checks the PR’s mergeable_state field from the GitHub API during each poll cycle. When the state is DIRTY or CONFLICTING, conflict resolution triggers automatically:
-
First attempt — API rebase: Calls GitHub’s Update a pull request branch endpoint. This is equivalent to clicking the “Update branch” button on the PR page. It merges the base branch (typically
main) into the PR’s head branch. -
If rebase succeeds: The PR transitions back to the
WaitingCIstage. GitHub automatically triggers CI on the updated branch, and autopilot resumes monitoring from there. -
If rebase fails: The conflict is non-trivial (e.g., both branches edited the same lines). Autopilot closes the PR with an explanatory comment describing the conflict, then returns the original issue to the execution queue. On re-execution, Pilot creates a fresh branch from the latest
mainand re-implements the changes from scratch.
Cost Savings
Auto-rebase avoids ~$8–15 per full re-execution for conflicts that are trivially resolvable (e.g., upstream added a new file, or unrelated lines in the same file changed). In projects with frequent parallel PRs, this can save significant cost over a week of operation.
Configuration
# ~/.pilot/config.yaml
orchestrator:
autopilot:
enabled: true
max_rebase_attempts: 3 # Attempts before falling back to close+requeue (default: 3)Auto-rebase itself is not a toggle — autopilot always attempts an API rebase on conflict, then closes-and-requeues on failure (see the flow above). max_rebase_attempts (autopilot.Config.MaxRebaseAttempts in internal/autopilot/types.go) is a flat, top-level field on the autopilot config struct — not nested under a conflict_handling: block — and is the only part of this behavior that’s configurable today.
CI Fix Dependencies
When autopilot’s feedback loop creates a CI fix issue (after a CI failure), the fix issue body includes a Depends on: #N annotation linking it back to the original parent issue. This provides traceability between fix attempts and the tasks that triggered them.
Dependency Annotations
The annotation is added to the fix issue body as a metadata line:
Depends on: #142
CI failed on PR #145 (branch: pilot/GH-142).
Error: `test_login_redirect` assertion failure on line 47.
Fix the failing test while preserving the original feature behavior.This lets you trace the chain: original issue → PR → CI failure → fix issue → fix PR. GitHub also renders #142 as a clickable cross-reference, so the original issue shows all related fix attempts in its timeline.
CI Fix Iteration Limit
Controls how many chained CI-fix issues autopilot will spawn for the same PR before giving up, preventing infinite fix-issue-creates-fix-issue cascades. The counter lives in each fix issue’s <!-- autopilot-meta branch:... pr:... iteration:N --> marker (see Review Feedback below for the exact format) and is compared against this limit on the next CI failure.
# ~/.pilot/config.yaml
orchestrator:
autopilot:
max_ci_fix_iterations: 3 # Maximum CI fix cycles before giving up (default: 3)This is a CI-monitor safety guard, but the key itself is not nested under a separate ci_monitor: block — autopilot.Config.MaxCIFixIterations (internal/autopilot/types.go) is a flat, top-level field on the autopilot config struct (yaml:"max_ci_fix_iterations"), a sibling of max_failures rather than a child of review_feedback-style sub-config.
| Guard | Description |
|---|---|
| Iteration limit | Controlled by max_ci_fix_iterations (default: 3, autopilot.Config.MaxCIFixIterations in internal/autopilot/types.go). Closure semantics when the limit is reached depend on orchestrator.execution.mode: under sequential, the PR is closed to unblock the queue. Under any other mode (including the default auto), the PR is held open with the pilot-needs-human label and the branch left intact for manual continuation. |
max_ci_fix_iterations: N produces exactly N fix issues for a given PR — the Nth fix PR’s own CI failure hits the limit and gets no further fix cycle.
Stagnation Monitor
Detects stuck executions by tracking state changes and escalating through progressive intervention levels.
How Detection Works
Each execution turn, Pilot hashes the current state using the pattern phase:progress:iteration. This creates a unique fingerprint for each distinct execution state.
- History buffer: Recent state hashes are stored (configurable size, default 5)
- Repeat detection: Consecutive identical hashes indicate the execution is stuck in a loop
- Time-based detection: No progress for extended periods also triggers escalation
When either condition is met, the monitor escalates through intervention levels.
Escalation Levels
| Level | Trigger | Action |
|---|---|---|
| None | Normal progress | Continue execution |
| Warn | 3+ identical states OR warn_timeout (10m) | Log warning, continue |
| Pause | pause_at_iteration (12) OR pause_timeout (20m) | Pause execution, attempt recovery |
| Abort | abort_at_iteration (15) OR abort_timeout (30m) | Stop execution, optionally commit partial work |
Configuration
# ~/.pilot/config.yaml
executor:
stagnation:
enabled: true
warn_timeout: 10m # Time without progress before warning
pause_timeout: 20m # Time without progress before pause
abort_timeout: 30m # Time without progress before abort
warn_at_iteration: 8 # Iteration count to trigger warn
pause_at_iteration: 12 # Iteration count to trigger pause
abort_at_iteration: 15 # Iteration count to trigger abort
state_history_size: 5 # Number of state hashes to track
identical_states_threshold: 3 # Consecutive identical states for warn
grace_period: 30s # Ignore stagnation during startup
commit_partial_work: true # Save progress on abortPartial Work Commit
When commit_partial_work: true (default), Pilot salvages progress on abort:
- Commits whatever changes were made before stagnation
- Creates a PR with partial implementation
- Marks the issue as
pilot-failedwith stagnation reason - Allows manual continuation from the partial branch
The stagnation monitor is disabled by default. Enable it for long-running tasks where stuck executions waste tokens. The default timeouts (10m/20m/30m) work well for most projects.
Review Feedback
When a reviewer submits CHANGES_REQUESTED on a Pilot PR, autopilot automatically creates a revision issue, closes the original PR, and re-executes with the reviewer’s feedback incorporated.
How It Works
Reviewer submits CHANGES_REQUESTED
│
▼
┌─────────────────────────────┐
│ Collect review comments │
│ (top-level + line-level) │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ Learn from review │ ← Pattern learning (pre-execution)
│ (confidence boost, │
│ anti-pattern extraction) │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ Create revision issue │ ← Formatted review comments in body
│ with iteration counter │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ Close original PR │ ← Unblocks sequential poller
│ Clean up branch │
└────────┬────────────────────┘
│
▼
┌─────────────────────────────┐
│ Pilot picks up revision │
│ issue → re-executes on │
│ same branch with --from-pr │
│ for session context │
└─────────────────────────────┘Detection Modes
Review feedback works in both operational modes:
- Webhook mode (instant): The
OnPRReviewcallback fires immediately when GitHub sends apull_request_reviewevent withaction: submittedandstate: changes_requested. - Polling mode (periodic): During each
processAllPRstick, autopilot callshasChangesRequested()to check for unresolved change requests. Bot reviews (self-review) are filtered out automatically.
Configuration
# ~/.pilot/config.yaml
orchestrator:
autopilot:
review_feedback:
enabled: true
max_iterations: 3 # Maximum revision cycles before giving up (default: 3)Safety Guards
| Guard | Description |
|---|---|
| Iteration limit | Controlled by max_iterations (default: 3). Prevents infinite review-fix cycles. Closure semantics when the limit is reached depend on orchestrator.execution.mode: under sequential, the PR is closed to unblock the queue. Under any other mode (including the default auto), the PR is held open with the pilot-needs-human label and the branch left intact for manual continuation. |
| Self-review filter | Bot reviews (usernames containing [bot] or ending in -bot) are excluded from change-request detection. Pilot’s own self-review won’t trigger a revision loop. |
| Per-reviewer tracking | Only the latest review state per reviewer is considered. If a reviewer approves after previously requesting changes, the change request is resolved. |
The iteration limit reuses the same counter mechanism as CI fix iterations. Each revision issue body includes an <!-- autopilot-meta branch:pilot/GH-N pr:M iteration:K --> marker that the controller parses to track the current cycle.
Because the counter increments once per continuation issue, max_iterations: N produces exactly N revision issues for a given PR — the Nth revision PR’s own change request hits the limit and gets no further revision cycle.