# Scheduler priority

When multiple features are enabled on a repo (explore mode, issue solving, PR
watcher, issue scout), the scheduler processes them in a **deterministic order
each cycle**. Only one feature dispatches per repo per cycle — no parallelism
within a single repo.

## Priority order

The scheduler checks features in this order for each enabled repo:

| Priority | Feature | Consumes a run slot? | Notes |
|----------|---------|---------------------|-------|
| 1st | **Explore mode** — auto-select | Yes (via normal spawn) | Falls through; the selected candidate runs as a normal spawn |
| — | **Explore mode** — scout | Yes (takes the cycle) | Kicks a scout when queue is empty; `continue`s |
| 2nd | **PR watcher** | **No** | `verify_pr` doesn't call `InsertRun`; doesn't count against daily limits |
| 3rd | **Issue solving** | Yes | Spawns a fix run; one issue per cycle |
| 4th | **Issue scout** | Yes | Spawns a read-only audit run; no branch or PR |
| 5th | **Normal spawn** | Yes | Uses the repo's objective or focus |

## How the cycle works

```
For each enabled repo (one per scheduler cycle):

  1. Explore? ──► queue has candidate? → auto-select focus, fall through
              │                    ↓
              └── queue empty? → kick scout, continue (skip rest)
                              
  2. PR watcher? ──► has PR to verify? → dispatch verify_pr, continue
                              ↓
                         no PR → fall through
                              
  3. Issue solving? ──► has issue to solve? → spawn fix run, continue
                                  ↓
                             no issue → fall through
                              
  4. Issue scout? ──► enabled? → spawn audit run, continue
                            ↓
                       disabled → fall through
                              
  5. Normal spawn ──► use objective/focus → spawn run
```

Once any feature **dispatches** (sends a command to a worker), the remaining
checks are skipped for that repo this cycle via `continue`. The repo gets
re-evaluated on the next scheduler tick (~10 minutes).

### Explore mode: special case

When explore auto-selects a candidate, it **updates the repo's focus** in the
database and **falls through** to the remaining checks (instead of `continue`).
This means:

- The selected candidate's focus becomes the repo's current focus.
- **PR watcher** and **issue solving** still get a chance to intercept the cycle.
- If nothing else dispatches, the **normal spawn** fires with the
  explore-selected focus.

When the explore queue is empty, a **scout** is dispatched to survey the repo
and propose new candidates. This consumes the cycle — no other feature runs
until the next tick.

## Standing objective overrides

A **standing human objective** (set in repo settings or via the API) overrides
autonomous features:

| Feature | Blocked by objective? |
|---------|----------------------|
| Explore mode | Yes — checks `repo.Objective == ""` |
| Issue solving | Yes — checks `repo.Objective == ""` |
| Issue scout | Yes — checks `repo.Objective == ""` |
| PR watcher | **No** — runs regardless of objective |
| Normal spawn | **No** — runs the objective |

When a human sets an objective, the scheduler skips explore, issue solving, and
issue scout entirely. Only PR watcher (which doesn't consume a run slot) and the
normal spawn (which executes the objective) remain active.

## Why no parallelism

Each scheduler cycle (~10 minutes) evaluates every enabled repo once. For a
given repo:

1. Only one run can be active at a time (the scheduler skips repos with a
   running run).
2. Only one feature dispatches per cycle (each `continue` exits the feature
   chain for that repo).
3. This prevents conflicts (two agents modifying the same repo simultaneously)
   and keeps the system predictable.

## Behavior by feature

### PR watcher — doesn't consume a run slot

`verify_pr` uses a lightweight command that doesn't call `InsertRun`. This means
it:

- Doesn't count against the daily run limit ([plan limits](/docs/plans-and-limits))
- Doesn't block a normal spawn in a later cycle
- Can run even when the daily run cap is reached
- Is still subject to the per-repo cooldown

### Issue solving — one issue per cycle

The scheduler picks the highest-scored issue candidate (or an in-progress
candidate needing additional solutions), spawns exactly one fix run, and waits
for the next cycle to pick again. Multiple solutions per issue are spread across
consecutive cycles.

### Issue scout — read-only run

An issue scout run is tracked in the `runs` table (status `issue_scout`) but
never creates a branch, commit, or PR. It audits the repo and files GitHub
issues directly.

## Examples

### Example 1: Objective + explore enabled

```
repo.Objective = "Bump deps"
repo.ExploreEnabled = true
repo.PRWatchingEnabled = true
```

Standing objective blocks explore. Each cycle: PR watcher runs (if PRs to
verify), then normal spawn runs the "Bump deps" objective.

### Example 2: Explore + issue solving + PR watcher, no objective

```
repo.Objective = ""
repo.ExploreEnabled = true (Max)
repo.IssueSolvingEnabled = true
repo.PRWatchingEnabled = true
```

Cycle 1: Explore queue has candidates? → auto-select, fall through. PR watcher
finds a PR → dispatches verify_pr, continue. Explore-selected focus waits for
next cycle.

Cycle 2: No explore candidate (still processing). PR watcher has no new PRs.
Issue solving picks an issue → spawns fix run, continue.

Cycle 3: Explore queue empty → kicks scout, continue.

### Example 3: Issue solving only

```
repo.Objective = ""
repo.IssueSolvingEnabled = true
```

Each cycle picks the highest-scored issue and spawns a fix. When all issues are
solved, the scheduler idles.

## Related

- [Explore mode](/docs/explore-mode)
- [Issue solving](/docs/issue-solving)
- [Issue scout](/docs/issue-scout)
- [PR watcher](/docs/pr-watcher)
- [Scheduling](/docs/scheduling)
