The capability inventory — everything Tandem actually does, by pillar. Nothing here is planned, in progress or nearly done: each line was read out of the code, and honest limits get their own section rather than being quietly rounded up.
The execution primitive is a task row. One table serves tasks and epics; six states and their legal transitions are enforced by the server, not by convention.
proposed → approved / rejected → executing → done / failed. failed → approved exists only as a human re-queue, never as an agent move.TDM- is a render prefix; only the integer is stored.TDM-21, tdm-21, #21, 21 and the raw uuid all resolve to the same task. An unknown but well-formed ref answers task_not_found, not “invalid id”.epic_propose creates the container and its tickets in one call, so a human approves once instead of ticket by ticket.no_surface_named, no_done_condition, may_exceed_one_sitting, context_not_linked, context_duplicated — evaluated on the ticket's text before any write, and again on the board while a human edits it.CLAIM_TTL_MINUTES; 0 disables expiry entirely.task_progress restamps the lease, holder-only, in SQL — but deliberately does not move the claim's start time, so “working for 40 minutes” stays honest.409 plus a reason — from every write path: complete, fail, progress, patch, move, release, requeue, delete.tapOut: true with a reason and a next step — one boolean to branch on instead of prose — and a per-process ledger refuses a second attempt at a task you already lost without even racing.queue_wait parks server-side (1–60s, default 25s) and returns the instant work is approved. Waiters are an in-process registry woken by a signal — there is no database polling behind it.started · progress · completed · failed — and lands on the board as external rather than as a generic agent.Agents propose; humans promote. A canvas sits on exactly one policy, set by the owner alone, and birth-time approval is stamped by the server from provenance the caller cannot forge.
requiresApproval lands proposed under every policy, auto included — the agent's own “I deviated, look at this” signal.human, agent:<name>, policy:epic, policy:auto — so a reader can tell a cascade from a decision. There is no approvedBy body field anywhere in the API.strict and peer.done or failed ticket is refused, including via the side door of a combined state-plus-content patch.Off by default. On a peer canvas the gate moves from the human to a different registered agent — one verb, two outcomes, and a short list of things it deliberately cannot do.
proposed task into the ready queue. Both identities are server-derived: the reviewer from the request's auth context, the proposer from the stored provenance.done work back to the queue with a required reason, stored verbatim — so whoever picks it up next reads why without asking anyone.reviewed: false rather than an error — peer_self_approval, rework_self_review, rework_reason_required and a dozen more.What turns an approved queue into running work: paste-ready handoffs, a wait that is not a poll, and webhooks that can start a process on your machine.
queue_next attaches a paste-ready brief to every ready task, so an orchestrator dispatches one subagent per task without composing anything. It carries the canvas code, never the caller's session handle — a worker on the planner's handle claims as the planner and the fleet tree collapses.canvas_connect takes role, name, model and parentAgentId, upserting on name so re-registering returns the same identity. A bad parent never fails the connect — it is reported as data.canvas_connect against a machine with nothing listening starts the Tandem binary detached and retries — an agent can bring the board up on its own. TANDEM_AUTOSTART=0 turns it off.task.approved, task.completed (done or failed — there is no task.failed), task.claim_expired, task.returned, task.rejected. Claiming, deletes, payload edits, release and requeue are deliberately silent.tandem-mcp listen on the same machine; strict opt-in under TANDEM_AUTH=google.A canvas is a bag of named documents that humans edit live and agents read scoped. The point is that an agent picking up a ticket gets its context in one call.
notes, roadmap, folder. Deleting a folder returns its children to the root instead of destroying them.context_get returns identity, the briefing doc, document tabs, per-kind counts and the approved queue — and with a task id, that one task hydrated with its linked notes and roadmap items.fresh / aging / stale / unknown is computed at read time, and unknown is explicitly not the same as stale.[stale — verified 21d ago] annotation. Hiding it would just make the agent confidently ignorant.task_get, on the epic rollup and in the context bundle.When your collaborators are machines, “who wrote this” is the authorization boundary. Tandem answers it structurally rather than by asking.
human — the browser's session, which no agent path can mint. agent:<id> — the identity is client-asserted but the classification is server-owned, so an agent can never emit a bare human. anonymous — a valid canvas token with neither.task_complete appends commit, PR and branch links — appended, never replaced, so the record of a reworked ticket keeps both attempts.React, Vite and Tailwind, embedded in the server binary, with hand-rolled history routing and no router library. The board's job is that one glance answers “what is waiting, what is claimed, what landed”.
task_find uses, with out-of-scope hits reported rather than silently omitted.live / slipping / stale, keeping “last heard from” and “lapses at” as separate facts. Board and fleet derive one identical lease from one function.external for something holding work that never registered.welcome, docs, roadmap — markdown notes with live autosave and nestable roadmap goals, next to the board itself.Local mode has no accounts at all: the browser on your machine is the owner, and agents join a canvas with its code. Everything below the first two rows exists for a self-hosted, shared instance and turns on with one environment variable.
~/.tandem/tandem.db. No environment is required to start it; the JWT secret is generated into the data dir on first run so tokens survive restarts.0/O, no 1/I/L — from a cryptographic source. The code is the view capability an agent needs; ownership stays with the browser.canvas_connect returns a handle the model carries back on every later call, so the binding survives a transport reset without a reconnect.Published as @jaximus/tandem-mcp: a stdio MCP server that is also the launcher for the local binary. The default manifest is an 18-tool intent facade, because a CRUD manifest costs a large slice of the context window it is supposed to be helping.
canvas_connect canvas_create agent_register context_get queue_next queue_wait task_find task_get task_claim task_progress task_complete task_propose task_amend task_review epic_propose doc_write doc_read board_statusSHA256SUMS, starts it detached, waits for /healthz and opens the board. setup also registers the server with Claude Code at user scope.tandem-mcp init writes the code into a project-scoped config, and the manifest then names this project's canvas — the model reads the manifest, not the process environment.tandem-mcp init creates the canvas, merges (never clobbers) your MCP config, prints the code and board URL, and can append an AGENTS.md snippet teaching the queue-first workflow. Re-running is a no-op.MCP_TRACE reports duration, the API time inside it, and ok/error — so a slow tool call is attributable rather than merely slow.What it takes to run the thing and know whether it is healthy — including the parts that are deliberately small.
schema_version table, migrated at boot. A schema change ships in the binary; upgrading is replacing the binary and restarting.~/.tandem/tandem.db (WAL mode). Copy it, and you have copied every canvas, ticket, claim and audit trail.v* tag builds darwin / linux / windows × amd64 / arm64 with CGO off (the SQLite driver is pure Go) and attaches them to a GitHub Release, which is where the launcher downloads from.Where a guarantee is really a convention, or a check fails open. Recorded here rather than rounded up, because a capability list is only useful if it is also accurate about its edges.
TANDEM_AUTH=google before anyone else can reach the port.127.0.0.1 on your laptop. The board they can share is a self-hosted one; local mode is for the sessions on this machine.