Retrieval
lore search ranks passages, not files. It takes several wordings of one question in one call, runs offline, and never claims an answer is missing.
Spans
Every Markdown file is cut into spans. Headings come first, because a heading is where the author already decided one idea ends and the next begins. A section longer than 40 lines is then divided at a blank line, so no span is longer than one screen.
Each result is a span, with five fields: path, anchor (the heading it sits under, or none), startLine and endLine (counted from the first byte of the file, frontmatter included, so they match your editor's gutter), score, and text.
Nothing is derived from a path. Meaning is read from the text, which is why you can reorganize the folders without breaking retrieval.
Several wordings
Lexical search, one wording at a time, is not good enough, and this project measured that rather than assuming it. The same question asked in other words reaches notes the first wording could not. The examples below search the eight invented notes from the agent demo.
lore search vault "nightly export failed" --limit 3
notes/nightly-export.md#When it runs:16-18 score 0.0164
~/vault/notes/nightly-export.md
The nightly export starts at 02:10 and usually finishes before 02:40. It is
scheduled rather than triggered, so a failed export does not retry on its own
and waits for the next night unless somebody starts it by hand.
notes/nightly-export.md#Restarting a failed export:28-30 score 0.0161
~/vault/notes/nightly-export.md
Starting the nightly export by hand is safe. The writer is keyed on the record
identifier, so an export that runs twice lands the same rows twice and the
warehouse keeps one of them.
notes/nightly-export.md#What it writes:22-24 score 0.0159
~/vault/notes/nightly-export.md
Each nightly export writes one file per topic, named for the topic and the
date, into the warehouse bucket. An export that produced no file for a topic
either had nothing to move or did not finish.
lore search vault "nightly export failed" "run stopped partway and reported success" "consumer lease renewal committed offset" --limit 3
notes/consumer-leases.md#A lease is lost silently:15-22 score 0.0323
~/vault/notes/consumer-leases.md
The renewal interval is thirty seconds. A consumer that spends longer than that
inside a single batch never gets to renew, and the broker hands the partition
to somebody else while the first consumer is still working.
Nothing raises an error at that moment. The consumer finishes its batch and
tries to commit an offset it no longer owns, the commit is refused, and the run
stops at the last offset it did commit. From the outside the job simply stopped
partway through and reported success.
notes/consumer-leases.md#How to tell this is what happened:26-28 score 0.0315
~/vault/notes/consumer-leases.md
Compare the committed offset with the topic's end offset. A run that ended
cleanly has caught up. A run that lost its lease is short by exactly the batch
it was working on when the renewal was missed.
notes/oncall-triage.md#A run that reported success:20-22 score 0.0315
~/vault/notes/oncall-triage.md
A run that reported success and still left the warehouse short did not fail in
the writer. Look at what the consumers committed rather than at what the writer
uploaded.
Each wording is ranked on its own, with BM25 (k1 1.2, b 0.75) over the span's text, plus a second BM25 over its metadata, weighted at 0.6. The metadata is the title, the heading trail, the anchor, tags, aliases, about, and the folder. The top 20 of each list are then fused by reciprocal rank fusion with k = 60. A span that repeats the same text in several files is reported once. Every one of these numbers was fixed before the evaluation ran and has never been tuned against a failing question.
JSON
--json prints the same results in a machine-readable shape. It is the retrieval contract an agent reads, and it does not change to make a number look better.
lore search vault "nightly export failed" "run stopped partway and reported success" "consumer lease renewal committed offset" --limit 1 --json
{
"query": "nightly export failed | run stopped partway and reported success | consumer lease renewal committed offset",
"queries": [
"nightly export failed",
"run stopped partway and reported success",
"consumer lease renewal committed offset"
],
"brain": "~/vault",
"exhausted": false,
"count": 1,
"results": [
{
"path": "notes/consumer-leases.md",
"anchor": "A lease is lost silently",
"startLine": 15,
"endLine": 22,
"score": 0.03225806451612903,
"text": "The renewal interval is thirty seconds. A consumer that spends longer than that\ninside a single batch never gets to renew, and the broker hands the partition\nto somebody else while the first consumer is still working.\n\nNothing raises an error at that moment. The consumer finishes its batch and\ntries to commit an offset it no longer owns, the commit is refused, and the run\nstops at the last offset it did commit. From the outside the job simply stopped\npartway through and reported success."
}
]
}
score is the fused reciprocal-rank score that ordered the list, not a BM25 value. queries lists the wordings as given. exhausted is true when the brain holds more files than one search reads, so the ranking covers only part of it. A ranking over part of a vault is a different claim from a ranking over all of it, so the flag is reported, never swallowed. At a terminal, the same condition is written to standard error.
What a score cannot say
A score ranks spans against each other. It does not separate knowledge that is present from knowledge that is missing, so there is no relevance threshold: a cutoff would turn an honest miss into false confidence. When the results do not settle a question, search again in other words. When they still do not, say that the evidence is insufficient. Never report that the brain lacks something because a score looked low.
Measured: the context saving
Source: Feature 07, context/features/07-agent-integration-artifact.md,
## Verification. Ticket 07.2.
Measured: 2026-09-17, by npm run bench:context, from a clean build.
Corpus: 3,000 deterministic synthetic notes at seed 20260917, 3,193,061
bytes in total, averaging 1,064 bytes each, generated into a temporary
directory and removed. Nothing real, nothing private, nothing committed.
Machine: Apple M5, macOS 25.6.0 arm64, Node v26.5.0.
The call measured is the one AGENTS.md teaches an agent to make: three
quoted wordings, --json, default limit. The wordings come from the
generator’s own vocabulary, because the synthetic corpus holds no content for
the artifact’s illustrative TLS-certificate example to find. What was measured
is the shape of the call — several wordings, fused — not those particular
words.
| Measure | Prescribed call | Sensitivity check, --limit 20 |
|---|---|---|
| Results | 5 | 20 |
| Distinct files behind them | 5 | 18 |
| Whole notes, baseline | 5,604 bytes | 21,034 bytes |
| JSON payload received | 2,847 bytes | 9,985 bytes |
| Span text within it | 1,564 bytes | 5,913 bytes |
| Saving, payload against whole notes | 1.97x | 2.11x |
| Saving, span text against whole notes | 3.6x | 3.6x |
The average returned span is 313 bytes.
The headline, and its ceiling
Approximately 2.0x, rounded up from the measured 1.97x. That is the ceiling on what is published for the prescribed call — not a claim about the best case, which the generous-baseline caveat below addresses — and it is the JSON payload against the baseline, because the payload is what a calling agent actually receives.
Span text is recorded beside it as a diagnostic, not as the headline. It is the evidence without the addressing that carries it, and no agent ever receives one without the other.
npm run bench:context prints ratios to one decimal, so the measured 1.97x
appears as 2.0x in its own output, and 2.11x as 2.1x.
What the number does not claim
- Bytes are a proxy for token cost, not a literal token count. No tokenizer and no provider coupling was added to produce these numbers. Anyone quoting this as a token saving is quoting something that was not measured.
- The baseline is deliberately generous. It charges only for the files the returned spans came from, as though the agent had already known which files to open. Without span retrieval it would not have known — so 1.97x is a floor for the realistic case rather than a best case.
- The corpus was not lengthened to flatter the result. Synthetic notes average roughly a kilobyte, so a span can only be about 3.4 times smaller than the note holding it. A vault of longer notes would show a larger saving, and the corpus was left as it was rather than tuned toward one.
- The payload is pretty-printed, which is 12% of its bytes. Compact JSON
would read 2.2x. The output format is a
05.1contract and was not changed to improve this number.
The saving is real and modest, and it is reported as measured rather than as improved.
Frombrand/proof.md · The context saving
Measured: retrieval performance
Source: Feature 05, context/features/05-span-index-and-search.md,
## Verification. Ticket 05.3.
Measured: 2026-09-17, by npm run bench:search, from a clean build, over a
deterministic synthetic corpus the command generates into a temporary directory
and removes. Nothing generated is committed and nothing is read from a real
vault.
| Measure | Value |
|---|---|
| Notes | 3,000 (seed 20260917) |
| Spans | 10,537 |
| Corpus | 3,193,061 bytes; 907 paragraphs duplicated across files |
| Walk + read + split | 81 ms |
| Build index | 64 ms |
| Query, one wording | 2.3 ms median of 20 |
| Query, three wordings fused and suppressed | 6.9 ms median of 20 |
lore search end to end, three wordings, --json |
129 ms median of 20 |
| Machine | Apple M5, 24 GiB, macOS 25.6.0 arm64, Node v26.5.0 |
The honest headline is the last row: a fused three-wording search over 3,000 notes completes end to end in about 130 ms, including building the whole in-memory index from scratch, because there is no index file and none is kept between commands.
What the number does not claim
- “Interactive” is an interpretive reading, not an acceptance threshold. Feature 05 read the overview’s performance target as a fused three-wording search, index build included, finishing inside two seconds — the round trip an agent tool call tolerates. That reading was recorded as a reading. The measured result sits roughly fifteen times inside it.
- One machine, one corpus, one shape of note. These are medians of twenty runs on an Apple M5 over generated notes, not a survey of real vaults on real hardware.
- Building the index every time is a measured decision, not an oversight. At this size, persisting it would save roughly 150 ms and cost a manifest-owned file. Revisit only on a corpus where the walk and index phases, which scale with the vault, approach the reading.
Frombrand/proof.md · Retrieval performance
What has not been measured
Recorded so that the gaps are visible rather than inferred.
- No measurement over a mature personal second brain. This is a recorded evidence boundary: the retrieval prototype was validated over substantial real-world Markdown project corpora, not over a populated personal brain. The real vaults available at the time were far too small to exercise ranking and were rejected for that reason, so the real-vault evidence base behind this project is very small and supports no quantitative claim at all. Proxy corpora under-represent note-to-note conceptual linking and dictated fragments.
- No token-level measurement. See the proxy caveat above.
- No measurement of answer quality end to end, with a real agent, on real questions. What is measured is what the retrieval layer returns and what it costs to return it.
Frombrand/proof.md · What has not been measured