What counts as a definition-of-done check was the origin project's stack (pytest / lint-imports / frontend) frozen into core: emit.py classified against inline literals and the Focus panel carried three fixed rows with duplicated regexes. Per the three-layer law that is project knowledge, so it now lives in one file: core/checks ships the old rows as the default, and a checks file in manager/local/ replaces it wholesale — the same filename-wins resolution as prompts. - core/checks: '<label>: <command regex>' per line, self-documenting; read fresh on every use. - emit.py classifies Bash commands against the resolved file (kind 'check', the label carried into the summary) and judges pass/fail generically — counted results, broken totals, OK/FAILED verdict lines — since the hook payload carries no exit status. The runtime moved under a __main__ guard so the classifier is importable. - config.checks() mirrors the parser (the bridge stays standalone) and httpd serves it in /api/state; the Focus panel renders one row per served entry, matching events with the same patterns — no fixed rows, no duplicated regexes. - manager/local/checks gives bench its real definition (unittest), so the self-hosted board shows a check that can actually run. - The default BOARD_AGENT_COMMANDS drops its pytest prefix: core no longer names any stack outside the shipped checks default, and a test walks manager/core to keep it that way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bench
A live kanban for coding-agent work: task files in stage directories are the only source of truth; a stdlib-only board narrates everything that happens to them — agents working in git worktrees, PRs opening on review, CI and Copilot state on the cards, drives of the app from a task's own branch, and an archive that is never a delete.
Install into a repo
git clone <this repo> .task-manager && rm -rf .task-manager/.git
./.task-manager/start.sh # wires the project (idempotent) and serves
Commit .task-manager/ into the host repo — core is vendored on purpose,
so clones work offline and updates show up in the host's own diffs.
Update
# in manager/local/.env: BENCH_SOURCE=<this repo's git url>
./.task-manager/update.sh # latest
BENCH_REF=v1 ./.task-manager/update.sh # a specific release tag
Updates replace manager/core/ and the top-level scripts wholesale and
touch nothing else — tasks, plans, reference, and everything under
manager/local/ (your driver, commands, prompt overrides, settings,
state) survive every update. Then python3 .task-manager/install.py and
restart the board.
The three-layer law
Core knows about tasks, worktrees, PRs and events. It knows nothing about
any particular app (drivers do: local/driver/start), agent vendor
(adapters do: core/adapters/), or project (local/ does). Full docs in
CLAUDE.md; the adapter contract in manager/core/adapters/README.md.