# Work: one session, one worktree, one branch, pushed always `tatbot work` is how changes reach `main` on a development machine. Every session works in a worktree of its own, on a branch that is pushed as soon as it exists and after every commit. Landing is one command that rebases, runs the land gate and pushes to `main`. A session cannot end with work that is neither landed nor parked with a reason. ## Why Two or three agents used to share one checkout and were told to "pull --rebase and push often". A shared tree is never clean, so the rebase always refused. Agents improvised with cherry-picks into throwaway worktrees, which put every landed commit on origin under a new SHA while the original stayed on local `main`. After two weeks that local `main` was 25 commits ahead and 89 behind, eleven "dirty" files were stale copies of landed commits nobody dared touch, and a real fix sat unpushed for a day because a commit-time gate had refused it. Work was durable only when someone remembered to land it. Here work is durable the moment it exists. ## The commands | Command | What it does | | --- | --- | | `tatbot work start --task NAME` | A worktree under `~/tatbot-work/` on branch `agent//`, cut from `origin/main` and pushed at once. `--adopt` moves uncommitted changes out of the mirror checkout into it, cutting the worktree at the mirror's own revision so they apply as written; `land` rebases them onto `main`. | | `tatbot work status` | Every worktree on this node: dirty files, commits ahead of `main`, unpushed commits, parked reason, idle time. Stale agent branches on origin with no worktree here. | | `tatbot work land` | Rebase onto `origin/main`, push the branch, run the land gate (lint, disclosure, the export gate, complexity of the changed files: what the author answers for, under a minute), push `HEAD:main`, retrying the rebase while `main` moves, then delete the agent branch. `--full-check` runs the whole fast tier first; `--no-check` skips the gate; CI runs everything on `main` regardless. `--done` removes the worktree. After the push, the repository's post-land hook (`internal/ci/post-land`, when present) receives the landed range as `base head`; a private tree uses it for infrastructure that must react to what landed — here, rebuilding the isolated CI worker's images when the diff changed an input they are built from, so the worker does not sit at "rebuild and redeploy required" until someone notices. A failing hook fails the land's exit code; the push is never undone. | | `tatbot work park "why"` | Leave the work unlanded on purpose: a `wip:` commit if the tree is dirty, pushed, with the reason recorded. | | `tatbot work sweep` | What the hourly timer runs: autosave dirty worktrees idle for 30 min as `wip:` commits, push anything unpushed, move edits left in the mirror for a day into a pushed `mirror-rescue` worktree, fast-forward the mirror, prune worktrees that are clean and landed and idle for a day, delete remote agent branches already contained in `main`. | | `tatbot work init` | Once per machine: mark this checkout as the mirror, arm the git hooks, install the user systemd timer. | The shared checkout on each machine (the one `config/nodes.json` names as its `checkout`) is the **mirror**. Its pre-commit hook refuses commits; the sweeper fast-forwards it; nothing is edited there. Deployment reads from it, and so does `tatbot --on `. ## What the hooks do - **Claude Code `SessionStart`** (`.claude/settings.json`, tracked): creates the session's worktree and tells the agent where it is. A resumed session in its worktree is told its branch state instead. - **Claude Code `Stop`**: refuses to end the session while the worktree holds uncommitted files or commits not on `origin/main`, unless it is parked. The message names the two ways out. After three refusals with no change it lets the session stop and the sweeper autosaves what is left. - **git `post-commit`**: pushes the agent branch. Never fails the commit; an unreachable origin is reported and the sweeper retries. - **git `pre-commit`**: lint and ratchets as before, plus a guard that refuses commits in the mirror. - **git `pre-push`**: the public export gate, only for a push to `main`. Agent branches push free. ## Reading `tatbot work status` ``` tatbot work — dev1 2026-09-16T18:20:11Z mirror ~/tatbot: clean fix-leader-mount-20260916T181233Z UNLANDED agent/dev1/fix-leader-mount-20260916T181233Z dirty=2 ahead_of_main=3 unpushed=0 idle=14.0m task=fix leader mount remote agent/dev2/calib-20260915T090000Z (a1b2c3d) unmerged, 31.5 h old, no worktree here unlanded=1 parked=0 stale_remote=1 ``` `unpushed=0` with `ahead_of_main=3` is the normal healthy state: the commits are on the server, they just have not been landed. `tatbot status` carries the same counts as the `unlanded_work` observation, so the fleet view shows which node is sitting on work. ## Recovering work Nothing in this flow deletes a commit. An autosaved or parked branch is on origin under `agent//`; check it out anywhere with `git fetch origin && git worktree add ~/tatbot-work/ agent//` or resume it on its node with `tatbot work start --session `. A branch already contained in `main` is deleted by the sweeper an hour after it merged; a branch with unlanded commits is never deleted by anything here. ## Conventions - Commit in your worktree the way you always did: small, Conventional Commits. `wip:` commits are what the sweeper and `park` make; squash or reword them before landing if they would be noise on `main`. - Land before you stop. Park only with a reason another person could act on. - Run `scripts/check` (the fast tier), `scripts/check rust` or `scripts/check sim-fast` in the worktree as the change warrants; `land` runs only the gate the author answers for, so that someone else's red job cannot strand a change. - A worktree costs a checkout of the tree, not a build: uv environments, `node_modules` and `rust/target` are per worktree and start cold. For a Rust or simulator task, keep the worktree for the whole task rather than starting a new session per step.