Skip to content

v0.32.0-beta — five corpora redrawn, and the collision is silent

Pre-release
Pre-release

Choose a tag to compare

@joslat joslat released this 01 Sep 12:30
· 3 commits to main since this release
3fc321b

Breaking — five corpora changed bytes, and the collision is SILENT

temporal, bitemporal, conjunction, episodic and forgetting all moved corpus_sha256.
arithmetic, prospective and semantic did not — their sidecars changed; their corpora did not.

vertical corpus_sha256 hops
temporal 31d26e60fd219b83da0cd8ea 2
conjunction c62ef47735979f6a0f37a506 3
bitemporal f5b384d7f0ffabf2f3f43219 1
episodic 2c6000a6912e542da3fa1767 1
forgetting ba759097b9bd7fe6e166dbf1 1

Read this before comparing any number to a 0.31.0-beta baseline. All five redrawn corpora keep
100% identical question_id sets with 0 byte-identical items, and corpus_id
(agenteval-typedmemeval-<vertical>-v5) and revision (v5) do not move either. corpus_sha256
is the only field that distinguishes the two corpora.

In bitemporal, 27 of 60 items keep the exact same question text with a different gold answer
(tme-bit-003: same question, gold Northolt BayKelsford); only 4 kept both. A consumer keying
a cache, leaderboard row or regression baseline on question_id will silently mis-grade rather
than fail loudly. Compare on the sha, never on the corpus id, revision or question id.

temporal additionally lost its entire G=1 stratum — the 20 single-gold questions are gone,
redistributing to G=3 (5→11), G=4 (5→12), G=5 (5→12). Retrieval controls change in kind, not only in
value.

Added — two shapes that could not rank anything now can

  • temporal/occurrence-order: headroom 0.05 → 0.75, now the strongest shape in its vertical. It
    asked about two adjacent chain events, so the single link between them stated the answer outright
    while the question handed BM25 both rare names — a lexical lookup in the vertical whose premise is
    that narration order must be followed. It now asks the two ends, so every link is necessary.
    V9 19/20 → 5/20; gold 1 → 3–5 sessions; 0 of 20 questions have any single session naming both
    asked events
    , against 20 of 20 before. V1 held at 20/20.

  • bitemporal/belief-at-instant: headroom 0.11 → 0.31, and on pairs 0.167 → 0.556 against a
    scaled floor of 0.254 it previously missed. Only two sessions per haystack named the asked subject;
    same-subject, other-month distractors now compete (median 2 → 5, exactly K_REF).

  • conjunction/order-then-value: V9 15/15 → 7/15 — the second-largest per-shape V9 move in the
    release. It was saturated under BM25 and could not discriminate retrievers at all.

  • Paired-arm discrimination. Bitemporal's 30 pairs have different correct answers and disjoint
    gold, so per-question scoring averages the capability away. pair_headroom ships with
    pair_floor_scaled and pair_separation_sd: a shape must clear both, because the floor assumes
    independent arms and they measurably are not, while the standard-error separation assumes nothing
    about correlation.

  • A per-shape coverage band gate, which immediately found three shapes outside ADR-026's
    [0.50, 0.90] that nothing had reported.

  • --dry-run for the probe runner — every case through the real code path against a stub: no API
    calls, no credentials, nothing written. It found a real bug on its first execution.

  • --recalibrate on all nine generators. Calibration is now an authoring step, not a build step:
    a plain regeneration rebuilds at the echo the sidecar records and reproduces byte for byte.
    Previously a corpus was a function of (generator, seed, search algorithm), so changing the search
    silently desynchronised every committed corpus from the generator that produces it.

Fixed — previously published numbers that were wrong

Per-shape realised coverage never described the shipped corpus. calibrate_per_shape recorded
each shape's value mid-search, while later shapes' knobs were still 0, then rebuilt with all knobs
set.

shape published actual
arithmetic/delta 0.7767 0.7350
arithmetic/duration 0.6528 0.6250
conjunction/alias-then-count 0.3444 0.3356
semantic/co-reference 0.6889 0.7222
prospective/not-yet-true 0.6667 1.0000 — saturated
prospective/due-later-reminder 0.75 0.50
prospective/due-window 0.2222 0.4352
prospective/expiring-validity 0.8333 0.6667

