Skip to content

Changelog

Changelog

All notable changes to this project are documented here. The format follows Keep a Changelog, and this project follows Semantic Versioning.

[0.14.0] — 2026-10-06

Added

  • check(answer, evidence, question) — the gate without the memory (W213). Everything here was reachable only by building a Memory and re-ingesting; the one thing this project can show a measurement for is a handful of pure functions over two strings. Keep your retriever and your model:
from lmm import check

v = check(answer, "\n".join(chunks), question)
if not v.ok:
    print("not carried:", v.unsupported)

No engine call, no store, no state. Returns ok, figures_ok, coverage, unsupported and kept. It is a pre-filter, not a faithfulness judgement: a paraphrase the evidence does carry comes back not-carried, a sentence spliced from two lines the document never put together comes back carried, and it says nothing about whether the answer is relevant. Those are what the engine-side checks cost money for. - A line carries the date it states, not the date it was written on (W210). generate.event_dates reads a date for each of many lines in one call; _dated_by_text keeps one only when it equals the line's stamp or its day is written in that line's own text with the year within one — W95's arithmetic, over a set. - Session.ENOUGH, off (W202): the evidence is asked whether it holds an answer before one is written. The verdict is good — four declines on the NIST set, all four right, none of the nine answerable questions wrongly stopped — and it saves nothing, because a refusing turn has already paid its routing by the time it is reached. Shipped off, with the measurement beside it.

Fixed

  • A count keeps only what falls in the stretch of time asked for (W212). Asked how many doctor's appointments the user went to in March, the memory answered three where the answer is two: one appointment was on April 1st and all three were mentioned in messages stamped the same day. Dates are now read as dates — each line's own, and the question's window — and the library compares them. No month name is written anywhere in this codebase.
  • Provenance is written down, not read back off the sentence (W199–W201, W205–W207). Answer.sources was parsed out of the printed text, so a turn that spoke without printing a mark reported a claim resting on nowhere — and the graph path prints its mark only below CERTAIN, so the surer the memory was, the less it could say about where the answer came from. Also: a source whose name does not begin with # now reaches sources at all; the informed refusal signs the documents it named and no others; a derived figure is stamped with its source rather than with the line it was read from; and a cached turn reports what the paid turn reported.
  • A derived number says what was done to what (W204): 12 — 60 (coffee mugs) ÷ 5 (coffee mugs) where it used to read 12 — coffee mugs (60), coffee mugs (5).
  • The planner and the rewording pass are shown the store (W203, W209, W211). Both were asked about the memory without being shown it. The planner proposed a plan for three of eight derivation questions blind and six with the lines in front of it; it is shown them only when the question alone produced nothing, because showing them always cost a question that already worked.
  • The quoted contract's own decline is a decline (W198, W208), and a decline names no line — ANSWER: None citing a line is a value, not a refusal.

Measured

  • Where a question's calls actually go. Routing 21 of 83 on the NIST set, answer 19, extract 13, relation-check 12, organ 8, judge 6, read-back 3. The check everyone optimises is three calls of eighty-three; what costs is deciding what KIND of question this is. standalone=True, shape=... takes a question from 6.4 calls to 3.3 with nothing lost and no gate skipped.
  • The first answer-relevance figure this project has: 8 of 9 relevant answers kept, 8 of 9 irrelevant ones stopped, with the negatives built rather than written — a true, sourced sentence from the same document, about something else.
  • The vocabulary gap has two halves. Where retrieval is close, showing the rewording pass the document now gets the document's own term; where retrieval is lost, nothing downstream can help, and the bundled encoder ranks the answering lines 410th and 1,251st of 12,253. Recorded with the refuted repairs beside it.

[0.13.0] — 2026-09-27

Changed — read before upgrading

  • ask(shape=...) now refuses a word that is not a shape (W192). It raises ValueError, naming the valid words, where it used to accept any string in silence. This breaks callers that passed something other than a shape — shape="ask", for instance, which is a kind of turn and not a shape, and which is what this project's own benchmark harness passed. Such a call never did what it appeared to: the organ doors that check for count, sum, order and the rest stayed shut for the whole turn. The valid words are generate.SHAPES: material, count, sum, derive, span, order, when, recap, none. Passing no shape at all is unchanged, and so is every valid word.
  • Two new shapes, derive and span (W193, W194). Questions that ask for a difference, a share, a per-unit amount, or how long between two moments are now classified as such and answered by the plan organ where they used to be filed under a neighbouring shape. A caller that declared one of the older shapes for such questions keeps its declaration.

Fixed

  • One breath is not a series — in the plan organ too (W197). W145's first guard says that candidates all wearing one stamp are one telling, not a series, and it lived only in the counting organ. Showing the evidence (W196) is what exposed its absence next door: asked how many mummies the party will face, the plan seat answered 12 — 2023/05/29, 2023/05/29, 2023/05/29, 2023/05/29, … — twelve lines of ONE conversation counted as twelve things, where the gold is 4. A telling is prose, and prose is the chain's to read. The guard counts distinct days and reads no word of any language.

This is the third time this cycle that one rule written in two places disagreed with itself — after W192 (the declared shape) and the turn_shape reply parser that threw derive away. The same lesson applies to the stated-tally reading, which was about to be written a second time and is now _store_states_a_tally, shared.

The measured cell, against the best previous one — one question gained, none lost:

30 questions, quoted=True, shape READ W190 W191-193 now
correct 9/30 12/30 13/30
wrong 3 3 4
precision when speaking 70% 77% 73%
calls 248 262 248

Corrected after it was first published. This table first read 13/30 and 14/30, with 2 and 3 wrong. The harness's strict scorer searched the whole reply, including its source stamp, and one wrong count — 3: Dr. Johnson, Dr. Smith, Dr. Thompson. against a gold of 2 — was accepted because the stamp chat 2023/03/27 contains a 2. The engine judge had rejected it. Counting a question correct when either scorer accepted it therefore added one false correct answer and hid one wrong one, in every cell from 9af01b8 on. The harness now reads only the reply before its source mark. The same correction makes multi-session 1/5, not 2/5: the one real gain there is the derived difference (50 — ... (250) ... $200), and the counting organ's 3 for a gold of 2 is a wrong answer.

The one remaining regression in precision is a turn that used to abstain and now speaks 3 days where the gold is 7. That is not a new defect: it is the anchoring defect open since W190 — the plan composes correctly and one of its two anchors lands on the wrong line — made visible by the seat that now lets a span be spoken.

  • A bare number is not an auditable answer (W196). Every typed value the plan organ speaks carries its own evidence — a span names both anchors and their dates, a derived number names both amounts and the lines they came from, a date names its phrase and its stamp — except one. A count spoke the figure alone, because the count primitive kept the length and threw the lines away.

Measured on LongMemEval: asked how many mummies the party will face, this seat answered 12. — nothing named, nothing dated, nothing a reader could check, and the gold is 4. The figure was ours and derived, which is the part this project promises; what was missing was the showing. A number nobody can audit is exactly what the rest of this organ exists to refuse. The count now names the days of the lines it counted, capped at four so a large count states its size rather than reciting itself.

How it was found, and what was wrong on the way — recorded because the method is the point. It surfaced when W195 silenced a one-item count and the plan seat answered in its place. The first reading was that 4 — Mummies. (~ * Mummies (4):) had been a stated tally broken by the census rule, and the attempted fix looked for the number on the verifying LINE rather than in the item's name. The suite refused that at once: "I lead a team of 5 engineers" is a line with a number too, so the widening would have made every numbered line a tally. The route said the rest — chain · refuse · plan, never count. The counting organ was not involved at all, and the defect was in what the PLAN speaks. Two wrong attributions in one afternoon, both caught by reading the route instead of the answer.

  • One item is a lookup, not a census (W195). The counting organ's own rule, which two of its three exits did not keep: the source-counting exit states it (len(matched) >= 2, "one name is a lookup, not a census") and the two list exits spoke whatever survived verification, down to a single name.

Measured on LongMemEval, and it is the failure this project minds most: asked "how many engineers do I lead when I just started my new role", whose gold is "the information provided is not enough" — so the right answer is silence — the engine offered one item, it verified against a real line, and the organ answered 1: 5 engineers. A count that finds one thing has not counted; it has found a line, and a line is what the ordinary chain is for, under a read-back this organ does not run.

The cost is stated: a genuine count of one is unreachable at this seat. "How many cats do I have" over a store holding one cat abstains here and is answered, if at all, as the plain fact it is.

And one exception the suite handed back immediately. W145 exists because the store may SAY the number — "I have 20 playlists in total" — and enumerating the three the graph happens to hold is the error it guards. But a stated tally survives verification as a SINGLE item, so the first cut of this rule silenced the very reading W145 protects. The distinction is structural and reads no word of any language: a lone survivor that CARRIES A NUMBER OF ITS OWN is the store's stated tally and may be spoken; a lone survivor without one is a lookup wearing counting grammar.

What this cost to find, recorded because it is the method: the first hypothesis was that adding an eighth shape had shifted the classifier. It had not — measured on eight questions including the class the earlier control sets were missing (counting grammar with no answer in the store), every boundary held, count included. Reading this question as count is correct. The defect was one level below the reading, in what the organ does with one survivor.

Added

  • How long is its own question, and order does not answer it (W194). Asking HOW LONG between two moments and asking WHICH of them came first are different questions with different answers — a number of days against a name — and only the second was a category in the shape vocabulary.

It cost nothing while the LongMemEval harness declared one shape for every question, because nothing was read. The moment the declaration was refused (W192) and the shape was actually read, "how many days had passed since I finished reading 'The Seven Husbands'" read as order, whose seat accepts only a verdict or a date from the plan — so the days the plan had correctly composed, 18, the gold answer, was refused by the seat and the turn abstained. It had been right in the previous cell. That is the one question the W191-193 cell lost, and it is why the cell is reported with its regression rather than without.

Widening order's seat to take days would have been the patch, and it would have been wrong: a seat for "which came first" that accepts a number of days answers the wrong question whenever it is right. What lacked the word was the vocabulary. So span is a shape with its own seat, and order's definition now says out loud that its answer is a name and never a number of days.

Measured on the readings (12 engine calls): four of the five temporal-reasoning questions read span, the fifth — "I mentioned visiting a museum two months ago. Did I visit one?", a verification and not a duration — correctly reads none, and seven controls hold at order, sum, count, none, when and derive. order matters most there, because span was cut from directly beside it.

  • A difference is composed, not guessed — and never added instead (W193). The plan organ's vocabulary was wholly temporal: every one of its ten primitives read a date off a source stamp or counted lines. Not one read a NUMBER off a line, so a question about two quantities could not be composed at all and the plan answered NONE.

Four of LongMemEval's five multi-session questions ask for exactly that — a difference, a share, or a per-unit amount ("how much MORE than my goal", "what PERCENTAGE", "how much did I spend on EACH mug", "how much will I SAVE") — and count (how many) and sum (how much in total) cover none of them.

The absence was not silence, it was a wrong answer. Having nowhere else to file it, the shape reader put "how much more money did I raise than my initial goal" under sum — measured — and the summing organ, over a store saying 450 and 400, answers 850 (raised: 450 + initial goal: 400) to a question whose answer is 50. An overloaded category does not abstain; it computes the neighbour's arithmetic and states it with a straight face. That is verified in the invariant, not asserted.

So: amount:, minus:, ratio: and per: are primitives, and derive is a shape with its own seat that never meets the summing organ. The arithmetic runs under the law the dates already run under (W103) — on verified values or not at all. amount: does not parse a line; it asks the reader the summing organ asks and keeps a pair only if the digits are written on a line that also carries the thing's words, which is now _amount_home, one rule shared by both organs rather than two copies that drift. The sign is not hidden: a difference answered with a minus sign is a plan that read the question backwards, and making it positive would turn a visible misreading into a confident wrong claim.

Two things this cost, both kept in view:

  • The dated-store guard was no longer the whole story. amount: and its arithmetic never touch a stamp, so a store with no dated source can still execute a plan made of them — an undated spec sheet is where "how much more does A cost than B" lives. The guard now also passes when the store binds tokens to numbers; both readings are free, and a store with neither still buys no call.
  • The derive seat falls through rather than refusing, unlike count and sum beside it. Their hard refusal rests on a measurement — the chain measurably cannot hold those shapes. No such measurement exists for this one, and the first cut refused anyway: it cost W63 immediately, because the local engine reads "what is the Redwood contract worth" as derive and a question the chain answers correctly was refused for a misreading.

Measured on the readings themselves (16 + 11 engine calls, not a benchmark run): all four derive-shaped questions now read derive, the counting one still reads count, and six control questions that must stay sum, count, none and when all do.

Then it was run end to end, nothing pinned — and it got one wrong, which is now a rule. Two of three live questions came back right through the plan route: 50 — charity cycle ride raised (450), my initial goal (400) and 20% — Women hold 6 ... (6), leadership positions (30). The third, "how much did I spend on each coffee mug" over a store saying "The four coffee mugs for my coworkers cost 48 pounds", resolved BOTH steps to the same 48 on the same line — the count is a word and this reader reads digits — and per answered 1. One figure read twice is not two amounts, and a derivation over it is arithmetic on a single fact pretending to be two. The guard tests the figure AND its line together, so a genuine zero ("I raised exactly my goal", two separate lines) is still spoken. That question now abstains.

Two contract fixes came out of the same live run: the plan contract never SAID that A, B operations take the names of earlier steps (the examples always showed it), and a weaker reader wrote out = minus: amount: raised, amount: my goal and out = ratio: a, b with no a or b defined. The interpreter refuses both correctly, so the organ simply never ran.

Fixed

  • One shape vocabulary, read from one place. turn_shape matched its reply against a hand-written tuple of its own — ("material", "count", "order", "sum", "when", "recap") — beside the list in its prompt. The two drifted the moment a category was added: the prompt offered derive, the engine answered derive, and the reader threw the word away and returned none. It took three rounds of measurement to find, because every symptom pointed at the classifier rather than at the reader of its answer. It is W192 one level down — a vocabulary written twice is a vocabulary that will disagree with itself — and the loop now reads SHAPES.

  • A declared shape is a word the reader could have said (W192). ask(shape=...) exists so a batch of one kind does not pay, once per question, for the engine to read a kind the caller already knows (W157). What was never checked is that the caller declared into the right vocabulary. turn_shape answers with one of generate.SHAPES — material, count, sum, order, when, recap, none — and anything else was stored and honoured in silence. The cost was invisible from outside: the turn reported that word as its shape for the rest of its life, every door that asks shape in ("count", "order", "sum") answered no, and the organ behind that door never ran.

We did this to ourselves. Our own LongMemEval harness passed shape="ask" — a turn KIND, out of extract's vocabulary, not a shape at all — for all thirty questions, so every measurement taken through it ran with that declaration standing.

AND IT IS NOT WHY multi-session SCORES 0/5 — that was this author's first attribution and the probe refuted it. With the declaration removed AND turn_shape pinned to "count", the route over those five questions is unchanged and the counting organ still never runs. The reason is one level down: the organ counts RECORDS, and the graph holds 0 of them. The harness learns with deep=False — which is what makes a 50-session ingest affordable at all — so nothing is extracted, and 13,307 sentences of evidence sit beside an empty graph. Retrieval was never the problem either: engine-free, the answer session is in the six seats 5/5 for that type and the gold answer is covered by the block 5/5.

So the real gap that category names is arithmetic organs that only work over the graph, in a configuration that has no graph — and four of those five questions ask for a difference, a ratio or a per-unit amount, which count and sum do not cover even with a graph present. That is the next piece of work, correctly stated. W192 is a genuine defect fixed on its own merits, not the cause of that score.

