A diff says what changed; git blame says who. Neither says why. The why did exist — briefly, in the model's own words, immediately before the edit call — and then it scrolled away.
The Annotator Engine keeps that sentence, anchors it to the exact lines that edit wrote, re-finds those lines by content when the code moves, and folds the repo's per-region why into one durable map every later agent reads before it opens a file. Here it is running:
Everything below is how a sentence gets into that column, and how it stays pointed at the right code.
deriveEditIntents walks a transcript holding one pending say/think per lane; when an edit tool fires it stamps that text onto the edit. Drag through one capture:
Main conversation and sidechain keep separate pending slots, so a sub-agent's reasoning can never label the main lane's edit. An empty intent is kept, not dropped: a worker that edited without saying why is itself a signal.
An IntentHunk is { newStart, addedLines }. addedLines is the key; newStart only breaks ties between duplicates, and zero means the first content match wins.
Move the file and watch what survives. Padding shifts every line; a rewrite defeats the exact match; deletion ends it.
On disk reanchorWriteRegions has no fallback at all — it would rather lose a note than mis-anchor one.
live is the author's own sentence, caught at edit time. after is a model reading finished code and writing down what it appears to be for. Nothing here conflates them:
The outranks predicate hard-codes it: an after-intent never evicts a live one, whatever the sequence numbers say.
It folds into .loom/annotations — index.json for machines, STRUCTURE.md for anything that greps. Every entry carries a content fingerprint:
FNV-1a, 32-bit, through Math.imul, printed as eight hex characters. Cheap, stable, exactly enough for the one question asked of it.
When an agent writes with a stated intention the fold moves the hash to the new content, so staleness says: fine. But nobody has read that file.
So IndexFile carries readAt beside annotatedAt, and the write fold deliberately does not bump it. The sweep gates on the second lamp.
A burst of twenty edits should cost one re-annotation, not twenty. Written files wait; a tick drains the ones that have gone quiet:
drainWriteQueue is pure and tested: idle files oldest-first, capped, the over-cap ones pushed back. The debounce is a queue, bounded at both ends.
Hand-picked islands do not make a map, so the sweep takes the whole repo — source bands first, shallow before deep:
Stop it anywhere. Resumability is not a checkpoint file: a file whose current content was already read simply skips at read time.
Three lanes write the same index, asynchronously. queueIndexWrite is the single door: every fold chains onto one module-level promise, so read-merge-write can never interleave.
Saves used to fail invisibly. One observer now drops every result into the header. The map is an accelerant, never a blocker — but a broken accelerant has to be visible.
An annotation layer only humans read is write-only overhead. STRUCTURE.md is injected into the system prompt of every agent run, inside the cached prefix:
It sits directly above the intent-first rule, so an agent reads what the repo already knows and is then told to state its own intention before it writes. That is the loop closing.
Loom for macOS — download, read the docs, or see the verdict and the brain.