185 lines
8.9 KiB
Markdown
185 lines
8.9 KiB
Markdown
# Blitz workers: aoe sessions
|
|
|
|
How blitz fans out: each ready issue gets an **external `aoe` session** — its
|
|
own tmux pane, worktree, tool (claude / codex / opencode) and model. Sessions
|
|
survive the orchestrator restarting, and routing across providers keeps one
|
|
provider's outage or blind spots from shaping the whole run.
|
|
|
|
Everything else in blitz (DAG, integration, review cadence, readiness gate,
|
|
ship, notify) is defined in SKILL.md. This file covers spawning, prompting,
|
|
babysitting and cleanup.
|
|
|
|
## Model routing
|
|
|
|
Two inputs decide who runs an issue: the **roster** (what this box may spawn)
|
|
and the **notes** (what previous blitzes learned about them).
|
|
|
|
### The roster
|
|
|
|
`~/.config/agent-skills/config.json` — the same file the PR daemon reads, so a
|
|
model added once is available to both. Query it rather than parsing it:
|
|
|
|
```sh
|
|
~/.claude/scripts/roster.sh --role blitz --tier execution --format aoe
|
|
# --tool claude --extra-args "--model sonnet"
|
|
```
|
|
|
|
Entries carry `roles` (`blitz` entries are yours; `review`-only ones are not)
|
|
and `tiers`. Exit 3 means no config on this box and exit 4 means the tier is
|
|
empty — in both cases fall back to the defaults below and say so in the run
|
|
summary, since an empty tier is usually a config gap worth fixing.
|
|
|
|
### Picking a tier
|
|
|
|
**Read `~/.local/state/agent-skills/models.md` first** — it carries what
|
|
previous blitzes learned (see "Model notes" below). A note that contradicts
|
|
the tier definitions here wins, because it was written against real runs.
|
|
|
|
Blitz is conservative by default: start with the least expensive capable
|
|
entry in the selected tier and escalate only when the issue or a failed pass
|
|
justifies it. In particular, use medium/standard models for `execution` and
|
|
ordinary `design` work; reserve high-effort models for genuinely `subtle`
|
|
issues or escalation. Codex medium is therefore a normal choice for execution
|
|
and design, while Codex high is not a default for every design issue. This
|
|
keeps subscription usage bounded without removing stronger models from the
|
|
roster or from review rotation.
|
|
|
|
Before each wave, the orchestrator runs
|
|
`python3 <skills-root>/blitz/usage-budget.py --json`. When its
|
|
`preferMedium` flag is true, choose medium/standard entries even when a high
|
|
entry is available. A `park` verdict means do not refill the wave; finish and
|
|
record the current state. The probe is intentionally conservative when a
|
|
provider exposes no machine-readable short-window percentage.
|
|
|
|
**Assess each issue at spawn time.** You have just read its body to write the
|
|
prompt — use that read. The question is not "how big is this" but **how much
|
|
judgment does the session still have to exercise**:
|
|
|
|
- **`execution`** — body settles the approach (root cause named, fix shape
|
|
decided, numbers suggested, files pointed at); the thinking happened at
|
|
filing time. Regardless of size: a large mechanical CRUD issue is still
|
|
execution.
|
|
- **`design`** — body states the goal, but the session must design the
|
|
interface, choose the data model, or amend the design doc.
|
|
- **`subtle`** — the design doc is thin or contradictory where this issue
|
|
lives, correctness is subtle, or the change is cross-cutting with unclear
|
|
blast radius.
|
|
|
|
Defaults if the roster is unreachable: `claude --model sonnet`, `--model opus`,
|
|
`--model fable` for the three tiers in that order. Apply the same conservative
|
|
rule to those fallbacks: use the lower tier first and escalate only on
|
|
evidence.
|
|
|
|
A `difficulty/` label is one input — a filing-time guess that cannot see how
|
|
much the body scaffolds. Trust your read of the body over it; the label is a
|
|
tie-breaker. When in doubt between two tiers take the lower one: escalation
|
|
on failure is cheap, and a failed cheap run teaches something a successful
|
|
expensive run does not.
|
|
|
|
The operator overrides any of this by just saying so in the invocation ("run
|
|
these on codex", "use ox alpha for the easy ones") — no config edit needed.
|
|
When a tier holds several entries, alternate rather than draining one first —
|
|
a bad run should be visible early. Escalate *sideways* (a peer in the same
|
|
tier) before escalating up, and never de-escalate mid-issue.
|
|
|
|
## Model notes: `~/.local/state/agent-skills/models.md`
|
|
|
|
The shared memory across blitzes. Read it at spawn time, **update it before the
|
|
run ends** — including a run that ends blocked, since a model failing is the
|
|
evidence that's hardest to come by.
|
|
|
|
It is a compiled summary, not a log. Four sections, fixed: difficulty tiers,
|
|
task fit, cost effectiveness, caveats. Rules:
|
|
|
|
- **Under ~60 lines, always.** If an edit pushes past that, something in there
|
|
has stopped earning its line — cut it in the same edit.
|
|
- **Rewrite in place.** Merge new evidence into the claim that already exists;
|
|
never append a dated entry or a per-run section. The next blitz should read
|
|
the current belief, not reconstruct it from history.
|
|
- **Record what would change a routing decision.** Something that repeated
|
|
across sessions, or that was decisive once (a model that couldn't finish an
|
|
issue the tier below finished). Mark a single-run claim `(1 run)`.
|
|
- Caveats are for annoyances that don't veto a model — "the free 0x endpoint
|
|
times out often, retry and it lands" belongs there; it is not a reason to
|
|
route around it.
|
|
- Contradicted by a newer run? Replace the line, don't stack a qualifier on it.
|
|
- If the file is missing, create it with those four sections and note that the
|
|
tiers are seeded from this doc rather than measured.
|
|
|
|
Bounce anything with wider reach than blitz — an entry that should leave the
|
|
roster, change tiers, or drop a role — to `week-review`, which owns config
|
|
changes. These notes stay advisory; the roster is the config.
|
|
|
|
## Spawning
|
|
|
|
```sh
|
|
aoe add <repo-root> \
|
|
--title "<repo>-<n>-<slug>" \
|
|
--worktree issue/<n>-<slug> --new-branch \
|
|
--tool <tool> --extra-args "<model args>" \
|
|
--launch
|
|
```
|
|
|
|
Known traps, all confirmed the hard way:
|
|
|
|
- **`aoe send` races with `--launch` and fails silently.** Never trust it.
|
|
Deliver the prompt with `tmux send-keys -l "<prompt>"` followed by a
|
|
**separate** `Enter` a second later, then verify with
|
|
`tmux capture-pane -p` that it landed.
|
|
- **tmux truncates session names.** Look panes up by prefix
|
|
(`tmux list-sessions -F '#{session_name}' | grep '^aoe_<title-prefix>'`),
|
|
never by exact title. A failed exact lookup can dump the prompt into your
|
|
own pane.
|
|
- `--new-branch` is required for a branch that doesn't exist. The worktree
|
|
path comes from `--title`, not the branch — distinct titles or the second
|
|
session collides.
|
|
|
|
## The prompt
|
|
|
|
Two phrasings matter, learned from models that do the work and then stop:
|
|
|
|
1. **Exit criteria beat autonomy language.** "Work fully autonomously" does
|
|
not stop a model from ending its turn after a big tool result. What does:
|
|
*"Do not end your final reply until ALL of these are true: … If you catch
|
|
yourself summarizing progress before those are true, you have stopped too
|
|
early — keep going."* Fill the criteria with the terminal state you
|
|
actually assigned: under blitz that is gate-green + **branch pushed** (the
|
|
orchestrator merges and closes); when driving direct-to-main it is
|
|
merged + pushed + issue closed with a comment naming the commit.
|
|
2. **Fence parallel sessions off each other's files.** When two issues run at
|
|
once, each prompt names what the other owns: "Do not touch X — issue #M
|
|
owns it and runs in parallel." Merge conflicts are cheaper to prevent in
|
|
the prompt than to resolve after.
|
|
|
|
Also include: read CLAUDE.md and the design doc first; fetch the issue body
|
|
via the tracker API; merge (never rebase) the base branch if it moved; and
|
|
never wait for input.
|
|
|
|
## Babysitting
|
|
|
|
Some models stall — idle turn-end after absorbing a large tool result, work
|
|
half done. Don't hand-poll; arm a **self-nudging Monitor** per session:
|
|
|
|
- Poll every ~90s. Idle means the pane shows no in-progress marker *and* the
|
|
context/size indicator is frozen across two consecutive checks — one check
|
|
is not enough, models legitimately pause.
|
|
- On stall: send the nudge yourself via `tmux send-keys` — restate the exit
|
|
criteria and where it stopped — capped at ~6 nudges before escalating to
|
|
a human.
|
|
- Exit (and notify the orchestrator) on: issue closed, session gone, nudge
|
|
cap, or timeout. Silence must not look like success — every terminal state
|
|
emits a line.
|
|
|
|
## Collect, integrate, clean up
|
|
|
|
- Detect completion by **tracker state** (issue closed) or the integration
|
|
branch moving — never by grepping commit messages; fuzzy matches fire on
|
|
the wrong branch's commits.
|
|
- After a session's work is merged: `aoe remove <title> --delete-worktree`.
|
|
Sweep every couple of waves; stale sessions pile up. (`aoe` leaves removed
|
|
worktrees locked — `git worktree unlock` before a manual
|
|
`git worktree remove`.)
|
|
- Verify the landed result yourself against the live system when one exists
|
|
(deploy health, a smoke request against the changed endpoint). A session
|
|
reporting success is a claim, not a verification.
|