semantic's echo never moved, so this was long-standing and independent of the calibration work.

The guide asserted something false. It read "V1 − V9 is the headroom a better retriever can
capture."
It is not — a real retriever returns gold plus what else it ranks highly, so its ceiling
is V8, not V1. headroom_reachable had been in the sidecar since 0.31 and the word "reachable"
appeared zero times in the docs. prospective/due-window publishes 0.94 of which 0.17 is
reachable.

The calibration search was fixed twice. Bisection assumed a monotone most shapes violate; the
sweep that replaced it was itself a regression on cliff-shaped curves, returning coverage 0.000
on arithmetic/delta where bisection returned 0.735. A sweep locates and bisection resolves;
both are needed. See ADR-028 §12.

Three sidecars had lost fields they carried at 0.31.0-beta — structure.retrieval_ceiling and
structure.scaffolding_dependence on bitemporal, episodic and forgetting. Regenerating those
corpora dropped them correctly (the values described the old bytes) and nothing re-ran the stampers.
Restored, with no sha change.

Known defects shipping in this release

  • prospective/due-window's answer key is wrong. Class parity requires filler to use gold's own
    construction, and this shape asks a set-membership question whose membership criterion is that
    construction
    — so filler reminders falling inside the window satisfy the question and are not
    gold. The reference model is marked wrong for being right. A fix is written and measured
    (V8 4/18 → 13/18, reachable headroom 0.17 → 0.44) but is not in this release; it cannot land
    while the separability tell below is unfixed. Read its published 0.94 / 0.17 as mis-keyed, not
    hard.

  • prospective/not-yet-true is saturated at coverage 1.0 — BM25 returns gold for every question.
    Its headroom of 0.1667 is one question out of six.

  • A measured, unfixed separability tell. Gold's first assistant turn is longer than every
    distractor's in 8–10 of 50 prospective questions against 4.1 expected by chance, and 6 of 50 in
    episodic. Diagnosed in tools/diagnose_padding_asymmetry.py: gold's first assistant turn starts
    45 and 36 characters shorter than filler's in those two verticals, so it takes the most
    whole-sentence padding steps and the last one overshoots. semantic, temporal and bitemporal
    have comparable base texts and show no tell.

  • 61 of 75 measured real-world name collisions remain unremediated, declared in
    tools/name-collision-audit.json. The guard scans only temporal and conjunction, and only for
    the 14 remediated names.

  • forgetting/never-known is checked by nothing. Its 15 questions have no gold, so every validity
    arm reports 0/0 and both discrimination assertions silently continue.

Changed — defaults and CI a fork inherits

  • TypedMemEvalOptions.JudgeMaxOutputTokens 512 → 1500, the value the shipped judge calibration
    was measured at. ExternalBenchmarkOptions.JudgeMaxOutputTokens is unchanged at 256, so LongMemEval
    judge budgets are untouched.
  • llm-integration-tests.yml default deployment gpt-4o-minigpt-5.5.
  • A release-blocking gate in release.yml, between dotnet pack and dotnet nuget push, verifies
    every embedded corpus against the working tree — 9 corpora × 3 frameworks. A release can now fail
    after packing;
    that is this gate, not a packaging outage.
  • corpus-reproducibility.yml regenerates all nine corpora and compares bytes, triggered by changes
    to tools/gen_*, typedmemeval_common.py or the corpora.
  • New test classes: TypedMemEvalPackagedCorpusTests, TypedMemEvalNameCollisionTests,
    TypedMemEvalCoverageBandTests, TypedMemEvalDiscriminationTests, TypedMemEvalJudgeBudgetTests.
    Several read from disk, so the suite is no longer runnable outside a full repo checkout.

Sidecar schema

  • Added: coverage.echo_by_shape, coverage.per_shape_realised, and per shape
    headroom_perfect_selector, headroom_reachable, limited_by, discriminates, plus pair_*
    fields where a shape has arms.
  • Removed, and the absence is the disclosure: probes.empty_completion_disclosure,
    probes.empty_rate_scope and probes.no_answer_captured are conditionally emitted. With zero empty
    completions on the current corpora there is nothing to disclose. Read their absence as "none",
    not as "not measured".

