| name | harness-loop |
| description | Drive a unit of work through the basicly harness loop end-to-end (intake → classify → decompose → build → verify → ship → teardown → retro) using `basicly loop` + `br`, agent-agnostic across Claude/Codex/Copilot. Use when starting or resuming non-trivial development in a harness-enabled repo, when deciding what phase a tracked issue is in, or when coordinating the checkpoints, gates, and bounded rework the loop enforces. |
Harness Loop
Scope
Owns driving one tracked unit of work through the whole development loop with the
basicly loop command group over the br (beads) tracker. The loop is
agent-agnostic: the same commands drive it under Claude, Codex, or Copilot — the
agent supplies the phase inputs (the work type, the child plan) and does the
coding in the worktree; the engine records state, gates, and advances.
It is not for the mechanics each phase delegates to — those have their own
skills: tool-br (create/claim/close issues), worktree-isolation (isolate a
node's build), conventional-commits (format the commit), tool-git (staging,
diffing). This skill sequences them; it does not replace them.
The tracker is the state — start by reading it
br is the single source of truth; the loop keeps no side-state, so it is
resumable across restarts and across agents. Whatever happened last session, begin
by reconstructing where a track is:
basicly loop status <issue>
Pick the next actionable issue with br ready / br scheduler --json; basicly loop status then tells you which phase it is parked in and what it is waiting on.
Tracker state is zero-touch — the engine commits it, you never do
One tracker, everywhere: a loop-provisioned worktree shares the base
checkout's .beads via br's git-ignored redirect file, so br commands
(create, update, comments, gates) work identically from base or any
worktree and always hit the one real DB/JSONL. There is no worktree tracker
copy to diverge and nothing to reconcile at landing.
The engine makes the only tracker commits, at the three natural points:
- At provisioning, the claim (status, work type, classify approval) is
committed before the worktree is created, so the claim is in git history
from the moment work starts — a teammate who pulls sees it immediately.
- At landing,
loop advance rolls the .beads/** dirt accumulated
since (checkpoints, gate records) into one chore(beads) commit before
merging. A base that is dirty with anything outside .beads/ still
blocks — that is someone's uncommitted work.
- At ship, after
br close, the engine commits the closing tracker
state itself.
So: never git add .beads yourself for loop-tracked work, and never commit
.beads/issues.jsonl on a harness branch (restore it if something staged
it).
Push cadence — tracker-only pushes are CI-free by design (paths-ignore .beads/**; the local commit-msg hooks are the deterministic floor), so the
choice is purely about who is watching the remote:
- Solo repo: push once per track, after ship — one CI run for the real
change, none for the tracker noise.
- Team repo (other agents or humans pull the same tracker): push each
engine tracker commit as soon as it lands — especially the provisioning
claim commit, so nobody starts work someone already claimed. Cross-machine
races that still slip through are caught by
br scheduler --stale-claim-hours and br coordination.
Advancing the loop
Each step is one resumable transition: the engine re-reads the phase from br,
does the phase's action (or blocks waiting on an input, a checkpoint, or a gate),
and returns the outcome. A blocked step exits non-zero so scripts and CI can
branch on it.
basicly loop advance <issue> [--work-type T] [--children plan.toml] [--mode M]
basicly loop run <issue> [flags]
The flags are the agent-supplied inputs a phase needs; pass the one the current
phase is asking for (basicly loop status/advance names it as needs input).
Phases and what each one needs
| Phase | What advances it | Command |
|---|
| intake | agent proposes the br work type | basicly loop advance <id> --work-type {bug|chore|task|feature|epic} — records the type, then blocks for the classify checkpoint |
| classify (human checkpoint) | approve, then the Definition-of-Ready gate must pass | basicly policy checkpoint <id> classify --approve; check readiness with basicly policy dor <id> |
| decompose | features need an agent child plan; leaves (bug/chore/task) skip straight to build | basicly decompose <feature> --plan plan.toml (or --children on advance); preview with basicly decompose <feature> --plan plan.toml --dry-run |
| decompose (human checkpoint) | approve before fan-out | basicly policy checkpoint <id> decompose --approve |
| build | fan out one worktree per dependency-unblocked child (ranked by br scheduler, concurrency-capped), do the work, land through the serial merge queue | basicly worktree list; do the coding; basicly loop advance <id> lands it |
| verify | the landing already ran verify and recorded the required gate; inspect it, then approve ship | basicly policy gate <id>; basicly policy checkpoint <id> ship --approve. Re-run gates (e.g. after rework) with basicly verify --mode full --issue <id> — from the base checkout only |
| ship | tear down the worktree and close the issue | basicly loop advance <id> (runs teardown + br close) |
Leaf types (bug/chore/task) build directly in their own worktree; features
decompose into children and, once every child closes, land the child worktrees
that are still live through the merge queue. A child driven through its own
loop self-lands (its ship phase tears the worktree down), so the parent fan-in
counts it as already merged instead of failing. See worktree-isolation for
the sibling-worktree placement and provisioning rules the build phase relies
on.
Gates: deterministic blocks, semantic advises
Deterministic checks (tests, lint, type, build) report a required gate via
br gate report --status pass|fail; a failed required gate blocks advancement.
AI-semantic verification reports a non-required gate — advisory, never
blocking. Inspect the decision with basicly policy gate <id>.
Gates recorded from a loop-provisioned worktree are safe: its .beads
redirects to the base checkout, so the record lands in the one real tracker.
basicly verify --issue refuses only from a linked worktree without that
redirect (a throwaway tracker copy — the record would be discarded at
landing). During build you never need to record the gate manually anyway:
the landing advance verifies the rebased tree in the worktree and records
the required gate itself.
Rework and escalation
A failed node enters a bounded rework loop (default n=2), tracked with gate
results and comments — not a status change (br has no rework status). At the
cap it escalates to a human. Inspect or record attempts with:
basicly policy rework <id> --gate verify [--record]
Any track can escalate a tier (carry work forward, re-hit only the decompose
checkpoint) without restarting.
Ship and retro
Ship merges to the entry branch recorded at intake (default: main), tears down
the worktree, and closes the issue. After an epic slice lands, capture a retro as
tracker comments and file a br issue for each finding the user does not choose
to ignore:
br comments add <epic-id> "Retro: <finding>"
One-shot vs task-by-task
Default is task-by-task (every checkpoint is a stop). One-shot mode collapses the
middle (decompose) checkpoint for small, well-scoped work; the classify and ship
checkpoints still hold.
Resuming after a switch
Because state lives in br (issue status, the worktree binding on
external_ref, recorded gate results, checkpoint/rework comments), resuming — after
a crash, or when switching from a rate-limited agent to another — is just
re-reading it: basicly loop status <issue>, reconcile against live worktrees
(basicly worktree list), and continue. Start on one agent, finish on another.