Skip to content

Phase 9: faster evaluation - #28

Merged
rohan-hotdata merged 9 commits into
mainfrom
phase-9-faster-eval
Oct 16, 2026
Merged

rohan-hotdata merged 9 commits into
mainfrom
phase-9-faster-eval

Conversation

@rohan-hotdata

Copy link
Copy Markdown
Collaborator

This PR makes the end-to-end runner fast for runs that change only recall or the reader, adds an 18-question smoke run, and keeps finished work when a run stops. It changes bench/ and the docs only; no file in src/hotmemory changes. Closes #25.

How

  • --keep (task 1): the memory arm runs each question over its own local database and keeps it. The name holds what decides the memory: hotmemory-kept-<sample>-<extractor><prompt digest>-<cites>-<ranking>-<last src/hotmemory commit>-<question id>. Settings that act only in recall (episodes, budget, k) stay out of the name, so one kept memory serves many runs. It refuses when src/hotmemory has uncommitted changes or the databases exist; a failed question deletes its database.
  • One database per question, not per worker: BM25 ranks the whole table before the namespace filter, so the memory of other questions in a table changes the block and the known facts that capture shows the extractor. Agreed on Phase 9: faster evaluation #25.
  • --reuse (task 2): opens the kept databases, skips capture, refuses when one is missing, never deletes one. local_store takes delete (always, on_error, never). Each memory row records block_sha256.
  • bench.diagnose (task 3) reads a kept database when the result file names one, with no capture replay.
  • --smoke (task 4): the 18 questions of bench/sample-smoke.json (3 per type from sample A, seed 20261016) on MemoryStore, one store in each worker.
  • Rows file and --resume (task 5): each finished row is appended to bench/results/<name>.rows.jsonl under a file lock, after a header with the configuration. --resume skips the rows it holds and refuses another configuration's file. A failed model or engine call fails only its question, and its error crosses the process pool as text, which fixes the BrokenProcessPool of phase 8. A run with a failed question writes no result file.

Verification

All observed on the local stack, sample A, --extractor detail --session-episodes, from the caches at $0 except where a cost is given (docs/benchmarks.md, "Faster evaluation"):

Run Time Result
search 64,000 --keep, 4 workers 762 s equals phase 8 on 54 of 54 rows, facts written included
search 32,000 --reuse 13.0 s equals phase 8 on 54 of 54 rows
cited 64,000 --reuse 15.3 s equals phase 8 on 54 of 54 rows
cited 32,000 --reuse 14.2 s equals phase 8 on 54 of 54 rows (README example check)
search 64,000 --reuse 11.1 s same block SHA-256 as the --keep run on 54 of 54
diagnose of the --keep run 7 s equals the phase 8 diagnosis on 9 of 9 misses
smoke, first run 2,080 s, $1.07 16 of 18
smoke, replay 12.7 s (3.8 s with 4 workers) same answer, verdict, and block on 18 of 18

The 54 kept databases hold 192 MB. A replay killed after its first row kept that row, and --resume finished the other 2 with answers equal to phase 8. make verify (252 passed) and make integration (120 passed) pass.

Not verified: a real loss of the internet connection (a unit test raises ConnectionError in the reader), and a first smoke run with 4 workers. The first cached smoke command waited 563 s in all for a cause that was not found; later tries took about 15 s.

Notes

  • A commit that changes src/hotmemory makes every kept database unusable, by design: capture calls recall for the known facts, so any library change can change the memory. Such a change also makes the next smoke run pay for extraction again. Kept databases are deleted by hand.
  • MemoryStore ranks by vector only, so the first smoke run of a configuration misses the extractor cache of the engine runs and pays.
  • --smoke takes --workers, which widens the "in the process" of task 4 in Phase 9: faster evaluation #25.
  • The 54 kept databases of this PR stay on the local stack for phase 10.
  • Closing phase 9 in the roadmap waits for a follow-up PR after merge, as for phases 7 and 8.

With --keep, the memory arm runs each question over a local database of its
own, named by the sample, the extractor variant and a digest of its prompt,
how the facts cite their session, the memory ranking, the last commit that
changed src/hotmemory, and the question id. The database stays after the
question, and a question that raises deletes it. The runner refuses --keep
when src/hotmemory has changes that are not committed, when a kept database
of the run exists already, and with an arm other than memory. The result
file records the name.

