A Kanban Board My AI Agents Actually Use

A two-person kanban for Ray & Toản that runs on a single Cloudflare Worker with D1 — and that my AI agents edit directly through SQL because the browser UI is for the humans.

My agent workflows generate a lot of work: deployed features, incidents, fix-it-later notes, things that got half-finished at 1am. For months that work lived in a PROGRESS.md / DONETASKS.md pair at the top of each repo. It worked, but only for repos I happened to be in. Two people, a handful of machines, and a dozen agents working across them all means "the board" needs to be a thing, not a file in a repo — somewhere both of us and every agent can reach it without SSH-ing anywhere.

So I built a tiny kanban. Two-person. One Cloudflare Worker. It's been running for a few days now and it's quietly become the load-bearing piece of how we track the magic-manager work.

The shape of it

It's aggressively boring on purpose:

  • Svelte SPA served as static assets off a single Cloudflare Worker.
  • D1 (SQLite) is the source of truth. No separate API server — the Worker handles reads and writes.
  • Cloudflare Access with Google login is the auth. The whole hostname sits behind it; the Worker reads Cf-Access-Authenticated-User-Email on every request. No session tables, no password hashing, no forgot-password flow. Google does that.
  • Two users: Ray and Toản.

The board itself is standard issue — columns, cards, labels, assignees, comments, drag-and-drop with optimistic moves and poll deferral so it feels instant even though every write is a round trip. Nothing here is clever, and that's the point. The clever part is who else uses it.

The part that matters: agents work it through SQL

Human edits happen in the browser. Agent edits happen through wrangler d1 execute:

wrangler d1 execute kanban --remote --command "SELECT id, name FROM boards;"
wrangler d1 execute kanban --remote --file=db/kanban.sql

There's a db/kanban.sql in the repo holding the canonical recipes: read the board, add a card, move a card, comment, query by label. Any agent authenticated to the Cloudflare account — from any machine — can see what's on the board and update it without touching the SPA or knowing anything about the frontend.

Two rules keep it honest:

  1. Agent SQL must set created_by / author_email explicitly. The default is 'cli', which makes every change attributable. If an agent forgets, the card shows up as authored by "cli" and it's immediately obvious the attribution was skipped.
  2. The board is the single source of truth. New work gets added as a Backlog item before it gets touched. When a task's branch merges to master it moves to "Done (recent)"; housekeeping sweeps it into the flat archive. No keeping the board and the git log in sync by hand — the board is the log everyone reads.

That last rule is the real behavior change. Agents used to only learn about work from the repo state in front of them. Now they start a session, read the board, see the three things in Backlog, and pick one. The board became the shared memory of "what are we even doing" that both humans and agents consult.

The bug that taught me the DB is the source of truth, really

The day after launch, the board would load — and render nothing. Columns existed in D1, the API returned them, and the SPA drew zero columns. The root cause was one line: the first load's patch was computed by applyBoardPatch(prev, ...) where prev was null, and the reducer returned null for null input instead of the initial state. So the very first render after a refresh silently wiped the view — not the data, just the picture of it.

I'd put the fix at the top of a list of "things agents found and fixed" if I kept such a list, but the more useful lesson is the architecture one: because D1 is the source of truth and the SPA is a projection of it, that bug could only corrupt the view, never the data. A refresh-and-fix cycle had the board back in a few minutes, and no card was ever lost. Building the projection-on-query model meant the worst case was "ugly screen," never "lost work."

That's the argument for a boring, boring database behind a toy frontend: the database is what everyone — humans and agents — actually agrees on.

What I'd tell someone else building one

  • Put it behind Cloudflare Access and skip auth entirely. The Workers + D1 + Access stack is maybe 200 lines of real code. You don't get to skip the "who is this user" question with most stacks; here you genuinely can.
  • Design for agents, not just users. A board your AI can read and write as a first-class citizen is a different thing from a board that happens to have an API. Write the SQL recipes, pin the attribution rule, and treat "agent wrote this" as a visible property, not an accident.
  • The humans will forgive an ugly board. They won't forgive a lost one. Put the truth in the database, not in a reducer.

The full project is small enough to read in an evening — Svelte shell, an API client, polling stores, and a pure drag-and-drop module. That was intentional too. Two-person kanban should feel like a weekend project, because it basically is one — and then it should earn its keep for a year.