Every project doc rots the same two ways | Building Products  

 [Mischa Sigtermans](https://mischa.sigtermans.me)

   Menu Close        [Thoughts](https://mischa.sigtermans.me/thoughts) [Books](https://mischa.sigtermans.me/books) [Consultancy](https://mischa.sigtermans.me/consultancy) [About](https://mischa.sigtermans.me/about)  

 [← Thoughts](https://mischa.sigtermans.me/thoughts)   October 2nd, 2026  · AI Open Source 

Every project doc rots the same two ways
========================================

I audited the decision logs, research notes, and architecture docs across 30+ of my projects. Around 400 records. Everything that went stale, went stale for one of two reasons.

On 21 September I sent five read-only agents through every project on my laptop. More than 30 repos. They came back with around 400 records: decision logs, research notes, architecture docs, session logs, status files.

The conclusion fit in one sentence. There's no shared system, there are three good inventions scattered across different projects, and everything that rots, rots in the same two ways.

I wrote the rules these projects are supposed to follow. That's what made it uncomfortable.

Why I keep records at all
-------------------------

When agents write most of the code, the code stops being where the knowledge lives. The code says what the system does. It doesn't say why the stats page shows live totals instead of hiding quiet days, or why a migration empties a table instead of merging it. Somebody decided that, usually me, usually in a session that ended an hour later.

So my newer projects keep records next to the code. In [TAP](https://tap.fm), an append-only decision log collected 476 decisions in its first 13 days. In Onoma, Bron, and a few others, there's an `architecture/` folder that describes how things work now, and a `research/` folder that explains why.

My global rules already say how to write them. Write the end state. The result reads as if the current state was always true. History lives in git, never in the artifact.

Good rules. Here's what happened to them.

Rot one: status that depends on someone remembering
---------------------------------------------------

Most of my projects mark each research doc with a status. The most common one is `active`. It means nothing, and it never changes.

In Onoma, all nine `active` docs were older than 60 days. In another repo, four out of seven. Those docs are supposed to move to `implemented` or `superseded` when someone runs a sweep that compares them to the code. In that repo, nobody ever ran it.

Onoma had a rule that implemented docs move to an `archive/` folder after 30 days. Nine docs got moved without their status being updated. 35 of the 167 links between docs broke because files moved.

The lesson is boring and it's the whole thing: if a status only changes when someone remembers to change it, it's wrong by next month. And never put status in a file's location. Folders are the worst place to keep a fact, because moving a file breaks everything that points to it.

Rot two: agents reading whole files
-----------------------------------

The second rot is newer, and it's mine.

TAP makes every agent session read the full feature register and the full decision log before it starts. That's around 60.000 tokens of mandatory reading, every session. Bron's CLAUDE.md is 144KB. Nobody reads 144KB carefully, human or model. They skim, and they trust what they skim.

Meanwhile the docs drift away from the code, and the only way to notice is a manual sweep. Onoma's memory-system doc was 252 commits behind the code it describes. Stagent's docs were 105 commits behind and didn't mention TAP at all, even though [Stagent handed its whole press kit to TAP](/thought/the-press-kit-outgrew-stagent) in August.

The sweep itself exists in six different versions across my projects, each forked from the last. Only Claude sees them. Codex and Grok read the same repos and never get them.

And 11 of my 19 Laravel projects don't say anywhere where records should go.

What worked
-----------

Three things held up, and they're worth stealing.

**A status that says what the conclusions are still worth.** Bron uses five: `proposed`, `decided`, `measured`, `superseded`, `done`. They don't describe what kind of document it is. They describe whether you should still believe it. All 29 docs in Bron use one of them, and none are out of date.

**The split between end state and journal.** Architecture docs describe how things work now. Logs and decisions describe what happened and why. Where a sweep actually runs, it holds: one repo's architecture docs name 84 things in the code, and all 84 still exist.

**Decisions small enough to write.** TAP's log is one line per decision, with a date and an issue id. 443 of its 476 decisions reference an issue. Nobody writes a two-page decision document at 23:00. A one-liner, they write.

The fix: records that can prove they're stale
---------------------------------------------

The convention I'm settling on has four kinds of records, each with one job:

- **Why it exists**, in `purpose.md`. Including a section on what not to do.
- **How it works now**, in `architecture/`. End state only.
- **Why it was decided**, in `research/`, with a date in the filename and one of Bron's five statuses.
- **What happened**, in `logs/`. Optional.

None of it is mandatory reading. An agent pulls what it needs.

The one new idea is a field called `covers:`. An architecture doc lists the code paths it describes, like `app/Services/Council/**`. If those paths have 40 commits the doc hasn't seen, the doc is provably behind. No agent has to read it to know. The check is `git log`, not judgment.

That's the difference that matters. A doc that can tell you it's wrong is useful even when it's wrong.

Where Library comes in
----------------------

[Library](/thought/how-i-run-claude-code-with-solo-and-library) already indexes every Claude chat and Claude Code session I've had. It also keeps the arguments of every file write in those sessions. So it knows which session wrote which research doc.

That's the part nobody else has. Ask 'why did we decide X?' and you get the decision, plus the conversation where it was made.

So Library is where this lands. It'll index the records next to the sessions, report which docs have fallen behind their `covers:` paths, and flag decisions made in a session that never made it into a record. A session hook can tell an agent at startup that the doc it's about to trust is 252 commits behind.

Two lines I won't cross:

- **The repo stays the source of truth.** The records live in git, readable by Codex, by Grok, and by Lennert and the rest of the team, without Library installed. Library indexes them. It doesn't own them.
- **Nothing from a transcript lands in git on its own.** Sessions contain client names, amounts, and half-finished thoughts. When Library finds an unwritten decision, it drafts a record, puts it in a queue, and waits. I commit.

The two ways, one more time
---------------------------

Everything that rotted in that audit rotted because it depended on someone remembering, or because it asked an agent to read everything and trust it.

A status nobody updates is a guess with a date on it. A doc nobody can check is a rumour in markdown.

The code will always say what the system does. The records have one job: say why, and say when they've stopped being true.

 *thanks for reading*

Hi, I'm [Mischa](https://mischa.sigtermans.me/about). I've been *shipping products* and *building ventures* for over a decade. First exit at 25, second at 30. Now Partner &amp; CPO at [Ryde Ventures](https://ryde.ventures), an AI venture studio in Amsterdam. Currently shipping [Stagent](https://stagent.com), [TAP](https://tap.fm) and [Steddle](https://steddle.com). I [write](https://mischa.sigtermans.me/thoughts) about what I learn along the way. [More about me](https://mischa.sigtermans.me/about).

Keep reading: [The MCP server that can't see who you are](https://mischa.sigtermans.me/thought/the-mcp-server-that-cant-see-who-you-are).

  [← Thoughts](https://mischa.sigtermans.me/thoughts) Connect
-------

  [X](https://x.com/mischamartijn) [LinkedIn](https://linkedin.com/in/mischasigtermans) [GitHub](https://github.com/mischasigtermans)  

 © 2026 Hold My Beer B.V. · [RSS](feed:https://mischa.sigtermans.me/feed)