plan.md task 1 now says one database for each question: BM25 ranks the whole
table before the namespace filter, so a shared table changes the block.
With --reuse, the memory arm opens the kept database of each question, skips
capture, and runs recall, the reader, and the judge. The runner refuses when
a kept database of the run does not exist, and never deletes a kept database
in a reused run. local_store takes `delete` (always, on_error, never) in
place of `keep`. A reused row has None for facts written and sessions at the
limit, and each memory row now records the SHA-256 of its block.

Also fixes the name of the database of bench.diagnose, which the task 1
commit passed without its prefix.
When the result file names kept databases (a run with --keep or --reuse),
bench.diagnose opens the kept database of each missed question and reads
its memory, with no capture replay. It refuses when a kept database does
not exist, and never deletes one. A result file without kept databases
replays capture as before. The diagnosis of one miss moves into
`diagnose`, and the output records the kept name.
With --smoke, the runner runs the 18 questions of bench/sample-smoke.json,
3 of each type drawn from sample A with seed 20261016, over one MemoryStore
with a fixed clock, in the process. The result file records the smoke
sample, the memory driver, and the vector ranking, and is named
e2e-smoke-<tag>.json. --smoke refuses --keep, --reuse, --workers, and
--sample.

MemoryStore ranks by vector only, so capture shows the extractor a
different list of known facts than a run on the local engine, and the first
smoke run of a configuration pays for its extraction (observed: a replay
stops at the first extractor call).
The runner writes bench/results/<name>.rows.jsonl as it runs: a header line
with the arms, the configuration, the sample, the limit, and the kept name,
then one line for each finished question, appended under a file lock. With
--resume, it reads the file, refuses a header of another run, and runs only
the questions that the file does not hold. Without --resume, it refuses to
start over an existing rows file. The result file is written, and the rows
file deleted, only when every question finished.

A question that raises is listed as failed, its namespaces are emptied, and
the next question runs. The error text crosses the process pool as a string,
so an APIConnectionError no longer breaks the pool. CacheMiss still stops
the shard. A resumed --keep run deletes the kept databases of the questions
that did not finish before it starts again.
docs/benchmarks.md records the proof. On sample A, from the caches, with 4
workers: search at 64,000 with --keep took 762.4 s and equals the phase 8
run on all 54 rows. search at 32,000 and cited at 64,000 with --reuse took
13.0 s and 15.3 s, and equal their phase 8 runs on all 54 rows. search at
64,000 with --reuse took 11.1 s, with the same block SHA-256 as the --keep
run for each question. The 54 kept databases hold 192 MB. The diagnosis of
the --keep run read the kept databases in 7 s and equals the phase 8
diagnosis on all 9 misses.
bench/README.md gets the options --keep, --reuse, --smoke, and --resume,
and a section "Faster runs": the name of a kept database and its parts,
the refusals, the smoke sample and why its first run pays for extraction,
and the rows file. The diagnosis paragraph says that a result file with
kept databases is read without capture. The result file name of a smoke
run and the new field block_sha256 are listed.
docs/benchmarks.md records the smoke run: the first run paid $1.07 for 842
extractor calls in 2,080 s, and the replay took 12.7 s at $0 with the same
answer, verdict, and block SHA-256 for each of the 18 questions. The first
replay command waited 563 s in all, for a cause that was not found, and a
later one took 14.9 s.

The docs audit: the docstring of bench.e2e names --keep, --reuse, the rows
file, and --resume, and bench/README.md says that a worker stops if it
cannot empty the namespaces of a failed question.
--smoke now accepts --workers. Each worker runs its questions over a
MemoryStore of its own, with the fixed clock, and empties the memory of each
question after it, so a question gives the same row in any worker. A replay
of the smoke run with 4 workers took 3.8 s and gave the same answer,
verdict, block SHA-256, and count of facts for each of the 18 questions as
the first run. A first run with 4 workers, which pays for extraction, was
not measured.
@rohan-hotdata
rohan-hotdata requested a review from a team as a code owner October 16, 2026 11:48
@rohan-hotdata
rohan-hotdata requested review from eddietejeda and removed request for a team October 16, 2026 11:48
@rohan-hotdata
rohan-hotdata merged commit 2df6881 into main Oct 16, 2026
3 checks passed
rohan-hotdata added a commit that referenced this pull request Oct 16, 2026
The roadmap closes phase 9 with PR #28 and records what it changed for later
phases: one kept database for each question, kept memory that a library
commit makes unusable, and a first smoke run that pays for extraction. Phase
10 is open as issue #26. The plan drops phase 9, and phase 10 says how its
runs use --keep, --reuse, and --smoke --workers 4. Phase 11 gets a part
that measures recall over a narrow scope in a shared table, because BM25
ranks the whole table before the namespace filter.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 9: faster evaluation

1 participant