Adopting tuhdoo
This page is for the person bringing tuhdoo to a team. By the end, tuhdoo runs in your repo, teammates can join, agents can connect, and you can steer the backlog from a terminal.
Adoption is small: one person initializes tuhdoo inside the repo, everyone else joins with a clone and one command, agents connect through a one-snippet config, and the team steers from a terminal. There is no server to stand up, no vendor, and no accounts. The entire shared state is the data branch: a git orphan branch (named tuhdoo) inside the repository itself, synced through the remote you already have. Whoever can write to the repo can plan in it. Clone the repo and the plan comes with it. A public repo can keep its plan private instead: see Keeping the ledger in a private repo.
1. One person runs tuhdoo init
From inside the repository, run:
tuhdoo init
The command does three things:
- Creates the data branch.
- Starts the per-repo background daemon: the local process that owns all writes to the data branch and syncs it with the remote.
- Prints the snippet that connects agents (step 3) and the git-host settings (below).
The command is idempotent: running it again at any time is safe. To verify, run tuhdoo status: it shows the data branch, syncing with "origin", and a running daemon.
Adoption comes with two one-time settings on the git host, both printed by tuhdoo init:
- Exempt the data branch from pull-request and review rules.
- Exclude the data branch from continuous integration (CI) triggers.
Details are in the repo-admin section of joining.md.
2. Teammates join
Every other machine — a teammate's laptop or your second workstation — joins in three steps: clone the repo, install the binary, and run the same tuhdoo init. On a machine that joins, tuhdoo init detects the existing data branch on the remote and adopts it rather than minting a new one. joining.md is the walkthrough, including clone shapes and identity setup.
Everyone sees the same backlog, and everything anyone writes is attributed to a principal:
- A human acts as a principal derived from their git identity:
sarah@example.comacts assarah. - An agent acts as a sub-principal under the human who runs it:
sarah/claude-code-1.
Every action on the ledger — the append-only record of tasks, claims, notes, outcomes, and questions on the data branch — traces to a responsible person.
3. Agents connect
Any agent harness that supports the Model Context Protocol (MCP) connects through one snippet, also printed by tuhdoo init:
{ "mcpServers": { "tuhdoo": { "command": "tuhdoo", "args": ["mcp"] } } }
If tuhdoo came in as an npm devDependency, use { "command": "npx", "args": ["tuhdoo", "mcp"] } instead.
Connected agents follow agent-protocol.md: the instruction text you load into your harness. tuhdoo protocol prints it straight from the binary. The protocol defines the loop agents run: claim a task, work it, escalate questions to a human, and finish with an honest outcome. That is the agents' half of the contract. Your half — writing tasks worth claiming and steering the backlog — is steering.md.
4. Pick a code workflow
tuhdoo coordinates who works what. How code gets written and merged stays ordinary git, whatever your team already does. An agent fleet still benefits from a deliberately chosen workflow, and the workflow recipes are recommended patterns to adopt or adapt. Start with the trunk-based pull request (PR) flow: one task, one branch, one squash-merged PR. Each recipe ends with a copy-pasteable block for your repo's agent-instructions file.
5. Steer
Day to day, the human surfaces are a terminal.
The TUI. Bare tuhdoo opens the interactive terminal user interface (TUI) for steering: answer escalations in place, reprioritize, move tasks between ready, on hold, and inbox, cancel tasks, and drill into any task's history. It acts as you. tuhdoo --watch is the same screen read-only, safe to leave open beside a working agent.
The CLI. The command-line interface (CLI) offers one-shot commands for reads and quick writes, all scriptable:
| Command | What it does |
|---|---|
tuhdoo status |
One-screen overview: sync state, counts, active claims. |
tuhdoo backlog |
Every task, one aligned line each; grep a state (ready, in-progress, blocked, on-hold, inbox, done, cancelled) to filter. |
tuhdoo task <id> |
One task in full: its description plus a chronological history of claims, notes, runs, and escalations. |
tuhdoo escalations |
Every escalation, one line each, open before answered. |
tuhdoo create <title> |
Add a task. --status inbox captures cheaply, --desc - reads a description from stdin, and flags exist for priority, labels, and dependencies. |
tuhdoo update <id> |
Change fields: title, description, priority, status, labels, dependencies. |
tuhdoo answer <id> <text> |
Answer an open escalation. |
The work-loop tools (claim, finish, release) are deliberately not CLI commands. A claim's lease renews only while a live agent session holds it, so a claim taken by a one-shot command would just lapse. Agents work through tuhdoo mcp; humans steer.
The git host, for free. The data branch carries generated views: browsable markdown (a backlog page and per-task pages) that lets anyone read the plan from GitHub or GitLab without installing anything.
The rhythm that follows is small: capture ideas the moment they occur (tuhdoo create … --status inbox), triage the inbox periodically, and keep escalations answered — they are the questions your fleet is blocked on. steering.md covers that loop.
Keeping the ledger in a private repo
By default the data branch lives on origin, beside the code, so a public repo publishes its plan too. To keep the code public and the plan private, put the data branch in a separate private repo, by convention named <repo>-internal, and commit a small pointer file named .tuhdoo to the code repo. Anyone can see that the repo plans with tuhdoo and where its ledger lives. Only people with access to the private repo can read or write the ledger.
-
Create the private repo on your git host, empty. tuhdoo needs nothing from the host but git access: the daemon fetches and pushes the
tuhdoobranch with your ordinary git credentials. -
Push the existing ledger, if there is one. From a machine that has the data branch:
git push git@github.com:owner/repo-internal.git refs/heads/tuhdoo:refs/heads/tuhdoo -
Commit
.tuhdooat the root of the code repo. It uses git's config format, like.gitmodules:# This repo plans its work with tuhdoo. The ledger lives in a private repo. [ledger] remote = git@github.com:owner/repo-internal.gitThe value is anything
git fetchandgit pushaccept: a URL, a local path, or the name of a configured remote. -
Restart the daemon on every machine. The daemon reads
.tuhdooonce, when it starts, and a running daemon keeps its old target until it restarts. Stop it as inuninstall.md, then run anytuhdoocommand to start a fresh one.tuhdoo statusnow showssyncing with "git@github.com:owner/repo-internal.git" from .tuhdoo. -
Optionally, delete the old public branch. Once every machine has restarted, the
tuhdoobranch onoriginis a stale public copy. Delete it withgit push origin --delete tuhdoo. This is a branch delete, not a force-push: the history lives on in the private repo. Don't delete it sooner, because a daemon still syncing withoriginwould publish the branch again.
What the pointer guarantees:
- It pins the target. With
.tuhdoopresent, the daemon syncs with the repo it names and nothing else: never withorigin, and never quietly local-only. A clone without access to the private repo shows a plain sync error intuhdoo statusand keeps working locally, but it never writes the ledger toorigin. Its agents can still claim tasks, butconfirm_claimrefuses until the private repo is reachable, because a confirmation is only won at the ledger's home. - The clone remembers it. At startup the daemon records the target in the clone's local git config as
tuhdoo.remote, which every linked worktree of the clone shares. A worktree on an older branch or tag that predates.tuhdoostill syncs with the private repo. A fresh clone learns the target the first time a daemon starts on a branch that has the file, so run tuhdoo there first. You can also set the key by hand for one clone:git config tuhdoo.remote <url>. When the file and the key disagree, the file wins and the key is updated. - A broken pointer stops the daemon. If
.tuhdooexists but has no usableledger.remote, the daemon refuses to start and writes why to.git/tuhdoo/daemon.log. tuhdoo never guesses where the ledger lives. - Changing the target takes a restart, exactly like setting it up.
With the ledger in its own repo, the host settings from step 1 move with it: data-branch pushes no longer reach the code repo's CI or branch rules.
Leaving
Adoption is reversible. A machine walks away with a handful of ordinary git commands and zero trace, and a team can retire the ledger entirely if it ever wants to. uninstall.md is the walkthrough. It's worth reading before you adopt, because it proves adopting costs nothing to undo.