istosandClaude Opus 5 6c06583109 site: serve bench.12vectors.com from a Cloudflare Worker
site/wrangler.jsonc puts site/dist/ behind bench.12vectors.com as static
assets. No `main`: the site is files, and a Worker with no script is the
cheapest correct way to serve them.

  html_handling      force-trailing-slash, so /x redirects to /x/ — the
                     url the pages link and rel=canonical names. One
                     page, one address; no url ends in .html.
  not_found_handling 404-page, so an unknown path gets dist/404.html
                     with a 404 status rather than the landing page
                     with a 200.
  routes             bench.12vectors.com as a custom domain. Cloudflare
                     takes the hostname at the zone level and makes the
                     DNS record; nothing else on 12vectors.com moves.

site/root/_headers carries the response policy. HTML revalidates on
every view, so a deploy is visible on the next reload without anyone
clearing a cache; /static/* is kept for a year and never re-checked,
which is safe because the stylesheet and icon urls carry a hash of their
contents. The general rule is written first and the specific one second,
so a host that merged the two instead of overriding would still land on
max-age=0 — the safe side. Alongside it the baseline a public page owes:
nosniff, a referrer policy, a year of HSTS without preload,
X-Frame-Options, and a default-src 'none' CSP that makes "no analytics,
no third-party anything" something the browser enforces rather than
something a test asserted once.

Deploys are run by hand, as releases already are — no Cloudflare token
in repository secrets, no first deploy pipeline. site/README.md names
the account, the Worker, the route and the four-command sequence, plus
the four things to check after a deploy that no test here can reach.

The tests cover everything before Cloudflare: that the config says what
the site needs, that the build writes the files it names, and that
wrangler.jsonc, pages.json and README.md cannot drift apart about which
domain this is. A live response is not among them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 11:47:12 +02:00
2026-07-31 11:34:06 +02:00
2026-07-30 07:40:17 +02:00

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.

S
Description
Bench is a local task manager that integrates with coding agents to make it easier to manage work in a repo.
Readme MIT
1.7 MiB
Languages
Python 78.2%
HTML 17.5%
CSS 2.1%
Shell 2%
JavaScript 0.2%