| 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, commit it on the branch, then land through the serial merge queue | basicly worktree list; do the coding; git commit the work on the harness branch (referencing the bead — conventional-commits); 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) |
Never record the verify gate by hand during build. The build→verify
loop advance is the only step that merges the worktree back to base
(_verify_and_land); it runs verify and records the gate itself. Recording
the gate out-of-band (basicly verify --issue, br gate report verify)
makes the derived phase jump to verify with the merge skipped, and the loop
then ships and closes the bead with the code stranded on the harness branch.
Let loop advance record it; re-run basicly verify --issue only after
the landing has merged (e.g. rework), never before. Ship refuses to close a
node whose worktree branch has not landed, as a deterministic backstop.
Commit the build's work on the branch before you advance. Landing
rebases the harness branch, so the agent (or, in manual/handoff mode, you)
must git commit the changes on the branch first — the loop never
auto-commits the agent's work. Leaving the worktree dirty (or the branch
with no commit) makes the landing block with "commit the work on
<branch> before landing"; it no longer misreports it as a rebase
conflict or spends a rework attempt on it (basicly-4psl).
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.
Blocked on a missing fact — don't guess
When a dispatched agent cannot resolve a required fact, it must not guess.
Write .basicly/usage/needs-input.json ({"fact": ..., "detail": ...}) and
stop — the loop blocks and surfaces the missing fact instead of landing a
wrong answer.
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.