SuperAPI AI Directory

loop

Agents & models

View repository

Updated 10-26 20:53

About this skill

/loop (bro)

All mechanics live in the bro CLI. This skill is policy only. bro flow next schedules one bead; bro flow loop runs the queue to exhaustion — claim → worktree → agent → push onto the gate stack → repeat — with no per-item "should I continue?" The backlog's existence is the approval.

Configure the agent once

Prefer the provider lane — a providers.<name> registry entry resolves the spawn exactly like bro fleet agents up:

{
  "providers": {
    "kilo-cli": { "type": "acp", "command": "kilo --acp", "model": "typesafe/jev-1.13" }
  },
  "loop": { "provider": "kilo-cli" }
}

An acp entry spawns bro acp-worker — a stdio ACP server driven by the protocol, headless by construction (no TUI can open on WSL interop). A cli entry substitutes its command for the template. The picks: --provider/--agent <name> flags → fleet.profiles preset (--profile, loop.profile) → loop.provider → agents.native.provider. --model and --auto-approve tune the lane. bro flow loop --agent kilo-cli works too — a bare --agent value that exactly names a configured provider IS a provider pick.

Per-bead routing — declare fleet.routing and each claimed bead's class picks its own provider chain, same as bro agents up: the class:<name> bead label wins, unclassed beads take default, and --class <name> pins the whole run to one class:

{
  "fleet": {
    "routing": {
      "default": { "chain": ["devin"] },
      "sweep": { "chain": ["kilo-free"] }
    }
  }
}

The chain head supplies the provider (and its inline model pin) unless a flag-tier pick already named one — explicit --provider, --agent <provider>, --profile still pin the lane for every bead, with the resolved class stamped as provenance (BRO_AGENT_CLASS); the config picks (loop.provider, loop.profile, agents.native.provider) are standing defaults the declared table outranks. A resolved class missing from the table is a config error — default is preflighted before the first claim, a bad class: label parks its bead instead of silently falling through. fleet.router (shadow/enforce) classifies unclassed beads by judge verdict, same as agents up.

Raw template — loop.agent (or an --agent value that names no provider) is the escape hatch:

{ "loop": { "agent": "devin --prompt-file {promptFile} -p" } }

{promptFile} is replaced with the work-order file bro writes into the fresh worktree (no placeholder → the path is appended as the last arg). Examples: claude -p "$(cat {promptFile})", codex exec "$(cat {promptFile})". The spawn env carries BRO_BEAD_ID, BRO_BEAD_TITLE, BRO_PROMPT_FILE (provider lane also pins BRO_AGENT_PROVIDER, BRO_AGENT_MODEL). Whatever permission flags your agent needs for unattended work are yours to choose — e.g. devin's --permission-mode dangerous --respect-workspace-trust false skips all human confirmation, which is the point of the loop but obviously grants the agent full autonomy; scope it to machines/repos you trust. A template --agent flag wins over a configured loop.provider — the provider flags can't sit beside it. It also bypasses fleet.routing entirely (a warn line says so): every bead runs the literal template.

Other loop keys: bootstrap (runs once per worktree), stallMin (advisory output-silence flag at check-in — never a kill), mergeTimeoutMin, fixRounds, maxItems, maxOpen — the gate stack's open-PR cap (default 3) — worktreeMb / diskMinSlots, the disk floor below — and batch / batchMinPriority, the clump knobs below.

Commands

