Skip to content

v1.0.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 15:40
· 200 commits to main since this release
5d639fe

Changed

  • brigada init uses a repository's sole git remote whatever its name, prefers origin when several exist, and accepts --remote NAME to select another (issue #102). Client commands use the same default and resolve SSH aliases through the configured hostname. diff and land fetch from, and land names in its printed push command, the remote whose URL matches the project (--repo included), origin preferred; when no remote matches, they use origin, else the only remote. remove lists and names the remote it derives the project from.

  • A runner restart drains instead of cutting the running jobs (issue #75): brigada init and brigada agents add — which now restarts the runner itself instead of telling you to stop and start it — ask the runner to take no new lease, finish its running jobs, exit and start again, and brigada status shows runner restarting after N running jobs while it waits. --now on either cuts the running jobs instead.

  • A provider outage no longer spends restarts (issue #71): a CLI that cannot reach its provider — it cannot connect, cannot resolve the host, or the connection is refused or reset — before it produced any message or tool call fails with the error kind infra, which never counts against the agent. Work, fix and plan-review jobs that fail on infra are requeued on a backoff of their own (1, 2 and 5 minutes, then every 15) for up to three hours, the task keeping its state and spending no restart, round, foreman wake or agent health; a spent window parks the task, or holds the plan review, with the reason naming the outage. A foreman turn that fails on infra keeps its wake armed on the same backoff and raises one needs-you entry when the window is spent. brigada status and the QUEUE section of brigada top show a waiting job as task #4 work: retry in 5m: could not resolve github.com.

  • The interactive foreman is brigada attach: it joins the brigada-owned foreman session, so your chat and the headless turns are one transcript, and it runs in the foreman's reading checkout with your own harness config and user-scope MCP servers; attach --model M --effort E overrides the project's foreman choice for that chat alone. Mode A — your own harness session as the foreman, wired by brigada init — is retired: brigada init writes .brigada/project.yaml and .brigada/operator.md and nothing else, and a rerun or brigada remove strips an older init's wiring. The hold command, brigada mcp --foreman, the settings hold_idle_minutes, hold_max_minutes and interactive_quiet_minutes, and the tools hold_acquire, hold_yield and decision_note are gone; brigada hold yield and hold confirm remain as no-ops so old hooks do not block. A repository whose .mcp.json still carries an older init's brigada entry must drop it (brigada remove, or a rerun of brigada init), or the two brigada servers clash.

  • A fix round whose CLI fails for a transient reason — a quota error, an authentication error, a server error, a lost runner — is retried once instead of parking the task (issue #41): the author's session resumes on the author's agent when it can still take it, else the round runs fresh on another agent of the tier with the round's feedback (the check output or the review findings) in its prompt. A second transient failure, or a failure that is not transient, parks the task for the foreman.

  • With land_target set, a new task's work starts from the land target instead of the default branch (issue #59): its worktree is built on that branch and its land job diffs against that base, both at plan_create and for a task plan_amend adds. A plan task may also depend, by id, on a landed task of another plan of the same project when its work is on the land target — done and pushed there: such a dep counts as done from the start, no edge waits on it, and one landed on another branch, not pushed, not landed, or belonging to another project is refused with the reason.

  • The Co-Authored-By trailer of a landed task carries the vendor's conventional noreply address for all five shipped model families — anthropic, openai, moonshot, deepseek and zhipu — instead of anthropic alone; any other family (google, alibaba, meta, mistral, xiaomi, meituan, nvidia, or opencode's unknown default) still gets the model name alone and the missing address is still reported (design §7.1).

  • An agent that is cooling is unavailable, however soon its reset (issue #112): the asked tier no longer waits out a cooling of an hour or less, and the dispatch falls back at once to the closest tier with an available agent, one tier up first, then down, where the usual cost/score ranking picks. A busy-but-ready agent still holds the job for a free slot, and only when no tier has an available agent does the job wait, for the earliest reset. The session affinities follow the same line: a continued dep's session and a re-review's reviewer resume only on an agent that is ready, and a retry's reviewer exclusion holds only while another reviewer is not cooling.

  • review.family (issue #126), a new engine config key: prefer (the default) or require. Behavior change: reviews no longer wait for another model family under the default — they go to another family when one is available, else the same family or the author's own agent (labelled same_family or same_agent in history); review.family = require restores the old wall. brigada init follows it: with fewer than two review families, prefer prints a warning naming the fallback and reports ready without --accept-limited; require keeps the LIMITED SETUP stop and the acknowledgement. Its intro, workers step, Reviews line and completion text, --accept-limited help, and the user docs state the effective policy.

  • The review placement is visible (issue #126): the history of a review's job_agent_picked event names the placement kind the lease recorded — cross_family, same_family, same_agent or unknown — with the reviewer's agent, in brigada history and the history tool; brigada status shows a leased review's placement beside its reviewing task or plan when it is same_family, same_agent or unknown and leaves cross_family unsaid; a review verdict or park from a non-cross-family review names its placement once in the needs-you reason, the foreman's wake text and the notification, while a review no one has leased names its unservable reason, never a placement. docs/design.md §6.2, §7, §7.2, §8 and §13.1 and the glossary describe the review.family rule (prefer or require) and its three steps.

  • The plan-closing rule counts a land-gate hold as a landing still to come (issue #133): a done task held at the land gate keeps its plan open until the user releases the hold and the landing settles, by its land job or by its trailer on the branch, instead of the plan closing under "waits at a gate". A hold on the push of a landing already recorded is a waiting push: the plan may close over it, named under "waits to push", and every path that settles a landing or a hold — the land job's outcomes, a hold release or rejection, the trailer detection — re-evaluates the close. A landing the user rejects (brigada land reject) closes a plan the hold kept open, the done report naming it under "landing declined".

  • Worker and reviewer roles no longer fall back to the text embedded in the binary at run time (issue #119): a role with no row in the store runs on the contract alone, and a row recorded empty is empty by choice; the standalone review --branch resolves its reviewer and worker texts the same way.

Fixed

  • With land_target set, the foreman's reading checkout (headless turns and attach) and the plan reviewer's worktree read the land target instead of the default branch (issue #122), so a plan review finding no longer goes stale against work that landed there. A land target the remote does not hold yet falls back to the default branch, and the foreman's opening prompt and the plan reviewer's prompt name the ref read and the default branch, with the fallback noted. A standalone branch review keeps the default branch as its diff base. The foreman and plan review grants carry the default branch beside the land target as their base ref, and the attach result carries land_target; the plan review template moves to version 4.

  • A Codex reset time at or within 15 minutes before the limit message now cools the account for two minutes instead of rolling the reset into the next day or year (issue #114).

  • brigada init resolves SSH origin aliases through ssh -G after confirmation and accepts --repo as a full URL or, with an SSH origin, a canonical identity (issue #102). Registration stores the canonical identity and a usable transport.

  • A model the provider rejects for the account, or that does not exist (a 400 invalid_request_error), was retried and probed as if the agent were flaky (issue #82). It now has the error kind model: the engine marks the agent broken with the provider's words, never probes it, and only brigada agent reset or removing the agent clears it; the needs-you line names the model and points at brigada agents, and a review whose reviewer's model was rejected is requeued without spending its no-verdict retry. An error codex prints before turn.started is a startup notice and reads as a warning, not the job's error; turn.failed stays the final verdict.

  • An amendment sent while a plan review round ran was refused, and an approve verdict woke nobody, so the change was lost until you asked again (issue #84). plan_amend now accepts it: the running round judges the plan as it was queued, the added and respecified tasks wait as the plan's amendment work, and the round's verdict — approve included — queues the next review round over them. brigada status shows the waiting amendment under the plan.

  • An authentication break on dsh, whose key comes from the environment, read as a bare auth failure; it now names the variable (DEEPSEEK_API_KEY) and the time the runner process started, so a runner started without the key shows (issue #75).

  • Credentials leaked to the code a task runs and to the files it writes: workers, setup commands and checks inherited the runner's GH_TOKEN, SSH_AUTH_SOCK, cloud keys and registry tokens (design §5.8 promised a scrub that nothing configured). They now run without those, and setup commands and checks also without brigada's own variables, keeping PATH, HOME and the build caches. The runner refuses to commit and push a new env file, key, credentials*, .netrc or other hidden file a worker left behind, failing the job with the files named; its git commands run with GIT_TERMINAL_PROMPT=0; URLs in its errors and in the plan mirror's lose a lone token as well as a password. Token-file and config parse errors give the line number and not the line; the history tool fences its text in <event> delimiters like events; the webhook requires https (http only on a loopback host), follows no redirect, and leaves a failed check's output out of its text; notify-send takes -- before the message; snapshots, run records and artifact blobs are written owner-only.

  • The env scrub missed the ambient tokens of the self-hosted git forges (issue #106): their variable names join the credential list, so a worker, a setup command and a check start without them, exactly as for the hosted forges.

  • A task landed by hand, or by a cherry-pick, stayed parked: the trailer scan recorded the landing but left a task parked by a land conflict or a rejected push at needs_foreman with its needs-you lines, and a landing still waiting at the push gate kept showing waits to push. The scan now returns such a task to done, marks its waiting landing pushed and releases its dependents; a waiting refs/brigada/land/<target> ref the target no longer fast-forwards to is dropped by the next landing; and a push rejected for a task already recorded landed raises nothing.

  • opencode and dsh workers could not run go test or git status: opencode denied all bash and now allows the commands of the project's worker_tools scope (the Go default scope when unset; a review still none), and dsh workers now get a GOCACHE under the temp directory and GIT_OPTIONAL_LOCKS=0, so the sandbox no longer refuses the build cache or git's index lock.

  • A failure before an agent's CLI started counted against that agent's health (issue #39): a worker, reviewer or foreman command that could not be started, or a worktree that could not be built from its base ref, is the environment's failure, never the agent's. These failures now complete the job with the error kind infra, as a failed fetch already does, so they never count toward the failure streak, and a work job behind one is retried like a fetch failure instead of charging the agent.

  • The wall-time hard cap measured from a job's first lease, so a job that lost its lease and was re-leased could be cut short partway into its next lease (issue #36); the cap now measures from the current lease, so a re-leased job gets the whole cap again.

  • claude and opencode workers could not read a task's extra repositories (§5.8): their read and search tools were denied outside the worktree, and their scoped Bash could not echo the BRIGADA_REPO_<NAME> path. The runner now grants each checkout's absolute path as a read dir (claude --add-dir, opencode external_directory allow) and names the paths in the job's prompt.

  • dsh jobs failed at start with "unknown option '--patch'"; the adapter now passes the launcher's --patch before the app's --json.

  • A land job diffed a task whose recorded base was its dependency's branch against that branch even after the dependency landed as a squash and the task's branch was rebuilt on the target, so the patch carried every change the target made since the stale merge-base: bogus conflicts in files the task never touched, different ones each attempt. The land diff now starts a branch that no longer descends from its recorded base at the merge-base with the base it lands on, so the patch holds the task's own changes alone.

  • A desktop notification tool that never returns no longer stops the engine (issue #104): the tick's notification delivery runs off the main loop — one delivery in flight, a tick that finds one still running skips the flush — so the sweep, the snapshot and the idle exit go on; a banner tool's exec is bounded past its context kill by its own wait delay, and the shutdown flush, and the wait on a delivery still in flight, are abandoned within the flush grace instead of holding the exit.

  • A dependent could still fail to land with "the task's diff no longer applies onto main" when its branch descended from its dependency's old lineage while its tree was the target's content plus its own work — a restart's or a continuing task's rebuild — and the dependency had landed, by hand or as a squash, its Brigada-Task trailer on the land target: the three-dot diff from the dependency's branch started at the stale merge-base and conflicted in the very files the task touched. When the base's task has landed — the trailer read on the history the squash lands on — and the branch's tree still holds that history's whole content (the trivial three-way merge of the two over their merge-base resolves to the branch's own tree), the landing patches with the tree diff of the branch against that base, so the patch holds the task's own change alone; a branch that lacks the base's later changes — another task landed in between and the branch was never rebuilt — keeps its three-dot diff, which lands its own change without reverting them.

  • A stopped runner's last lease poll could still win a job queued just after it went away, leaving the job leased to a runner that was not listening until its lease lapsed (issue #109); a poll whose caller is gone now takes no grant.

  • A land_ready hold waited for the user to release it (issue #142): the engine now re-checks it every 10 minutes, running the command again exactly as the landing ran it. The re-check's exit 0 releases the hold itself — the landing proceeds with every other gate applying, and a history event of its own, distinct from the user's release, records it; exit 1 keeps the hold with its reason moved to the command's new first line and queues the next re-check, recording nothing when the line is unchanged; any other exit keeps the hold and requeues the landing on the not-ready backoff, as the gate always has. brigada land approve and reject cancel a pending re-check, approve landing exactly once with the gate skipped, and a hold on the push phase re-checks on the same interval, a passing re-check pushing the recorded prefix and releasing the hold as an approval does. In a batch each member's hold is re-checked on the same interval, and a member whose re-check passes is released alone, once, into one next batch with its gate skipped, while the other members' holds wait for their own re-check or the user.

  • brigada project import skipped a project's skills when .brigada/project.yaml named no skills_dir: it now reads .brigada/skills when that directory exists — as brigada init --take-default <skill> writes it — and a named skills_dir remains the only directory read, and the file digests brigada status compares follow the same rule.

  • Three flaky runner tests: the stall tests read a pid marker the worker had created but not yet written; the concurrency tests' runner also leased the finished job's review, whose run of the same worker script left an extra marker; and the suite's seeding writes failed outright on a store the running engine held busy past the timeout under -race, where they now ride it out with the store's bounded retry.

  • A lease's events could carry a time earlier than the job they lease, so history could show a job leased before it was created: the lease's queue walk now reads its own time instead of the poll's arrival time.

  • Review execution under review.family (issue #126): under prefer a re-review resumes the previous reviewer's own session whatever its family, the author's own agent included, while require keeps today's family check and every fresh fallback is unchanged. A review on the author's own agent runs in a fresh session of its own or resumes only its own earlier review, never the author's, and the next fix round still resumes the author's. The review, plan review and re-review prompts state the placement the lease made — cross-family, same-family, same-agent, or that the author's family is not known — instead of always claiming cross-family (review template 9, plan review 6, re-review 2). Under prefer a branch review of an unknown family whose default reviewer's harness has no ready agent goes to any ready review-class agent, a plan review retry that only the excluded reviewer could take holds the plan saying so, and require's refusal of an unknown family names review.family=require.

  • Plans closed done before the live-work rule of #133 shipped, still holding tasks parked at needs_foreman with their dependents waiting, are reopened once at engine start-up (issue #137): one pass, before the wake recovery and before the API serves, returns every done plan whose nodes still hold live work by the plan-close rule's own test — every done plan, not only the ones known when the pass was written — to active, writes one plan_reopened event naming the plan and the task ids that kept it open, and wakes no foreman. Reopening was chosen over loosening the land_retry and task_cancel guards: the guards stay as they are, the data is brought to the invariant once, and the dependents run and the plan closes again through the existing paths — a retried landing, an amendment's cancel, the closing rule itself. A land_retry that returns a parked task to done now releases its dependents there, as any finish does, so a dependent added pending behind the parked task gets its work job. The plan's file is marked owed again, so plan-file recovery cannot restore done; cancelled plans and every other state are untouched; a second run changes nothing; and a reopened plan's re-close counts once more on the plan class, which is measurement only.

  • The sweep and a heartbeat ride out a busy store (issue #135): both run their one maintenance transaction under the store's bounded busy retry, on a context of their own within their existing deadline, so a write lock held past the busy timeout — whatever held it, an intermittent busy's source being unpinnable from a CI log — no longer fails a lease-keeping heartbeat or a sweep. A retried attempt starts clean, so only the committed attempt's report, answer and expiry return, a sweep tick is recorded only after its transaction commits — an attempt that rolls back after detecting a suspension no longer consumes the gap, and the retry still writes engine_suspended — and the engine's write transactions serialize through that commit bookkeeping, so concurrent sweeps behind one suspension detect it once and shift its deadlines once. The retention prune and the sweep a job read runs ride out a busy store the same way, each its own retry unit: a prune refused busy retries alone, never re-running the sweep's committed maintenance pass, and its report counts only the committed attempt's removed blobs plus the stale upload temps. The retry is bounded: a store busy past its attempts or deadline still fails the call with the busy error, and the idle sweep and the runner's heartbeat try again on their next tick.

  • A plan with a task parked at needs_foreman or needs_user, or held at the land gate, stays open instead of closing done (issue #133): the foreman can still cancel, restart or replace the task, and a landing whose push lost the race to another landing retries from a fresh rebase, up to a bound, instead of parking after one retry.

  • The project's land check runs gofmt, the guard tool and go vet before the tests, as CI does (issue #132): default_check in .brigada/project.yaml runs gofmt, go run ./internal/tools/guard and go vet ./... ahead of go test ./..., and a test keeps the check in step with CI's pre-test steps (shellcheck aside).

  • The foreman's file tools read only its checkout and its project's state (issue #131): the settings brigada writes for a headless turn and for attach confine Read, Grep and Glob to the reading checkout, subagents included, and no allow rule in the user's own settings lifts the refusal.

  • scripts/land-ready.sh waits for CI on the remote staging head, holds when no run appears within the grace period, and detects conflicting or unresolved PR mergeability (issue #128).

  • brigada version of a source build from a modified tree inside a tagged history no longer passes Go's stamped pseudo-version off as a release: build metadata such as +dirty is set aside before the pseudo-version test, so such a build prints 0.0.0-dev+<rev12>-modified as the README says.

Added

  • brew install blackfire-ai/tap/brigada (issue #85): the release workflow writes Formula/brigada.rb from the release's SHA256SUMS and pushes it to Blackfire-AI/homebrew-tap on every tag without a suffix, so a release candidate never reaches Homebrew. The formula installs the prebuilt binary for macOS and Linux, amd64 and arm64; no Go toolchain is needed.

  • install.sh (issue #86): curl -fsSL https://raw.githubusercontent.com/Blackfire-AI/brigada/main/install.sh | sh installs the latest release, or BRIGADA_VERSION, into ~/.local/bin or BRIGADA_INSTALL_DIR without sudo, refusing a tarball whose checksum does not match SHA256SUMS. It is POSIX sh and CI runs shellcheck on it. The README's Install section names the supported platforms — macOS and Linux, amd64 and arm64, no Windows — and shows the manual download-and-verify steps.

  • Release files carry a build provenance attestation (issue #96): gh attestation verify brigada-<os>-<arch>.tar.gz --repo Blackfire-AI/brigada checks that a file was built by this repository's release workflow.

  • A test holds docs/COMPATIBILITY.md to the binary (issue #95): every frozen command, flag and MCP tool argument must exist, and a new flag or tool argument must be listed as frozen or named as unfrozen, so the 1.0 promise cannot drift from the code. --now on init and agents add is now frozen.

  • The README and docs/product.md say what the cross-family review is for, in the terms it was measured in (issue #92): every change gets an independent review, which catches real problems after a strong model's work has passed its check, and placing it on another family spreads the work across subscriptions. They do not claim that another family finds more than the author's own.

  • env.<harness>.<VAR> in the config file takes a reference, keychain:<item> (a macOS keychain item) or file:<path> (a file of mode 0600), resolved at every runner start, whoever starts it, and passed to that harness's jobs alone (issue #75). The config holds the reference; the value never reaches a log, the store, status or an error. The ambient variable stays the fallback.

  • A real version for every install path (issue #79): when the release build has not set brigada's version with -ldflags -X, the binary now resolves it from its build info — the tag go install …@v1.0.0 records, without its leading v, else 0.0.0-dev+<rev12> (the first 12 characters of the git commit) with -modified appended for a dirty tree, else 0.0.0-dev; the pseudo-version of go install …@<sha> and the (devel) of a plain go build are not taken as the version. Release binaries keep printing the tag without its leading v, and the README's install section says what each path prints.

  • brigada init writes .brigada/operator.md (issue #73): a generated, harness-agnostic guide for your own coding agent, the operator. It covers how to ask the foreman (brigada ask -), how to watch (status, history --task, history --job, diff --stat), what to do after a landing, how to recover after an outage, and what stays yours alone (plan, land and push approval, --and-stop-asking, tags and releases, rule changes). A rerun overwrites it, --no-wiring does not skip it, brigada remove deletes it, and init prints one line pointing at it. Init still never touches AGENTS.md or CLAUDE.md. The README now opens with the first five minutes (install, init, tell your agent "ask brigada to fix #12", watch brigada top, approve what the needs-you queue holds), and brigada attach moves under "Talking to the foreman yourself".

  • A plan review's changes verdict with a round left parks the plan at a new needs_foreman plan state (issue #66): the plan waits with its findings in the "needs you" queue — brigada status shows it as needs_foreman (round 1) with the findings as the entry's reason — no work queued and nothing retried on its own, surviving an engine restart. The foreman answers with plan_amend (allowed from the parked state; added and edited tasks go to round 2, an amendment that cancels or retiers only lets the plan proceed) or with the new plan_accept MCP tool (plan and a required reason), which accepts the plan as reviewed and runs the same evaluation a spent round's verdict does — the plan starts or waits for your yes, and the reason is recorded in its history. The second-round changes verdict still proceeds as before, and a review that gave no verdict still keeps the plan reviewing.

  • Plan review findings are kept whole under their own 16 KB bound instead of a note's 500 bytes (issue #70): a multi-KB verdict reaches the foreman's wake and round 2's prompt intact, while the short views — the status plan line and the needs-you entry — cut at render time and end with … (full: brigada history --job N) pointing at the review job's history.

  • NOTICE and a generated THIRD_PARTY_NOTICES.md carry the attribution the release binaries owe their embedded dependencies: internal/tools/licenses runs go-licenses v1.6.0 over ./cmd/brigada's dependency tree and writes every module's name, version, licence and full licence text, refusing anything not compatible with brigada's Apache-2.0 licence; sh internal/tools/licenses/regenerate.sh regenerates the file. The go-licenses CLI itself cannot be run here (its go run needs an approval the worker sandbox denies, and its v1.6.0 report carries neither version nor licence text), so the pinned tool lives in a nested module — not a trivial script — and a root-module test runs its golden test, which the nested module alone would escape.

  • A new plan, and an amendment of an active plan that adds or respecifies tasks, is reviewed before its tasks are queued: an agent of another family checks the plan against the code and the docs, and at most two rounds send findings to the foreman. plan_review turns the review off; plan_review.tier and plan_review.effort set the reviewer (§7.2).

  • The approval rule that stands on by default is the plan budget: approval_budget holds a whole number of tokens, a $ amount, or both joined with + (for example 2000000+$5), a plan's spend is the usage the engine records per job — input plus output tokens, and dollars when the CLI reported them — summed over the plan's jobs, and when the spend passes the budget the engine holds the plan's new jobs: every task that would newly start, a freed dependent or an amendment's addition alike, parks at the plan gate with a needs-you line naming the rule and the spend, while work already running goes on. Only the claude CLI reports dollars; codex, opencode, kimi and dsh report tokens, so on a subscription harness without a price the budget counts tokens and a $ side never trips. approval_max_tasks defaults to 0, off: set a count to bring the count rule back (gate_plan: ask still holds every plan).

  • brigada agents compares the models your CLIs list (claude, codex, opencode) with the runner's agents file and prints the unregistered ones, each with its name-based tier and account, and the registered models a CLI no longer lists (issue #49); brigada agents add <harness>/<model> [--classes ...] [--effort ...] appends one validated line and keeps every other line. brigada status and brigada top show a one-line hint when unregistered models exist, from a listing cached for 12 hours. Nothing registers a model automatically.

  • Landing is a job (§7.1): a task that is done, approved and whose dependencies are landed is squashed by a runner onto land_target as one commit, the default check reruns on the result, and the push follows the push gate — auto (the default) pushes, ask waits for brigada push approve, never leaves the push to you: the commit waits on the remote's refs/brigada/land/<target> and the needs-you queue carries the exact command, runnable from your own repository. Landing is per task, so a stack lands in dependency order; a conflict or a failed check on the landed result parks the task for the foreman, nothing pushed. after_land runs after a successful land; its failure is reported — in the landing record and brigada history, where the commit, the push state, the push command and the after_land failure all show — and never undone.

  • land_ready gates the landing (issue #87): a repo-relative command path inside the repository (for example scripts/land-ready.sh) the land job runs in its landing clone before anything lands, and the push phase runs again before it pushes, under the environment of a check and the check timeout. Exit 0 lands; exit 1 holds the landing on the command's first output line — nothing squashed, pushed or recorded; any other exit, a command that could not start, or a timeout is not ready, and the landing is requeued on the growing not-before backoff (5 s to a 2-minute cap) with no failure and no restart spent, the next try time and the last exit kept on the task for brigada status. The repository ships scripts/land-ready.sh, a land_ready command that reads the latest gh run list run on brigada/landed — exit 0 when it succeeded or the branch has no runs, 1 when it finished without success — failed, cancelled, skipped (its URL the first output line), 2 while queued or in progress or when gh fails. It is not enabled by default: brigada project set land_ready=scripts/land-ready.sh turns it on.

  • A task landed outside the land job — a hand squash, or brigada land in your clone — is no longer invisible to the engine: whenever the runner refreshes the default branch (a headless foreman turn, or brigada attach), it scans for Brigada-Task: <id> trailers and records each task it finds as landed (source: trailer), so brigada status and the plan's done report can tell "already merged" from "not yet landed", and the read is idempotent with a task the land job landed.

  • Gates and rules (§6.3): the loop is brigada's and you stop it with flat keys in .brigada/project.yaml — gate_plan, gate_land, gate_push, approval_tiers, approval_restarted and land_target join approval_max_tasks, approval_classes and approval_protected_paths, which now also holds at the land gate. Held landings and pushes appear in the needs-you queue; brigada land approve <task> and brigada land reject <task> decide a held landing, brigada push approve releases every waiting push. The foreman has no tool to pass a gate.

  • The foreman session rotates when it grows large: after a turn that read more input tokens (input plus cached) than the project's foreman_rotate_tokens (default 400000, 0 = never), the next headless turn or attach starts a fresh session and logs foreman_session_rotated. The fresh session's first prompt carries a bounded digest of the active plans, what needs you and the latest history. Set it with brigada project set or in .brigada/project.yaml.

  • Mode B: brigada init in a parent folder registers several repositories under one coordinator session (brigada-coordinator in .mcp.json).

  • Plans with dependencies, created and amended by the foreman, mirrored to the brigada/plans branch.

  • Workers run in git worktrees on their own branches; the project's check runs after every result; fix rounds, and restarts with notes (task_restart).

  • Cross-family review of each implement change, and brigada review --branch for any branch.

  • Dispatch by outcome record and cost weight within a tier (Thompson sampling); concurrency caps per runner, account, model and plan.

  • Quota cooling per account, until the vendor's reset time when the CLI gives one.

  • An agent broken by repeated failures recovers by itself: a few minutes after it was marked broken the runner probes its CLI, a passing probe clears it, and a failing one backs off with a growing, capped delay; an auth failure still waits for brigada agent reset.

  • Tiers (light, standard, heavy) and effort per task, with an escalation ladder that raises effort first, then the tier.

  • A task may continue one of its deps: its first work job resumes the dep author's session on its own agent, saving the cold start; fresh whenever the session or its agent is not available.

  • brigada stats: usage per job and cost per success.

  • brigada land, the hand-driven form of the land job: squashes approved tasks onto your branch locally; it never pushes.

  • Adapters for Claude Code, Codex, Kimi and opencode.

  • brigada status, brigada history, brigada logs, brigada diff and desktop notifications.

  • docs/COMPATIBILITY.md states what 1.0 promises until 2.0.

  • task_retire, a foreman tool, an API call and brigada task retire <id>... --reason TEXT (issue #140): tasks that will not land — done work that landed elsewhere or is superseded, a task parked past repair, held at the land gate, failed, or never run — settle by retirement in one atomic call. Their live jobs stop (a queued land job or retry included), their land holds drop, their branches stay untouched and nothing ever lands on any path — brigada land approve|reject on a retired task refuses naming the retirement — with the reason recorded verbatim and the caller (foreman or user) on one task_retired event per task. A new retired task state (migration 0071) reads the settle apart from a cancellation in status, the history and the plan file; a landed, cancelled or already retired task is refused, and a task with dependents blocks the call unless each is retired in it or is already cancelled or retired — a failed dependent blocks too, deliberately unlike plan_amend cancel. The plan then closes by the usual rule, its done report naming each retired task with its reason, the start-up reopen pass leaves the closed plan closed, and retired tasks leave the needs-you queue.

  • The README names the models that built brigada, by family, explains why GitHub's contributor list shows only some of them, and gives the git log command that counts commits per model.

  • The design of batch landing (issue #134) in docs/design.md §6.3 and §7.1: one land job per target lands every ready landing in dependency order and pushes once, with per-member checks, holds and recovery. It is design only. This release does not implement it, and land_batch_max is refused as an unknown key until a release does.

  • land_retry, a foreman tool (issue #133): it queues the landing again for a task parked by a landing that failed before a landing was recorded, outside its content — a rejected push or an environment failure, with the outcomes queued, held, stale_base or refused — through the same gates; a task whose landing is already recorded (pushed or waiting) is refused in favour of task_restart, and so is a conflict or a failed landed check.

  • issue_read, a read-only foreman tool (issue #131): it answers the title, body and comments of an issue or pull request of the project's own repository inside <issue> data delimiters, with the user's gh login. When the repository is not on GitHub, gh is missing or not logged in, or the number does not exist, it returns that reason; the foreman's report says so and asks the user for the text, and the foreman never searches the disk for it.

  • Documented editable role instructions, task skills, their import and snapshot rules, and the diff/reset commands (issues #119 and #117). Enabled the documentation content verifier.

  • Embedded default role instructions and task-shape skills for future prompt configuration, each skill a numbered procedure, with a versioned archive (internal/defaults/versions/<ver>/…) holding every shipped version's texts for exact three-way diffs, tested equal to the live embed.

  • Per-role instruction texts in the store and engine (issue #119): migration 0064 seeds every project with the four roles — an existing instructions setting kept as the worker text, the rest generated from the shipped defaults, provenance generated from <version>, by upgrade — and the engine sets, reads and bounds them (8192 bytes, valid UTF-8, no NUL, never truncated; not configured, empty by choice, or file), the worker role mirroring the instructions setting and an import's provenance, hash and text both.

  • Role files and skills in the project (issues #119 and #117): .brigada/project.yaml may map instructions: to a file per role and name a skills_dir:; brigada init writes the shipped defaults there with a version header, a rerun rewrites only unedited files, brigada init --take-default <role|skill> resets one, and brigada instructions diff <role|skill> diffs a file three ways against the default it was generated from — read from the binary's archive of every shipped version — and the new default. brigada project set instructions.<role>=… sets a local override, and project show and status print each role's state, provenance, size, digest and whether its file or skill changed since import. The worker's file is instructions_file spelled in the map: its text is the instructions setting and imports with project.yaml, in its hash, so an empty one unsets the setting; the other roles' files import beside it, an empty one empty by choice, a local override kept with a conflict that shows both texts.

  • The store holds the project's task skills (schema 0065): name, description, for: audience, text, digest and the skill directory's tree digest. The engine imports and replaces the whole set; each skill is bounded at 4096 bytes of UTF-8 with no NUL, and a refusal names the skill, the size and the limit, never truncates, and leaves the previous skills in effect. A task's snapshot gains its reviewer text and its ordered skills beside the worker text it already recorded; the engine resolves them once, refusing an unknown or repeated skill and a worker prompt over 16384 or a reviewer prompt over 24576 bytes of instructions plus skills (a for: skill counts only where it appears), with the size, limit and task named. Tasks created before the upgrade keep their instructions as their worker text, with an explicitly empty reviewer text, so their fix rounds and reviews carry exactly the guidance they were first given.

  • Every creation path resolves the snapshot through that resolver, and every job prompt carries what it recorded: plan_create, a plan_amend addition and the direct task submission record both role texts and the task's ordered skills when the task is made, and each of the task's jobs composes from the record — the worker text and the worker-audience skills ride every work, fix, answer and restart prompt in <task-skill> sections after the instructions, the reviewer's own text and the reviewer-audience skills ride the review prompt beside the worker convention — so a later change to a role text or a skills import never alters a created task's rounds or reviews. Each task of plan_create and plan_amend's add, and the task submit, takes a skills list; a respec or retier entry takes one too, replacing the recorded skills of a task that has not started (omit to keep them, [] to clear them) and re-resolving the role texts, while a started task keeps its record. A refusal — an unknown or repeated skill, a prompt over its bound — refuses the whole call naming the task. A worker or reviewer role with no row in the store contributes no text — the prompts run on the contract alone, never on a default from the binary (issue #119); the worker and review template versions move up for the new sections.

  • A foreman turn and a plan review resolve their role texts from the store per turn and per job (foreman.md for the turn, plan_review.md and foreman.md for the review) and pass them to the composition; a role with no row never falls back to a text in the binary — not configured runs on the contract alone, so a changed text reaches the very next turn and a seeded row keeps the default it recorded. The plan review's input names each task's ordered skills with their recorded descriptions, and says none for a task without them, so the reviewer can judge the picks (check (h)).

  • A standalone branch review (brigada review --branch) composes its guidance from the role texts the engine resolves when it is queued — the reviewer text in its own section and the worker text as the project's current conventions, a role with no row contributing nothing rather than a default from the binary — instead of the legacy instructions setting, and records both digests.

  • Every engine-composed job records beside its prompt what the prompt was composed from (schema 0066, jobs.prompt_meta): the template's version and the SHA-256 digests of the instruction and skill texts the prompt carried — the worker template's for a work, fix, answer, restart or continue job; the review template's with both role texts for a review; the re-review's, the plan review's with its role texts, and the foreman turn's with the foreman text — so a recorded prompt is reproducible from the recorded snapshot. A retry or requeue that reuses a job's prompt carries its meta too.

  • A task's skills are recorded where you look (issue #117): status shows skills:a,b beside the task, the history's lease event of a job carries the template version and the digests of the instruction and skill texts its prompt carried, and the plan file mirrors the skill names in order (a skills list, written only when the task has some, round-tripping through the plan-file reader). The outcome record is split by skill count (schema 0067), so brigada stats gains a SKILLS column and the API and MCP rows a skills field; one-skill and two-skill tasks are never pooled, and the dispatcher ranks on the sum, so its scores do not change.

  • brigada init also writes the operator guide as a Claude Code skill, .claude/skills/brigada-operator/SKILL.md, from the same text as .brigada/operator.md; a rerun rewrites it and brigada remove deletes it.

  • .brigada/operator.md tells the operator to put an issue's content into the ask when the issue is outside the project's own repository, which issue_read cannot reach, and covers a landing on the default branch with no pull request.

  • Blocked plans (issue #122): a plan whose done, approved tasks can never land — an implement dependency cancelled or failed under them, computed transitively over the graph and only for tasks the land rule would land, so a class that never lands neither strands nor is stranded — stays active instead of closing. One plan_blocked wake per blockage names every stranded task with the dead dependency, the needs-you queue carries one line per stranded task, and plan_amend's retarget is the one move that frees such a done task — dropping or replacing the dead dependency makes it land without running again — refused, saying a follow-up task on a new base is the way, when the task's recorded base is the dead dependency's result branch. Cancelling a task that still has dependents is refused, the whole amendment applied nothing, unless the same amendment cancels each pending dependent or re-points it off the cancelled task; a dependent with a job or a result built on the cancelled branch blocks and is named with its state, and one already cancelled or failed never blocks. The done report also says the skips the user approved as cancelled, and the per-task lines the foreman reads — the status views and the digest — carry landed@<sha> or not landed for every done implement task, so a task done and not landed reads differently from a landed one.

  • The stale-base rule (issue #124): a dependency reopened after its dependents started — its landing failed, it was restarted, and it finished again from a new -r<n> branch — no longer leaves them on its old round. When the dependency completes done from the new branch, every started, unlanded dependent stacked on a superseded round parks at needs_foreman with task_base_stale naming the superseded base and the new branch; one with a live job is named in the one dep_rebuilt wake that covers the rebuild — it names the dependency and every stale task — and is refused when its own landing comes. A land job is never queued for a task whose recorded base no longer matches its stacked dependency's current branch, whatever asked for the landing, a gate approval's release included; a pending dependent releases on the new branch as before, and a landed one is untouched. The foreman rebuilds each held task with task_restart, which now re-resolves a stacked task's base to its dependency's current result branch — spending one of the task's restarts and starting fresh, a continue dependent included.

  • status and brigada project show where a role's instructions stand (issue #119): not configured, empty by choice, or the recorded provenance — the version the import or seed generated the text from.

  • Import refinements (issue #119): removing a role key from .brigada/project.yaml makes the role not configured, and an init rerun no longer re-adds a removed key once the instructions map exists; a project with instructions_file gets foreman.md, plan_review.md and reviewer.md from init too. brigada project import applies in one transaction: an import into an archived project is refused whole instead of leaving a partial import, and a failed step reports the one operation import the project configuration.

  • brigada init's file header and the client provenance recorded with an import carry the brigada version, and brigada instructions diff prints its three-way comparison as one diff (issue #119): the diff's base is the archived default of the version the file's header names, verified against the header's digest, else the shipped text whose digest matches the header's from any version, and when neither is found it prints the reason and falls back to a two-way diff of the file against the new default.

  • The plan-review template keeps only the verdict contract (issues #119 and #117): the checks and the size guide come from the project's plan_review.md and foreman.md; skill framing states that a skill wins over the general instructions on a judgment question; a for: worker skill is named to the reviewer as it was given to the author; and the per-skill directory line in the worker and reviewer prompts names the skills_dir recorded on the task at creation, <skills_dir>/<name>/ (.brigada/skills/ when none is recorded).

  • A worker that cannot find a documentation section its spec cites stops with needs_help, naming the missing section and the base ref and commit it read, and the foreman treats that as blocking (issue #129).

  • When a task is created, the project's skills_dir is recorded on it beside its skills, and the directory line in the worker and reviewer prompts names that recorded directory; a later import, a fix round, a resume or a re-review keeps the recorded value.

  • Documented setup through your own harness (README, GUIDE §1 "Through your own harness"): a paste-ready prompt that has the coding agent already in the repository install brigada, probe the plan, collect your confirmations and apply init with --yes — the door before .brigada/operator.md, which carries the same agent from setup through everyday work. The prompt's steps map to init's own: the sign-in probe (an opencode on environment keys needs --signed-in opencode, for the probe cannot confirm it), the --probe plan, and the --yes apply; init's guardrails hold throughout — no login commands, no model calls, nothing written before you confirm.

  • OpenCode can be the foreman (issue #152): opencode joins claude and codex in ResumeCapableHarnesses, so init offers its known-family models for the foreman and brigada project foreman --harness opencode --model <provider/model> chooses one. A headless foreman turn carries the brigada mcp bridge through an opencode.json the adapter writes beside the runner's own MCP config, inside that config's private per-session directory, named to the CLI with OPENCODE_CONFIG_DIR (the run path has no MCP-config flag); the repository's own config stays out as for every opencode run, while the user's global config still loads. The turn's read-only scope rides OPENCODE_PERMISSION: bash, edit, webfetch and reads outside the reading checkout denied, headless turn and attach alike. brigada attach runs the CLI's own terminal continuing the session by id (-s) with the bridge and that scope; there is no fork onto a client-minted id, so the detach writes the resumed id back through the new adapter.SessionWriteback interface, and a chat that started fresh has its id read from the CLI's own session list afterwards — the same mechanic the codex attach lacks. The attach's opening prompt arrives in the TUI's input box (the 1.19.5 --prompt fills rather than sends), so the chat's first Enter submits it; the attached TUI has no status bar, so a worker status line does not show. The runner removes a foreman turn's MCP config directory whole, for the adapter's file carries the same token. The adapter is verified against OpenCode 1.19.5 (OPENCODE_CONFIG_DIR arrived in January 2026). GUIDE §11, §2 and §8, the README, product.md and design §4.1 describe the harness's place; init's foreman menu, transcripts and flag help name all three harnesses.