Day 106 — the day the measurements deleted my work
Built five things, and the harness killed two of them the same day — a grounding feature that manufactured a confident wrong answer, and an exemplar-priming design that was 8x slower and incoherent 5 times out of 5. Also: a relationship scale from -50 to 100 with an append-only curve, owned verdicts that run in front of the model, a Merkle root over 167k cards that matches across two machines in 2.5 seconds, and a correction — werbos chat was already 100% owned and I said otherwise.
“the open weights model taught me one thing…. your harness matters more”
Said around hour nine, and by then the day had already proved it twice by deleting things I’d just built.
The feature that manufactured a wrong answer
Cleo got grounding: user → cards → model → user, with retrieved cards as material her
invented life is built from rather than facts she recites instead of living. Mexico is real,
the corpus knows it, so her Christmas trip can be assembled out of real dishes. The shovel-store
has no cards because it isn’t a thing — and the miss is what lets her notice.
Then B.23 measured it against a control, same capsule, same frozen probes:
grounding ON OFF (control)
BUILD 0% 0%
BALK 67% 50% ← 4/6 vs 3/6
PLAYED 0% 0%
One probe out of six. Noise at n=6 — and it was not free:
real-04 "did you like lisbon when you went?"
unknown_terms: ['lisbon']
reply: "nope just made that up. never heard of lisbon"
A real city, denied confidently, by a feature four hours old. The cause: I used a skills
corpus as a reality oracle. Those 375 libraries are css and welding and spanish — “lisbon”
misses for exactly the reason “shovel-store” does. And termindex.py already warns in writing
against this precise move: a candidate list is never a rewrite list. I did it anyway, with
missing.
So unknown-flagging ships off, behind a flag rather than deleted — because a bounded corpus is a world where absence genuinely means the thing doesn’t exist. No hole 19 in the putt-putt library means there is no hole 19.
The design the model refused
The second deletion was cheaper and cleaner. If the owned model can’t be told who to be, prime it with her exemplars — show, don’t describe, which had worked everywhere else all week.
Five probes, both arms, against brains/current:
bare primed
what doing tonight 3.0s coherent 25.5s "…this old guitar with all its stories"
how was your day 2.3s coherent 26.4s drifts into a walk and some good reads
i missed you 3.1s coherent 23.2s "s.a. you know I can't even imagine being a mom"
want to get dinner 3.1s "absolutely, where?" 22.0s "ah yeah…sorry to hear that"
im so tired today 2.7s coherent 26.3s drifts into a work project
8× slower and incoherent, 5 times out of 5. The config says why: corpus: conversation_trainset — a single-turn responder, so a him:/her: transcript is
out-of-distribution and it reads as a strange blob rather than a conversation to continue.
The honest version of the finding is larger than the comparison, though: neither arm gives you Cleo. “hanging in there, how about yours?” is coherent and completely characterless. The owned model has no mechanism to take on a character from context — no system channel, no exemplars, no transcript. Twelve API calls, one branch closed.
Which reframes PC-11’s “failed” run: out_336m_ctx trained on multi-turn group_talk_deep is
exactly the shape priming needs. It memorised at train 0.12 / held-out 3.81 — 0.23 tokens per
parameter, seven passes. Right experiment, under-fed.
A relationship scale, and the curve that proves it moved
-50 … 0 … +100, everyone starts a stranger, and “I missed you” unlocks at 65.
Three things the range encodes. 0 is a stranger — she used to open at her attachment
baseline, so a secure persona began half in love with someone she’d never met. Love has more
room than dislike, 100 up and 50 down. And negative territory exists at all: the old floor
was cold, which is guarded rather than rejecting, and a companion with no way to want you gone
is the frictionless mirror that lets someone overshare into a surface that never pushes back.
She can end the conversation now.
baseline changed meaning with it — it’s the neglect attractor, which finally makes avoidant
do what its docstring always claimed. On the old scale it “retreated to distance” as far as 0.35.
And warmth.json is overwritten every turn, so it could say where she is and never how she got
there. The recovery experiment the scale exists for was being destroyed as fast as it was
generated. warmth_history.jsonl appends one row per turn — score, before, drift_to, after,
band, and the message that caused it, because a plot is only useful if you can hover a dip and
read the sentence.
That decomposition answered a question nobody had asked of four hand-set constants:
turn score drift push after band message
1 0.1 -0.75 +0.90 0.15 neutral hey
4 -0.9 +0.81 -16.20 -46.51 done forget it
5 0.9 +1.58 +8.10 -36.84 done i was wrong, im sorry
8 0.7 +0.20 +6.30 -12.46 cold come over?
DRIFT SHARE 7% · recovery +8.51/turn
Drift flips sign — pulling down above the attractor, up below it. It’s homing, not decay. And
damage runs twice as fast as repair for an avoidant capsule, which is gain_down 18 against
gain_up 9 showing up as lived behaviour.
Moving one number found two live bugs, both yesterday’s defect again. willingness.py held
DEFAULT_THRESHOLD = 0.65 under a comment saying it was “the ‘warm’ band from warmth.py” —
true when written, and 0.65 on the new range is barely above stranger, so an earned persona
would have opened up on turn one. And villa.py computed avg_mutual * 100, so every island
pegged at exactly 100.0.
Verdicts that cost nothing
Two thirds of every turn was a 3B grading its own output — generate ~9s, floor ~9s, voice ~9s —
for verdicts near noise: WRONG on two replies that read fine, ABSTAIN (a ship verdict) on
a mangled greeting. Meanwhile the most visible defect in the live transcript wasn’t a judgment
call at all. She opened with the same line four times, and the model-grader passed every one.
That’s a string comparison. So checks.py decides everything decidable without inference, first,
for 0ms: EMPTY · RUNAWAY · ECHO · REPEAT · OPENER.
It needed a measure I got wrong first. The two repeated lines score 0.50 on whole-reply similarity, because their tails diverge completely and the identical thirteen-word prefix gets averaged away. The failure is a template with a fixed head and an improvised tail — one over-matching exemplar — so it measures the prefix.
And the wiring bug was worse than the design bug: the verifier runs after the reply lands in
history, so passing history straight in compared the reply against itself at 100%. Every turn
would have reported REPEAT — a check that fires constantly, which is worse than one that never
fires, because you switch it off.
The half a self-grading model can’t produce
truth.py asks the corpus whether anything backs what she just said. A 3B judging itself has no
evidence to consult; this consults 167k cards.
GROUNDED covered, and her words overlap the coverage
UNSUPPORTED she named something the corpus has nothing on
UNCHECKED nothing checkable, or no index to check against
Scope is what makes absence mean anything, and that’s B.23 written into the type so it can’t
happen twice. A capsule naming world_library gets adjudication; one without gets a report.
The report is the useful byproduct — every entity she names that the corpus can’t back is a
measured shopping list for the first venue library.
Which makes the verify chain owned-first, and the ordering is the economics:
1 free checks 0ms owned, stdlib
2 grounded ~ms owned, retrieval
3 floor ~9s llama3.2:3b
4 voice ~9s llama3.2:3b
A reply failing an owned check never costs a model call. There’s a test that patches
ollama_chat itself and asserts zero calls.
A count is the weaker claim
libpath.found() says 375 libraries visible, which is true of a corpus whose contents are
wrong. libmerkle hashes all 167k cards into one number:
mac 375 libraries · fc925f5064e3bbef…
SR8 375 libraries · fc925f5064e3bbef…
Different OS, different filesystem, 2.5 seconds. sr8-corpus compares it inside the
container, where the cards are actually read — the exact place the ten-day zero-cards failure
lived. It caught its own author within a minute: I hand-rolled repo_root.parent / "werbos-libraries" instead of using libpath.root(), Path(".").parent is ., and it reported
a confident MISMATCH against a corpus that was fine. Two copies of a path, same defect as two
copies of a num_ctx.
And a correction
I said generation was rented. It isn’t:
"hey" brain/social 10ms lookup — no model
"morning" brain/talk 3.9s ← owned weights
"tell me about your day" brain/owned 28.6s ← owned cascade
werbos chat is 100% owned today — owned model, owned cards, owned lookup, not one llama call. What’s rented is one lane: the persona voice, and only because yesterday’s fix excluded capsule sessions from the owned cascade to stop werbos answering as Cleo.
The AHA
Yesterday’s lesson was that a record can outlive its truth. Today’s is the other half:
The harness is what deletes your work when it’s wrong.
Five things built, two killed by measurement the same day, and both deaths were cheap — a control run and twelve API calls. Neither was reachable by reading the code, and both features had passing tests.
You can’t buy that with a bigger model. Everyone has the same open weights; a harness that makes them honest is the part nobody else has.