Skip to content

Commit 776b84b

Browse files
authored
docs: document why the graph is a computed snapshot, not a maintained store (#174)
Adds docs/SNAPSHOTS.md, the design argument that was missing everywhere: enola's output is a verdict on a change, a verdict is a function of two states, and a graph updated in place destroys the earlier state with the very edit being graded. Everything otherwise unusual — pinned baselines, content-fingerprint snapshot IDs, the comparability guard, from-scratch re-linking on append — follows from treating the graph as a value rather than as state. Includes what the choice costs and where a continuously maintained graph is the better design. Registered from README (Learn more), ARCHITECTURE.md (a pointer section after "Determinism & incremental updates" plus one line in "The idea"), the BENCHMARKS footer bar and section 1, and the CLI gate section. Accuracy fixes found while writing it: - README cited 30 benchmarked repositories; BENCHMARKS measures 38 - set_baseline listed three pinned artifacts; the code copies four, including receipt.json, which the comparability check depends on - the A/B cost column was cited as evidence about cost per query. It compares an agent that checked its work against one that did not, and the cheap arm shipped the cycle 3/3, so it isolates nothing about what enola itself costs. Corrected in BENCHMARKS (three places) and in the new document. Trims retrospective narration from the shipped surface: six passages in ARCHITECTURE.md and one in docs/extraction/python.md now state each rule in present tense, as the failure mode the design prevents, rather than recounting how it came about. The repo-root write-up they referred to is removed with its eight references — one in CONTRIBUTING.md, six source comments, one in a working document.
1 parent 14146b3 commit 776b84b

14 files changed

Lines changed: 290 additions & 29 deletions

File tree

ARCHITECTURE.md

Lines changed: 20 additions & 10 deletions
Large diffs are not rendered by default.

CONTRIBUTING.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,9 @@ hook runs it automatically when your push touches one of those paths, and skips
6969
loud message if `claude` is not on your `PATH`.
7070

7171
Please do not treat it as optional. The failure mode it guards against is a hook
72-
configuration that parses, reports success, and does nothing: every cheaper check
73-
passed while the `Stop` hook was silently never firing, because a unit test can only
74-
compare the output against the same belief that produced it. The full account is in
75-
[`DEFECTS_FOUND.md`](DEFECTS_FOUND.md).
72+
configuration that parses, reports success, and does nothing — and every cheaper check
73+
passes while that is true, because a unit test can only compare the output against the
74+
same belief that produced it.
7675

7776
If you cannot run it, say so in the PR so a reviewer can. `enola doctor` is the
7877
same question asked after the fact, on a real repository.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ Framework- and platform-specific detection for each language is described in **[
175175
## Learn more
176176

177177
- **[docs/CLI.md](docs/CLI.md)** - setup, every command and flag, the exit codes, and the `--explain` report.
178-
- **[docs/BENCHMARKS.md](docs/BENCHMARKS.md)** - reproducibility, delta precision, cross-repo coverage and scale, measured on 30 public repositories - including the three defects the run found in enola itself.
178+
- **[docs/BENCHMARKS.md](docs/BENCHMARKS.md)** - reproducibility, delta precision, cross-repo coverage and scale, measured on 38 public repositories.
179+
- **[docs/SNAPSHOTS.md](docs/SNAPSHOTS.md)** - why enola computes a graph on demand and keeps it as an addressable snapshot rather than maintaining one continuously-updated graph - and where the opposite choice is the right one.
179180
- **[docs/extraction/](docs/extraction/)** - per language, what specific code produces which facts, from committed fixtures - and what each extractor deliberately does not resolve.
180181
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - the concept, the fact model, the pipeline, the MCP tool reference, and the value model.
181182
- **[examples/](examples/)** - ready-made per-language and multi-repo configs, plus a pre-commit hook and a CI workflow.

cmd/enola/doctor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
// It exists because of a failure mode a test cannot cover. `enola install --hooks`
2121
// wrote a configuration the agent silently ignored, so the hooks never fired — and
2222
// nothing anywhere said so. The installer reported success, the hook binary worked when
23-
// invoked by hand, and the agent simply never called it. See DEFECTS_FOUND.md.
23+
// invoked by hand, and the agent simply never called it.
2424
//
2525
// The shape of that configuration is a contract owned by the agent, which ships on its
2626
// own schedule and can change after enola is released. So the durable check is not a

cmd/enola/hook.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func runStopHook(ctx context.Context) {
7676
// Record the run BEFORE deciding whether to speak, and on every path. The silent
7777
// paths are the ones worth recording: a hook that never fires and a hook that fires
7878
// and finds nothing are indistinguishable in a session, and only one of them is
79-
// broken. See internal/hookstate and DEFECTS_FOUND.md.
79+
// broken. See internal/hookstate.
8080
declineKey := ""
8181
if ok {
8282
declineKey = verdict.DeclineKey()

docs/BENCHMARKS.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ This is the property everything else rests on. `snapshot_id` is
9696
commit are provably the same graph — which is what lets `compare_receipts` refuse to
9797
diff snapshots that are not comparable instead of reporting churn as your change.
9898

99+
An ID a third party can re-derive is the point rather than a hygiene property: it is
100+
what makes a graph a value you can keep and compare, instead of a state you are
101+
currently in — [SNAPSHOTS.md](SNAPSHOTS.md).
102+
99103
## 2. Delta precision — the ratchet
100104

101105
The claim is that enola reports **what your change did** and stays silent about
@@ -327,7 +331,9 @@ of the three did — and the transcripts say why. From one of them, unprompted:
327331
328332
That is the loop working as designed: the agent introduced the cycle, the Stop hook
329333
graded the session and handed back the verdict, and the agent corrected itself before
330-
reporting done. It cost roughly double the turns and 3.6× the money of the bare run.
334+
reporting done. It took roughly double the turns and 3.6× the money of the bare run —
335+
which is what checking and then fixing costs against not checking at all, since the
336+
bare run's $0.31 bought a tree with a dependency cycle in it.
331337

332338
**Read this before quoting the table.**
333339

@@ -339,6 +345,12 @@ reporting done. It cost roughly double the turns and 3.6× the money of the bare
339345
tool, `generate_snapshot`. The loop arm did not succeed because the agent asked
340346
enola the right question — it succeeded because something asked on its behalf when
341347
it stopped. That is the whole argument for the hook over the instruction.
348+
- **The cost column is not enola's price.** It compares an agent that checked its work
349+
against one that did not: the bare arm is cheap because it stopped early and shipped
350+
the cycle three times out of three. The arm that would isolate what enola costs does
351+
not exist here — an agent told to establish the same property *without* a graph,
352+
re-deriving the module structure from source on every task. Do not read $0.31 → $1.12
353+
as the overhead of adding enola to a run that was otherwise identical.
342354
- **The instruction arm improved on bare anyway** (1/3 vs 3/3), which is worth being
343355
honest about: having a structural map in context appears to help even when the
344356
agent never queries the delta. With three trials, that could equally be noise.
@@ -350,8 +362,9 @@ reporting done. It cost roughly double the turns and 3.6× the money of the bare
350362
Stated so the numbers above are not read as more than they are.
351363

352364
- **Retrieval speed, token counts and cost per query.** Deliberately absent. It is
353-
the axis where every tool in this space competes and the one where enola's evidence
354-
would be weakest — the A/B above shows the enola-equipped arms costing 2–3.6× *more*.
365+
the axis where every tool in this space competes, and enola has no evidence to offer
366+
on it. The A/B's cost column is not that evidence — it compares checking against not
367+
checking, not one retrieval path against another.
355368
- **The session hooks in interactive use.** Section 6 exercises them headlessly, under
356369
`claude -p`. The configuration is the same either way, but interactive sessions were
357370
not measured.
@@ -397,4 +410,5 @@ enola coverage cluster.yaml
397410

398411
Per-language extraction detail: **[docs/extraction/](extraction/README.md)** ·
399412
Commands and flags: **[docs/CLI.md](CLI.md)** ·
413+
Why the graph is a snapshot: **[docs/SNAPSHOTS.md](SNAPSHOTS.md)** ·
400414
How the engine works: **[ARCHITECTURE.md](../ARCHITECTURE.md)**

docs/CLI.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,8 @@ enola baseline pin /path/to/repo # 1. freeze how it looks now, BEFORE editing
453453
enola check /path/to/repo # 2. grade what they did
454454
```
455455

456+
The baseline is a pinned artifact rather than "whatever state the tool last held" - it survives re-snapshots, publishes atomically, and travels to another machine. Why the graph works that way at all: **[docs/SNAPSHOTS.md](SNAPSHOTS.md)**.
457+
456458
| Exit | Meaning |
457459
|------|---------|
458460
| `0` | **clean** - no structural regression |

0 commit comments

Comments
 (0)