Settings
The whole of manager/core/.env.example, read out of the file itself on every build. Precedence runs process environment, then your manager/local/.env, then the defaults below — so a key you never copied is a key at the value shown here.
Task manager settings — copy to manager/local/.env (gitignored) and edit. Precedence: process environment > local/.env > these defaults.
A first run of install.py with no local/.env writes this file there for you, asking only what it cannot guess (solo or team, which agent adapter, the project's test command) and leaving every other key at the default below.
install.py --setupasks again later; nothing else ever rewrites your copy, so new keys added here by an update are yours to adopt by hand.
BOARD_PORT
BOARD_PORT=26071
Port the board serves on. Pinned by default so the URL is bookmarkable; adapters and drivers read the same value, so changing it here changes it everywhere at once.
BOARD_TITLE
BOARD_TITLE=
What the tab calls this project: the title is "<project> · bench", so two benches side by side are told apart at tab-bar width. Empty = the repo directory's name, which is the right answer unless every checkout on this machine is called "app".
BOARD_AGENT_ADAPTER
BOARD_AGENT_ADAPTER=claude
Which agent adapter runs headless jobs (core/adapters/<name>, overridable in local/adapters/<name>). Ships with: claude, opencode.
BOARD_CLAUDE_BIN, BOARD_OPENCODE_BIN
BOARD_CLAUDE_BIN=claude
BOARD_OPENCODE_BIN=opencode
The binary each adapter launches jobs with. Point them at stub scripts to test the board's plumbing without spending tokens.
BOARD_AGENT_MODEL, BOARD_AGENT_MODEL_WORK, BOARD_AGENT_MODEL_ACT_PR, BOARD_AGENT_MODEL_REVIEW
BOARD_AGENT_MODEL=
BOARD_AGENT_MODEL_WORK=
BOARD_AGENT_MODEL_ACT_PR=
BOARD_AGENT_MODEL_REVIEW=
The model headless agents run on — an opaque vendor-native name the adapter passes through untranslated (claude: a model name or alias for --model; opencode: the "provider/model-id" config key). Empty = inherit the vendor's own default, i.e. whatever the CLI on this machine would pick anyway. The per-intent settings beat the general one for their intent only; _REVIEW covers both PR reviews and relevance checks. Work agents write code; reviews just read and judge — they can ride a cheaper, faster model.
BOARD_AGENT_COMMANDS
BOARD_AGENT_COMMANDS=python3 -m unittest
Command prefixes headless agents may run in their worktree — the
project's test/check commands, comma-separated, in plain neutral form
(each adapter renders them into its vendor's permission rules; the
git/gh grants per launch intent are built in). Headless runs have no
human at a permission prompt, so a test runner missing from this list
is a test the work agent cannot run. Left empty — or holding only
whitespace or a lone comma — the board says so: a quiet no agent commands chip in the header, and a note on every work launch.
What counts as a definition-of-done check (the Focus view's CHECKS panel) is a file, not an env var: core/checks ships a generic default, and a
checksfile in manager/local/ replaces it wholesale. Format (<label>: <command regex>) is documented in the default file itself.
BOARD_WORKTREES
BOARD_WORKTREES=.worktrees
Where work-agent worktrees are created, relative to the repo root.
BOARD_GH_BIN, BOARD_GIT_REMOTE, BOARD_PR_POLL_INTERVAL
BOARD_GH_BIN=gh
BOARD_GIT_REMOTE=
BOARD_PR_POLL_INTERVAL=60
GitHub plumbing. The gh CLI used for PRs (stub-able like claude); the git remote this board's work goes to (empty = the repo's first remote) — PRs and BOARD_SYNC both ride it, and a name set here is used as named, never swapped for another remote; and how often to poll open PRs of cards sitting in review/ for reviews and checks.
BOARD_FETCH_TIMEOUT
BOARD_FETCH_TIMEOUT=10
Seconds a work-agent launch waits for git fetch origin main before
giving up and branching from local HEAD. Fresh launches branch from
origin/main when the fetch succeeds; no remote or a dead network just
means today's behaviour, never a blocked launch.
BOARD_COMMIT_MOVES
BOARD_COMMIT_MOVES=
Claim on move, and commit it. On: a board-made move out of backlog/ or
to-do/ writes Assignee: <git user.name> into the task file (first
claim only — an existing assignee is preserved), walking a card back to
backlog/ clears it, and each move commits itself — the move and the claim
in one commit touching only that task file, messaged
board: <number> → <stage> (<name>). Nothing is pushed. Off (the
default) means moves neither write nor clear the assignee and never
commit — today's behaviour exactly, tasks/ committed by hand. An
assignee added to a file by hand is still read and shown either way;
the gate only governs whether a move writes it. Anything but
empty/0/false/no/off turns it on.
It is also what makes the claim mean something: on, ▸ start work refuses a card someone else holds (▸ take over is the deliberate way in) and claims an unheld one, and board-made edits to a card in place — the PR: line — commit themselves too. Off, nothing writes an assignee, so nothing reads one as a lock.
BOARD_SYNC, BOARD_SYNC_INTERVAL
BOARD_SYNC=
BOARD_SYNC_INTERVAL=30
Sync boards through origin/main: the second half of team mode, and off by default. On, it implies BOARD_COMMIT_MOVES above (a move that never commits has nothing to publish) and: each board commit is pushed as it is made; a beat fetches and fast-forwards every BOARD_SYNC_INTERVAL seconds and once at startup; moves that arrive are narrated with their commit author's name; and a card someone else moved first wins the race — the local move is dropped with a toast naming who took it.
Two things it will never do. It never publishes a commit the board did
not make: one local-ahead commit on main without the board: prefix
stops the push and says which. And it never merges past a divergence or
into a checkout with uncommitted changes — it says so and waits for a
human. Both assume the discipline bench already has: code work lives in
worktrees and PRs, so local main advances only through the board and
origin.
State syncs; reactions don't. A move that arrives over origin renders
and narrates on every board but triggers nothing — opening a PR belongs
to the board whose user made the move, and a review card left without
one carries an explicit ↑ open PR action instead of N boards guessing.
The same discipline moves merges to origin: merge & clean up runs
gh pr merge and lets the beat deliver the result, so local main only
ever fast-forwards. Whoever clicks needs merge rights on the repo.
One board fetches twice a minute at the default interval; raise it on a
rate-limited or metered remote. Sync rides one remote and main only —
the remote BOARD_GIT_REMOTE names, or the checkout's first one, exactly
as PRs do. Nothing to ride (no remote, or a BOARD_GIT_REMOTE naming one
this checkout does not have) is a sync stalled chip and a ticker line,
never a board that looks healthy while syncing nothing.
BOARD_PHASE_INTERVAL
BOARD_PHASE_INTERVAL=30
Seconds between passes of the phase runner: a phase card being run
(Type: Phase, in in-progress/) has its list worked into its own
phase/<stem> branch one card at a time. Each pass recomputes what the
phase needs from the cards and from git and does the one next thing, so
raising this only makes a phase slower to notice, never wrong. Nothing
runs at all while no phase is being run.
BOARD_WATCH_INTERVAL
BOARD_WATCH_INTERVAL=2
Seconds between disk polls of the stage directories.
BOARD_EVENTS_CAP, BOARD_HISTORY_CAP
BOARD_EVENTS_CAP=800
BOARD_HISTORY_CAP=300
In-memory caps: events kept per session, and board-level history (moves + agent lifecycle). Full logs always persist to local/state/.
BENCH_SOURCE
BENCH_SOURCE=
Where update.sh fetches the task-manager's releases from (owner/repo, or any GitHub URL). Normally unset: the shipped update.sh is stamped at build time with the repo it came from, and this overrides the stamp. Updates replace manager/core/ and the top-level scripts; they never touch tasks/, plans/, reference/ or manager/local/.