Added

  • Every shape now publishes what KIND of hard it is, and a gate on whether it can rank anything.
    Implements ADR-028 §3a and §3e.

    V1 − V9 is what a perfect selector buys — a retriever returning gold and nothing else. A real
    retriever returns gold plus whatever else it ranks highly, so it cannot beat having everything:
    its ceiling is V8, not V1. Where those diverge the published headroom is unreachable, and a
    consumer reading it buys retrieval work that cannot help.

    shape headroom (perfect) reachable limited by
    prospective/due-window 0.94 0.17 reasoning
    semantic/co-reference 0.40 0.27 retrieval
    arithmetic/delta 0.90 0.90 retrieval

    due-window is the case that motivated it: 0.94 published, 0.17 actually reachable. Both figures
    and the classification now ship in every sidecar.

    The gate asserts each shape clears a 0.15 discrimination floor. It is a ratchet, not a
    wall — a gate that stays red for the life of a known-weak shape stops being read, and a regression
    introduced while fixing something else then lands invisibly. Two lists, both with reasons:
    saturated by design (WorkingMemory's three short ladder rungs, where the gradient is the
    measurement and deleting the saturation deletes the construct) and pending redesign. Listed
    shapes may improve and may not regress; anything unlisted must clear the floor outright.

    The pending-redesign list shipped empty. It held temporal/occurrence-order at 0.05 and
    bitemporal/belief-at-instant at 0.11 when the gate was written; both were fixed later in this
    same release, to 0.75 and 0.31, and removed. Their reasons are kept in the now-empty dictionary
    rather than deleted with the entries — a list that only ever grows is a list nobody reads.

    Red-first verified: raising the floor to 0.25 turns three verticals red; restored, 1073/1073 green.
    No corpus regenerates and no consumer control moves — the raw arm counts were already recorded,
    so this is reporting and acceptance only.

    The 0.15 floor is a judgement, not a measurement. Nothing in the data derives it; it sits where
    the distribution has a natural gap (three at 0.00, two between 0.05 and 0.11, twenty-five at 0.17+)
    and should be revisited once a second reference retriever exists, because the whole scale is
    BM25-relative.

