You run several agent sessions at once — terminals, worktrees, subagents — and they coordinate through a TODO.md three of them are editing. Tandem gives them one queue to claim work from atomically, one briefing they all read, and one gate you hold: you approve the plan before a line of code exists. One binary, a SQLite file, no account.
Starts Tandem on your machine, registers it with Claude Code, opens the board. macOS, Linux, Windows.
Any MCP client: Claude Code, Codex, Cursor, or your own script. No SDK, no adapter per vendor, nothing hosted.
Claims are one atomic update in the database — race two sessions or two hundred, exactly one wins. Zero double-claims, enforced in tests.
Coordination used to fit in a file. One terminal, one checkout, a TODO.md that mostly held. That isn't the shape any more: you run several sessions at once — a second terminal, a worktree on another branch, subagents an orchestrator spawned — and each one reads the copy of the list it happened to open.
A file can't hold a claim. Each agent picks the same top item, and you find out at commit time.
two sessions, one file, and no way to ask who has what.
Five tool calls and one human decision. Every agent runs the same loop, whatever model it's on.
One canvas code, any MCP client. A terminal, a worktree on another branch, a subagent an orchestrator just spawned — all connect to the same queue on your machine. Nothing needs a file in common.
The goal, the constraints, the decisions already made, what shipped last. Sessions start from the same page instead of whatever their own chat happened to remember — and the briefing reports its own age, so an agent can see the picture is going stale and ask before acting on it.
Agents propose tasks; nothing is handed out until you decide. You're reading a plan, not a diff — a title and a done condition, before any agent has spent a context window building the wrong thing. One pass clears a whole epic, so approving doesn't become a second job, and saying no costs the same single click. Your reason travels back to the proposer verbatim.
you, in the browser
Each session asks the queue what's next and gets one approved task back, in order. Unapproved work is invisible to the pull — there's nothing for an agent to wander into.
Two sessions reach for the same task; the server decides, not whoever writes last. The loser is told in the same call and takes the next one. Claims carry a TTL, so a session that dies mid-task releases it back to the queue instead of parking it forever.
What was done and where — commit, PR, files — attached to the task and on the board while you watch. Commits carry the ticket, so the chain reads in both directions.
Steps 04–06 repeat, in every session at once, until the queue is empty.
Two unedited screen recordings: a fleet picking up a plan, and the plan changing under it. No mock data — this is Tandem planning Tandem.
One MCP connect, a proposed epic, one approval on the board — and the whole batch turns pullable, with workers claiming tickets seconds later.
You change your mind while the fleet is working — and nothing has to stop.
One person, several agents, no shared memory between them. A typical afternoon looks something like this:
| Where it runs | Client | Lives for | Can it see the rest of the fleet? |
|---|---|---|---|
| terminal 1 | claude code | as long as the tab | nosees its own chat history, and nothing else |
| two more worktrees | claude code | hours | nosame disk, different branch, no idea who took what |
| subagents, fanned out | orchestrator | one task each | nofresh context, no memory of the sibling that ran first |
| a CI runner | script + MCP client | one job | noruns while you sleep, merges nothing on its own |
Add a queue every one of them can reach and the last column reads yes for every row — including for you, watching the board in a browser tab.
Running one agent at a time in one checkout? You don't need this — a file in the repo is genuinely fine. Tandem starts paying for itself at the second session. The CI runner needs a self-hosted instance it can reach; everything else is your laptop.
Every one of these is a thing the server does, not a promise about behaviour.
One claim per task, decided by the server, not by whoever writes last. The loser is told claimed: false in the same call and moves to the next task. A claim that goes quiet expires and the task returns to the queue — a session that died mid-task can't hold work hostage.
Same goal, same constraints, same decisions for every session — and the briefing says how old it is: freshagingstale so an agent can tell the difference between the current picture and last week's, and ask instead of guessing.
Agents propose; only what a human approved is pullable. Approve an epic once and the batch clears — the gate is on what runs, not on every step. Each task keeps who proposed it, who approved it, and which session claimed it.
The batch arrives as text before any of it is worked — every ticket with its title, the surface it touches and the condition that says it's done. Amend one there, or send a finished one back, and the reason is stored on the task: the next session reads why off the ticket instead of asking. You find out a ticket was wrong while it's still a sentence.
Claude Code, Codex, Cursor, a script with an MCP client — same queue, same tools, same canvas code. Nothing on the server knows or cares which model is calling.
An agent's plan is a list of titles you can read in a minute. The code that plan produces is an afternoon. Tandem moves the moment you find out an agent was wrong from after the diff to before it exists.
“Fix auth, the email service, and messaging” names three areas and zero surfaces. The agent proposes it as an epic of named tickets instead — each with the surface it touches and a done condition someone else could check. Nothing in it is claimable yet.
Approve, reject or amend, inline on the row. Rejecting costs exactly what approving costs — one tap, with an undo — because a gate whose cheap button is “yes” isn't a gate. Amend is the third verb: right idea, wrong scope, fixed in place without letting it run.
A rejected ticket isn't a dropped one. Your reason is stored verbatim and comes back to the proposing agent in its next task_get as a review block — so it corrects the plan instead of guessing why the count dropped.
Out of scope — messaging stays on the current schema this cycle.
// what the proposing agent reads next
task_get TDM-209 → review: {
outcome: "rejected",
reason: "Out of scope — messaging stays on
the current schema this cycle."
}a vignette of the board mid-triage — approve, reject, amend, in place
Every decision keeps its provenance: who proposed the ticket, who approved it, and under which gate — human, reviewing agent, or policy. Three weeks later, “who let this in” is a question the board can answer.
Tandem is built through its own queue — every feature on this page was proposed, approved, claimed and completed as a ticket on Tandem's own board, by the agent sessions that build it. The board lives on the builder's machine now, but the receipts are public: more than two hundred commits carry the ticket that produced them.
git log --oneline | grep TDM-
a vignette of the board's Done column
A completed task records who proposed it, who approved it, which session claimed it, and the commit that closed it. The ticket id lives in the commit message and the commit hash lives in the task result — so the chain is traceable in both directions, from board to repo and back.
Done — token-bucket limiter on the hub, unit tests green. a1b2c3d
same ticket, same hash — board and repo agree
One command starts Tandem on your machine and registers it with Claude Code; one paste teaches your sessions the loop. No account, no server to run, no config to write.
1 · install and start (Claude Code)
Downloads this platform's binary once, keeps its data in ~/.tandem, and is available in every project. Prefer to register by hand? claude mcp add --scope user tandem -- npx -y @jaximus/tandem-mcp
3 · Cursor, Codex, or any other MCP client
{
"mcpServers": {
"tandem": {
"command": "npx",
"args": ["-y", "@jaximus/tandem-mcp"]
}
}
}Same stdio server everywhere; it starts the local Tandem server on first use. Every client, every flag →
2 · teach your sessions the loop — paste into CLAUDE.md
## Tandem task queue
Shared work queue for this repo: Tandem canvas `AB3XK9QZ`.
1. `canvas_connect` with code `AB3XK9QZ` (once per session).
2. `context_get` for the current briefing. If it comes back `stale`,
say so before acting on it.
3. `queue_next` for the next approved task, then `task_claim` to take it.
If the claim returns `{ claimed: false }`, another agent won — take
the next task instead.
4. Do the work. Start commit messages with the ticket id (`TDM-7: …`).
5. `task_complete` with a result — always include the commit hash.Or let npx @jaximus/tandem-mcp init --write create the canvas and append this for you. The full block (epic planning, spec-drift checks) is in the README.
Everything your agents write lands as structured state you can actually read — the epic as a roadmap, results on the board, findings as a doc. Same data they see, rendered for the human who has to review it.
Tandem is a single Go binary with the web app embedded and a SQLite file under ~/.tandem. Nothing phones home, nothing needs an account, and your board is yours to back up with cp.
Tandem is a local queue your agents work over MCP while you watch and steer from the browser. The short version, in plain terms:
Give your sessions one place to find the work, claim it without collisions, and hand it back with receipts — while you stay the one who says go, reviewing the plan instead of the diff.