The index that was ordered to stay in sync
The board says Done, the commit index lacks the hash, and nothing protests. The fix removes the hand-copying.
TL;DR
The index was hand-maintained and drifted silently from reality, so Done cards referenced hashes it lacked. Fix: regenerate it mechanically from git log, lint that every Done card's hash exists in the index, and fail loudly on mismatches. Done is now a verified state rather than a trusted claim.
The board shows a Done card for a module update. The commit index, the append-only table meant to answer "what was finished, at which hash", does not contain that hash. What makes it nasty: no layer protests. The board believes it followed its flow, the index believes its rows are right, and the person tracing work by hash gets two answers that do not support each other.
The reason the index existed at all was that first question: the first door when tracing work by ID or hash. Its origin was humble, every row hand-copied from git history. The first guess followed that logic: a discipline lapse. Someone closed the card and forgot to update the index, or mistyped a hash. The standard recipe for such a guess is equally mature, add a checklist and tighten the process.
The theory did not survive contact with the data. Tracing back through several cycles showed the pattern repeating: the index is a snapshot, produced at one moment, then the world moves. Newly closed cards are never reconciled back into that snapshot. The root cause is not negligence, it is data architecture: a status document maintained by hand drifts from reality, and the only question is when. The question "Is this document up to date?" recorded in Software Engineering at Google as the classic engineer complaint is a symptom of the wrong architecture, not of careless people. The same book concludes that Google's most successful efforts came when documentation was treated like code and incorporated into the traditional engineering workflow [3].
Removing the hand from the equation
The first fix was not stricter discipline but removing the hand-copying. The index is now regenerated mechanically from git log, the command whose job is to show commit logs, complete with the parent links reachable from a given commit [1]. No transcription, no drift: the index is derived data, not a claim.
A small detail of the regeneration script turned out to carry the most useful lesson. The file header is preserved verbatim, only the commit-count and date placeholders are refreshed. The first version of the script stayed silent whenever the header did not match its expectation: no message, no change. The revised version is the opposite, failing loudly with an explicit message when the placeholders were not substituted. A tool that stays silent while failing is a trap; a silent failure reads as success.
The second layer is a lint on the board side. Every Done card carrying a hash must have that hash present in the index; a Done card with no code diff must literally carry the text "(no diff)". With that, the condition "finished but untraceable" stops being an event waiting for someone to notice and becomes a machine-rejected error. The pattern matches the argument in the pre-commit documentation: simple issues are caught automatically before review so the human reviewer spends attention on architecture rather than administrative nitpicks [2].
The format gap the first invariant missed
The first lint version only scanned hashes wrapped in backticks. Review found the gap within hours: thirteen legacy rows carried bare hex without backticks, and all of them escaped the membership check. The fix did not force everyone onto backticks, it switched the matching pattern to plain hex so both formats read identically. The first version of an invariant almost always has a format gap, and an adversarial review finds it, not good intentions.
Testing is pinned down too. The lint selftest accepts injectable paths, so the footprint and index cases run against temp fixtures without touching real files. The closing decision of the trio was one line from the project owner: the commit index must never drift. It was implemented as a mechanical invariant, not as a vow in a process document.
The end state changes what Done means. It is no longer a claim to be trusted but a verifiable state: the hash on the card exists in the index, and the index matches git log. The "what was finished" document stops being one you have to ask about freshness.
Sources
- git-log documentation, git-scm.com (accessed 2026-10-12): "Show commit logs"; "List commits that are reachable by following the parent links from the given commit(s)".
- pre-commit.com documentation (accessed 2026-10-12): "Git hook scripts are useful for identifying simple issues before submission to code review"; freeing reviewer attention for architecture.
- Software Engineering at Google, ch. 10 Documentation, abseil.io (accessed 2026-10-12): "our most successful efforts have been when documentation is treated like code and incorporated into the traditional engineering workflow"; "Is this document up to date?".