An out-of-vocabulary declaration now raises at the door, naming the words it should have been, and it is checked before the kept-answer replay — an invalid argument is not made valid by the same question having been asked once already, and checked after the cache the mistake stayed invisible exactly where a batch would meet it.

benchmarks/longmemeval.py takes --shape from the vocabulary now, and warns that one declaration over six kinds of question is wrong for most of them.

  • A long line no longer answers better for being long (W191). The lexical score adds one IDF weight per matched stem group, over the union of that group's sentences — so there is no term frequency here and BM25's saturation half has nothing to saturate. The half that was missing is the one that mattered: nothing charged a line for its size, so a long line touched more of the question for no reason other than being long. Three defects measured separately this cycle turn out to be that one defect in different clothes — a 2008-character window outranking the 556-character line that answers, a rich old line beating the short current one, and an anchor landing on a derived window because the window was long enough to contain the phrase.

The divisor is BM25's, 1 / (1 - b + b * |d| / avgdl), with |d| the line's word count and avgdl the whole store's mean — a property of the corpus, not of the question.

What it buys is not rank, it is the size of the block. The first instrument tried here was the rank of the line carrying the gold string, and it called the change a trade. That instrument is biased toward doing nothing: a long window is likelier to CONTAIN a short gold string, so it pays for burying the answer. Measured engine-free over six seats, on two corpora that share nothing:

LongMemEval (30 q) NIST SP800-63B (11 q)
LENGTH seats chars gold in block seats chars gold in block
0.00 6.00 4972 10/30 5.55 1874 9/11
0.20 6.00 1765 10/30 5.45 1330 9/11
0.35 6.00 1207 10/30 5.36 1239 9/11
0.75 5.97 659 9/30 5.45 1116 9/11

The block does not narrow — the seats stay filled — and what leaves is length, not evidence. Asked "how long did I wait for the decision on my asylum application", the six seats were landlord advice, fishing rods, mattress returns, HR roles, JFK and hologram concerts: 6829 characters with the answer buried inside the first one. After, the top three seats are all the asylum conversation and the third states "Over a year of..." outright.

LENGTH = 0.20 is not the best-scoring value — from 0.20 up they are equal on evidence and differ only in how much text they save. It is the largest value at which all 259 existing invariants still hold. Above it J6 fails, and it fails by being overtaken: the spec line it rescues (Gösterge 21.5" OLED, once 33rd) now reaches the block on its own. That is probably a defect fixed rather than caused, but retiring a measured mechanism is its own change with its own measurement.

Refuted along the way and recorded rather than dropped: applying the divisor to the ranking ONLY, leaving the noise floor on raw scores. It is strictly worse than doing nothing (gold in the six seats 8→8, at the top 5→2). The gain comes from the floor re-reading the normalised score, not despite it.

  • An anchor is a line the document wrote (W190). Measured on LongMemEval, and it was the promise this project sells that was breaking: the memory spoke eleven times on thirty questions and was wrong five, and three of the five came from the plan organ, every one on the route chain · refuse · plan — the chain had already refused, and the rescue seat then answered with a confident, stamped number (212 where the gold is 25, 0 days where it is 7, 14 days where it is 18).

The arithmetic was never the problem; it is ours and verified. The ANCHORING was. Both anchor readings walked every sentence the store holds and took the earliest or latest carrying the phrase — and most of what a store holds it BUILT. Counted on the three failing questions: 10 of 11 candidates were derived windows, then 79 of 91, then 24 of 30. A window spans several sentences (in a chat store, several turns of one session), so it carries the phrase even when the event is in another turn, and it answers with the session's stamp.

That is W174's law one level up: the plan organ writes its sentence from a typed value through our own template, so nothing reads the result back — which is exactly why its anchor may not rest on something this layer assembled.

thirty questions, shape="ask", quoted=True before after
correct 8/30 9/30
spoke 11 10
wrong 5 3
precision when speaking 55% 70%
calls 247 248

Refuted on the way, by the measurement

  • The first cut of W190 was correct and inert. It guarded _anchor_line, which serves a plan's latest: step — and every failing plan uses anchor:, which goes through _event_anchor. The slice came back byte-identical: same 8/30, same five wrong answers, same three numbers. The law now sits on both doors and the invariant pins both, because a law on one door is not a law. (This is W172's lesson repeated: right rule, wrong door, and only the measurement could tell.)

  • One of the three is still wrong, and differently: the workshop span moved from 0 days to 3 days where the gold is 7. The anchor moved but has not landed. Recorded rather than claimed closed.

[0.12.1] — 2026-09-24

Fixed

  • A placeholder is not an item, and a silent drop is counted (W189). The conversation benchmark's last failing cell, and the missing thing was not the capability. Asked how long two courses took altogether — the durations stated in two different sessions — the memory abstained. The router was right (turn_shape → sum), both numbers were in the retrieved lines, and the organ still produced nothing.

The engine was answering ITEM :: 2 days, copying the word out of the instruction "List each as ITEM :: NUMBER". Every pair then died in verification, because the organ looks for a stored line carrying the amount AND the item's own words, and no store says "item". Each drop was silent, so the organ returned None and the turn refused — a capability reported as an absence.

