Repository navigation
Add Proof compaction records, proof brief, and a recall benchmark - #281
Draft
tonyketcham wants to merge 27 commits into
Draft
tonyketcham wants to merge 27 commits into
tonyketcham wants to merge 27 commits into
Conversation
An agent that resumes an Effort must read every live record to learn where the work stands. This adds a short, current account of an Effort that never hides the records behind it. - WriteCompaction (the 18th mutation) creates a Finding with kind: compaction. The writer stores `covers`, a list of which records were live and where each stood, and supersedes every earlier head compaction. `derives_from` holds at most 10 ids and may not name another compaction. No other mutation can create, supersede, or chain a compaction. - `flatbread proof brief <effortId>` (the 6th read) prints the head compaction in full, older compactions as one-line checkpoints, and each record whose standing changed since `covers` was taken. The envelope's `compaction` field says whether the head is stale and whether to write a new one. Reads never print `covers`. - The skill, reference, glossary, and README explain when to compact, when to supersede, and when a hand edit is enough. Proof records: Decision dec-compact-an-effort-with-writer-built- superseding (accepted), the first compaction of the agent-memory Effort, a measurement Finding (brief is 84 percent smaller than the status-briefing read on that Effort), and a new version of the mutation-count Constraint that names 18 mutations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW Change-Id: I6d8587f3d01cd6ad96278300a9e2a651c107ee9d
Stacked pull requests now go through GitHub's own `gh stack` extension instead of `mergify stack`. - Remove the Mergify stack Git hooks. They added a `Change-Id` trailer to every commit and steered `git push` toward `mergify stack push`. The lint `pre-commit` hook stays. - Replace the vendored `mergify-stack` agent skill with GitHub's `gh-stack` skill, installed with the `skills` CLI and recorded in `skills-lock.json`. - Rewrite the AGENTS.md section to explain the `gh stack` workflow and the flags agents must pass so commands never wait on a prompt. Mergify's merge queue (`.mergify.yml`) and its `mergify-*` skills stay. `gh stack merge` only knows GitHub's own merge queue, so AGENTS.md says to keep merging through Mergify. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Record what the review of agent-memory research found worth acting on, as open Issues in the agent-memory Effort: - Proof has no recall benchmark, so no change to reads can show that answers stay correct. This blocks the compaction Decision's reversal test. - Records are not tied to the code they govern, so a Decision can describe code that has since changed. The Issue sketches Git-hash anchors and a CI check. - Memory reaches an agent only when the agent asks for it. Four Citations hold the outside sources. Two are papers read only from search summaries; their Citation bodies say so. The new records pushed `brief` past its threshold of 5 changes, so a fresh compaction supersedes the first one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Add benchmarks/proof-recall, the start of the recall benchmark the open Proof Issue asks for. It measures how well an agent answers questions from a Proof graph, and at what cost in tokens and tool calls. - build.mjs replays each Effort's spec, an ordered list of mutations, through the real Proof writer, with fixed ids and timestamps, so the graph is valid and identical on every build. - check.mjs proves each question's answer and path against the built graph and prints coverage by tier, hops, and category. - grade.mjs scores a run; prepare-run.mjs makes a clean folder that holds the graph and question text but no answers. - README.md sets the authoring contract; WORLD.md sets the fictional game every Effort describes. The built graph is kept out of Prettier, because it must match the writer's output byte for byte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Add the two small benchmark Efforts: tools (34 records, 16 questions, ends completed) and netcode (32 records, 15 questions, ends abandoned). They supply distractors and test Efforts that default reads no longer list. check.mjs now finds the shortest route a reader can follow between a question's entry record and its answer, using only links stored on the record in hand. It warns when that route is shorter than the stated path, since the question is then easier than its tier claims. Three questions had such shortcuts and are re-tiered to their real routes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Add the medium benchmark Effort for the Meridian and Skiff ports: 88 records and 34 questions across all five tiers and 18 categories. It holds the deliberate distractors that other Efforts reference by title: the "Skiff memory budget" and "Frame budget" Constraints, the on-device pipeline cache that prose also calls a shader cache, and the Decision to run Tern on Skiff at a lower preset. A retracted "Frame budget" record carries the render Effort's 8.3 ms figure as a trap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Add the first complex benchmark Effort, Kestrel's world streaming and simulation core: 165 records and 81 questions. It holds a six-version job-system chain that moves from a thread pool to fibers and back, three versions of the streaming pool budget, a three-version tide drift measurement, 12 invalidated records, and 7 retracted look-alikes. Its head compaction is not stale, but 7 records changed after it, and two of its claims are contradicted by records it does not cite. Two facts now agree with the ports Effort. The streaming pool cut in June cites the Skiff memory cut that ports made in February, instead of reporting it as new. Ports now gives Skiff internal storage as about 200 MB/s, so world's 190 MB/s retail measurement sits under it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Add the second complex benchmark Effort, the Lantern renderer overhaul: 174 records and 83 questions. It holds a six-version visibility buffer chain, three versions of a "Frame budget" Constraint plus a retracted look-alike with the Skiff figure, 9 invalidated records, 3 reopened Decisions, and 13 Blobs of capture data. Its head compaction is stale: a record it cites changed standing after it was written. Commit the built graph in .flatbread-proof/ so a run needs no build. A rebuild from spec/ is byte-identical. The full benchmark now holds 498 records and 229 questions across five Efforts, and check.mjs proves every answer and path against the graph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Add 17 questions that need two or more Efforts. Efforts link only by title in prose, so each question joins records that no edge connects: the two live "Frame budget" Constraints, the shader caches that prose names alike, the tide Decision behind the netcode abandonment, and the Skiff memory cut behind the streaming pool cut. Checks gain two features for this. A `select` can now scan a list of Efforts, or all of them, and a new `efforts` check counts Efforts by status. Run folders now tell the answering agent the form each answer takes, without its content. The streaming pool Constraint now dates the Skiff memory cut to February, matching the ports Effort. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
The run-as-script guard read process.argv[1], which is undefined under `node -e`, so importing check.mjs from inline code crashed. Check that it exists first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
The first pilot showed agents answering "46 player-proximity reads" for "46" and "textures (3,184 MB)" for "textures". The grader marked these wrong. It now accepts a value answer that equals the gold value, starts with it, or carries the same leading number and unit. It also reads "true" and "false" given as strings, and counts given with a noun after them. Authoring checks stay exact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Two fresh agents answered the same stratified sample of 46 questions, one session each. Starting from `proof brief`, one scored 46 of 46 (46 tool calls, 203,004 tokens). Without `brief`, the other scored 45 of 46 (27 tool calls, 239,556 tokens). Audits of both agents' tool calls found no access to answers. Both read most of the graph early and answered from it, so the benchmark hit its ceiling and the tiers stopped mattering. The README now asks for one session per question, one graph command per tool call, and token and tool-call counts from the harness, since the agents' own tallies were 2.4 and 4.5 times too high. The runs are saved under runs/. A Proof Finding records the result against the open recall-benchmark Issue, which stays open until a run shows whether answers differ between read strategies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Add collect.mjs, which builds a run file from one-question-per-session runs. It reads each session's answer from its folder and its cost from the agent's transcript: tool calls, turns, tokens that entered the context, and tokens processed including cache reads. The pilot showed agents' own tallies cannot be trusted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Ten questions said "this Effort". That read fine inside a question file grouped by Effort, but a run shows each question alone, so two answering agents could not tell which Effort was meant. Each question now names its Effort by title, and check.mjs rejects "this Effort". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Ask the pilot's 46 questions again, one fresh Sonnet session per question and per strategy: 92 sessions, with cost taken from each session's transcript. Five questions did not name their Effort, so the agents could not tell which Effort they meant; both strategies missed all five. Sixteen questions now name their Effort, and check.mjs warns when an Effort-wide check would also find answers in another Effort. A question whose wording names its Effort another way carries "scoped": true. One rubric now also accepts the tide Decision's id. On the other 41 questions, starting from `proof brief` scored 38 and going without it scored 39. The extra miss gave the right record's id where the question asked for its title. `brief` cut read tokens by 17.5 percent on average (15,365 against 18,624), but the median question saved about 1,000 tokens, and `brief` was cheaper on only 22 of 41. A Proof Finding records this against the compaction Decision. That Decision says to revisit it if agents answer no better from `brief`, and on this evidence they do not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Repeat pilot 2 on Haiku: 46 questions, one fresh session per question and per strategy. On the 44 sound questions, the brief arm answered all 44 and the no-brief arm 41. Brief also cut graph reads by 26 percent on average (median saving about 4,000 tokens). Fix three benchmark flaws the run exposed: - render-040 now names its Effort. Unscoped, the ports compaction was a fair answer. - cross-017 now asks about the record titled exactly as quoted, since browse reads hide the retracted duplicate. - The Skiff beta retrospective no longer puts its 6-week cost on a span the record dates make about 18 weeks. Record the result as a Proof Finding against the compaction Decision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
The scope warning in check.mjs only re-ran Effort-wide checks in other Efforts. Questions whose `asof`, `head`, or `chain` check starts from one record slipped past it, as render-040 did in pilot 3. For those checks, look in every other Effort for records a reader could take for the start record: the same kind and title, or another compaction. If the check answers from any of them, the question must name its Effort. The new rule flagged four more questions, which now do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Benchmark agents launched as subagents could reach session-control tools, and Haiku agents called them in about half of pilot 3's sessions: messages to sessions that do not exist, one empty cloud session, and one scheduled reminder. scripts/run.mjs now starts one `claude -p` session per question and per strategy. Each session gets only Bash, Read, and Write, no MCP servers, no settings files, its own session id, and none of the environment variables that tie a process to the session that launched it. The runner saves each transcript beside its answer, resumes an interrupted run, and writes both run files. Also: - scripts/task.mjs holds the task text each session reads. - scripts/compare.mjs pairs two runs: scores, questions only one got right, a sign test, and read tokens. - collect.mjs records each session's first turn as `fixed_tokens`, so read tokens no longer rest on an estimate. - prepare-run.mjs, collect.mjs, and grade.mjs can now be imported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Haiku agents often add evidence after the verdict, as in "false, fnd-...". The grader took only an exact "true" or "false" and scored those answers wrong. It now reads the leading word. No earlier pilot's score changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Run every question once with `proof brief` and once without, one headless Haiku session each: 492 sessions, all of which answered. - Accuracy: 221.4 correct with brief, 226.3 without. Twelve questions were right only without brief and six only with it (sign test p = 0.24). Pilot 3's 3-to-0 edge for brief came from its sample. - Reading: brief cut read tokens by 23 percent on average and 5,300 on the median question, and was cheaper on 157 of 246. On compaction questions it read 70 percent less. - Eight questions ask about retracted records, which no read command can list. Every run answered "unknown" to all of them. The runner now alternates strategies question by question, so a change in API speed over a run falls on both alike. A new Proof Finding supersedes pilot 3's, whose accuracy claim the full run reverses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Browse reads hid retracted records with no way to ask for them, and Retract strips a record's id from every other record, so nothing could find one without already knowing its id. In the recall benchmark, every run answered "unknown" to all eight questions about retracted records. `proof records <effortId>` takes `--retracted exclude|include|only`. The default, exclude, is unchanged and keeps existing query hashes. Every digest marks a retracted record so it cannot pass for a live one: - its index line ends in "(retracted)"; - a RETRACTED banner with the date and reason sits under its heading, in browse reads and in `get`; - the envelope summary counts "retracted N". The benchmark task text now mentions the flag. A rerun of the eight retraction questions on Haiku scored 7 of 8 in each strategy, up from 0 of 8. The decision is recorded and accepted in Proof. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
To test whether `brief` helps once an Effort is too large to read whole, the benchmark needs a bigger graph whose answers do not change. - `build.mjs --scale N --out <dir>` merges filler into each Effort until it holds about N times its own records. Filler (spec/_filler.mjs) is seeded, so builds repeat exactly. It is routine measurements, captures, and small question-to-decision threads about subsystems no spec or question names. It never links to a real record and is all written before the Effort's head compaction, which therefore covers it. The build refuses filler whose subsystem appears in real content. - Each Effort now builds in its own folder and then moves into place. The writer reads every record under its root on each mutation, so a shared root made later Efforts pay for earlier ones. The unscaled build is byte-identical to the committed graph. - `run.mjs --graph <dir>` runs against another build. Sessions now link to one shared public copy of the graph instead of copying it, and the run warns if that copy changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Every Proof read built the whole graph and exited. On the benchmark's 10x graph (4,980 records) that cost 6 to 9 seconds per read. `flatbread proof serve [--socket path]` keeps the graph loaded in one process and answers the six read commands over HTTP on a Unix socket. It serves nothing that writes. With FLATBREAD_PROOF_SERVER set to the socket, `bin/flatbread.js` forwards those reads before loading the CLI bundle and prints exactly what a local read prints; anything else, writes included, runs locally, as does any read when the socket is unreachable. The in-memory graph is keyed by the graph root's real path and its journal generation, so folders that link to one graph share it, and a write makes the next served read rebuild. Digests are still written under the caller's folder. On the 10x graph a later `records` read takes 0.4 s and a `get` 0.17 s. The benchmark runner starts one server per run (`--no-serve` turns it off) and points each session's `./flatbread` at it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
Each run started its own `proof serve` and made its own graph copy, so the 6-second graph load and 500 MB of memory came back with every run. run.mjs now keeps both under <tmp>/flatbread-proof-recall/: - graphs/<hash>/ holds one read-only public copy of each built graph, named by a hash of its contents. A copy whose files no longer match its name is made again, and the post-run tamper check compares against the same hash. - proof.sock is one server for every run. A run reuses it if it answers, and otherwise starts it detached and leaves it running. A lock folder makes runs that start together start one server. `run.mjs --stop-server` stops it. `proof serve` now refuses to start on a socket a live server answers on. Before, it deleted the socket file, which left the old server running but unreachable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
The leading theory for why `proof brief` did not raise accuracy was that each Effort was small enough to read whole. This run tests it on a graph built with `--scale 10` (498 to 4,980 records), asking all 246 questions on Haiku in both strategies through one read server. On the 237 questions comparable with the 1x run: - Accuracy fell about ten points in both: 220.4 to 197.4 with brief, 225.3 to 198.7 without. "Unknown" answers nearly doubled; agents named a filler record once in 474 sessions. - Brief gained nothing measurable: per question it held up better on 26 and worse on 22 (sign test p = 0.67). - Agents read about the same amount at both sizes, so more noise meant more misses. Brief still read 28 percent less. So graph size does not explain the missing gain; the limit is finding a record by its text. The README now proposes testing a search read next. A Proof Finding records the result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
`proof serve` keyed its in-memory graph on the journal generation. That counter lives in a gitignored folder and only `proof write` bumps it, so after a pull, merge, or checkout the server kept serving the old graph until it restarted. The key now also holds a fingerprint of the record files: each file's path, size, and modification time. Statting a few thousand files takes milliseconds, far less than rebuilding the graph. A new test changes a record file directly, as a pull would, and checks that the next served read sees it; it fails with the old key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
This was referenced Oct 19, 2026
main now allows `derives_from` links across Efforts (#280). Six files conflicted: - `digest.ts`: the index keeps main's filter for foreign records and this branch's `(retracted)` marker. - `SKILL.md` and `glossary.md`: the Retract text says both that the writer strips links across every Effort and that `--retracted` lists retracted records. - The Constraint "Mutation enum stays deliberately small" had two new versions, one from each side. The chain is now one line: main's version replaces the original, and this branch's version, which adds `WriteCompaction`, replaces main's and takes its new Retract wording. `WriteCompaction` now calls `assertDerivesFrom` without an Effort, matching main's rule that `derives_from` may cross Efforts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW
4 of 8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary of changes
This branch does five things.
proof brief. Proof is Flatbread's Git-native agent memory. A compaction is a Finding withkind: compactionthat sums up an Effort (a unit of work) without hiding its history.WriteCompactionmutation stores acoverslist of<id>@<standing>entries. Standing is a record's state when the compaction was written, such asopen,accepted, orsuperseded.derives_fromholds at most 10 ids and may not name another compaction.flatbread proof brief <effortId>prints the head compaction, older ones as lineage, the records that changed since, and any derivations that now rest on dead records. It suggests a new compaction after 5 changes.WriteFinding,Supersede, andInvalidatefrom making or targeting compactions by the back door.proof records <effortId> --retracted include|onlylists them; the default still hides them. Every digest marks a retracted record with(retracted)on its index line, a RETRACTED banner with date and reason, and aretracted Ncount in the summary.flatbread proof servekeeps the graph loaded and serves reads only. WithFLATBREAD_PROOF_SERVERset to its socket, the six read commands go to it before the CLI bundle loads; writes, and any read when the socket is unreachable, run locally. A write bumps the journal generation, so the next served read rebuilds. On a 4,980-record graph a read drops from 6 to 9 seconds to 0.2 to 0.4 seconds. A second server refuses to take a live server's socket.gh-stackskill and a section inAGENTS.mdexplaingh stack. Mergify's merge queue stays.benchmarks/proof-recall/: a fictional game studio's graph of 498 records across five Efforts, built by replaying specs through the real Proof writer, with 246 machine-checked questions from simple lookups to four-hop chains.scripts/run.mjsgives each question its own fresh, headlessclaude -psession per strategy, with only Bash, Read, and Write. Runs share one read-only graph copy per build and one read server.scripts/compare.mjspairs two runs with a sign test.build.mjs --scale Ngrows the graph with inert filler that changes no answer but one.brief, 226.3 without (p = 0.24).briefread 23% fewer tokens and 70% fewer on compaction questions.briefgained nothing measurable (26 questions better, 22 worse, p = 0.67) and still read 28% less. So graph size does not explain the missing accuracy gain; the next test is a text search read.check.mjs's scope warning.The branch also records the compaction Decision, the retracted-listing Decision, Issues for gaps found along the way, a new compaction, and a measurement Finding per run in the repo's own
.flatbread-proof/.Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
compaction.test.ts, planner and schema tests, end-to-endproof briefand retracted-listing tests, andserve.test.tsfor the read server)Does this introduce any non-backwards compatible changes?
WriteFindingnow rejectskind: compaction; useWriteCompactioninstead.Does this include any user config changes?
The Mergify stack git hooks are removed from
.husky/. Contributors who usedmergify stackshould installgh stackinstead (seeAGENTS.md).FLATBREAD_PROOF_SERVERis a new, optional environment variable.🤖 Generated with Claude Code
https://claude.ai/code/session_01DVVnhYL1Bz7mCfEKDoyLMW