When we switched off Linear on 12 October, the estate plumbing project alone had 406 open issues. The company’s own admin had 58, ZooLog had 16, and every other business had a pile of its own. None of it moved to another vendor. Each live issue became a Markdown file in the git repo of the business it belonged to, and the old issues went into a read-only archive.
Two days later we wrote the standard down and built the tool around it: a small task command, one file per task, and an estate-wide overview that a background job rebuilds every five minutes from every business’s tasks/ folder. That evening we ran two pruning passes, fourteen minutes apart. The first closed 218 tasks that were stale, duplicated, or tied to a server we had already retired. The second closed 50 more.
Why a file and not a ticket
Most tasks in our trackers are now picked up, worked and closed by AI coding agents, with one of us reviewing. An agent is good at reading a file and poor at clicking through a web app. A ticket in a hosted tool needs an API key, a client library and a network round trip before the agent can even see it. A file is just there, next to the code it is about.
So the rules came out like this. One task is one file, named after its id, and an id is never reused. The frontmatter is flat key: value, so a person can read it and a script can parse it. The body says why the task exists and what “done” means. Under that sits a thread of dated, signed notes that nobody is allowed to rewrite.
---
id: ES-###
title: "websites devroot is diverged from origin (ahead 143, behind 62 on 30 Oct): every task note commit fails to push"
status: done
priority: high
area: estate
created: 2026-10-30
closed: 2026-10-30
reporter: "Claude"
---
Found 30 Oct 13:30 while adding a note to a websites task: "committed locally,
push failed (rebase conflict aborted)". It was ahead 140, behind 56 at 12:55,
so it is growing by the minute. Do not force-push.
## Notes
### 2026-10-30 13:52 · Claude
Six diverged tracker repos healed; task check --all reports 0 diverged.
That is a real file from the estate tracker, trimmed, with its id and commit hashes taken out. It happens to be the one that recorded what went wrong.
What we got for nothing: every change to a task is a git commit, so its history is git log on one file. Agents and people edit the same thing. There is no vendor to pay or to migrate away from later. And each business keeps its list in its own repo, so a session working on ZooLog never loads anything from TipBop.
The phone was the gap. The first version of the standard also retired the to-do app we had on our phones, on the theory that one list beats two. Nine days later, on 23 October, it came back as a mirror: a Cloudflare Worker that runs every minute, copies each task owned by a person into Todoist, and writes ticks, priorities and due dates back into the files. Tasks an agent will do itself stay out of it. The first run pushed 168 tasks.
What broke on 30 October
By late October there were often several agent sessions working in the same business at once, each filing notes, starting tasks and closing them. Every one of those writes committed two files: the task it touched, and the business’s tasks/INDEX.md, a generated table of open work.
That index was the flaw. Any two writers changed it at the same moment, so any two writers conflicted. The push used git pull --rebase, the rebase aborted on the index, and the task commit stayed on the machine that made it. The command printed a warning and carried on, so the stranded commits piled up quietly.
On 30 October at 13:30 an agent tried to add a note to a websites task and the push failed. It checked: the websites tracker was 143 commits ahead of origin and 62 behind, up from 140 and 56 thirty-five minutes earlier. When we checked every tracker, six had drifted. By the time the repair ran that afternoon, websites stood at 157 ahead and 78 behind.
How far each tracker had drifted, 30 October
Commits the local checkout had that origin did not (ahead), and the reverse (behind). Bar length is the two added together.
Source: Estate decision log, 30 October 2026
The drift was not the only damage. Two task ids had been handed out twice, once in TipBop and once in ZooLog, because two machines each took the next free number from a checkout that could not see the other’s. The same audit turned up habits that the files had made easy:
The second one is the one that hurt. The CLI set the owner to whoever created the task, and inside a session “whoever” means the person who started the session. So agents quietly filed task after task in a director’s name, and one of us had 213 open tasks, only two of which had not come from an agent. A list like that is not a to-do list. It is noise with someone’s name on it.
The fixes, and the ones we turned down
Everything below went in the same afternoon.
The index came out of the write path. task now commits only the task files it touched. INDEX.md is regenerated separately, by task index and by the Todoist Worker, and is allowed to lag.
A rejected push now merges instead of rebasing. It takes a lock, fetches, merges origin into the checkout, and resolves task files field by field: notes from both sides are kept, the later status wins and is flagged for a person to check, and lists are merged as sets. A conflict anywhere outside tasks/ stops the merge and names the file.
We turned down two cleverer fixes. A rebase with a smarter conflict driver would have replayed 157 websites commits one at a time and met every contradiction again. A custom git merge driver for the index needs config in every clone, and nobody sets that up on a new machine. Resolving with -X theirs would have made the conflicts disappear by losing task edits without a word, which is worse than the conflicts.
task check now fails when a checkout has diverged on tasks/, and the brief every session prints at start-up warns about it. task new without --owner exits with an error. --owner none means an agent will do it. A name means it lands on that person’s phone.
The repair was dull on purpose. Each drifted repo was merged in a throwaway clone and pushed, the live checkout was fast-forwarded, and a backup ref was left in every repo. The websites merge met 19 tasks where the two sides disagreed about status. The later note decided each one, all 19 were flagged, and when we checked them by hand none needed changing.
Update, 7 November 2026
The files are still the source of truth. On 6 November we copied 2,737 tracker tasks into a native task app we are building for ourselves, as a test against real data with no cutover. Todoist and the sync Worker stay live while it proves itself.



