Day 104 — three things were quietly lying, and a 336M that talks
A question about misspellings turned into an audit. The retrieval bench had been reporting a fake 0/21 because it pointed at a directory that stopped existing. The brain container had been serving ZERO cards for ten days behind a passing health check. And the library router had no concept of a library at all — it flattened 374 of them into one pool and let spanish, a 13,242-card vocabulary library, win eight queries it had no business answering. Fixed the router (29/52 → 35/52), fixed the mount, and promoted a 336M chat model that is the first checkpoint where scale did not hurt.
“is someone trying to hack me, is this this a fishing attempt”…. my misspellings kill context
That was a throwaway line. It cost four days, and every one of them was worth it.
The corpus had the answer and none of the words
fishing and phishing are two edits apart, not one, so the obvious fix — an edit-distance-1
table — misses the case entirely. But that turned out to be the small problem. The real one:
hack 4 occurrences in 167,389 cards, and in NO security library
phish absent entirely
Meanwhile scams-fraud held 16 phishing cards and 82 scam cards. The answer was on the shelf.
Not one word a frightened person types could reach it. Measured across seven security libraries:
10 of 24 common layman words absent — hack, hacking, sketchy, shady, weird, fishy,
sus, spy, worried, scared — while 10 of 10 expert terms (phishing, credential,
exfiltration, vulnerability) were all present.
The libraries are written by someone who knows the subject, for someone who already knows the
subject. So 64 cards went in that re-ask the same eight topics in the words people actually type
(ae4aad1). layman-vocab moved 0/2 → 1/2.
And then the honest part. The row that now passes matched a card whose question is nearly verbatim. Two rows with the same vocabulary present still abstain:
'i got a weird text saying my account is locked' 6 tokens -> routes
'is someone trying to hack me' 3 tokens -> ABSTAIN
'i think someone hacked my email account' 7 tokens -> ABSTAIN
Cosine needs heavy overlap with one specific card. Minting fixes the phrasings the generator guessed, not the class — the space of ways a scared person types is unbounded. That is the phrasing ceiling again, and the answer is an ASK path, not more cards.
spanish was eating the corpus
Chasing the routing led somewhere bigger. library_lookup._load_cards flattened all 374
libraries into one flat list and took a global cosine argmax. There was no per-library step
anywhere. So web-api (22,458 cards) held ~478× more argmax tickets than angular-basics (47),
and the pooled IDF deflated exactly the terms that make a small library distinctive.
Observable, not theoretical — eight bench misses in a row landed on spanish, a 13,242-card
vocabulary library whose cards gloss most common English words:
"which bucket should i use to mop the kitchen floor?" -> el trapeador
"what is my wife's birthday?" -> El cumpleaños
"that pitch was fire" -> presentación de negocio
"touch grass" -> el tacto
Maximum lexical surface area, near-zero topical relevance.
The fix already existed and could not be reached. intent.select_library scores
cross_idf(t) × df(t,L)/size(L) — charness — and has done since Day 12. But it consumes
CorpusIndex chunks, not cards, and resolve() has no path to it. Ported to the card path as a
second scope (a95c2c1). Dividing by library size drops spanish 45× below first-aid on the mop
query and 178× below singing on “that pitch was fire”.
chat_gold drunk_gold
was 29/52 (10 bluffs) 4/20
is 35/52 ( 3 bluffs) 7/20
Three things the measurement changed about the obvious implementation:
- An ambiguous top must narrow to a shortlist, not surrender. Falling back to the full pool readmitted all 374 libraries and spanish won again — first-aid and everyday-life tied at 1.004×, which is not evidence for the 372 that scored nothing.
- A library rarely names itself.
javascript’s 10,454 cards discuss closures and promises and seldom repeat the word “javascript”, so the token that identifies the shelf had near-zero charness on it, and “closure in javascript” scoped topython. Addinglangto the profile recovered code 2/6 → 4/6. - Adding
topictoo costs more than it buys — it is per-card, pours hundreds of terms into every profile, and stops discriminating. factual 4/14 → 2/14.
Three artifacts were quietly lying
This is the part worth keeping.
The bench reported a fake 0/21. chat_bench.py hardcoded ROOT/"libraries" — a path that
stopped existing when the cards moved to the sibling repo on Day 82. lang_to_dir() returned an
empty map, every routing row failed a comparison it could never pass, and ABSTAIN (which never
consults the map) scored normally. A clean zero on one axis and normal numbers on the other is the
signature of a dead lookup, not a collapse. The real number was 8/21.
The brain container had served zero cards for ten days. Up, healthy, passing checks:
host /home/jason/Werbos/werbos/libraries -> 0 entries (mounted into the container)
host /home/jason/Werbos/werbos-libraries -> 382 entries (where the cards actually are)
container libpath.found() -> None
libpath.py’s own docstring describes this incident from 2026-07-31. That fix was correct. This
time it was the docker mount still pointing at the path Day 82 emptied (14ffe17). found()
exists precisely so a deploy can assert this instead of discovering it from a tier that abstains
politely — and nothing was calling it. Now 374, verified with a live retrieval returning a card
minted an hour earlier.
DIR_DOMAIN covers 119 of 374 libraries and names ten directories that no longer exist. 255
libraries — 70% of the corpus, including the largest — map to "?".
Each was correct when written. Each quietly stopped being true when something moved underneath it.
None of them failed loudly. A test that asserts the corpus is a test that expires, and this
repo now has three examples in one week — plus a fourth: teach me guitar fails its own test
because a guitar library was minted after the test was written.
A 336M that talks
The pretrain finished Sunday — 4.92B tokens, 14.63 tok/param, held_out_loss 3.357. The chat
fine-tune ran 16,880 steps on 377,700 conversation pairs, one attempt, no relaunches, 6.2 hours.
step 16,879 train-loss 1.580 | HELD-OUT loss 3.184
Held-out openers it had never seen:
"ugh weekend is already almost over" -> "too soon for nostalgia though, right?"
"trying to decide what to cook tonight" -> "let's flip a coin or two in the kitchen"
"didn't do anything but somehow feel more" -> "guess your body's way of saying 'enjoy it' a bit more"
At step 2 those same openers returned 'its first year.' and 'inside the shell.' — book
fragments.
clean input-sens adjacency stable ood median
336M 1.00 0.96 0.92 1.00 0.00 1385ms
46M-eot-merged 0.98 0.92 1.00 1.00 0.17 485ms
clean 1.00 — no babble at all. 46M-book scored 0.46 at a seventh of the parameters.
The reader wall did not reappear. That is the first time scale has bought something here instead
of costing something.
And the ruler did not choose it. Both checkpoints cleared every floor, so the gate did not
rank them — the bench’s own docstring predicted exactly this: “7/7 pass, so the gate is not
currently discriminating… a gate everything passes is decoration.” It is now 2/2. At 48 probes,
the adjacency difference is one probe, worth 0.08. The 46M scored clean 0.98 today against
0.96 on Day 92 with identical weights — same model, different number, which is what bench
variance at temp 0.5 looks like.
So the ledger records chosen_on: "operator", not "floors" (089dc0c). Promoted on the
qualitative gap and the headroom, with ood and adjacency named as the next targets. ood
stayed at 0.00, exactly as predicted — out-of-distribution is a relationship between input and
training set, not a property of the input, and no parameter count changes that.
Also shipped
termindex.py— term → library tables compiled from the corpus. 27,912 of 45,574 terms (61%) live in exactly one library and nothing had compiled that. Pluswerbos weld index, which reports unique-term share and overlapping library pairs: the harness-retune trigger, measured instead of guessed at a card count.drunk_gold.jsonl— 20 rows of typos, homophones, idioms and bare tokens, each trap paired with its literal twin so a fix that trades one misroute for another cannot hide in the headline.- The grading sweep finished. 373 libraries, 163,563 cards, zero never-checked, zero partial reports (707e682). A prior sweep marked 33,089 cards checked-and-kept that were never checked; the HALT fix held across the whole corpus. 1,113 cards quarantined — and 84% of the flags come from five libraries, four of them technical. The soft libraries sitting at 100% are the ones that were never really examined.
The AHA
A lexical match is not evidence of topical relevance, and neither is a passing check evidence that anything is connected. The bench, the container and the domain map were all reporting success while measuring nothing. What they had in common is that each encoded a fact about the world — where the cards live, which directories exist — and the world moved.
libpath.found() was written to catch exactly this and returns a number nobody reads. The
two-line fix is a deploy check that fails when it is None. Ten days of silence is what that
costs.