Harness loop
One-command harness distribution for coding agents — a versioned YAML catalog projected into agent instructions, skills, and deterministic git gates.
npx -y skills add niksavis/basicly --skill harness-loopAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 24 days oldThe repository was created 24 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
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.
SKILL.md
13.2 KB, as published. Nobody here has run it
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> # phase, worktree binding, gates, checkpoints, rework, ready/blocked
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 advancerolls the.beads/**dirt accumulated since (checkpoints, gate records) into onechore(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).
The base clean-tree check counts untracked files. Landing mutates the
shared base checkout, so it runs git status --porcelain there with
untracked paths included — unlike the worktree readiness check, which passes
--untracked-files=no because git rebase does not abort on untracked
files. So a stray untracked file in base blocks the landing exactly like a
modified tracked one. Check base before every landing:
git -C <base-checkout> status --short
When the dirt is not yours — one shared base checkout with a second
concurrent session in it is the usual cause — hold the branch until that
session commits its own work, then land. Never git clean, git checkout --
or stash another session's files to unblock your merge; the branch waits, the
other session's work does not survive being cleared.
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-hoursandbr coordination.
Advancing the loop — one command per phase boundary
Drive the loop with basicly loop run. It is one command per boundary, not per
step: it advances until it needs an agent or a human, resolving every checkpoint
it is authorized to resolve on the way. A leaf bead therefore takes two
invocations, one at each boundary, plus one relay of the confirm code each:
basicly loop run <issue> --work-type task # intake -> awaiting the agent's work
# ... do the coding in the worktree and COMMIT it on the harness branch ...
basicly loop run <issue> # land, verify, ship, close
Without an interactive terminal it stops once per boundary with a one-time
confirm code and reprints the whole command to re-run — relay the code on that
command, not on a bare policy checkpoint --approve, or the checkpoint is
approved and the loop stays parked. A covering autonomy grant (basicly policy grant, with --root <epic> on the run) resolves the checkpoint with no relay at
all. Nothing here widens what may be self-approved: a TTY, a covering grant, or a
relayed code are still the only three ways past a checkpoint.
Answering a confirm-code challenge
The challenge protects the human decision, not the keystrokes. So do not hand the command over and wait for someone to type it — that wastes a round trip and races the code's 15-minute TTL, and a ship code has expired mid-ask before. The protocol is:
- Show the exact command, including the code.
- Say what approving it does in one line — which bead, which checkpoint, and what happens next (a classify approval provisions a worktree; a ship approval merges, closes and tears down).
- Get an explicit yes. Silence, "sounds good" about something else, or an earlier approval of a different checkpoint is not one. Approval covers the one checkpoint in front of you, never the next.
- Then run it yourself.
Ask when the challenge appears, not after finishing other work — the code expires
whether or not you are ready for it. If it has expired, re-run the command with no
--confirm to mint a fresh one and ask again.
basicly loop advance <issue> [--work-type T] [--children plan.toml] [--mode M]
loop advance is the single-step form — reach for it to inspect one transition
in isolation, or when a boundary stopped somewhere you want to step through by
hand. Both exit non-zero when the track stopped short, so scripts and CI can
branch on it.
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
A reference for what each phase is waiting on — not a checklist to type out.
loop run performs every engine step in this table itself; what it cannot do is
the agent's own column: propose the work type, write the child plan, do the
coding, and commit it.
| 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>. Never guess which sections the DoR wants — basicly policy scaffold --type <work-type> prints the body with every required heading (a bug also owes ## Steps to Reproduce, an epic ## Success Criteria); fill the TODOs and pass it to br create -d / br update -d |
| 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 advanceis 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. Letloop advancerecord it; re-runbasicly verify --issueonly 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 committhe 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.