The placeholders are now bracketed, as every other contract in this file writes them, and a pair that names the placeholder is a broken contract: refused and counted (W184's rule, applied where it was missing) rather than dropped in silence. Asked three times, the memory now answers "5 (Foundation training course: 2 + Advanced training course: 3)" — the total with its addends, by the store's own arithmetic.

Refuted on the way, by the suite

  • The first wording put the placeholders on their own line with a fuller explanation. It fixed the hosted engine and broke the local one: the small model returned nothing parseable at all, the sum organ went silent, and a question the chain used to answer refused instead. W63 caught it. The wording that shipped keeps the original sentence's shape and changes only the placeholders — measured on both engines.

A fixture corrected, and said plainly

  • benchmarks/chat wrote two durations as words ("Two solid days"). The summing organ reads only lines carrying a digit, on purpose: an amount lives only if its value is written, digit for digit, on a line the store holds. Reading "two" as 2 would need a numeral list per language, which this project forbids itself. So the corpus now writes the digits and the cell tests composition rather than numeral-reading. Summing written-out numbers is not supported, and will not be.

[0.12.0] — 2026-09-24

0.11.0 gave the conversation surface its first number and the benchmark immediately found a defect; this release fixes it at the root.

The claim is a mechanism, not a percentage, and that is deliberate. The first run after the fix scored 14/15 against 13/15 — and the repeat scored 13/15. One cell is exactly the variance an integrator warned us about, and publishing it as the headline is the mistake their report was about. So the evidence is the cell the change was built to move, asked on its own and repeated: "who does the user report to?" answers the current manager 3 times out of 3 with the vote, and the manager replaced three months earlier 3 times out of 3 without it. Asked individually, all four paraphrase cells are stable; the aggregate's wobble is a different cell and is not this mechanism's.

Document question-answering is byte-for-byte unchanged — the vote does not open on an undated store, and thirteen NIST questions came back with zero verdicts changed.

The defect was one class with three carriers, chased in order over two days. A paraphrased question whose discriminating word the store never writes retrieves on its remaining generic words, and a richer OLD line beats a terser NEW one — true, correctly stamped, past every gate, and out of date. Two of the carriers are fixed below; the third is named, measured and still open.

Fixed

  • On a dated store, WHEN a line was written is a third vote (W188). The defect the conversation benchmark kept finding, fixed at its root. Asked "who does the user report to?" the memory answered Delia Varga — the manager replaced three months earlier — because the March line reads "My manager is Delia Varga. She runs the reporting team" and shares more words with the question than June's terse "Kerem Aksoy is my manager now". True, correctly stamped, past every gate, out of date. Two carriers were fixed or ruled out (W186, W187); underneath them nothing preferred a later line in retrieval at all.

Retrieval already fuses its rankings by RRF, and that function's own reason is the whole design: no weights — a channel votes by ORDER, which is the only thing two different scorings can honestly share. So recency is a third ranking, not a weight, not a threshold, and not a new constant: the same lines, newest first, handed to the same fusion. It is cast whether or not the meaning channel is attached.

It does not open on a document. The ranking is built only where every retrieved line carries a day, by stamp_day's rule — the one W186, the counting organ and the telling seat already keep. NIST SP 800-63B has 12,253 sentences and not one dated source.

without the vote with it
"who does the user report to?", asked alone, ×3 the replaced manager, 3/3 the current one, 3/3
conversation benchmark, independent questions 13/15 13–14/15 (one cell of wobble, not this one)
NIST SP 800-63B, 13 questions 10/13 · 42 calls identical, zero verdicts changed

What it does not reach, and why the conversational arm does not move: a vote can only reorder what was retrieved. In one running session the consultation's accumulated terms ride the query and the current line is not retrieved at all — the brief carrier, measured engine-free and still open. standalone=True clears it.

A limit stated before it was measured, and still unmeasured: a speaker who repeats themselves will have the terse latest repetition preferred over a richer earlier telling. No question in this benchmark has that shape. m.session.evidence.RECENT = False restores 0.11.0.

  • On a tie the ride ADDS, it does not replace (W187). A follow-up whose own words seat junk borrows the previous turn's subject, and the ridden proof was to stand "only if it covers the question at least as well as the turn's own". Measured, that comparison never fires: coverage(question, block) came back identical for both queries on every case tried — three pointer questions and two complete ones — because a handful of lines carries a question's common words either way. Every tie kept the ride, so the ride always won and the guard was decoration.

Flipping the tie to the turn's own query was the obvious repair and is refused, measured: on an eight-document corpus a pointer question's own block carries none of the anchor's lines while the ridden one carries them, and the tie is exactly what rescues it. The ride is load-bearing.

So on a tie neither wins: the two blocks are interleaved best-first and capped at the same seats, with the stamps woven by the same walk so no seat is attested by another line's source. A strict winner still takes the whole block, unchanged. NIST: 42 calls, verdicts identical.

Measured, and open

  • The brief still carries it in one running session. A vote can only reorder what was retrieved, and when the consultation's accumulated terms ride the query the current line is not retrieved at all — measured engine-free: with the brief appended it disappears from the block entirely. The brief exists for a consultation whose turns build on each other, and a batch of independent questions is not that; standalone=True clears it, and is what a matrix of cells should use.

  • Composition across sessions. The one remaining benchmark failure: asked how long two courses took altogether, where the two durations are stated in two different sessions, the memory abstains rather than guessing a sum. Honest, and still a gap.

[0.11.0] — 2026-09-24

The half this project's name promises had no current number: every published measurement was document question-answering, and the last conversational figure was twelve releases old. It has one now, it found a defect on its first run, and it found one in the benchmark harness too.

Added

  • The conversation surface has a number at last (benchmarks/chat). Every published measurement of this project is document question-answering; the half its name promises had no current figure — the last one was 18/30, twelve releases ago. A synthetic four-session conversation and fifteen labelled questions now exercise the classes recorded as open: a fact the speaker supersedes, a quantity composed across sessions, an ordering, a preference asked about in other words, and questions whose discriminating word the conversation never writes. It is small and invented and is not a substitute for LongMemEval.

13/15, identical across two independent runs, zero flips: facts 6/6, traps 3/3 (no fabrication), paraphrase 3/4, composition 1/2. The two failures are honest — one abstains rather than guessing a sum, the other is W186's class, below.

  • How many lines the block seats is a number, and it was measured (W185). The answering call is the largest single consumer of prompt in a turn, and the size of what it reads had never been asked about: the block seats evidence.WINDOW lines because that is the scale the STORE is built at — a reason for the store, not a reason for a block.

Measured engine-free on NIST SP 800-63B with this repository's labelled question set, asking at which seat the line carrying the gold answer arrives: seat 1 for nine of eleven questions, seat 2 for the tenth, never for the two paraphrases the memory abstains on anyway. Seats three through six change the miss count not at all and are two thirds of the block's characters.

Measured again with the engine, two arms over the same thirteen questions — six seats against three: zero verdicts changed, same route, same abstention, same sentence, question for question, at 42 → 40 calls and 112,010 → 92,024 prompt characters, −18%.

The default does not move on that, and the reason is what one corpus cannot show. A standard asks literal questions of literal lines. Two risks live outside it: an answer whose attribute sits on one line and whose value on another (W166's own case) may need both seats, and the gates read this block as their WIDE view, so narrowing it leaves a claim the focused view rejected less to appeal to. A narrowing argument has misled this project before — 0.7.8's "narrowest view" was refuted for selecting headings.

m.session.SEATS = 3 is the instrument. A corpus of paraphrased questions over documents that split an attribute from its value is exactly the case that would settle it.

Fixed

  • A wholly dated block is read newest first (W186) — the first defect the conversation benchmark found. Asked "where does the user live now?" the memory answers Istanbul; asked the same fact in other words — "which city is the user based in?" — it answered "the user is based in Ankara", stamped with the session before the move. True, sourced, past every gate, out of date. W181 fixed this where the graph holds it; the evidence path has no arbitration, only a ranked block, and nothing in the block said which line was later.

When EVERY line comes from a source carrying a day (stamp_day's rule, the one the counting organ and the telling seat already keep) the block is ordered newest first; one undated line and the order is retrieval's, untouched, which is every document corpus here. Lines and stamps move together or not at all, so an answer cannot be stamped with another line's source.

  • A run in the same process is not a run (conversation_eval). The multi-run harness exists because byte-identical code gave opposite verdicts forty minutes apart, and it answers with a median plus a FLIP count. Measured on its own output: with --runs 3 the second and third runs took zero seconds and agreed with the first on everything, because the engine pools its deterministic calls for the life of the process. Three runs in one process are one run reported three times. That is this repository's own documented measurement trap, walked into by the tool written to avoid it. Each run now gets a fresh interpreter.

Measured, and open

  • WIDEN off was the right default for conversation too. Yesterday's change was argued from three document corpora, and conversation is where paraphrase actually lives — so it was the one default at risk. Measured on the new benchmark, both arms: 13/15 either way, the same two failures, and the widening costs five more calls to rescue nothing. A fourth corpus agreeing.

  • A follow-up inherits the previous turn's subject even when its own words name a whole question. Asked alone, "which city is the user based in?" answers Istanbul; asked after "how many days did the Foundation training course run?" it answers Ankara, because the anchor label from that turn rides the query and the March session is where the Foundation course and Ankara were written together. Asked after a turn whose subject was the generic "user", it answers Istanbul again — so the carrier is the ANCHOR, isolated in three probes.

An earlier note in this file attributed it to scope inheritance (W109). That was wrong and is corrected here: on the ask() door the conversation's scope is not even computed — _scope_now is empty unless the turn is conversational — and what travels is _prior_subject. Diagnosed and not fixed: the anchor exists for a measured reason (a follow-up that says "the first one" names nothing a search can hold), and telling a question that already names its subject from one that does not is the whole of the work.

[0.10.0] — 2026-09-23

A day of measuring instead of reasoning. Every change below was found by attributing a turn's engine calls to the organ that made them, and two of them are the FIRST measurements of mechanisms that had been justified by argument alone. On the thirteen-question NIST slice the same run went 57 → 42 calls (−26%) with the verdicts identical question for question, and the turns that end in "I don't know" — which were costing 2.6× an answered turn — went 41 → 26.

Fixed

  • A speaker who corrects himself is not silenced by his old self (W181). A DOCUMENT that contradicts itself is a contradiction and this system surfaces it rather than picking a side (W54, and that is right). A PERSON who says "I have moved" is not contradicting himself — he is superseding his own earlier fact, and the two were treated the same.

Measured end to end, engine-free: taught "I live in Ankara" and then "I moved to Istanbul", the graph keeps both and links them as rivals — and arbitration left the stale one at 0.75, above the speaking threshold, while the correction was lowered to 0.20, below it. The memory went on asserting exactly what the speaker had just retracted.

The cause was an omission: dynamics.arbitrate ranked by (-level, -witnesses, -trust, key) and time was not a criterion, so two claims from one speaker at one level with one witness each fell to the record key — insertion order, and whoever spoke first won permanently. at now ranks below witnesses and above trust: it is a property of the evidence, written once and touched neither by strengthen nor by arbitration, so the function keeps the order-independence its own docstring owes. A thirty-witness claim is not unseated by a later one-witness one — asserted, not assumed.

One behaviour changes beyond the defect: between two rival values from one document, the later-written line now wins the slot where the earlier one used to. Both were arbitrary, nothing pinned the old one, and the evidence path surfaces the disagreement either way.

Changed

  • The widening is off by default, because it was finally measured (W178). A turn that abstained used to buy one more retrieval with words the engine proposes and the store approves. The justification written into the code was an ARGUMENT — lexical retrieval's price is paraphrase — and these are the first measurements of it, over three corpora and thirteen turns that refused:
corpus turns it ran on calls rescued
Turkish legal guide, 15 questions 4 69 → 61 (−17% prompt) 1, and it was the slice's known off-subject answer
NIST SP 800-63B, 13 questions, labelled paraphrase class 5 57 → 52 0, verdicts identical question for question
an integrator's 44-cell catalogue, "almost entirely paraphrase" 6 38.8% of the run 0

The second corpus is the test that matters: its question set labels a paraphrase class, which is the very thing this organ exists for, and the widening rescued none of it. The obvious explanation was tested and refused — with the meaning channel switched off, the channel that arrived after this organ and might have taken its job, it still rescued none. m.session.WIDEN = True restores it exactly.

  • The relation check's disciplines travel with the shape too (W180). W165 gave the claim reading and the answer writer prompts that carry only the disciplines this evidence exercises; the relation check was left whole at 2,353 characters on every judged turn. An integrator's client-side trace is what made that matter: on an answered turn the gates are 54% of the tokens, so the check costs more than the answer it is checking.

Its three examples are each about a SHAPE — a record row, a terse notation with a unit, a value joined across two items — so a block with none of them pays for none of them. Measured over the thirteen NIST questions against their real retrieved blocks: 30,589 → 25,634 characters of relation prompt, −16%; a plain prose block drops from 2,353 to 1,528. With no evidence in hand the whole prompt travels, and an uncertain reading INCLUDES the clause, because a missing discipline turns a "no" into a "yes".

  • A standalone question does not pay to be classified (W182). The extractor was 10–12% of every run on two corpora, and on the question door it has nothing left to decide: ask() cannot write memory and W164 settled that a statement handed to that door is the question, so the kind is ASK by construction. What it still earns its call for is the two CONVERSATION features that read its triples — the composer bridge's brief, and whether a message is the bare "yes" answering a research offer — and standalone=True ends the conversation on both sides of the turn, so both are dead. Narrow on purpose: a non-standalone ask() still classifies. Measured: 52 → 47 calls.

  • An undated store does not pay for a plan it cannot execute (W183). The plan seat is a rescue and, unlike the widening, must NOT be switched off — it is the only path to a dated answer. What it does not need is to be asked where it cannot execute: every date-taking primitive anchors by reading the day out of the line's SOURCE STAMP, so in a store where no source carries one they all die on the first step. Measured on NIST: 12,253 sentences, none dated, and the call was bought on all five abstaining turns. 47 → 42 calls.

What the precondition costs, stated rather than hidden: a plan of now: alone anchors nothing, so on a store with no dated source at all this seat can no longer answer "what is today's date".

Added

  • A reply that broke its contract is counted, not only survived (W184). 0.9.1 shipped a defect whose own release note called it "silent by design": the fused judgment capped its reply at eight tokens, two labelled lines did not fit, every fused call was refused as malformed, and the turn paid three calls where it used to pay two. Nothing was wrong with the answers — the only symptom was the bill, and it took a fifteen-question measurement to find it.

Refusing a reply that does not fit its contract stays right. What was missing is that the refusal left no trace. runtime.MALFORMED is runtime.CALLS' twin, with runtime.BROKE holding what the last one looked like. A DECLINE is not a broken contract: an engine answering NONE because no line carries the answer (W160) has obeyed perfectly and is not counted. Measured on NIST: 0, on 42 calls.

Considered and not done, with the reason

  • Structured output — a JSON schema on the backend — is not being added. It is the half of the typed-decision idea our own engine could take for free, and it would make a shape error impossible rather than merely refused. But with W184 in place the rate of the failure it prevents is measured at zero, and the change is not free: it is backend-specific, and constraining decoding can move a judge's verdicts, which would have to be measured against every gate. A fix for a problem measuring zero, priced in a risk to the gates, is not a trade worth making. If MALFORMED climbs it earns its place then, which is what the counter is for.

Measured, and answering a question that was put to us

  • "Is there anything downstream of two refusals that can still succeed?" — asked by an integrator who had traced an abstaining turn running a shape reader, a second answer, a fact checker, three language calls, the widening's words and the plan organ, all after the quoted reading and the relation judge had both declined.

Measured on NIST SP 800-63B: five turns saw a refusal, none was rescued by anything downstream, and those five took 41 of the run's 57 calls — 72%. The same shape held with the meaning channel off. On the Turkish slice the one rescue was a wrong answer. Two of the four changes above came out of that question.

[0.9.3] — 2026-09-23

Added

  • A word the store has never seen is a fact about the store (W179). An integrator grading a coverage matrix asked the right question: is there a point at which "the store holds nothing about this subject" is already knowable, before the rescues run? There is, and it costs nothing — the inverted index either carries a word or it does not.

Answer.unseen (and m.session.unseen_demands(question)) reports the question's own content words the index has never held, in any sentence of any document. missing is about the ANSWER — what it failed to carry. This is about the STORE.

Measured on NIST SP 800-63B with this repository's labelled question set, by each question's rarest demand:

question class unseen
yokluk — the document cannot answer 2 of 2
dogrudan — it answers plainly 0 of 8
esanlam — a paraphrase of what it answers 1 of 3
six further unanswerable questions, written for this test 5 of 6

The one false positive is why this is reported and never acted on. Asked who heads NIST, the document says "acting director" and heads appears nowhere — which is exactly the class the widening exists to rescue. A reading that abstained on it would trade a paraphrase for a saving. So the turn behaves exactly as it did, and what the caller gains is the distinction this library declines to draw on their behalf: "the document never mentions a fee" is not the same verdict as "we did not find it".

Engine-free, deterministic, and no word of any language in it.

[0.9.2] — 2026-09-23

Measured: where the calls actually go

The first per-organ attribution of a turn. Every engine call is charged to the organ that made it, by finding the nearest lmm/generate.py frame on the stack — and, this time, counting only the round trips the deterministic pool did not absorb. That distinction is this repository's own documented measurement trap and it was walked into again: every call figure published today before this one counts pooled hits as calls and overstates the bill by about 8%. The corrected total for the fifteen-question slice is 69 calls, not 75.

turns calls per turn
answered 12 42 3.5
abstained 3 27 9.0

A fifth of the turns spend two fifths of the budget, and they are the ones that end up saying "I don't know". By organ: the quoted reading 15 calls, the answering call 11 (and 77k of the 235k prompt characters, the largest single consumer), the relation gate 9, the extractor 8, the language naming 8, the fused judgment 7, the spoken refusal 4, the widening's word proposal 4, the plan organ 3.

A quoted turn costs exactly two calls — the reading and its relation gate — which is what 0.9.0 said it would and had not been measured.

Added

  • The widening is a number, because it is a wager (W178). A turn that abstained buys one more retrieval with words the engine proposes and the store approves, then answers again. On this slice it ran on four turns and cost eight calls of sixty-nine — 69 → 61, and 17% of the prompt — and rescued exactly one turn, whose answer was the slice's known off-subject one (asked what capital market instruments do, the rescued sentence is about institutions; with the widening off, it abstains).

The default does not move on that. One slice, one wobbling question, and the widening was measured onto a corpus where a question and a document use different words for one thing — which this document is not. m.session.WIDEN = False is for a corpus that has measured its own answer, beside CANDIDATES, VIEWS and QUOTED_RELATION.

[0.9.1] — 2026-09-22

Changed

  • Two questions about one view are one engine call (W177). Borrowed from the typed-decision model class and implemented with our own engine rather than theirs: a state travels once, and every question about it rides along. The two gates were sending the SAME evidence separately — run concurrently, which hid the latency and paid for the tokens twice.

Everything the structure settles still settles first, for nothing (_read_back_plan), so the fusion only ever replaces two remaining questions with one. And it happens only where both organs would read the same view: the read-back narrows to the lines a claim NAMES and the relation check does not, so fusing there would judge one of them on evidence it was never meant to see. The reply is typed by contract — two lines, fixed order — and a reply that is not that shape is refused rather than guessed at, the caller asking the two questions separately instead.

Measured on the fifteen-question slice: 79 calls against 86, prompt 239k against 247k, with gold 6/6, unsourced assertions 0 and the same abstentions. It recovers a quarter of what W175 cost.

Measured, and worth saying plainly

  • The borrowed economics did not transfer; the borrowed shape did. For a model priced so that the state is expensive and extra questions are nearly free, fusing is a large win. Here the SYSTEM PROMPT dominates, not the state: two full disciplines travelling together come to 5,574 characters against 5,925 for the same two questions asked separately — 6%, not half. What was actually saved is a round trip per judged turn, not the tokens.

Refuted on the way, and recorded

  • The first cut capped the fused reply at 8 tokens, the cap a one-word verdict uses. Two labelled lines do not fit in one line's budget: the live engine returned SUPPORTED: yes \nANSWERS: with the second verdict truncated away, every fused call was refused as malformed, and the turn paid three calls where it used to pay two — measured at 90 calls, worse than the 86 it was meant to improve. Recorded because the failure is silent by design: a malformed judgment is refused, not guessed at, so the only symptom is a bill.

[0.9.0] — 2026-09-22

Fixed

  • Quoting replaces the read-back; it does not replace the relation (W175). The quoted reading dropped BOTH gates and only ever replaced one. Whether the evidence says this, the store answers for nothing — every word is checked against lines it offered. Whether the claim answers what was asked is a different question, and nothing about quoting a line makes it relevant. That judge was dropped with no replacement, which is why the path could speak a single, whole, true sentence about the wrong subject: reported from the field as the failure remaining after 0.8.1's W172, and verified here — such a sentence passes the region reading (one sentence is one region) and containment at 1.0, and was spoken.

The judge the ordinary path runs now runs here too, over the lines the answer rests on, and only on turns about to commit.

What it cost, and what it bought. On the fifteen-question slice the quoted reading carries 7 turns where it carried 9, and the turn costs 86 calls against 58 — most of that not the judge but the full ordinary cascade the two refused turns then pay. Against the ordinary path's 84 that is parity, so on this corpus the quoted reading's call advantage was substantially the cost of being less careful. What the 28 calls bought: a genuinely wrong answer became an honest abstention (asked what capital market instruments do, the memory had been answering about institutions — the reported class, on a real question), and a second answer that had named one of several items now names them. Gold 6/6 and unsourced assertions 0 both hold.

An integrator measuring 44 cells on another corpus and another engine found the cheaper trade profitable — four empty answers became one wrong one, which suited them. It stays theirs to make: m.session.QUOTED_RELATION = False restores 0.8.1 exactly, beside CANDIDATES and VIEWS.

Added

  • A judgment is a seam (W176). Both gates ask one shape of question — does the evidence say this (says), does this answer what was asked (answers) — and both pay a frontier engine to write "yes" in prose that is then parsed. A model class now exists that answers exactly this shape as a typed decision with a calibrated probability, for a fraction of the latency and the price.

This adopts none of them. What is added is the seam one could sit behind, beside encoder= and reranker=, which are the slots this library already parameterises:

Memory(judge=lambda kind, question, answer, evidence: 0.93)

Pass nothing and the engine is asked exactly as every measurement in this repository was made. Three properties make it safe to hand to a stranger. A judge returning None has declined and the engine is asked — a provider that cannot answer must not be able to refuse an answer silently. One that raises is the same: an outage is not a verdict. And the boundary the probability is read at is this memory's (Session.JUDGE_BOUNDARY, the majority), so a supplier cannot move a gate by changing what it calls confident.

Where such a judge belongs is not "wherever there is a call". Its declared failure mode is confident wrongness inside its schema, which on a gate is a fabrication that passed the audit. It belongs where a miss costs recall rather than the promise — and above all where this memory has no judge at all today, because there it can only add.

[0.8.1] — 2026-09-22

Three findings from an integrator's 0.8.0 report, each verified before it was acted on. Two of the three diagnoses turned out to be wrong — including one of ours — and the verification is written down here because it is the part that changed what got built.

Fixed

  • A quotation may span lines, but not two passages (W172/W174). The reported failure: asked how internal control checks are performed, the quoted reading answered with a clause from a TESTING paragraph joined to a clause from a REQUIREMENTS paragraph — one fluent sentence about neither. Every word was in the document, the stamp was right, the sentence was true, and the document had never put those two clauses together.

Reproduced byte for byte, and it is engine-bound: on gpt-4o-mini the same reproduction abstains 4 times out of 4; on gpt-4.1 it commits 4 out of 4. On the same engine the ordinary path abstains, so the asymmetry really is the quoted reading's, as reported.

Two diagnoses were refused on measurement. The reporter's — that containment weakens as lines grow, since a long line supplies enough vocabulary for any on-register sentence — does not apply here: coverage(answer, the named lines, question) is 1.0, because the answer is a verbatim splice and containment is therefore satisfied by construction at any line length. Ours — that the splice crossed two named lines — shipped a guard that never fired: three of the six retrieved candidates carried both clauses inside ONE line, because that line was a window this layer had built. A window is neighbouring sentences, so where two topics meet it holds the end of one and the start of the next, and an answer can take a clause from each without ever leaving its quote.

So the quote is re-split into the sentences the document wrote, those are folded into regions by the same word overlap the rest of the evidence layer uses, and one region must carry the whole answer. W166 keeps its permission wherever the lines are views of one passage; what is refused is the JOIN of two passages — which is table_windows' own law across two rows, one level up. No model call is added.

  • Text this layer assembled is marked as assembled (W174, the premise underneath). table_windows' fallback gatherings and the masthead were going into the store without the derived mark, so every organ that stands on "did the document write this, or did we" — the fusion region cap, _window_texts, and now the quoted reading — was reasoning from a false premise. A row stays the document's own; an assembly says it is one. evidence.table_windows now returns (text, assembled) pairs.

  • A turn that abstained carried nothing, and says so (W173). A refusal came back covered=1.0 with missing=() — full coverage for an answer that answered nothing. It happens when the refusal restates the question: every demand is then present in the refusal's own words. Measured on the reported sentence: 1.00 restating, 0.00 not restating, 0.25 for a correct verbatim answer out of the document, 0.88 for a fluent restatement. The count was rewarding saying the question back, which W152 established is not a claim at all. The count stays a count; the session's structural stamp now decides whether there is anything to count.

Measured, including the price

On the fifteen-question document slice, 0.8.0 → 0.8.1:

0.8.0 0.8.1
gold (six checkable) 6/6 6/6
unsourced assertions 0 0
turns the quoted reading carried 11/15 9/15
engine calls 50 58

The two turns the quoted reading gives up are still answered correctly by the ordinary path, so the price of closing this class is calls, not accuracy. Stated plainly because the trade is real: a caller whose cost of a wrong "yes" is low may prefer the old behaviour.

Refuted on the way, and removed

  • A blunter first cut — let the quoted reading name only lines the document itself wrote, never a window — closes the same failure and was measured at 1 of 15 carried and 96 calls, against 9 and 58 for the rule that shipped. It was taken back out.
  • Our own cross-line region guard, written before the mechanism was understood, was measured redundant once the region reading moved inside the quote, and deleted rather than left beside the rule that replaced it.

[0.8.0] — 2026-09-22

Four architectural readings, all of them structural: what a table's last column means, what a delimited row is, and twice over what it takes for a sentence to count as an answer. Nothing here knows a word of any language.

Added

  • A delimited row is a row, and its cells keep their column (W169). table_windows exists because a sliding window over a shattered table's cells puts side by side what the document never put in one row — "the camera's regulatory risk is medium", past every gate because the claim really is in the evidence. A table written with DELIMITERS was falling into that exact failure: its row is whole, on one line, so the shattered reader found no period in the cell lengths, answered "no row structure", and the rows went in mixed together. On the commonest table shape an ingested document has.

A run of consecutive lines cut into the same number of cells by | or a tab is a table. Where a rule — a line with neither letter nor digit, which is all any markdown or ASCII separator is — follows the first row, that row NAMES the columns, and every cell is stored under its own name, so a detached value still says which attribute it is the value of. A column name is not a number: a first row of bare numbers is data, and the table goes in unnamed. An empty header cell is that column's silence, not a disqualification — measured on a live table whose description column carries no heading.

Measured: 89 records recovered from this repository's own README alone, each under its column names, where before there were mixed windows.

Fixed

  • An index column points at a place, and a place is not a value (W168). Asked for a regulation's number, the memory answered 140. The document's number for it is III-52.1; 140 is the page its section starts on. Both sit in the same row of the table of contents, so every gate passed — the claim really was in the evidence. What failed was not the audit but what the row was taken to SAY: a contents row states where a thing is written, not what it is. (0.7.15 recorded these two as "correctly refused"; they are now correctly answered.)

The reading is typographic: a run of rows whose last cell is a bare number, the numbers never decreasing and rising across at least three rows, with no column NAME above them. The name test is what separates an index from an ascending price list — a named column is a field and keeps its values however it is sorted. The strip happens once, on the document's own lines, before anything is split or windowed, so the pointer never enters the store and every organ downstream inherits the correction for free. The row keeps its words: the heading that WAS the answer stays readable.

Measured across 25 real documents: 13 lines changed, all of them the one contents table.

  • A sentence that carries no evidence is not an answer (W170). Reported from the field twice — a refusal stamped abstained=False with no sources, and a refusal in the wrong language spoken by the answer path — and reproduced here with no engine at all: four questions, one of them plainly answered by the document, all four spoken as the engine's own "I don't have that information".

The gates were not loose. _select grades a candidate that carries none of the evidence's words at zero and declines to speak it. The fallback beneath resurrected it: its spare list excluded only what the RELATION gate had refused, so a candidate the grounding score had eliminated came back through the back door, and verify had nothing to contradict it with because a shrug asserts nothing. That is the blind spot every audit of this kind shares: a gate that asks whether what a sentence says is in the evidence cannot fail a sentence that says nothing. The other reading was already on the session — _asserted_a_fact, a word intersection with the proof, no model call — and it now guards what the fallback revives and what it generates.

  • A rescue pass that refused did not produce an answer (W171). The same class one floor up, found by the live slice: two turns of fifteen came back as a refusal sentence stamped an assertion, on the route chain · refuse · refuse · widened. The first pass abstained, the widened rescue ran, it abstained too — and the rescue then handed its own refusal SENTENCE to the word reading and was told it was a claim.

Every refusal in this codebase leaves by one door precisely so that abstention is a fact on the object rather than a property of a sentence's words. last_abstained cannot say it here, because the rescue only runs on a turn that has already abstained and the flag is True on both sides of the call — so the door counts, and the structural stamp outranks the textual guess.

Measured

On the fifteen-question document slice, before → after:

0.7.15 0.8.0
gold (six checkable) 3/6 6/6
unsourced assertions 2 0
honest abstentions 1 of 3 3 of 3
turns the quoted reading carried 10/15 11/15
engine calls 50 50
prompt characters 183,316 129,681
seconds 82 68

The three regulation-number questions answer correctly for the first time (II-17.1, II-15.1, III-52.1). The three questions the document does not answer — verified against the file, which never writes a fee, a duration or the chair's name — all abstain and all say so. 240/240.

[0.7.15] — 2026-09-22

Fixed

  • An inflection is not a claim, and its host says so (W167). Three of fifteen questions were refused by the quoted reading for saying something "beyond the quote", and the words they were refused for were dir, tir and nin — suffixes an apostrophe had cut loose into words of their own. A document writing (II-17.1) and an answer writing II-17.1'dir say the same thing; the reading saw a word the evidence lacked. Our own splitter made the defect.

An apostrophe inside a token is TYPOGRAPHY, which this layer already reads structurally (the camel seam, the digit-letter seam). What it must not do is keep a list of suffixes — dir is grammar in one language and a word in another. The host answers it: a fragment is an inflection when the token it hangs on is itself carried by the evidence or the question, because inflecting a word the evidence already holds says nothing new. A fragment whose host is not carried stays a word and must be supported like any other, and a number the evidence does not carry is still refused — measured both ways.

The quoted reading now carries 10 of 15 turns, up from 9. The other two refusals in that family stay refused, and correctly: the engine had answered with a table of contents PAGE number.

Refuted on the way, and removed

  • A first version learned the rule from the CORPUS — a fragment that never stands alone anywhere in the store is an inflection — which is sound, tested, and measured firing almost never: the suffix that refuses these answers lives in the ANSWER, and the document never wrote that form. It was built, measured, and taken back out rather than left in the codebase beside the rule that replaced it.

[0.7.14] — 2026-09-22

Changed

  • An answer may rest on more than one line, and on no more than it names (W166). The quoted reading carried eight turns of fifteen, and the diagnosis said why it did not carry the rest: three questions the store genuinely cannot answer, two where the engine named the wrong line — and the documented cost, an answer spread over several lines getting only the one it quoted.

Both are one limit, and it contradicted this system's own rule: the fact checker's prompt has said for as long as it has existed that "COMBINING EVIDENCE IS ALLOWED — the attribute and its value may sit in DIFFERENT evidence items". The engine may now name up to three lines and the answer is checked against THOSE lines and nothing else, so a word from a line it did not name is still a refusal. What changed is that the frame may be two lines wide when the evidence is.

Measured

Fifteen questions, quoted mode, same harness and corpus — W165's conditional prompts and W166 together:

before after
calls 69 (4.6/turn) 47 (3.1/turn)
prompt characters 269,615 182,122
seconds 97 68
carried by the quoted reading 8/15 9/15
abstentions 4 3

−32% on all three, and the three abstentions left are exactly the three questions the document does not answer. No wrong claim appeared and no answer was lost.

Where that leaves the comparison: the day opened at 10.7× a naive RAG's call count on the field integration's corpus. A quoted turn now costs 3.1 calls on average and one when the reading carries it. The gap that remains is the turns it does not carry, and it is no longer the gap it was.

Still true

  • Asked which exams a study guide serves, the quoted reading still names one of four. Three lines were not enough for that cell, and it stays on the record rather than being rounded away.

  • Output length is NOT a cost: measured, this system's answers average 110 characters — about thirty tokens against four thousand of prompt. The instinct to shorten them was checked and dropped.

[0.7.13] — 2026-09-22

Changed

  • A discipline travels with the shape that needs it (W165). The cost of a turn is mostly OUR OWN INSTRUCTIONS, not the evidence — measured against the engine, the fact checker's prompt alone is 1,256 prompt tokens, and the read-back call the field trace put at 1,450 tokens is therefore ~85% instruction. Per turn the four system prompts came to roughly 4,100 tokens of standing text.

Most of that text is conditional. A record-row discipline, a tally discipline, a comparison verdict, an extremes row, a dateline, spec notation — each was added because a measured failure demanded it, and each is about a SHAPE the evidence may or may not carry. On five real blocks from a 355-page legal guide, four of the six conditional disciplines were needed zero times and two fewer than half the time.

So the prompts are assembled per turn from the clauses their own evidence exercises. Nothing is cut and nothing is softened: a shape that appears in the block brings its discipline with it, the reading is structural (the store writes these shapes and can see them without a model), and where a reading is uncertain the clause is INCLUDED — a missing discipline turns a "no" into a "yes", which is the dangerous direction. A caller that hands in no evidence still gets the whole prompt, byte for byte as before.

Measured on fifteen questions, same harness, same corpus: prompt characters 372k → 302k (−19%), calls 95 → 84, seconds 118 → 101, and no answer lost. (One question went from abstention to the correct answer, but that cell flips run to run on this corpus and is not claimed.)

Prompt caching does not make this moot: Azure returned cached_tokens 0, 1152, 0 across three identical system prompts — the discount is real and unreliable.

Measured, not shipped

  • The narrowest view of a region. Seats go to the widest windows, because a longer text intersects more query words — a bias BM25 answers with length normalisation and this codebase does not have. Replacing each seated window with the narrowest NESTED window that still carries the matched words was measured at −14% block characters with the answer-bearing line intact in 6 of 6 gold questions. A first, looser version of the same idea — the shortest line anywhere carrying those words — measured −69% and was refuted the moment it was checked: it selects HEADINGS, which carry every query word and no answer ("6362 sayılı SPKn md. 2/2…" became "SERMAYE PİYASASI KANUNU"). The safe version is recorded here with its number rather than shipped on one corpus's six questions.

[0.7.12] — 2026-09-22

Three changes, measured together and released together.

Added

  • A quoted turn does not pay the router it never uses (W163). The quoted reading needs retrieval and nothing else; the router — the call that reads a message for its kind and subject — exists to choose between the paths that come AFTER it, and a turn that has already chosen the quoted path uses none of them. It now runs above the router and below everything free: the graph's own lookup and the record door still answer first, because they cost nothing and answer better.

When the quoted reading carries a turn, that turn is ONE engine call — a RAG's call count, with a stamp on the answer and the store checking it for nothing.

Measured over fifteen questions: it carried eight, and the seven that fell back paid for the attempt too, so the average was 4.6 calls against the ordinary path's 6.3 — −27% calls, −28% tokens, −18% wall clock — and one question came back answered that the ordinary path had abstained on. Parity per carried turn, not on the average, and the remaining gap is entirely the turns the reading does not carry.

  • On the question door, a statement is the question (W164). An integration asks its cells in the catalogue's OWN wording, because a requirement's own sentence retrieves better than a paraphrase (15 of 20 against 11, 17 with both). Handed to ask(), that sentence is classified WRITE — correctly — and went to the chat voice, which answers a question nobody asked and pays for it. That rule was measured on respond(teach=False), the surface that carries the history and the persona, and it keeps it; ask() is the question door, cannot write memory, and a declarative handed to it names a subject.

Fixed

  • The meaning channel's fusion obeys the region cap too (W162) — see 0.7.11 for the measurement and for what it is not.

Not done, with the reason

  • Running the two verification calls side by side was NOT tried again. This codebase measured it once (W113): paying both always was "the biggest line on a refusal — ten calls and twenty thousand prompt tokens", because the first view is usually enough. Repeating a refuted experiment is not diligence.

[0.7.11] — 2026-09-22

Fixed

  • The meaning channel's fusion obeys the region cap too (W162). find has always capped a region at two seats — one states, a second corroborates, a third crowds out another voice — and then the channel's proposal is fused onto that list by rank and the result taken whole, so a region that wins on rank took as many seats as it liked. Measured on a 355-page study guide: asked a regulation's number, FOUR of six seats went to overlapping windows of the same table of contents, carrying page numbers that look like values, while the line holding the answer waited outside. With the channel switched off, the same question seats that region twice.

The same rule, the same organ (_same_region), applied where the fusion ends — and narrowed twice by what the tests and the measurements said:

  • only the windows this layer DERIVED are folded. Three catalogue lines reading "PROGRAMME: leadership track" are three programmes and one word count; capping them cost a count its third item and a named document its block.
  • only what the CHANNEL added is judged. The words' own list has already been through this file's seating — its region cap, its source caps, its dedicated seats for a named document — and re-judging it undoes decisions made with more knowledge.

A first cut also let the surplus step back in when the block would otherwise go unfilled — the rule belonging to the SOURCE cap — and was measured refilling the block with exactly the repetitions it had just removed.

What this is NOT

  • It is not the cost answer, and the hypothesis that it would be is refuted here rather than quietly dropped. On one question the block fell from 5,086 characters to 3,580; across six questions with known answers it fell from 28,531 to 28,444 — 0.3% — with the answer-bearing line present in the block in 6 of 6 either way. An engine-side A/B over fifteen questions came back slightly worse (98 calls against 90 and 84 in two earlier runs), which the engine-free comparison says cannot be attributed to this change: the blocks it reads are all but identical. Run-to-run variance on that corpus moves two cells, and two cells moved.

So this ships as what it is — two paths brought under one rule, a pathological case closed, no answer lost — and not as a saving.

[0.7.10] — 2026-09-22

Added

  • ask(..., quoted="only") — the store's refusal may stand (W161), and Session.quoted_refused says which failure happened (W160).

Diagnosing the quoted reading over fifteen questions turned its own scoreboard around. It carried nine; the engine DECLINED four, three of them correctly (the store holds nothing on those subjects); and the store REFUSED two. Those two were not misses — they were saves. Asked a regulation's number, the engine had answered with the PAGE number from a table of contents ("96", "140"), and the check caught both, because the digit appeared nowhere in the line the engine said it had used. The ordinary path then spoke one of them anyway.

So a decline and a refusal are different facts and the turn now keeps them apart. With quoted="only" a refusal stands; a decline still falls back, having offered nothing to refuse.

Measured, gold read off the document itself: falling back rescued one correct answer and spoke one false one; letting the refusal stand asserted nothing false and missed three — same score, a different KIND of error, and 54 → 32 calls, 72 → 48 seconds. Neither dominates, so neither is the default: the caller says which error is the expensive one where they work.

Recorded

  • A third measurement trap caught the author of the other two: the first version of the W161 invariant asked one question twice in one Memory and compared a cached answer with a live one. cache=False in a test that measures two paths against the same question.

[0.7.9] — 2026-09-22

Added

  • ask(..., quoted=True) — an answer that quotes the store verifies itself for nothing (W159). Verification is 46% of a turn's engine calls because it judges free prose AFTER it exists: a read-back re-extracts the claims, a relation check reads the edges. Every other answering path here already avoids that — a record, a count, a span are spoken from the store's own value through our template, and need no checking because nothing new was said. This is that discipline applied to prose.

One call returns the answer AND the number of the evidence line it used. The store then checks, with no model and no network: is that a line it offered, does the answer's every content word come from that line or from the question (W152), and does every digit appear in it. What survives is assembled out of a line the store holds, so the fabrication the gates exist to catch is structurally impossible rather than judged unlikely.

Measured, two runs of fifteen questions over a 355-line document: calls 90/84 → 54/69, prompt 368k/349k → 224k/273k, seconds 105/105 → 76/100. About 30% fewer calls and tokens, 16% less wall clock; the quoted reading carried 8–9 of 15 turns and the rest fell back. Both arms abstained on the same absence questions.

The cost is named rather than hidden: an answer spread over several lines gets the one line it quoted. Asked which exams a study guide serves, the ordinary path lists four and the quoted path names one. So this is a caller's choice, not a default — the promise is unchanged, only the mechanism keeping it and its price.

Refuted on the way

  • The first cut asked the engine to reproduce the evidence line character for character and matched it against the store. Measured on the same fifteen questions, it matched ONCE: copying a long line exactly is a task engines are bad at and nothing needs them to do, since the store already holds the line. Naming it by NUMBER cannot be misspelled and a number nobody offered is refused by construction.

  • The first A/B ran both arms in one process and reported a 99 → 34 second "improvement". That was the deterministic call pool this release's predecessor documented — the arms must each have their own process. Written down here because the author of that warning fell into it one release later.

[0.7.8] — 2026-09-22

Documentation only, and it revises this project's central claim.

Corrected

  • What the gate is for. A team integrating this library published a correction to their own report, and it corrects the argument this README had been making. Their first comparison put the whole document in the prompt — no retrieval at all — which made the gate look like it was buying two fewer invented answers.

Against the right control (the same retrieval LMM uses, one engine call after it instead of the gate), across 44 cells on two real tender documents: stuffing the document produced 5 false coverages, the same retrieval with one ungated call produced 3, and LMM produced 2 — inside a run-to-run variance of about two cells. Retrieval buys two; the gate buys the third, at the noise floor, because once retrieval is good the fabrication the gate exists to stop was mostly not happening. Naive RAG over the same retriever was 11× cheaper in tokens and 5.6× faster, and anyone choosing between them deserves to read that here.

Added to the record

  • What the gate does buy, which neither side had measured. Across the same 44 cells: unverified answering returned "partly covered" 31 times out of 44, and the whole-document variant never once said "not covered". LMM committed 34 to 10, and 8 of those 10 refusals are correct. A matrix where 31 rows of 44 say "partly" tells its reader nothing.

So the claim is not "the gate stops the model inventing things". It is that the gate stops a system from refusing to answer while appearing to — the answers do not drift to the middle. A system that never commits is never wrong and never useful. The shape of the number, for anyone repeating it: over a fixed question set, how often the system returns the non-committal option. It costs nothing and separates verified from unverified answering far more sharply than accuracy.

  • Relevance is the dominant failure mode, promoted on the limits page from one item among several to the first. The same run reports zero fabrications in 44 cells; both wrong answers were relevance failures, one of them with every content word verbatim in the document and a real source stamp — a true sentence answering a different question than the one asked. The gate had nothing to object to.

  • An assertion with no stamp is worth suspecting. While the abstention-stamp defect stays open, every instance in that run carried one signature: abstained=False and sources == (). Measured as a rule it is poor (it removes two wrong answers and costs two right ones); as a diagnostic it caught every occurrence, and it is now documented as one.

[0.7.7] — 2026-09-22

Added

  • Memory.reset() and ask(..., standalone=True) — a turn may decline the conversation it did not have (W158). Measured from the field, both directions, same question and document: a cell answers when asked first and abstains when asked after nine unrelated cells in the same Memory. Nothing but the order differed, and the reporter first attributed the change to a release — the sequence is part of the input, and anyone comparing runs needs to know it.

That is the follow-up inheritance doing its job: a turn naming no document of its own reads the one the conversation was about, which is right in a conversation and contamination in a matrix of independent cells. It is NOT turned off — the same reporter measured it earning its place even there (77.3% with it, 72.7% with a fresh memory per cell; isolation traded three invented coverages for three missed ones). What was missing is the caller's say: reset() ends the conversation, standalone=True does it for one turn, before and after.

What neither does is forget. The graph, the evidence index and the aids are the memory; a conversation is not, and the invariant checks that facts and every stored line survive a reset.

Documentation

  • cache=False is not "nothing is reused" — and a benchmarker must know it. Below Memory the engine pools its own deterministic calls (temperature 0, byte-exact prompt, per PROCESS and so across Memory instances) and the extractor memoises sentences already read. Asking one question twice in a single process can cost ZERO calls with the cache off and a freshly built memory — measured by somebody timing this library, who briefly recorded a spectacular and false "0 calls" for the change they were evaluating. The pooling is right in production and wrong to measure through; the fix is one process per variant, and it is now written down in both the code and the API page.

Recorded

  • Answer.covered (0.7.5) does NOT separate its proposer's three grades on their corpus, and they published the distribution showing it: partial cells at 0.31 and 0.82, a false-covered cell at 0.89. Partialness there comes from a QUALIFICATION — a footnote assigning the work elsewhere, a scope note contradicting the price table — and no word count reads that, in either direction. covered measures what it says and stays; it is simply not a grader, which is the second reason grades do not belong in this library.

[0.7.6] — 2026-09-22

Added

  • ask(..., shape=...) — a caller who knows the turn's kind is not asked to prove it (W157). Traced from outside, one cell of a batch: six engine calls and 5 015 prompt tokens, of which routing is 26% — one call reading the message's shape, another asking whether the message is about the responder ITSELF. In a conversation both are necessary. In a batch they are a constant: 44 questions of one known kind, and the second question asked 44 times whether the caller is asking the assistant about itself.

A declared kind seats both readings: 6 calls → 4, on every cell of a batch. Everything after the door is untouched — the organs, the gates, the read-back, the 46% of that trace which buys the ability to say no.

It is a declaration, not a hint: a wrong shape costs the organ that shape would have reached, exactly as a wrong shape read by the engine would. It is read in ONE place (Session._turn_shape), it lives for the turn and is cleared after it, so a batch's declaration cannot leak into a later ordinary turn. Left unset, every turn reads as it always did.

Held deliberately

  • The third proposal from the same report — ask() on a declarative sentence should ask rather than chat — is not in this release, and not because it is wrong: the reporter measured the catalogue's own wording retrieving better than a paraphrase (15/20 against 11/20, 17/20 with both). It changes retrieval behaviour, and the run that would judge it is in flight. Shipping it now would make those numbers incomparable.

[0.7.5] — 2026-09-22

Added

  • Answer.covered and Answer.missing (W156) — how much of the question the answer carried, and which of its demands it did not. abstained is binary, and an integration measuring 44 supplier/requirement cells hit a structural ceiling on it: three of its gold cells are PARTLY covered and no configuration could emit that.

The obvious repair was measured by the reporter and thrown away: asking the engine to grade completeness over the proof lines took a 79.5% strict score to 38.6%, because a model shown six retrieved windows and asked "how complete is this?" always finds something missing. What ships instead is a COUNT, not a judgement — the same instrument as evidence.coverage pointed the other way, engine-free and unable to fabricate, in the same class as where() and themes().

The demands are the question's content words, and deliberately not a cleverer subset: two cleverer subsets were built and measured here — dropping what the store carries often kept the modal ("shall") and dropped a real demand ("material classification") for the crime of being written down, and a corpus-relative median cuts where nothing was asked. There is no stopword list in this codebase and there will not be one, so a question's quieter words ride along in the denominator the same way in every question, which is what leaves the measure comparable — one question against another, one supplier against the next.

Three grades ("fully / partly / not covered") are NOT shipped: that is a procurement vocabulary, not a memory concept, and a library that learns one customer's matrix stops being a library. The measurement is the library's; the grades are the caller's.

Open, with the reasoning

  • A refusal sentence can be stamped as an assertion — reported from the same integration, two cells of 44, stable. A short refusal restates the question, W152 correctly subtracts the question's own words, and what remains is the refusal's register ("specific", "information", "have"), which an English business document carries. Reproduced here.

Two repairs were built and refused. Togetherness — a claim quotes one region, a refusal gathers words from several (W135's doctrine) — measured backwards: True for both refusals, False for W97's genuine claim. Store frequency, the reporter's own suggestion, is the right instrument and has no boundary that is not a dial: their own table puts "and" at 48% of sentences, under the majority this codebase uses everywhere, and a corpus-relative median was measured cutting the wrong words.

It also runs with the grain of a deliberate choice: this reading "errs towards ASSERTED", because claiming an abstention that did not happen is the damaging direction. The reporter measured the obvious guard (require a source stamp) at net zero — two false-covered removed, two true positives lost. So it waits on the chat slice being measurable again rather than on another idea.

Measurement

  • The ingestion-shape test (E5) was measuring the machine's mood. Its timed loop runs in single-digit milliseconds at the small size, and it was caught FAILING on a tree whose ingestion had got faster — the small run sped up more than the large one and the quotient rose past the bound. It now collects the garbage of two hundred earlier tests first and takes the best of three runs at each size; the bound did not move, and the shape it reports is 5.8x for 5x the data instead of a noisy 11x.

[0.7.4] — 2026-09-22

Fixed

  • A strict minority needs three regions to exist (W154). The reported "a second document destroys the correct answer" was real, and the mechanism was in neither place we looked. Both of us hunted a lost SEAT — three synthetic reproductions of that failed. What changed between the reporter's two runs was the number of REGIONS the proof folds to: one, then two.

At exactly two regions the load-bearing test is unsatisfiable. half is 1.0, and a word must hold holders >= 1 to be in the proof at all and holders < 1.0 to be in a minority of it. No word can do both, so nothing without a digit in it could ever be load-bearing and every such answer was stamped an abstention — a dead band exactly one width wide, entered only by going from one region to two, which is why a second document looked like the cause.

The boundary stands: register is what the proof BLANKETS. But a strict minority needs three regions to exist — with two, every word present is in one or both and neither is a minority, so there is no register to subtract and the reading defers to coverage, exactly as it already did with one region.

The report proposed widening the comparison to holders <= half instead. Measured on W97's own fixture, that breaks it: at four regions "have" and "information" (two lines each) become load-bearing and an honest refusal is stamped an assertion — the thing W97 exists to prevent. The population bound leaves every three- and four-region proof byte for byte as it was.

Still open

  • Seat displacement in a multi-source store: at most=6, a second document's line can take the seat of a lower-ranked line from the document that holds the answer (at most=10 the two orders agree). Reported with data. It may no longer be reachable end to end now that the dead band above is gone — the same report shows the rescue pass finding the target line and the turn refusing anyway, which is what that dead band did — so it is measured again before the seating rules, whose monopoly behaviour was measured on 62 sibling documents, are touched.

  • A refusal came back in mixed language ("Ich do not know.", route ('chain', 'widened')). The answer path, not _refuse, so the language match that refusals get never ran.

[0.7.3] — 2026-09-22

Fixed

  • Repeating the question is not a claim of its own (W152). The gate before the one 0.7.2 fixed, and the same reporter's corpus found it: a document writes 4-Augenprinzips, the engine answers with the same term spelled Vier-Augen-Prinzips. A numeral against its written-out twin, a compound against its split halves — three tokens read as unsupported, coverage lands at 0.44, and a correct, sourced answer is stamped abstained=True over a spelling variant of the question's own subject. 3/3 reproducible, and reproduced here before it was touched.

The reading already strips what is not a claim: the engine's parentheses, its #stamps. Restating what was ASKED belongs in that class — an answer that says the subject back has asserted nothing by doing so, and what it asserts is what it says BEYOND the question. evidence.coverage has taken a question argument all along for exactly this; the abstention reading simply never passed it, and now both of its callers do. Nothing is loosened: the gates that decide what may be SPOKEN are elsewhere and untouched. This reading only decides the stamp.

Reported, not reproduced

  • "A second document destroys the correct answer." Reported as 3/3 stable: one vendor's answer is correct alone and refused once a second vendor's document shares the store. It matters more than anything else on this list — a tender store holds eight vendors — and it is not shipped as a guess. Three synthetic reproductions were built (word channel alone, meaning channel on, target ranked low inside its own document) and the target line survived every one, so the mechanism is not yet in hand. Changing the seating rules on a hypothesis would risk the monopoly behaviour measured on 62 sibling documents. A reduced case, or the two documents, closes this.

  • A refusal came back in mixed language ("Ich do not know."). No reproduction yet; the turn's .route would say which path spoke.

[0.7.2] — 2026-09-22

The same reporter, measuring 0.7.1 against the same two tender documents: the abstention bug was not closed. 0.7.1's fold asked for strict containment, and the evidence index seats a passage as windows SLID by a sentence — each carries a few words of a neighbour, so none is a subset of another, nothing folded, and a correct sourced answer was still stamped abstained=True.

Fixed

  • Sliding windows of one passage are one attestation (W148, second cut). The fold now asks evidence._same_region — the question this codebase already had an organ for, majority by Jaccard or by containment ratio, whose own docstring warns that answering it with a second rule is how two organs end up disagreeing about what "the same" means. It is transitive, because three windows of one passage are one attestation and not two.

And it stops at the source. Measured while building it: "The Alpha course runs for two full days in Berlin" and its Beta sibling read as one region on words alone — folding them would have erased exactly the independence W39 exists to protect (the same sentence in two documents is two attestations). Lines from different sources never merge.

The folding is now a named organ, Session._regions_of, rather than a block inside the abstention reading: one place to test, one place to read. A proof that folds to a single region has no register to subtract and the reading defers to coverage, exactly as before W97 — which is right for a document seen at several scales, and leaves chat evidence, whose lines subsume nothing, measuring unchanged (W97's own fixture folds nothing at all).

Known, and not silently patched

  • A refusal can still be read as an assertion when one of its words (an "I do not have that" sort of word) happens to sit in a minority of proof lines. Present in 0.7.1 and before, not introduced here, and closing it properly needs the chat slice re-measured rather than a guess.

[0.7.1] — 2026-09-22

Five findings from somebody integrating 0.7.0 against two real tender documents — the kind of report that only comes from outside. Two of them inverted this project's central promise, and both are closed.

Fixed

  • A correct, sourced answer could be stamped abstained=True (W148). Reproduced 3/3 by its finder: every word of the answer was written in the document, and the turn reported it as a refusal with no sources. The abstention reading (W97) subtracts "register" — words the proof blankets — from the content that carries a claim, and it stands on proof lines being INDEPENDENT attestations. The multi-scale evidence index breaks that premise: it seats the same passage at several widths, so the answer's most distinctive words sit in a MAJORITY of proof lines and are struck as blanket. Now a line whose words are a subset of another proof line's is the same region read at two scales and casts one vote. Distinct lines — the chat evidence W97 was written on — subsume nothing and measure exactly as before.

  • An unreachable engine is no longer indistinguishable from honest ignorance (W149). With no engine — client package absent, key refused, network gone — every question returned "I don't know", abstained=True, empty route: infrastructure failure wearing the exact signature of the thing this project asks to be trusted for. In the reporter's use it marked a vendor's real capability as "not covered". The dispatch's failures now carry one name (runtime.EngineDown), the turn records an engine-error step in its route, and Answer.engine_error states it. The turn still abstains — nothing was asserted — but a caller can retry or alert instead of writing down an absence.

The configuration case keeps both readings: runtime.EngineMissing is an EngineDown and an ImportError, so a fresh machine still gets the message naming the four engines and the pip install line (W62), while the turn still reports an engine error.

  • The package stated two versions (W150). importlib.metadata said 0.7.0 while lmm.__version__ said 0.6.5 — the release bumped pyproject.toml and the hand-written string was missed. The string is now derived from the installed distribution's metadata, and the invariant checks the build and the report agree, so they cannot drift again.

Added

  • Learned.calls — what a reading cost in engine calls (W151), counted at the engine's one door and only on real dispatches. deep defaults to False for a file and True for text handed in directly, which is sound and was invisible: a reader who cleans a document into a string first, the ordinary thing to do, paid one call per sentence without being told. The trade is now a number beside what was gained.

Documentation

  • The API page carries Memory's full signature with the defaults the code actually holds (warmth and reply_tokens are None, not 0.6 and 1200), and documents the entry points it had been missing: learn(source=, deep=) — the parameter the whole multi-document story rests on — ask(fluent=), compose(seats=), themes(least=, most=), and Answer.engine_error with the three-branch pattern a caller should write.

[0.7.0] — 2026-09-13

Conversation memory learns what "living" means: a told fact can END, a question is asked ON a day, a thing HAS a kind, and a small telling can be read whole. Six mechanisms, each generic (no word of any language in code), each behind a failing test first. Measured on the same 30-question chat-memory slice throughout: 43% → 57-60%, wrong claims 7 → 5-6, no category below its start — while the 31-question document exam stayed at 30/31, byte for byte the same behaviour on undated stores. Invariants 211 → 218.

Added

  • The graph count asks which facts still stand (W141). "I canceled my Forbes subscription" distils into a record whose own value says it ended; the deterministic walk cannot read an ending in any language, counted a cancelled thing, and the number flipped run to run with the extraction's mood. Now the walk GATHERS, the candidates are laid out by day in their records' own words, and the engine — the one instrument that reads language — is asked which still stand. An offer no candidate wrote is discarded; an empty or failed reading falls back to the full gather; the count never exceeds what the graph gathered.

  • A count's block reads in time order when the stamps are dated (W140), laid down before the asker's stable priority seat so W98 holds — and items_of is told it may trust that order only when it is real. An undated corpus builds its block byte for byte as before.

  • The plan knows the day the question is asked (W142). now is a primitive beside anchor; the operator may set session.asked_at (a benchmark replays last year's questions, a letter is answered a week late), and with none set it is the calendar's today. Verified by construction — the clock is not a claim. A span that covers whole months says so in months too. "How many days ago did I harvest" went from an abstention to the exact answer.

  • A distilled thing carries its KIND as an index key, never a claim (W143). "I'm also getting Architectural Digest" holds no word of 'magazine' or 'subscription'; the kind the distiller files ("magazine subscription") lets the question reach the record. It lives in the evidence aids beside the bridges — a side-file, never the graph — widens what the gather can FIND, and no gate, label or spoken word reads it. Deliberately unverified against the passage, for the same reason a bridge can be: it licenses retrieval, never speech. The distiller also lists ONGOING facts the speaker reports about themselves — a subscription told in the present tense never reached the graph before.

  • A small dated telling is read whole when every organ dies (W147). Measured on twelve missed chat questions: handed the same lines whole and in time order, the bare engine answered six the selective gather threw away — dispersed clues connect only when they sit in one view. A new organ in the rescue seat, NOT the chain: it runs only on an ASK that still asserted nothing, only when every line is dated, only under a measured reading budget, and its output passes the chain's own two-tier gate — digits non-negotiable (an invented "14 years" dies structurally), word residue by read-back. "Which mode of transport did I use most recently" and "what are the two hobbies" now answer correctly, stamped.

Fixed

  • A list is not the name of one thing (W144). The distiller offered "A, B, and C" as ONE thing and the count answered "1:" while listing three. The guard is punctuation, not language: comma-split parts, two or more of them each carrying two or more content words, are a list — skipped, never rewritten; the evidence line stays.

  • Membership by name or kind, never by story. "Using Mendeley to organize my project sources" carries 'project', and the tool was measured entering a count of projects; a kind is matched as a UNIT ("project management tool" cannot vote on 'project' alone). The projects count went 8 → 2.

  • An enumeration neither outvotes a stated tally nor counts one breath (W145). The widened distiller filled the graph and the graph count began preempting every "how many" — four stated-tally answers ("20 playlists", "32 species", "15 videos", "10-12 hours") broke at once and came back with three structural guards: candidates wearing one stamp are one telling; a gathered line carrying a number beside met question-words outranks the enumeration; and that tally must live in the ASKER's line — an assistant's chatty digit silences nothing.

  • A leading ordinal is layout, not a name — the engine's "1. " no longer leaks into an item's name.

Recorded

  • A seventh mechanism was built, measured, and REVERTED before release: handing the organ-dead turn to the factual chain contradicted W93's own measured basis (the chain's 23 confident wrong claims were measured on chat, not documents). The telling organ above is the corrected design — a new organ with its own gates, not a loosened chain. The conflict and the reasoning are in the tests.

  • Scoring fidelity, harness-side: LongMemEval's insufficiency questions (gold text says the information is not enough) are abstention questions; an explicit abstention now scores as the correct behaviour it is.

[0.6.5] — 2026-09-13

Three defects reported by somebody testing a bot built on this library, which is the only place this kind of thing is ever found. Two reproduce and one was worse than described; the third does not reproduce. A fourth was found by re-running the exam afterwards and is a cost of 0.6.3's own change.

Fixed

  • An underscore is a space that survived a filename (W138). \w counts it as a letter, so a document named Alpha_Sales_101 was ONE token and a reader asking about "Alpha Sales 101" — two tokens — could never name it. Six of a 103-document catalogue were unreachable that way. The report said such a question went unanswered; it is worse, because naming BINDS the turn (W130): the name failed, a neighbouring document's name won, and the answer came back confident and stamped with the wrong document.

  • The graph speaks the spelling it was given, not its index key (W137). Folding is how two spellings become one concept and it is load-bearing, but it was applied at EXTRACTION, before identity was ever consulted, so the surface a document used was gone before anything could keep it — and the graph path speaks what it keeps. A document written with its reader's own accented letters came back with them flattened: correct, and misspelt in that reader's language. Extraction now only cleans invisible combining marks, which is its actual job, and the identity layer keeps the surface beside the folded key with label_of preferring it. Every lookup still folds first.

  • A document that does not write a field can still answer (W139). The record path owns a turn that names a document, and when its field reading came back empty the turn REFUSED — right before naming bound the turn, wrong after, because the scope IS that document now and the ordinary path reads it and nobody else's. Measured: a corpus writing its duration in days, asked for HOURS, bridged to a different head, came back empty, and refused a question the document answers in its own first line.

Did not reproduce

A refusal arriving in English against a Turkish setting. Refusals come back in the question's language.

Note for existing stores

Both index-key changes here — and the case-folding change in 0.6.2 — mean a store written by an earlier version searches under keys the current one no longer produces. Re-learn the documents and re-run bridge() to get the recall back.

[0.6.4] — 2026-09-13

Measured against a 101-question slice of LongMemEval and a 31-question document exam whose gold is extracted FROM the documents. The document exam holds at 30/31 (field questions 20/20); the chat-memory slice scores 34.7%, and the reason it did not move is written below because it is the most useful thing this release found.

Added

  • Memory.themes() (W136) — the subjects a corpus falls into, with NO engine. A corpus has subjects no single document names: a catalogue's programme families, a manual's subsystems. GraphRAG finds them by running community detection over an LLM-extracted graph and then paying an LLM per community summary; RAPTOR clusters embeddings and summarises each cluster. Detection costs nothing here because the graph already existed — entities are phrases whose words co-occur beyond chance, edges are witnessed co-mentions weighted by log-likelihood. Measured on 103 documents: 501 entities, 5,671 edges, groups in a hundredth of a second, zero calls, and the largest fell on the catalogue's own families.

It is deterministic (the textbook shuffles its nodes; this cannot), it writes no summary, and an entity carried by most of the corpus is left out — without that filter the top group was the field headings every document repeats.

It does not answer "what is this family about", and was measured trying. Scoping the composer to a community's documents returned one course's outline, not the shared subject. themes() is a structural reading and claims nothing more.

Fixed

  • Two events told the same day are not ordered by the clock (W134). The ordering organ already declined a tie, and the guard never fired because the comparison took every digit in the stamp — including the time the message was SENT. Two events reported on one day were ordered by which message was typed first. Three wrong claims became abstentions, each of which had been printing both events with the same date beside it.

  • A counted name is attested as a unit, not word by word (W135). Verification asked whether every word of an item appears SOMEWHERE in the block, and the block is many lines — so a whole clause passed when its words were scattered across them. The engine offered "which are your Data Mining project and your Database Systems project" as ONE item and the turn answered "1:" while listing two things. One line must now carry the whole name, which is the rule the sum organ already kept.

Why the chat-memory score did not move

The retrieval work in 0.6 took the answering line's chance of reaching the engine from 32% to 79% — on a corpus of 103 documents. This benchmark gives each question a median of TWO sessions and 24 messages. There is no "which document" problem in it to solve. Its failures are aggregation and arithmetic: of 32 wrong claims, 22 came from the counting, planning and ordering organs and none from retrieval. Two of those classes are fixed above; the largest remaining one is the class the README already names — a long conversation reports the same thing in different words each time, and the list comes back short.

[0.6.3] — 2026-09-13

Found by tracing one failing turn end to end instead of guessing at it. Every step before the last was right, and the last one threw the answer away.

Fixed

  • A field is its words, not its capitalisation (W132). One document writes "COURSE LENGTH:" and its neighbour "Course Length:". Keyed by the literal spelling, each looked like a field ONE document uses — so the reading that keeps what the corpus REPEATS threw both away. What followed all pointed somewhere else: the bridge nominated a field nobody held, the one-head rule saw a tie and declined, the record path stood aside, the chain ran, and the field veto fell back to matching heads by kinship, reached a DIFFERENT head that merely shared a stem with the question's word, and vetoed a correct answer for not carrying that field's digits. Measured on the live catalogue: the turn now answers from the record in 2.7 s where it refused in 25.3 s.

  • A greeting is not a failed question (W133). The rescue seats read last_abstained, which means "this turn asserted nothing" — true of every greeting by construction. So "hello" opened the delivery bridge and the composer's general door and paid for both. Seven engine calls for a greeting, of which one wrote the reply; six now, 13.6 s to 8.7 s.

  • The second record door stamps the route like its twin (W110's third case): a path that speaks says so.

Measured, and worth knowing before tuning anything

Latency is round trips, not model size and not prompt size. On a hosted deployment a bare call costs about 1.13 s with a 2,000-character prompt and 1.55 s with a tiny one; 200 output tokens costs 2.87 s. A turn's wall time is very nearly its call count times that. Our own code is not in the picture: metered over a greeting, the library's share was NEGATIVE against the sum of its calls, because two of them run in parallel.

An optimisation was tried and refuted: the door's three readings are independent and could be fired side by side, but a speculative prefetch puts a model call into the graph path, whose whole claim is that it has none. Four invariants said so. A second is not worth the property.

[0.6.2] — 2026-09-13

Found by asking a hundred real documents every kind of question a reader asks, with the gold answers extracted FROM the documents rather than written by hand. Field questions — the most basic thing this library does — were answered 8 times in 20. They are now answered 20 in 20, and two of the three causes were ours for years.

Fixed

  • A word folded from capitals now reaches the same key as from lower case (W131). casefold is not idempotent across case for every script: 'I' folds to 'i' and 'ı' folds to 'ı', so a word written in capitals and the same word in lower case became TWO index keys — and documents write their field headings in capitals. Measured: a capitalised field heading was indexed under one key while the same words typed in lower case searched another, so the line stating the answer could not be reached by the question that asked for it. Every gate then behaved correctly, so it read as an honest "I don't have that" about something the document plainly states. Raising the letter before folding it makes the fold agree with itself — same Unicode table, no language rule, length still preserved; it also merges Greek final sigma.

This changes index keys. A store written by an earlier version keeps answering, but to get the recall back, re-learn the documents.

  • A question that names a document is answered from it (W130). Naming used only to clear the previous subject; the turn then read the whole store. On a catalogue where every course states the same fields, the named course ranked first and its own answering line was not in the top forty. Naming now binds — and inside that binding the name stops scoring, because every line in play is from that document and the words that spelled it were pulling the masthead to the top.

  • Lines inside a proposal are ranked by what a word is worth, not by how many matched. This was the one place in retrieval that ignored rarity: a document about one subject writes that subject's word on every line, so the question's topic words outvoted the two that named the field.

Known, named rather than hidden

  • A compound value can lose a part: asked one course's duration, the memory answered "2 full days" where the document says "4 modules x 2 full days". Everything said is written; the gate cannot see what was left out.
  • A count-shaped question the corpus cannot answer can still be answered by counting the wrong things: "how many people completed X" came back with a list of other courses. Each item is written somewhere, so the gates pass it.

[0.6.1] — 2026-09-13

Two defects found while testing 0.6.0 the way a user would — installed from PyPI, against a hundred real documents.

Fixed

  • The reordering is given something to reorder (W127). The proposal was cut to the seats being filled and THEN reordered, so the reranker could only shuffle what the words had already chosen. It hid behind the multi-document case (five documents giving up three lines each is fifteen candidates); on a ONE-document store, asked for three seats, the answering line was missed entirely and with a pool it comes first. The pool is now a floor on the whole proposal, shared across the documents proposed — widening the per-document share instead was measured and was worse (78% → 75-77% in five).

  • A line is credited to the document that offered it (W128). The fused reading recovered each line's source by looking the TEXT back up in the store, first match wins — and a catalogue repeats lines across documents, so a line was credited to whichever document sat first. Read off a live 103-document corpus, four different questions came back stamped with the same alphabetically-first training. Provenance is now recorded as the line is proposed, and the lexical channel's own sources are carried through the fusion.

Measured, and named as a limit

The bundled static matrix is weak at mapping a PROBLEM STATEMENT onto a training title — a stated problem onto the course that treats it. On eight such questions over a 103-document catalogue it placed the right course in the top three once; a torch multilingual encoder (paraphrase-multilingual-MiniLM-L12-v2) placed it four times. Known-item retrieval on the same corpus is 100% either way, so this is a property of the class, not of the wiring: content matching is solved, need-statement matching is not. Where that class matters, pass encoder=.

[0.6.0] — 2026-09-13

The release where retrieval stopped being only words, and where three hedges against weak retrieval were retired because measurement said they had stopped earning. Every number below was produced by a protocol in the repository; none of it is an estimate.

Added

  • The meaning channel ships with the library and is on by default. A 30 MB static multilingual matrix travels in the wheel, so a reader who paraphrases — a lease that says RESIDES against a question that says LIVES — is understood with no download, no account and no network. It is a matrix, not a model: one lookup per word-piece and a mean, so there is no torch and no warm-up. Measured on 20,000 lines and 180 known-item queries it beats the transformer it would otherwise take (MRR 0.883 against 0.710) and builds its index a hundred times faster (0.3 s against 36.1 s). Source: minishlab/potion-multilingual-128M (MIT, the model2vec method), reduced to 108k pieces and 128 dimensions; the segmentation is thirty lines of our own, so no tokenizer library is required. See NOTICE.

  • Rank fusion, and late interaction over the same matrix. The word ranking and the meaning ranking are fused by reciprocal rank (RRF, K=60) — order, not score, so there is no weight to tune. The lines inside a proposal are then reordered by late interaction (ColBERT's idea without ColBERT's model). End to end, by whether the ANSWERING line reaches the five the engine is shown, on a 115,913-line corpus: 32% → 60% → 79%. Reordering is worth more the vaguer the question gets (six terms removed from the query: MRR 0.707 → 0.807), which is the way round that matters.

  • A parametric surface for all of it. Memory(encoder=...) takes any callable from strings to vectors; reranker= any callable from (query, texts) to an order, or None; dense=False removes the channel entirely. With no numpy, no bundled data, or an encoder that raises, the channel is absent, never degraded — the words carry the turn.

  • W123: the derived field-name index is written beside the store. It was built on first use, once per PROCESS: 10.6 s before the first answer on a 115,913-line store, paid again by every command-line run. Now 0.7 MB on disk and 0.13 s, with the sentence count and the bound it was built under, so a file that does not match is rebuilt rather than trusted.

  • W117: a deterministic question is asked once. At temperature 0 the engine is a function, and one refusal turn was asking the amount reader twice on the same 45,755-character block. Byte-exact pooling, and warmth is never pooled.

  • W125: every shipped entry point imports what it runs — including the lazy imports inside functions, which is the class of breakage no import-time check can see.

Changed

  • The meaning channel sits at the DOCUMENT layer, and the layer was measured. Over lines it moved nothing; over document profiles the store's own retrieval went 92% → 99% on 103 known-item queries. The opposite arrangement was measured too: choosing the lines INSIDE a proposed document by meaning rather than by words took the answering line from 78% to 31%. Meaning says which document; inside it, the question's own words are sharper.

  • CANDIDATES 3 → 1 (W124). Three evidence subsets were answered per question and the gate chose among them — insurance bought before the meaning channel existed. Measured after it: across eleven field questions every admitted answer came from the wide block and the narrow candidates won nothing. Collapsed, the run went from 139 engine calls to 104 and correctness from 4/11 to 5/11. It stays a number, not a deletion.

  • VIEWS 2 → 1 (W126). Both judges could buy a second, wider view when the first declined. In the same run the second view confirmed nothing, on either judge — eight calls of a hundred and four.

  • One dependency: numpy, named in pyproject.toml with its reason. "Zero dependencies" was true and is no longer; hiding that would be worse than losing it.

  • learn() lost its expand= argument, and Learned its expanded count.

Fixed

  • The memory answers the identity question it was asked (W120). Asked WHO WROTE IT, it answered with its own NAME — true, attested, past every gate, and an answer to a different question. The name is one field among the rows the operator declared, and the question is now mapped onto those fields — including the fields we could hold but do not, because a mapping shown only what it has can never discover that something is missing.

  • The meaning channel is attached where the store is built (Session, not Memory). A caller using the older class got a store with no vectors and no way to tell; the failure was silent.

  • The graph-direct path records its route (W110's fifth path). It spends no model call, so it was never instrumented, and a record question came back with an empty route — indistinguishable from a turn that never happened.

  • lmm/mind.py restored. It was retired as "an idle derivation loop on no product path" and is on the product path: the lmm console command builds it at startup. The import sits inside main(), so the package imported cleanly, every test passed, and the command crashed on launch. W125 exists so that cannot recur.

Removed

  • The offline expansion channel, entirely (−607 lines). It generated the questions each line answers and indexed them; measured on NIST SP 800-63B its keep filter is inverted — a real rephrasing never reaches its own line, so it kept only the queries that COPIED the line. Handed perfect queries by hand, it kept zero. The README recorded that for months while the channel stayed, off by default, costing a rung on the channel ladder, a side-file key, a prompt, a session pass, an api argument and five invariants. A switch nobody should turn on is a liability, not an option.

  • generate.wants_material — it was turn_shape's "material" asked a second time with a second prompt on the same sentence.

[0.5.0] — 2026-09-11

The release where question shapes stopped being hand-written. Everything here was found by measurement — a public benchmark, a live consultation, a drowned block read line by line — and every fix is a class, never a case: the two rules in CONTRIBUTING (no document-specific constants, no hand-written language rules) held through all of it.

Added

  • The composer (Session._plan_answer). A memory that answers only the shapes somebody wired by hand answers a finite set of questions. The engine now proposes a PLAN over the verified primitives — anchor a phrase to its dated line, take the latest, gather lines, span two dates, order them, filter by a cutoff, count, tally by month — and the interpreter executes only operations it knows, on phrases the store can anchor. The sentence is built from the final step's TYPED value by our own template: the engine contributes operation names and phrases, never an output word. An unknown operation, an unanchorable phrase, a dangling reference — the plan dies and no claim is born.

Measured on a dated store: five shapes nobody wrote an organ for — did A happen before B, in which month most, how many days between, when first, when last — answered correctly with no new code. And the seat binds the plan's output type: a count-shaped turn will not voice a month tally, however correct its arithmetic.

  • Four event organs, each the arithmetic its question asks for. A count is the length of a VERIFIED LIST — the engine lists the items, the store checks each against the block, and the number is the length of what survives, so a hallucinated item cannot be counted (and where the things counted are documents, the census counts them with no model call at all). A total is the SUM of verified amounts. "Which came first" is date arithmetic over anchors. "How many days between" is subtraction, not counting.

The shape reader that routes them is one small call at the door (material / count / order / sum / when / none), replacing three separate classifiers asked after the fact — and its verdict binds both ways: organs silent and plan dead, the turn REFUSES rather than gambling the prose chain. Measured on a benchmark slice, that chain had earned two right answers and twenty-three wrong ones.

  • Dialogue's two missing columns. WHEN an event happened is the sentence's word before the envelope's: people tell events days later, so the engine reads the candidate lines and proposes the date, and the arithmetic decides whether the reading may stand — the date must match a candidate's stamp, or its day must be written as a number in that line with the year in the stamp's neighbourhood. An invented date fails both and the envelope speaks. No month table, no date grammar. WHO SPOKE is metadata as real as the date: learn(..., speaker=) keeps it per line, session.asker says who is asking, and the event organs seat that speaker's lines first.

  • Memory.distil(text) — the write-time reading for passages where events melt into talk. The engine lists each event as thing/what-happened, the passage's own words admit it, and each survivor becomes one gated dated record, while the passage is kept as evidence so nothing is lost. Documents that state their own structure need none of it; their rows reach the graph for free.

  • Memory.bridge() — one call per field head, once, teaching the store what words readers ask each field with. Measured on a live 62-document corpus: a duration question fell from 12 s and six model calls to 2.7 ms and none.

  • Answer.route — which organs a turn passed through, in order (("record",), ("chain", "refuse", "count")). The orchestration was always there; now it is a fact on the answer instead of a story in the stack, and an organ a router can be asked about is an organ a planner can schedule.

  • LMM_SMALL_BACKEND — the turn's yes/no classifiers may take a cheaper road (a small local model agreed with the hosted engine 19/20 on fresh multilingual examples). OFF by default: one measured classifier is not a licence to reroute sixteen.

Fixed

  • No apparatus of ours can be spoken. Asked which documents state a field, the memory answered "K1, K2, K3, K4" — reciting the labels this code puts in front of evidence lines. The label is ours to choose, so it is now the name of the source that wrote the line: an answer that echoes a label cites a document.
  • An untrusted language name is never a command. The language classifier was handed the question bare, the engine began answering it ("I'm sorry, but"), the token cap clipped that to "Im" — and refusals came out in an invented language. The classifier's text is quoted material now; the name only compares; the retry may name a language only when a second, differently-phrased reading agrees. Six languages, six correct refusals.
  • A refusal in chat register is not an assertion. Chat evidence speaks a refusal's own words, so fifty-three honest shrugs wore provenance marks and were scored as wrong claims. An assertion now needs something load-bearing besides word overlap: a shared digit, or a shared word sitting in a minority of the proof's lines.
  • LMM_TIMEOUT=0 removes the limit, not the patience. It was removing the retry, so under load every rate-limit wait was raised raw — forty-nine of fifty answers in one benchmark run came back 429.
  • A derived row is spoken as a sentence, with the arithmetic preserved: "3: Dr. Lee, Dr. Smith, Dr. Patel." became "You visited 3 different doctors." A phrasing that changes or drops a number is refused and the row stands.
  • The answer cache now fingerprints the retrieval aids, so a question asked before bridge() is not replayed after it. The record path no longer matches field names by short-stem kinship — it speaks without a gate, and kinship called two sibling fields relatives. Newer Azure deployments (which refuse max_tokens and any non-default temperature) are handled by reading the endpoint's own refusal, not a model-name list.

Measured, and stated plainly

The field set is unchanged: 40/40 factual · 15/15 comparisons · 10/10 traps. On LongMemEval — a long-personal-conversation benchmark, not a document one — this release scores 37.6% with gpt-4o-mini, against published 63.8% (Zep) and 49.0% (Mem0) measured with far stronger answering models. Its temporal "how many" slice moved from 2 correct to 11 across this work; its cross-session counting slice did not move beyond noise, and README's Honest limits says so. 182 invariants.

[0.4.0] — 2026-09-10

The release where retrieval stopped being one channel. Two new readings, both built from the document itself with no model call and no vendor, and both able to show why a line arrived — which is the property this project would have had to give up to buy the same abilities from an embedding.

Added

  • The document's own entity graph (lmm.mentions). GraphRAG hands each chunk to a model and asks for the entities and relations; what comes back is a claim, invented at index time and unverifiable afterwards. This graph is built by the document: an entity is a phrase whose words occur together beyond chance (pointwise mutual information over the phrase's weakest split), which also keeps varied company (no neighbour taking more than half its appearances) and whose parts do not choose freely (seeing one, the other is nearly determined). On a public novel the three readings take 2,318 candidates to 487, headed by the book's actual names.

An edge says one thing — THESE TWO ARE MENTIONED TOGETHER, HERE — and carries the sentence that witnesses it, weighted by Dunning's log-likelihood ratio so an entity that appears everywhere does not become everyone's neighbour. An edge cannot be fabricated: it is an observation. What a relation MEANS is still a claim, and claims go to the gates with the sentence in hand.

What it buys is scale, and the honest version of that claim is the point: on a 36,472-line novel the lexical search already found a line carrying both entities, six pairs out of six, and took 417 ms a question doing it. The same lines come out of two posting lists in 0.01 ms.

  • The corpus as its own thesaurus (lmm.affinity). A text says a tenant RESIDES at an address and a reader asks where she LIVES; lexical retrieval cannot bridge that. Two words used for the same thing keep the same company, which is arithmetic over counts the store already holds: positive pointwise mutual information for what company is surprising, cosine between profiles for how much two words share, and an inverted context index so a query compares against the words sharing two of its contexts rather than against the vocabulary squared. A neighbour comes back WITH the shared company that earned it.

A substitute keeps the same company without being in it: words appearing together in more than half the lines of either are companions, not alternatives — measured, the first cut answered "lives" with "nearby" and "visitor".

  • One harness for any document (benchmarks/document_probe.py). Point it at a file or a folder; it builds the memory the way a user would, generates its own questions from the document's structure, and reports what each organ did — records, census, extremes, comparison, absent fields, nonsense, a rephrased question, the language of a refusal, whether claims carry stamps, and the calls, tokens and seconds each question cost. It asks the kind of question the document can answer: a head that opens a hundredth of the units is a field, one that opens three lines of a novel is a colon.

Fixed

  • A row is reachable by the name a question uses. A spreadsheet writes people as "Taylor, Mr. Elmer Zebley" and a question arrives as words; the graph kept only the written spelling, so on an 891-row public table the graph-first path — the one that answers with no model call at all — matched none of the questions a reader would ask. Row questions 0/8 to 7/8, and the cost per question 9.3 calls and 7,523 prompt tokens to 2.4 and 1,761.
  • The language judge did not work. Asked whether a reply was in the same language as the question, the engine answered NO for an English question answered in English — a verdict carrying no information, so every refusal was rewritten once and then kept whatever came back. Naming one sentence's language is a smaller question the engine answers reliably; two names compare by arithmetic. Twelve refusals across six languages, twelve right.
  • No person is named in a shipped prompt. The chat prompt introduced the library's author by name, so every product built on this told its users who wrote the framework. Identity is the operator's declaration (Memory(identity=...)); a memory that was told nothing says what it can attest, which is that it is a memory.

Regression across the release: the 73-question field set 40/40 factual · 15/15 comparisons · 7-8/8 frontier · 10/10 traps; twelve hardware specifications the system has never seen 8/8 · 4/4 · 3/3 · 4/4; a thousand generated documents 25/25 · 25/25; NIST 11/13; EN 17/17; 158 invariants.

[0.3.6] — 2026-09-09

Changed

  • A document that never uses the words is not asked about them. The mirror of the record answer, on the refusal path: a question naming one document and one of the corpus's own field heads, where that document never uses the head's words anywhere — record or prose — has no answer in the only source it may be answered from, so the three candidates and the three judgements that would refuse them are not paid for. Measured before it was written: the condition holds for zero of 123 factual questions across three corpora, and for 24 of 25 traps at a thousand documents. The fifty-question run over that store takes 109 s, from 205 s and from 620 s at the start of the day; quality is unchanged everywhere (field set 40/40 · 15/15 · 7/8 · 10/10, scale 25/25 · 25/25, NIST 11/13, EN 17/17), and the mechanically-derived set rises to 56/58 factual.

[0.3.5] — 2026-09-09

The release where the commonest question stopped costing anything, and the field set went to a full card.

Measured, three runs, medians, zero flips in every category. 62-document field set (73 q): factual 40/40, comparisons 15/15, frontier 7/8, traps 10/10 — and a factual question that names a document now takes 0.01 s instead of 10.4 s. Twelve hardware specifications the system has never seen, with no adaptation: 8/8 · 4/4 · 3/3 · 4/4. A thousand generated specifications (50 q): 25/25 · 25/25. NIST 11/13, EN 17/17; 151 invariants.

Added

  • A field of a named document is read, not generated. The commonest question a document store is asked needs no engine: the row exists, the source is named, the head is named. On the question door it is read before the router as well, because ask() is asked a question by contract. Tried once before and reverted for firing on one question in twelve — the cause was the naming underneath it, and a bullet being read as part of a head, both fixed here.

Fixed

  • A value belongs to the field it was written under. A specification carrying MEMORY: 128 GB and no STORAGE line was asked for its storage and answered "128 GB", stamped with that document — and every gate agreed, because the source was right, the number was in the evidence, and the sentence answered the shape of the question. Only the FIELD was wrong. A local veto now requires a claim's digits to appear on a line that carries the asked head. On the thousand- document set, traps 23/25 → 25/25.
  • A bullet is not part of a head. Documents open list items with "·", so the record reader saw "· DURATION" as a second field beside DURATION — the same rows twice in the corpus's own vocabulary, and no head ever unique.
  • A source stamp survives the space in a document's name. Answer.sources was read by splitting the answer on whitespace and keeping words that start with '#', so "#docx:Course 02" arrived as "#docx:Course" — a stamp two siblings share, which is not provenance.
  • A head is easier to recognise beside one of its values. Shown a bare list of field names, the engine could not see that a question about a SCREEN means DISPLAY; shown "DISPLAY (e.g. 13 inches)" it could. The values are the documents' own, and a question no field fits still returns NONE.

[0.3.4] — 2026-09-09

The release where the readings were measured above sixty documents for the first time, and where the comparison category — the one this project kept calling its weakest — went to 15/15 with zero flips.

Measured, three runs, medians. 62-document field set (73 q): factual 37→39/40, comparisons 10→15/15, frontier 7/8, traps 10/10. Twelve hardware specifications the system had never seen (19 q): 8/8, 4/4, 3/3, 4/4 — a complete card on a corpus with no adaptation. 1,000 generated specifications (50 q): factual 25/25, traps 23/25. NIST 11/13 and EN 17/17 unchanged; 148 invariants.

Fixed

  • The extremes of a field are the corpus's, not the block's. The first measurement above sixty documents found the worst kind of bug this system can have: a confident wrong answer with a source stamp on it. The census lays one row per source and stops at sixty, and the extremes row was computed from the rows that fitted — so on a corpus of a thousand the memory reported the highest price among the first sixty documents in alphabetical order. Every word was attested; only the superlative was false, and the superlative is the part nobody can check by reading one line. What is shown may be capped; what is claimed may not.
  • One question over a thousand documents took 315 seconds. The record rider rebuilt the head index — a read of every sentence — once per source. Built when the store changes and kept otherwise: same reading, 315 s → 11 s, and a 50-question run 620 s → 348 s.
  • The verdict speaks before the lines it is drawn from. The comparison's verdict row was appended after every line the two documents contributed, so the engine answered off two raw lines ("1 full day" versus "1 day"), concluded they differ, and the jury refused it — the turn abstaining with the answer two lines below where the reader stopped. The census learned this months ago; the comparison layout now has it too.
  • A sibling is not the document you named. Sibling programmes share their opening phrase and differ only in a tail, so a question about ONE of them was read as naming six, and the comparison layout fired for a question that compares nothing. Among the sources a question calls, the one whose name it accounts for most completely is the one it names; a tie keeps everyone, because two names matched in full is what a comparison is.
  • A missing engine says which engine is missing. pip install living-memory-model pulls in nothing, so a first learn() on a fresh machine reached the default local engine and reported ModuleNotFoundError: No module named 'torch' — a sentence that names neither the choice to be made nor the four ways to make it. The name in the message is the distribution name, kept in one place, because telling someone to install lmm[local] sends them to a different project.
  • CI had been red since the 0.3.2 rename, and nothing in the repository showed it. The dependency-free check struck out the package's old name from pip's list and so reported the package itself as a dependency; three consultation tests reached the engine through the small helpers a turn asks on its way, and failed on a machine with nothing installed. The suite's own claim — no model, no network — is now verified the only way it can be, in a virtualenv with neither.

Changed

  • The question door does not gamble on a chat. Every question was paying for a full chat completion that was thrown away: the speculation buys latency in a consultation, where the router's verdict and the chat reply are wanted at once, and ask() inherited it only because it passes teach=False. Same for the delivery bridge, which offers a composed catalogue when a consultation cannot answer — the question door has compose() for that. Factual questions 12.0 → 9.0 calls and 18,800 → 16,300 prompt tokens; traps 12.0 → 8.0 calls. Session.respond takes conversational= for callers that want the consultation behaviour explicitly.
  • The documentation site follows main. Publishing was a command someone had to remember, and the site had been serving 0.3.2 while the sources for 0.3.3 sat in the repository. It builds with --strict, so a broken link fails in CI rather than in a reader's tab.

[0.3.3] — 2026-09-08

The release where the memory leaves the process. A question asked in other words now reaches the documents' own vocabulary, a document that argues with itself says so, and the same three verbs are available over HTTP, in LangChain and in TypeScript — each with the audit intact.

Measured, three runs, medians. On a corpus of English hardware specifications this system had never seen, with no adaptation: frontier 0/3 → 2/3, factual 8/8, comparisons 4/4, traps 4/4. On the 73-question Turkish field set: frontier 5/8 → 7/8, factual 38/40, traps 10/10, comparisons 12/15. NIST 11/13 and EN 17/17 unchanged; 142 invariants.

Added

  • The same three verbs over HTTP, one memory per user (lmm.serve). A memory that lives in one process is a library; it becomes a product the moment a second person asks it something. Every user's store is chosen by their id and nothing else, no route reads across users, an id that could climb a path is refused before it becomes a filename, and each memory holds its own lock so different users answer in parallel. It is a transport, not a second brain: the abstention and the source stamps cross the wire intact. Localhost by default; LMM_TOKEN adds a bearer check. Zero dependencies.
  • LangChain adapters (lmm.adapters.langchain). A retriever whose passages carry their document stamps, and an agent tool that returns the answer that was AUDITED — with its sources, and with the refusal as itself, because an agent handed an empty string writes its own answer over the silence. LangChain is not a dependency: without langchain_core the same calls return duck-typed equivalents.
  • A TypeScript client (sdk/typescript). ask resolves to the same four fields a Python caller receives; a client returning a bare string would discard the only property that separates this from a chat completion. No dependencies. Its wire test starts the real Python server and checks across the two languages that what one user teaches, another cannot read.

Fixed

  • A refusal is spoken in the language it was asked in. Hidden behind a Turkish corpus: six English questions with no answer in the store were refused in Dutch, French and Spanish — never once in English. The question is now handed back as a language sample inside the instruction, the rule is repeated as the last thing read before writing, and the sentence is read back by the same kind of small judge the gates use; on a no it is written once more. No canned phrase in any language: the memory speaks in the engine's voice or it does not speak. Measured 3/9 → 9/9 across four languages.
  • The store's own vocabulary answers the question's. Asked which machine is the "priciest", every seat went to prose about value — the documents write LIST PRICE. The engine is shown the field names the corpus repeats and asked which one the question reaches for, so a bridge can only land on vocabulary that provably exists in the store, and it is asked only when the question touches no field at all. The finding then steers the writer's WORDS while the facts block stays byte for byte what it was — putting a synonym into the evidence would assert a synonymy the store cannot attest, inside the one channel that must stay attested.
  • Naming is a property of what was asked. Retrieval's query carries the previous turn's subject so a pointer-shaped follow-up stays on topic, and names were read off that query: in a corpus of siblings a question naming nobody came back "naming" eight of twelve documents, the comparison layout fired for eight sources, and it overwrote the corpus-wide reading that had just been laid out correctly. The ride still steers retrieval; it no longer confers namehood.
  • A line may hold several records. Ingested line by line, a specification shows the record channel three fields; ingested by the bulk path — how every benchmark and every customer load runs — the three arrive glued into one window and a partition at the first colon saw one. How a document was chunked is not supposed to change what the memory can read.
  • A source that contradicts itself is read as a contradiction. One value spoken, one hidden, and the stamp attesting both is what the gate exists to prevent. Scoped by what the field corpus taught: a repeated head is usually a list and sometimes a zoom, so three values under one head is an enumeration, one value carried inside another is the same length seen closer up, and what remains is a head stated twice with two values that exclude each other.

[0.3.2] — 2026-09-07

Two identities, one relation: the release where the comparison reading grew up. Measured on a 73-question field set (40 factual, 15 comparisons, 10 traps, 8 frontier), three runs, medians: comparisons 8/15 → 13/15, factual 37/40 → 38/40, traps 10/10 throughout. NIST 11/13 and EN 17/17 unchanged; 130 invariants.

Fixed

  • In a comparison, two sources saying the same thing say it twice. Sibling documents share a template, so two items of equal value write the same record line word for word; the assembly deduped by TEXT and dropped the second, leaving one value where the question asked about two — the verdict row could never be written, and the case is precisely the one comparisons are asked about most. Identity is per (line, source), the assembly-layer twin of the store's own rule.
  • Two inflections of one short root are kin. A comparison asking about a "süre" could not reach a record headed "SÜRESİ": four letters of shared root, a different ending on each, and neither relation admitted it — so the question never reached the line that answers it, and the record rider's "did the question ask about this field" ranking was blind in every short-rooted language. Kinship now means a shared opening of at least three letters with each remainder within TAIL, and it is spent on the retrieval side only: the gates keep same_stem, so kinship widens what can be FOUND and nothing about what may be SAID.

Added

  • The comparison verdict is written into the evidence: two named sources answering the same field head yield one system-written row — head, each source's value, joined by = when their digit sets agree and ≠ when they differ. The engine reads a verdict instead of building one, and every reader is taught the notation.
  • benchmarks/conversation_eval.py: the multi-run referee — a fresh store per run, per-category medians and the FLIP set, AND-golds for multi-part questions, and a frontier category. Sorting-layer changes are not judged without it.

[0.3.1] — 2026-09-06

The audit release: a day of field dialogue, a corpus-wide format audit, and a head-to-head benchmark against LangChain, LlamaIndex and Mem0 — with every finding, ours included, fixed architecturally or filed openly. Published numbers re-measured unchanged after every batch (NIST 11/13, EN 17/17; 124 invariants, W27-W42).

Fixed

  • One-row Word tables are fact boxes, and fact boxes are prose. The docx reader dropped single-row grids whole (no header row to read), and every duration/seats/audience box of a sibling-template corpus lived in one; cells now enter the evidence as "LABEL: value." lines, joining the record channel, and an unpunctuated Word paragraph closes with its full stop so headings stop gluing into mega-lines. Body coverage audited to 100% across the corpus.
  • The same sentence in two documents is two attestations. The duplicate guard keyed on text alone, silencing every sibling that shares a template line — cross-document comparisons refused forever.
  • Provenance notation belongs to the system. Generated candidates shed anything stamp-shaped; the mark names the line the answer actually rests on; the history carries unstamped text; a turn that says nothing carries no stamp.
  • An answer's substance cannot be borrowed from the question — one rule at both doors (read-back and informed refusal), and a claim that names its source is judged by that source's lines alone.
  • An echo cannot turn a question into its own assertion. On an ASK turn the chat echo draws on prior turns only; the trap-set fabrication ("yes, there is a certificate") is gone.
  • The prior subject rides retrieval as a challenger, not a preemption — restoring a benchmark answer to its honest, attested path.

Added

  • The comparison reading: a question naming two or more sources lays them side by side — per-source seats, record rows riding along, the named-source jury holding each claim to the source it names. A source is named by a pointer or by its bigram; family letters call nobody.
  • inflect.kin: retrieval-only kinship for short-rooted languages (four-letter roots and their suffixed forms), spent where surfacing a real line is the worst it can do; the gates keep same_stem.
  • An expansion is judged by the words it ADDS — the keep filter no longer discards a record line's honest bridge on the corpus-common words it shares with every line.
  • In a multi-document store, a same-region twin steps aside so a document's second seat reaches its other regions.

[0.3.0] — 2026-09-05

The consultation release. One evening of field dialogue against a live customer bot — a RAG competitor on the other screen — surfaced every gap in turn, and every fix below is architectural: no keyword, no language rule, no corpus-specific line anywhere. The published numbers were re-measured unchanged after every change batch (NIST 11/13, EN 17/17; 110 unit invariants, W14–W26).

Added

  • The consultation surface. respond(msg, teach=False): a statement is CONTEXT (routes to the chat voice; its extracted terms accumulate in the consultation's brief), a question searches with the whole consultation riding along, and a delivery request routes straight to the composer, which returns a source-stamped catalogue built from the brief. The bare refusal is reserved for surfaces that have no conversation — when the answering chain comes back empty, the already-gated conversational reply speaks instead.
  • The echo rule. In the chat reading, repeating what the user themselves said this conversation is not an assertion — a consultant may sound like one. A word the user never spoke still answers to the graph.
  • Gated streaming. compose(..., on_line=) (and respond(..., on_line=)): each draft line is judged by the composer's gate the moment its newline arrives and handed to the callback while the engine is still writing. A refused line is never seen; a backend that cannot stream degrades to batch by itself.
  • The operator's knobs. Memory(..., persona=, style=, warmth=, reply_tokens=). The persona is the voice and stops at the document's edge; the style is the document's shape and travels only to the composer; warmth and reply_tokens tune the voice surfaces alone. None of them can reach a classifier or a verifier.
  • compose(..., topics=) gathers per topic — dedicated seats, dedicated queries.

Changed

  • Latency, halved and then some (measured, same dialogue): a greeting turn 15.9 s → ~6 s, a catalogue's first line on screen 13.4 s → ~5 s. The verifier's per-sentence re-reads now go to the engine side by side where the backend allows; the re-extractor memoises (the gate and the abstention stamp used to read the same sentences twice); on the consultation surface the chat voice and the delivery question ride out beside the router and the loser is discarded.
  • Per-topic composer queries use the topic alone — appending the brief made every query converge on the same generic winners (measured on a 61-document store).
  • An ordinal list marker ("1.", "2)") at the head of a draft line is format, not a quantity — the digit gate no longer kills the engine's own section numbering.

Fixed

  • A context statement in a no-teach conversation no longer falls through to the question treatment (it used to end in a refusal wearing the persona's greeting).
  • A stale census line can no longer leak from a previous turn into the informed refusal.

[0.2.0] — 2026-09-05

The multi-document release. Everything below came out of one field trial: a corpus of 62 sibling documents sharing a template — a class no benchmark had covered — worked through question by question, with the published single-document numbers re-measured unchanged after every change (NIST 11/13, EN corpus 17/17).

Added

  • Memory.compose(brief) — long-form drafts from the evidence: the engine organises, the material speaks, and every line is re-read on the way out (invented numbers and unsupported mixtures are dropped, structure stands). Returns the draft with the documents it rests on.
  • Memory.where(term) — the census: which documents mention a term, with sentence counts as receipts. A count, not a retrieval; no engine, ms.
  • The source-name channel: a question that names a document gets that document's sentences, weighed by log(S/s) — a name-word every source shares weighs exactly zero, which keeps single-document stores byte-stable.
  • Datelines: in a multi-document store every evidence line opens with its document's name, so answers can say which programme — and the verifier can confirm it.
  • Source seat cap: one document cannot monopolise the block when others hold evidence (the region rule, one level up).
  • The census rides into conversation: listing questions name the documents that actually speak, counted at answer time.
  • The informed refusal: a turn that declines while the census is full offers the tally — with datelined reasons — gated so it can name and count only what the store attests.
  • Conversational follow-ups: a subject that resolves to nothing the memory knows points backwards, and the previous turn's subject rides along. No pronoun lists — the signal is resolution failing, in any language.
  • ask() cannot write memory (teach=False throughout): a question API is not a lesson, however imperative its grammar. Teaching stays on Session.respond, deliberately.

Fixed

  • The abstention stamp is read off the evidence, not asked of a model — a full correct answer was being recorded as a refusal when the re-extractor found no tidy triple in a long list-sentence. One model call fewer per answering turn.
  • A restated evidence line needs no jury: mutual word-coverage between claim and a single evidence line settles support without the judge — whose verdict on that class also wobbled across identical runs. One-way coverage still goes to the jury; a subset of a line can invert it.
  • The named document speaks first: admission was right, order was not, and the engine reads the block top-down.
  • learn_rows aliasing could fuse a repeated cell value with a row's identity and silently drop the very fact it was drawn from; the alias now requires a phrase unique in the whole table, on a row that has no name of its own.
  • The offline expansion channel is no longer listed as a capability: measured on a real standard, its filter keeps only the queries that copy a line — the ones that buy nothing. The code stays, off unless asked for, with the measurement in Honest limits.

0.1.0 — 2026-08-18

First public release. The layer had been working for some time; this release is the work of making it installable, readable and lawful to publish.

Added

  • Memory — one front door. Memory("mind.lmm"), .learn(...), .ask(...), .save(). learn() takes a file path or the text itself and routes on the extension: PDF and Word split into tables (straight to the graph) and prose (to the evidence layer); spreadsheets and CSV are all table; anything else readable as text is text. It is a wrapper — Memory.session is the real Session, and the older API is unchanged.
  • ask(explain=True) returns an Answer, which is the answer string and additionally carries .abstained, .sources, .subject and .wrote. The abstention flag is the session's own structural stamp, not a phrase list.
  • Word support (lmm.tables.read_docx / learn_docx), on the same contract as the PDF adapter.
  • CSV support, through the standard library, so the most common "here is a table" format costs the core install nothing.
  • LICENSE (Apache 2.0), NOTICE, CONTRIBUTING.md, SECURITY.md, examples/, and CI that runs the tests on Python 3.9–3.13 and verifies that the core installs with no dependencies.
  • Six tests for the new surface (59 total), none of which needs a model.

Changed

  • The core install has no dependencies. pandas was imported at tables.py's top level, which meant the pure-python layer could not be installed without it. Every optional reader is imported at call time now and reports the exact install line — naming the PyPI distribution, since pip install docx fetches a different, abandoned package.
  • src-layout. Everything importable moved to src/, so import lmm no longer depends on the working directory and an installed copy cannot be confused with the working tree.
  • v3 is now lmm.core. The substrate was named after an internal version number. It is the discrete layer — memory, gate, geometry, dynamics, transitivity — and it is now named for what it is. bench/ is benchmarks/.

Removed

  • Third-party and private benchmark material: a device manufacturer's manual, an institution's internal strategy document and an inspection report naming real individuals, together with every derived result file that quoted them. The invented corpora (benchmarks/corpus*.txt) remain — they are the publishable half of the benchmark, written so no answer can leak from a model's training data. Only aggregate scores are published for the rest.
  • Turkish internal design notes and conversation logs from the published tree.

Known limits

Documented in full in the README under "Honest limits". In short: the gate guarantees non-fabrication, not perfect relevance; there is a lexical-retrieval ceiling on spec lines reachable only through very common words; and the engine sometimes appends an explanation to a refusal that the memory contradicts — which is detected and scored against the system, but not prevented.