Fixed

  • value-then-count asked how many times the speaker ORDERED while its sessions recorded sending
    paperwork, booking collections and raising jobs.
    The question hard-coded "put an order in with"
    for every draw while COUNTABLE supplies four different actions, so 19 of 20 questions asked
    about an event that never happened.
    V1 sat at 19/20 only because the model treated the recorded
    events as orders anyway — leniency, not soundness — and part of the shape's apparent difficulty
    was that incoherence rather than the join it exists to test.

    A disabled attempt at this fix stood in the file as if False, deriving the verb by
    predicate.split("the speaker ")[1].split(" with")[0] — which silently returns the whole phrase
    for "sent paperwork to {entity}", because there is no " with" to split on. The verb is now
    a third, explicit element of COUNTABLE: three parallel forms are longer and cannot go quietly
    wrong.

  • And the question asked for less than its gold required. Gold is "{n} times, with {entity}"
    and names the entity deliberately — without it a judge cannot tell a correct count attached to a
    superseded value from a correct answer
    . But the question asked only for a count, so "4
    times"
    was fully responsive and scored wrong (tme-cnj-006 failed V1 on exactly that), and for
    the nineteen that passed, naming the entity was verbosity rather than evidence the semantic half
    had been performed
    . A conjunction shape has to ask for both halves or it cannot observe that the
    join happened. It now asks for both.

    before after
    V1 49/50 50/50tme-cnj-006 resolved
    value-then-count V9 2/20 6/20
    value-then-count headroom 0.85 0.70
    vertical headroom 0.76 0.72

    The headroom drop is the honest part. Some of the old difficulty was the question asking about
    an event the corpus never recorded; a shape is not hard because it is incoherent. 0.70 is what the
    join is worth once the question is answerable as written.

    Corpus sha 99f609c9…9f6a0f37…; Conjunction controls reset — its third move this arc, all
    inside the pre-tag window.

  • Episodic ran one echo knob across three shapes whose coverage spanned 0.66, and the vertical
    mean reported none of it.
    Single-knob calibration put the mean at a healthy 0.682 while the
    shapes sat at participant-attribution 0.933, assistant-stated 0.800, list-order
    0.275. Attribution was effectively saturated — V9 14/15, headroom 0.07, no two retrievers
    distinguishable — and list-order was far below band. This is the mean-satisfiable-by-averaging
    defect the family already fixed for Arithmetic; Episodic simply never received it.

    Opted into per-shape calibration, which four verticals already use. Spread 0.66 → 0.033:

    shape coverage headroom
    participant-attribution 0.933 → 0.667 0.07 → 0.20
    assistant-stated 0.800 → 0.700 0.20 → 0.30
    list-order 0.275 → 0.692 1.00 → 0.73

    list-order traded extreme headroom for being in band — it was out of spec at 0.275 and is still
    the widest shape in the vertical. Attribution's residual limit is structural: it is a k=2
    question ("was that me or you?"), so a retriever that finds the gold session gets it right and
    guessing covers half the rest. 0.20 is what calibration can buy.

    Corpus sha 2c6000a6…542da3fa…; Episodic controls reset.

  • The saturation screen reported the chance floor as if it were evidence of saturation. Added
    yesterday comparing V9 pass rate against BM25 coverage, it flagged the recalibrated
    participant-attribution at +0.133. That was the screen, not the shape: on a closed-choice
    question a model with gold missing still picks from the named candidates and lands at 1/k, so
    the score to beat is coverage + (1 − coverage) / k, not coverage.

    Against its actual floor of 0.833, attribution reads −0.033 — slightly below what evidence
    plus guessing explains. The screen now computes k from the question text. Nothing in the family
    exceeds its floor; the largest positive is +0.083 on a declared ladder rung.

    The same arithmetic has now corrected V2's reading, V3's threshold, and this screen. A floor
    derived once does not transfer
    — each instrument needs it computed against its own budget and
    its own baseline.

  • The judge's default completion budget could silently void it on the deployments its own claim
    was measured on, and CI was certifying a different judge.
    Two coupled defects, fixed together
    because fixing either alone makes things worse.

    The budget. TypedMemEvalOptions.JudgeMaxOutputTokens defaulted to 512. That is ample for
    a non-reasoning model and dangerous on a reasoning one, where the budget covers reasoning tokens
    too: the model can spend the whole allowance thinking and return an empty completion. This
    family has already paid for that once — empty completions scored as answers until the silence
    accounting was built — and any consumer pointing the judge at a gpt-5.x deployment on defaults
    would have hit it silently.

    Now 1500, which is not a guess: it is the value the calibration test floors at, and therefore
    the configuration the published agreement number (0.987 over 230 cases) was actually measured
    under. A default that differs from the configuration the claim came from means the out-of-box
    judge is not the judge the claim describes.

    The deployment. The integration workflow defaulted to gpt-4o-mini while the judge's validity
    claim is measured on gpt-5.5. A gate exercising a different model certifies nothing about that
    claim
    — its green is decoration, and a regression on the calibrated deployment would not turn it
    red. The default is now the calibrated deployment; the cheaper models stay selectable for manual
    runs where the question is "does the plumbing work" rather than "is the judge still valid".
    If cost ever bites, run the calibrated judge less often rather than a different judge more
    often.

    Behaviour-affecting, and recorded as such. Raising a cap can flip a formerly-truncated
    verdict, so the effective value is now stamped per run in
    BenchmarkRunProvenance.JudgeMaxOutputTokens. Two runs differing only in this number are not
    comparable, and that has to be visible rather than inferred.

    Pinned by tests rather than left to a comment — and the first draft of that test failed,
    correctly: ExternalBenchmarkOptions carries the same setting at a default of 256 and serves
    LongMemEval, whose own calibration was measured there. The guarantee is not "the base default is
    1500" but that the TypedMemEval facade overrides it on the way through. A value corrected in
    one of two places is the applied-once shape, caught here by its own guard.

  • conjunction/order-then-value could not rank any two retrievers, and now it can. It measured
    V9 15/15, headroom 0.00 — a perfect retriever and a plain BM25 retriever scored identically —
    while its BM25 coverage was only 0.667. Those two numbers together are the diagnosis: the
    retriever was fetching two thirds of gold and the model still scored perfectly, so the missing
    third could not have mattered
    .

    One gold session read "{anchor} happened while {middle} was the {attribute}." — naming the
    anchor and the answer in a single sentence. It was also the only session carrying both terms the
    question names, so it was simultaneously the easiest to retrieve and sufficient on its own. The
    join the shape exists to test was never required.

    The anchor is now pinned to the switch events rather than to the value, so answering takes two
    hops in different sessions: place the anchor between the two switches, then read which value that
    switch moved to. Asserted at build time — no gold session contains both the anchor and the answer.

    shape V9 before after headroom
    order-then-value 15/15 8/15 0.00 → 0.47
    alias-then-count 1/15 1/15 0.93
    value-then-count 2/20 2/20 0.90 → 0.85

    Vertical headroom 0.64 → 0.76. Corpus sha b756721c…99f609c9…; Conjunction controls
    reset. Folded in before the tag at the consuming project's request — their recall-fan-out router
    reads this vertical by shape, and a dead cell in that instrument would have cost them two control
    resets instead of one.

    The separability gate refused the first two builds, and both refusals were the same mistake:
    filler must carry every construction gold uses. The new anchor frame was gold-only on the first
    build; the "That was the week of X" clause dating each switch was gold-only on the second — 30
    gold sessions, zero distractors. The rule was already written in this file for the previous
    anchor frame, and I applied it to one of the two new constructions and not the other.