Command What it does
bro flow loop Run the queue until idle or gated
bro flow loop --max N At most N beads this run
bro flow loop --max-open N Cap the gate stack's open PRs — a full stack only services gates until a merge frees a slot
bro flow loop --disk-min-slots N Disk floor in slot units — pushes hold while free disk < N×loop.worktreeMb on any filesystem the run writes to (default 2; 0 disables)
bro flow loop --dry-run Print the top item's plan (claim, worktree, agent cmd) — changes nothing
bro flow loop --agent '<tpl>' One-off agent override — a provider name resolves through the registry
bro flow loop --provider <name> Provider pick (--profile, --model, --auto-approve tune it)
bro flow loop --class <name> Pin every claimed bead to one fleet.routing class (default: the bead's class: label, else default)
bro flow loop --label a,b Declared scope — only beads carrying one of these labels are claimable; the rest of the shared queue stays untouched. "Loop the debt beads" never bleeds into unrelated work
bro flow loop --batch N Batch claims — a claim pulls up to N same-affinity beads into one worktree + worker + PR (default 1 = solo). Affinity binds on the lead's spec/epic/area:/path-prefix key; loop.batchMinPriority (default 3) keeps urgent work solo; a solo label opts a bead out
--merge-timeout MIN, --interval SEC Gate budget and poll overrides

What happens per bead

  1. Claim — top bd ready item, bro flow next's rules: HUMAN GATE beads, epics, and molecule steps (convoy-owned) are never auto-claimed.
  2. Worktree — sibling <repo>--<bead-id> on branch loop/<id> off origin/main; loop.bootstrap runs once per bead, before the agent.
  3. Agent — the work-order prompt is written to the worktree and the agent runs to completion — no per-agent wall clock (a hard kill on a working session is an anti-pattern; the run record is how a check-in judges it instead). stdout/stderr append to <git-common>/bro/loop/<slug>.log and a <slug>.json record pins the pid — bro review watch/bro fleet status report output silence past loop.stallMin (45) as an advisory; the orchestrator decides, bro never kills. Its env pins BEADS_DIR to the runner's store (bd where), so bd writes inside the worktree reach the shared db regardless of version or a tracked .beads copy — and an agent bd close is honored as a verdict (closed), not reopened as a failure.
  4. Gate stack — the PR is discovered via gh pr list --head and joins the run's gate stack instead of blocking the queue. Each tick services the stack oldest-first, then pushes the next bead while a loop.maxOpen slot is free — a pending gate is claimed work, never orchestrator idle. A gate event is the preemption point: green → bro review act merge (merge slot + --match-head-commit apply); threads → the agent is respawned with the thread list; CONFLICTING → a rebase order onto the PR's base; BEHIND as the sole blocker → update-branch. Fix and rebase rounds share loop.fixRounds; each member waits out its own mergeTimeoutMin.
  5. Close — bd close <id> --reason "landed via PR #N", worktree and branch removed; the freed slot claims the next bead. A member's own loop IS the supervisor — bro review drive never needs to spawn fixers against loop-owned worktrees.

Policy

  • The loop ends at idle, gated, or --max N — idle means the backlog is empty; gated means only human gates / epics / molecule steps remain. Report which and stop.
  • Failures are visible, never silent — an agent that exits without a PR gets the bead reopened with a loop: note; a stalled PR parks the bead: a loop: note names the blocker, the claim releases into a deferred snooze for loop.parkedWakeMin (default 2h — then it wakes to open on the next ready read; 0 ices it until bd undefer), and the worktree stays as the resume cache.
  • The open-PR set is bounded — loop.maxOpen (default 3) caps how many gates sit on the stack; a full stack services gates only until a merge frees a slot. Independent branches drift against shared files fast — keep the cap tight rather than wide, or run --stack so members chain onto each other and conflicts surface at rebase-time.
  • Pushes also hold on the disk floor — a slot is priced at loop.worktreeMb (default 400 MB: worktree + bootstrap/agent litter); while free disk on the worktree parent or tmpdir drops below loop.diskMinSlots× that price (default 2), pushes hold like a full fleet — nothing claimed, nothing parked — until merges free the space. --disk-min-slots 0 is the emergency escape.
  • Already-attempted beads aren't re-picked within a run — a reopened failure can't spin the loop forever.
  • Batch claims are fail-safe, never silent — with loop.batch/--batch N a claim is a clump: one worker carries N compatible beads through one worktree and one PR. The contract is per-bead commits naming the bead id (fix(cli): drift (bro-x1)) — at merge the gate closes exactly the members the branch's commit log names and re-queues the uncovered tail. A mid-flight batch loses only the in-flight bead, never the landed prefix; a member that merged without a covering commit comes back as open work, not a false close. --max bounds beads, not items — a clump fills only up to the remaining claim budget.
  • --dry-run first on a new repo — verify the agent template and worktree path before the loop starts claiming.
  • One bro flow loop per repo — two runners would race the same top beads (claim is atomic, so the loser just takes the next one — wasteful, not corrupting).

Done means verified-clean, not exited

A loop iteration is done only when the bead reached a terminal, authoritatively verified state — PR MERGED per gh pr view and the bead closed per bd show, or parked with a loop: note naming the blocker and the claim snoozed deferred. A process exiting is not a verdict.

The loop runs this audit itself at every exit — idle, gated, or error (try/finally around the claim cycle). It first reaps provably-done litter — clean loop/* worktrees and branches whose bead is closed or whose PR merged (bro work prune --loop runs the same sweep by hand) — then names every surviving tail in the run summary: open loop/* PRs, kept worktrees and branches, beads still in_progress, and cleanup failures collected during the run, then bro work sync pushes artifacts and bead state. A clean run prints clean — no loop tails. Tails that survive the sweep — dirty trees, open PRs, live claimants — still need a human or a follow-up session to clear.

The process itself is held to the same contract: a ref'd heartbeat keeps the event loop open while a run is live (loop: alive — <stage> lines name the suspension point, so a stuck await is a visible stall, never a silent drain-exit), and an exit listener prints loop: process exiting mid-run — <stage> plus a bead note if the process dies with the queue open — signals included. A run can stall or die; it cannot vanish without a trail.

Cleanup after a merge wait is &&-sequenced or command-owned — never ;: a failed act wait must not touch the PR, branch, worktree, or bead.