Day 65 — 'anyone can mint one now — and the ruler still won't lie to me.'
Two arcs today, and they pull in opposite directions on purpose. First, the Factory: the whole owned-model ladder — tokenizer, reader-gap, grade, distill, gate, freeze — wired behind one mint() call and one self-contained page, so anyone can turn a folder of their own files into a signed model that learns, never lies, and self-improves safely. Four new document readers (pdf, docx, xlsx, json) turn your stuff into a corpus the factory eats; 'vary the library, not the model' is finally something you DO, not just read. Second, an honest wall: I took the owned reader onto a real GPU and threw everything at it — 8,000 synthetic pairs, models from 3.4M to 57M params, adversarial decoy data — and every single run sat at 3–6/19 on the Gauntlet, half of the 1-million-param champion's 12/19. The ruler refused to let me buy my way past reading with compute. The lever was never size or volume; it's data quality — real teacher-picked fences over real leaves. And one ruler fix so tomorrow's chase is honest: the v2 Gauntlet closes two L2 questions whose answers were buried out of retrieval, so its ceiling is a true 19, not a fake-reachable 17.
“anyone can mint one now — and the ruler still won’t lie to me.”
Two things happened today that point in opposite directions, which is exactly how a real project moves: one that says look how far the owned stack has come, and one that says here is precisely how far it hasn’t. Both are true. Neither cancels the other.
The Factory
Everything the last two weeks built — the owned tokenizer, the fenced reader, reader-gap,
grade-labels, distill, the gate, freeze — was a ladder you had to climb by hand, one Python
call per rung. Today it became one call and one page. mint() chains the real seams
end to end and streams every event — stage, row, grader verdict, loss, gate decision, minted
brain — and a stdlib http.server (zero deps, same ethos as the engine) serves a single
self-contained page on :8095 that renders a mint live: three knobs, a loss curve,
streaming verdicts scrolling past, a minted-brain card at the end, and a panel of your frozen
brains — each one selectable as the base for the next mint. werbos weld dashboard | mint
are first-class verbs now (89de132).
This is the thesis stopped being a slogan. “Vary the library, not the model” is now a thing you can do: point the Factory at a corpus, turn the knobs, watch a signed model that learns, never lies, and self-improves fall out the bottom — gated the whole way by a ruler it never trained on.
Bring your own corpus
A Factory needs feedstock, so today it grew a mouth. Four new document readers extend the
werbos.data interface — pdf (pypdf), docx (stdlib zip + defusedxml, no
python-docx dependency), xlsx (openpyxl), and json/jsonl — and corpus_build maps
their content-addressed chunks into factory rows {cid, source, text}, dedups by chunk id,
and optionally refines them into short fact-leaves (14bfefc). werbos weld corpus --in <files/dirs> --out corpus.json --refine. On the dashboard the corpus got promoted out of a
cramped advanced field into a headline Library block above the three knobs — because the
corpus is the substrate the whole thing acts on — plus an n_ctx knob and a live
“build corpus from files” ingest row (9edf6d0, bbbc92b). Drop in your docx’s, get a model
that reads them. (And numpy is finally a declared runtime dep — the owned stack needs it, so
say so — bd8d9c6.)
The honest wall
Then I spent the day trying to make the owned reader smarter, on a real GPU this time, and the honest answer is: I couldn’t, and I know exactly why. “Reverse train” — running an instruct model forward on an RTX 5070 Ti to mint training data — worked beautifully, ~500 pairs a minute where the CPU box couldn’t finish one batch in four. So I minted 8,000 pairs, then swept: 57M params, 3.4M params, adversarial data engineered so the decoy out-matches the answer on every query (the exact Gauntlet trap). Every configuration landed at 3–6/19.
The champion — a 1-million-param numpy model already sitting in the repo — scores 12/19 on the same harness. A model 57× smaller doubled my best GPU run. More data didn’t help; a smaller model didn’t help; adversarial data made it worse. The wall isn’t compute, it’s what I was feeding it: synthetic templates teach keyword-matching, and the Gauntlet’s whole point is that keyword-matching is a trap. The champion’s edge is its data — real, teacher-picked fences over real leaves — not its size. And one thing nobody clears, champion included: relate (opposites) is 0/5 for every from-scratch reader. The road to a perfect score runs straight through the one wall none of us has cracked.
That’s not a defeat. That’s the ruler doing its job — refusing to let me confuse fast with better.
A fairer ruler
Which is why the last commit is a ruler fix. An audit of the v1 Gauntlet found two L2 opposites — day→night, big→small — whose answer passage was buried out of the top-6 candidates by decoys, so no reader could ever answer them. v1’s honest ceiling was 17/19, not 19; those two questions measured nothing. v2 rewords only those two passages so the query anchor retrieves them while the decoys stay strong — fair reading-past-decoy tests, all 19 answerable in principle, ceiling a true 19. v1 stays byte-frozen as published history (71218e5). Tomorrow’s chase gets to be honest about what winning even means.