Known

  • value-then-count asks for less than its gold requires. The question is "How many times did
    I put an order in with my {attribute}?"
    and gold is "{n} times, with {entity}." — so a model
    answering "4 times" is responsive to the question as written and scores as wrong. One question
    (tme-cnj-006) fails V1 on exactly this. The wider implication is the reason it is recorded
    rather than patched: for the other nineteen, naming the entity may be verbosity rather than
    evidence the join was performed
    . Scoped as its own change under the agreed one-shape-at-a-time
    sequencing, not folded into a fix for a different shape.

  • Forgetting's coverage was 30% a constant, and the echo search had been optimising it.
    realised_coverage answers 1.0 when a question has no gold — vacuously, all of nothing was
    found. That is the right answer and the wrong thing to average. Forgetting's 15 never-known
    probes are G=0 by design, so the published mean_realised of 0.670 was three-tenths a
    constant; measured over the questions that have gold it was 0.529, against a band floor of
    0.50. The vertical sat a third of the way to the floor from where it read.

    The correct treatment already existed in this codebase, in one place.
    calibrate_per_shape's per-shape search drops these questions with the reasoning written out
    beside it — "no coverage to realise … excluded rather than allowed to pin a shape at saturation
    it cannot leave"
    . It was never carried to the vertical mean in either path, and Forgetting has no
    per-shape calibration, so it fell exactly in the gap between them. A correct treatment written
    once and applied once is how a defect survives review: the reviewer sees the reasoning and assumes
    its reach.

    before after
    mean_realised 0.670 (30% constant; true 0.529) 0.629 (measured over 35 gold-bearing questions)
    echo 0.500 0.250
    V1 32/35 34/35
    V8 interference −0.0571 +0.0857
    headroom 0.2857 0.3714

    A negative interference cost meant the full haystack outscored gold-only, which is incoherent;
    it is now positive. Which of the three changes fixed it — the echo, the new filler bank, or the
    redraw — was not isolated
    , and isolating it would cost a regeneration that destroys the probe
    record, so it is reported as resolved rather than explained.

    Corpus sha ba759097…7fe6e166…; Forgetting controls reset. No other corpus touched.

  • CHOICES was a gold-only construction, and the separability gate caught it mid-recalibration.
    Regenerating Forgetting under the corrected mean changed the draw, and the gate refused to write:
    "went with" appeared in 12 gold sessions and zero distractors. Every phrasing in the bank
    was gold-only for the corpus's whole shipped life — it had stayed under the 20% bar by luck, not
    by design
    .

    Filler now draws from the same bank, at a share sized below the re-affirmation one so the
    invalidation-shaped filler V3 needs is not crowded out. Filler states a parity value, never a
    gold one, so the invariant the statement session rests on is untouched: the gold value still lives
    in the gold session and nowhere else.

    The rule was already written in this same file, one bank above: "Re-affirmation frames, drawn
    from ONE bank for gold and filler alike."
    CHOICES simply never received the treatment its
    neighbour did — the third instance today of a correct treatment applied once and not propagated.

  • V2 now ships the caveat that makes its own number readable — and the threshold was deliberately
    NOT "fixed".
    The arm asks each question with no haystack and rejects at 2 hits in 10. On an
    open question chance is ~0, so any hit is signal and the arm does exactly what it claims. On a
    closed-choice question the model can pick from the candidates the question itself names, so it
    reaches gold at 1/k without evidence — and the reject line of 0.20 sits below that floor
    (0.50 at k=2, 0.33 at k=3). 71 questions are in that position: temporal 35, prospective 21,
    episodic 15.

    Measured across 7,540 cached V2 answers, 97.9% are explicit declines"I have no way of
    knowing"
    — under a prompt that ends "If you have no way of knowing, say so plainly." So a pass
    records that the model did not volunteer an answer, which is real and useful, and is not the
    guessability question.

    The obvious repair was tested and rejected on evidence. Raising the bar to the statistically
    correct value — 9-of-10 at k=2, 7-of-10 at k=3 — keeps tme-tem-013 (10/10) and misses
    tme-tem-046
    (5/10), a question we know was leaking: the model was reasoning from real-world
    knowledge about Yarrow Shipbuilders and landing right about half the time. Its 5 hits are
    statistically indistinguishable from guessing (p=0.213) and were not guessing. Calibrating the
    bar would trade a real detection for a tidier statistic
    — the naive threshold is more
    sensitive precisely because the model declines, so against a decliner any hit means it
    volunteered.

    So the number stays and the claim narrows. Every sidecar now carries a closed_choice block
    with the per-question candidate count and how many passes are not evidence of
    non-inferability
    . Compare with V3, where the identical arithmetic produced the opposite
    disposition: there the budget was 3 samples, no threshold could clear the floor, and not
    decidable
    was the honest verdict. Same rule, different budgets, different answers — which is why
    it had to be derived per arm rather than applied as a policy.

    No corpus sha moves, no verdict changes, zero model calls — the hit counts were already
    recorded, because V2 never short-circuited.

  • V3's leak threshold is now read against the chance floor, and 24 of its passes were never
    earned.
    The arm draws 3 ablation samples and condemned a question on a single hit. On a
    closed-choice question that is not a measurement: the question hands the model its candidates, so
    with every gold session removed it still reaches gold at 1/k. Against a pure guesser the
    false-failure rate is 1 − (1 − 1/k)³0.875 at k=2, 0.704 at k=3.

    The threshold is now the smallest h whose tail P(X ≥ h | 1/k) falls under 0.05, and a question
    is not decidable when no h within the sample budget reaches it. At 3 samples that gives
    k=2 → undecidable (even 3-of-3 leaves p=0.125), k=3 → 3-of-3, open → 1 hit as before. The loop
    no longer short-circuits on the first hit, because with a threshold above one the count is the
    evidence
    and a question that stopped at sample 0 can never be compared against a 3-of-3 bar.

    This retires a hand-curated list. _V3_GUESSABLE_SHAPES named occurrence-order, and this
    session added recency after four of its questions "failed" a clean regeneration. Both are
    consequences of the arithmetic above rather than facts about those shapes — and the list had
    silently missed Episodic's participant-attribution ("Was that me or you?", k=2) for its entire
    shipped life. A curated exemption list is a chance-floor bug that somebody patched once.

    vertical V3 before after what moved
    prospective 44/44 29/29 15 unearned passes removed
    episodic 44/50 35/35 9 unearned passes and 6 false failures removed
    temporal 15/15 30/30 recency restored to measurement — and all 15 pass at 3-of-3

    Temporal is the evidence the rule is right: measured against a threshold it can actually fail,
    every recency question passes. They never leaked; the single-hit rule was condemning coin flips.

    24 unearned passes removed, 6 false failures corrected, and no corpus sha moves — this is a
    probe-record change only, so no consumer control resets. No other arm changed: V1, V2, V6, V8 and
    V9 are identical across all nine verticals. The full family was re-probed under the new rule for
    16 new calls, everything else served from cache.

    Silence handling followed the threshold rather than staying at one: a silent draw now disqualifies
    only where hits-seen plus silent-draws could have reached the bar, which is the rule V2 already
    used. Records carry v3_hits, v3_required_hits and v3_candidates so the verdict can be
    re-derived instead of trusted.

  • The corpora were built from real place-names, and a model was answering from world knowledge.
    V2 asked "Which came first, the Fenn commissioning or the Yarrow move?" with no haystack at
    all
    and the reference model replied "Yarrow moved its shipbuilding operations to Scotstoun,
    Glasgow in the early 1900s, while the Fenn commissioning was during World War II."
    Real facts
    about Yarrow Shipbuilders. The generator's comment said this could not happen — "Invented
    milestone names. Arbitrary by construction (V2): nothing about a name makes it likelier to be
    first"
    — and had never been measured.

    A sweep of every entity bank in the family (tools/audit_name_collisions.py, 163 names, 0
    unparsed) found 75 are real-world referents: Harrow and Kessel are places, Bellamy and Vance
    and Ruskin are people, Calder is a river, Esker is a company.

    V2 sees only half of this defect. It scores a leak only when the leak agrees with gold. Five
    temporal questions show world-knowledge reasoning; V2 flagged two. tme-tem-013 hit 10/10
    purely because the corpus happened to order those events the way history did — ordered the other
    way, the model would have been confidently wrong ten times out of ten and the question would have
    passed. The arm that caught this cannot certify its repair, which is why the guard is a
    deterministic test rather than another probe.

    Fixed where the harm is, declared where it is not. A real referent is only exploitable when
    its real-world facts answer the question asked: "Meridian Tools" being a real brand does not tell
    a model which vendor the user chose, but "Yarrow" moving in 1906 does tell it which milestone
    came first. So the three ordering banks were remediated — temporal:MILESTONES (9 of 12
    colliding), temporal:FILLER_MILESTONES (5 of 6), conjunction:MILESTONES (4 of 6) — and the
    other 61 collisions are declared, not rewritten, in tools/name-collision-audit.json with the
    reasoning. Rewriting them would move all nine corpus shas and reset every control the consuming
    project holds, for no measured leak.

    Replacement names were verified by the instrument that condemned the originals, not chosen by
    taste. Re-probed under the V2 condition, the new names produce bare declines ("I have no way of
    knowing."
    ) with no world-knowledge reasoning.

    before after
    temporal V2 48/50 50/50 — both leaks gone
    temporal V9 / headroom 30/50 / 0.40 33/50 / 0.34
    conjunction V1 49/50 50/50
    conjunction V9 / headroom 18/50 / 0.62 18/50 / 0.64

    Corpus shas move: temporal 31d26e60…124458d0…, conjunction c62ef477…b756721c….
    Both verticals' controls reset. No other corpus is touched.

  • V3 was false-failing recency seven times in ten. A clean regeneration took temporal's V3 from
    30/30 to 26/30 and every one of the four "failures" was a recency question. Nothing had leaked.
    V3 draws 3 ablation samples and condemns a question on a single hit, so against a model
    guessing among the k candidates the question itself names, the false-failure rate is
    1 − (1 − 1/k)³:

    shape k false-failure rate strictest threshold
    occurrence-order 2 0.875 3-of-3 still leaves p=0.125 — undecidable
    recency 3 0.704 3-of-3 would give p=0.037

    occurrence-order was already exempt for exactly this reason; recency hands the model three
    candidates and had never been added. It is now, and the arithmetic is written down beside the set
    instead of left as a curated list. The previous run's 30/30 was luck, not evidence — the model
    usually declines rather than guessing, so the arm's verdict on these shapes swings on whether it
    happened to guess.

    The proper fix is a chance-aware threshold that derives the exempt set rather than curating it,
    and would keep recency measured at 3-of-3 instead of dropping it. That changes V3 semantics for
    every closed-choice shape in the family, so it is queued as its own change rather than
    smuggled in beside a name fix.

Added

  • tools/audit_name_collisions.py — asks the reference model whether it can state a concrete
    fact about each name in every entity bank, and refuses to count an unparseable reply as
    "invented". Its measured result is committed to tools/name-collision-audit.json so the finding
    and the harm assessment travel with the repository rather than living in a chat log.
  • TypedMemEvalNameCollisionTests — a deterministic guard, red-first verified against the old
    corpus, asserting that no remediated name reaches a question or answer in the two verticals whose
    questions turn on order. It also asserts the audit record still covers all three ordering banks,
    so a shrunken record cannot make the guard vacuous.