~non-deterministic/engineering
2026-06-25

Repo truth vs. workflow truth

#documentation#process

Every team eventually has the same argument about technical documentation. Half of it lives in the repo (READMEs, ADRs, design notes, scattered markdown). The other half lives in some collaboration SaaS: wiki, tickets, sprint boards, roadmap docs, design docs, status updates, slack threads. Nobody knows where to look first, everyone argues whether the service documentation should go into Confluence or a checked-in internal engineering doc, both halves go stale in different ways, and every few quarters there’s an attempt at a grand unification into a single source of truth.

These attempts usually fail. But they don’t fail because they are poorly executed, they fail because they are simply misguided.

On the surface, these tools do seem to be solving the same thing: storing and organizing textual information. However, it turns out they have pretty different properties around lifecycle and how they are meant to be used, and that’s where most of the trouble comes from.

It is tempting to want to move everything into the repository, right next to the code it refers to. After all, we already have git, and it’s a fantastic tool. Why would I need a whole different system for tracking work when I can use markdown, a sensible folder structure, git log for activity, and so on?

It’s a noble goal, and I’ve been trying to move more and more towards this model myself. Not just for its purity and elegance, but also because of AI agents. To have effective agents, you need the right context, and what better place for the context than the repo they already work in? One thing we learnt about coding agents over the last year is that they are very good at exploring a codebase with bash and figuring out how to solve a problem, it just seems natural to keep as much of the business context nearby.

But in practice, it doesn’t fully work. Not because it fundamentally cannot work, but because it’s just an awkward fit. Tickets, to take one example, have a lifecycle and an activity log: they get created, assigned, worked on, reviewed, eventually (if all goes well) closed. You can model that in git of course. Add a markdown file, use frontmatter yaml for the metadata. Use commits to update a status field, close it, and maybe delete the file depending on how you want to maintain things. It works, but it’s a lot heavier than just using the ticket system for what it was built to do.

What I’ve come around to accept is that there’s no need for a single source of truth covering all the information the organization needs to store for documentation and collaboration. What we need is a clear distinction between what type of information is owned by what source of truth.

I believe there are two essential information truths.

The first truth is the repo truth: what is true about the system itself. Architecture and invariants, schema and API decisions with their constraints and rationale, technical debt tied to specific implementation, ADRs and migration rationale, the known technical gaps that will bite the next change. It belongs in the repository because it is about the code, it changes when the code changes, and it gets reviewed in the same PR as the code.

The second is workflow truth: what is true about coordination and delivery. Priorities and sequencing, owners and team assignments, sprint or milestone status, delivery dates and risk, cross-team dependencies. It belongs in the project tracker because it changes on a different clock, has different reviewers, and has a half-life of days rather than quarters.

Agents Change Things

For a long time, none of this really mattered that much. A stale STATUS.md is annoying but everyone learns to ignore it. An old decision buried in a ticket comment is annoying to dig up, but you can usually just ask whoever was around at the time and piece it back together. Humans are pretty good at quietly routing around bad documentation, and most teams do that without even noticing they’re doing it.

Agents are not as good at this. They start each session from scratch, with no memory of which files in the repo are current and which were abandoned three years ago. If an agent greps its way into some old doc that was never deleted, it has no real way to tell. It will read the file and act as if it described how things work today. Every file in the tree is part of the prompt that steers the agent behavior.

Take the classical STATUS.md stored next to the codebase, written with the best of intentions. It is supposed to explain what was done so far, what is left to be done, and potential blockers. It was written carefully at the same time as the code in order to document things. It is not entirely duplicated info: part of it is (what was done is also expressed in the implementation) and part of it is valuable (what is our plan, what is left to do). What unfortunately tends to happen is that someone else will pick up the code at some point, add 2 new features, drop one, and not realize that there’s a markdown file next to it. Once committed, the status file is describing an outdated version of the code and is planning for something that doesn’t need to be done. Now let an agent run loose and randomly find this file, and it might lead to chaos, or at the very least wasted time and tokens.

If you split by the two types of truths, you would most likely just drop the description part because it’s already in the implementation, and you would use an issue tracker to track what is done, what is left to do, and link to any temporary context that is needed to understand why we are doing what we are doing. An agent looking at the code will just see it for what it is, the current state. It will likely be as effective, if not more, at understanding the system from its source. It will also not get confused by historical tasks that were done and need to be done still, and why. Unless of course it’s relevant, in that case it could still look up that information with an intended tool call, but that tool call into a ticket tracking system will carry a lot more context about the nature of the information, which is a lot more about temporary workflows, and the agent will know.

That changes the cost of putting things in the wrong place. Workflow chatter that sits in the repo gets pulled into the context of every code change it has nothing to do with. And repo truth that only lives in a Slack thread or a closed ticket might as well not exist, because the agent will never see it. It builds against what it can read in the tree.

None of this is genuinely new, really. The split between repo truth and workflow truth used to be a matter of taste, and you could mostly get away with being sloppy about it. With agents in the loop, sloppy starts to cost you for real.

Type of Truth, Not Type of Tool

The reason this works, I think, is that it splits on the type of truth being captured rather than on the type of tool you happen to be using.

Once you do that, a lot of things get smoother. Onboarding gets faster for everyone, humans and agents, because technical reality sits next to the code instead of being scattered around a company’s wiki and a folder taxonomy that was half migrated two quarters ago. Operational status stays fresh, because it lives where it’s already being updated several times a day. Decision history actually survives, because git is genuinely good at tracking how a decision evolved. And accountability lines up the way you’d want: the tracker is the system of record for who owns what and when it ships, the repo is the system of record for what the system is.

Looking back, “single source of truth” was always the wrong framing. It implies one folder, one tool, one canonical artifact, and real systems just don’t work that way. What does work, in my experience, is a deliberate split: system truth lives with the system, workflow truth lives with the workflow tools, and the link between them is explicit.