Turn design 1b (Dockside) into `/`: a terminal hero, the claim README.md already makes, six doors, and a strip at the foot. The layout is task 31's; what this commit is really about is that nothing factual on the page is typed by hand. - **Two facts are read, not written.** `site/build.py` reads the install one-liner out of README.md's "Install into a repo" block and the version out of `manager/core/VERSION`, and offers them to the template as `$install_block` and `$version`. A renamed section, a missing VERSION or an install section that lost its command block stops the build, exactly as a renamed heading already did. `pages.json` loses its `version` key, and a build refuses one if it comes back. - **A dead internal link stops the build.** Every href a rendered page emits — a door as much as a link inside a slice — must resolve to a route in the manifest or a file in `static/`/`root/`. The check runs after rendering and before writing, so a bad link leaves the last good build standing rather than shipping a 404 with a nice typeface. - **Six real doors.** `pages.json` grows the routes they open: install and first run, the five stages, agents on the board, PRs and review, team mode, the three-layer law. They are heading slices, i.e. the stub routes task 33 expected and task 34 will re-cut. - **The terminal is a transcript.** README.md's own command, then lines install.py and board.py really print, with the abridgement declared in the terminal's title bar. `tests/test_site_landing.py` holds every one of those lines against the source that prints it, so a reworded prompt fails the suite instead of quietly making the page fiction. - **No fake telemetry.** Turn 1's "most opened this week" strip becomes the version, read from VERSION, and a link to the releases. The landing page no longer carries a generated body, so the tests that read one from it now read `/concepts/stages/`, and the two scratch repos in the suite copy VERSION alongside the markdown. python3 -m unittest: 407 tests, OK. Co-Authored-By: Claude Opus 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. Turn on team mode
(BOARD_SYNC=1) and the truth is origin/main: moves commit and push
themselves, every board pulls on a beat, and the person who claimed a
card first keeps it.
Install into a repo
mkdir .task-manager && curl -L \
https://github.com/12vectors/bench/releases/latest/download/bench.tar.gz \
| tar -xz -C .task-manager
./.task-manager/start.sh # wires the project (idempotent) and serves
No token, no clone: releases are curated artifacts that never contained bench's own cards or settings, so the board starts empty by construction.
The first run asks three questions it cannot answer for you — solo or
team, which agent adapter, what command runs your tests — and writes
manager/local/.env from the documented example, so every other setting
is discoverable in your own copy. Bare Enter takes the default
throughout; with no terminal (CI, a script) it asks nothing and
install.py --setup asks later.
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.
The workflow brief ships as .task-manager/AGENTS.md — the cross-vendor
name coding agents read natively — with CLAUDE.md beside it as a one-line
compatibility pointer. Both live inside .task-manager/, so a host repo's
own root AGENTS.md is never touched.
Update
./.task-manager/update.sh # latest release
BENCH_REF=v2 ./.task-manager/update.sh # an exact release tag
The artifact is stamped with the repo it was built from, so updating
needs no configuration; BENCH_SOURCE=<owner/repo> in
manager/local/.env overrides the stamp. 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.
If the source repo has no published release yet, update.sh says so and
changes nothing.
Working on bench itself
git clone git@github.com:12vectors/bench.git && cd bench && ./start.sh
A clone carries bench's own cards and local/ content — that is dev mode,
not an install. (Installing from a clone anyway works: install.py
clears the shipped cards on its first boot in a host repo.) Releases are
built by ./release.sh from the manifest at
manager/core/release-manifest: tag = v<VERSION>, one stable asset
name (bench.tar.gz), contents at the tarball root — the two things the
install one-liner above depends on.
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
AGENTS.md; the adapter contract in manager/core/adapters/README.md.
License
MIT.