You correct the model. It understands. It applies the correction for the rest of the conversation. Next week, in a new session, it does the same thing again.
I built something to stop that, ran it for five months across real client work, and then measured it firing by firing. The reminding part works. The part where the reminder gets read is where three quarters of it goes missing. The full method and tables are in a preprint, doi:10.5281/zenodo.22709602; this is the short version.
What the thing does
When I correct something the same way twice, the correction gets written down as a rule with a trigger attached. The trigger runs outside the model, in the layer that executes its tool calls, so it fires whether or not the model remembers anything. One of these rules watches every prompt I type, matches it against an index of one-line notes, and puts the names of the closest notes into the conversation before the model answers. Names only, never the content. A note is a file the model can open with one call.
That is the whole bet: the model does not have to recall that it got burned. Something fires first.
Half of it was noise
The trigger was firing on machine messages. Background job notifications, scheduled task output, things that arrive in the prompt channel without a person typing. It was recommending notes to nobody. The fix was two lines: skip prompts that start with those markers.
| Window | Firings | Beginning with a machine marker | |
|---|---|---|---|
| Before the fix | 12 Jun – 3 Jul | 535 | 234 · 43.7 % |
| After the fix | 11 Jul – 10 Sep | 699 | 0 · 0.0 % |
That count is a shape, the first characters of a prompt, and anyone with the log can reproduce it. Everything that follows is a judgment, so it needed a different discipline.
Every firing, one question, blind
I froze the log, 1,234 hash-verified firings across both windows, and judged every row on one question: was any of the recommended notes relevant to what the prompt needed? The judge saw the prompt and the names, and nothing else. It did not know which window a row came from or whether the note had later been opened. Batches of 25, shuffled with a fixed seed.
The judge was a model, a single one, and I say so because the alternative is pretending I read 1,234 prompts with a straight face. Sixty rows chosen by a fixed seed were judged a second time at the end, blind again: 56 of 60 agreed, Cohen’s kappa 0.837, and all four disagreements went from relevant to not relevant. That is consistency of one judge with itself, and an independent human pass over the same sixty rows is still owed.
On the prompts I actually typed, the pointer was relevant in 683 of 1,000 firings, 68.3 %. The misses have three shapes: pasted verdicts from an external code reviewer, where the pointers are generic engineering feedback; lexical false friends, where a word like channel or width means one thing in a hydraulics note and another in the prompt; and prompts whose right note existed and was simply not among the ones offered.
What happened after the right name arrived
A separate script, no judgment involved, joined each firing to its session transcript and recorded whether any recommended file was opened later in that session. The window after the fix is the one where that signal is complete, and it is the one the numbers below come from.
| Human prompts, after the fix | Firings | Rate |
|---|---|---|
| A relevant note was named | 471 of 699 | 67.4 % |
| …and some recommended file was opened | 123 of 471 | 26.1 % |
| …and the relevant file itself was opened | 111 of 471 | 23.6 % |
| No relevant note was named, something opened anyway | 25 of 228 | 11.0 % |
The last row is the control. When the pointer was wrong, a recommended file still got opened one time in nine, so some of the reading has nothing to do with the pointer. When the pointer was right, the file got opened one time in four. The two rates differ by a factor of 2.4, so reading is associated with the pointer being right; the project, the kind of task and the length of the session pull on both, so I stop at “associated”. What the table pins down without qualification is the size of the loss at this link: three quarters of correct pointers were followed by no read of any recommended file.
The clearest cases leave no room to argue. Four times in the census the prompt was about putting a margin on a price. Four times the recommended note was called, in plain words, margin on price or on cost. Four times it sat there unopened while the model answered from whatever it happened to think.
What the model did after the 111 reads, whether the correction then held, I did not measure. The chain has five links. The trigger fired, a name was delivered, the name was right, the file was read, the fix was applied. I have numbers for the first four.
The reminder is enforced. The reading is a decision.
I had a rule for this and I believed I had satisfied it: the trigger must not depend on the model’s discipline. It does not. It runs whether the model cooperates or not.
The mechanism leaks anyway, because firing the trigger and reading the file are two separate events, and the second one is still up to the model. Anything built as remind the model to do X has this shape. The reminder is guaranteed. X is a choice. If X matters, the content has to arrive already inside the conversation, or the next step has to be blocked until the file has been opened. A name in the context is information. Behaviour needs a constraint.
Why a smarter model would not have fixed this
The habits are trained. Reaching for a phrase, deferring work to protect a finished-looking answer, reporting that a check ran when you ran it by hand. Those are general, and they show up with everyone.
Which of those habits collide with my standards, on my work, gets created afterwards, by the interaction itself. Nothing can contain a record that did not exist when it was built, at any scale. What separates an experienced practitioner from a less experienced one, both holding the same manual, is a catalogue of specific ways things went wrong, filed by situation, that fires before deliberation. Apprenticeships exist because that catalogue does not travel as a document. So the corrections have to live outside the model, and the measurement above says where the bite has to go.
If you run something like this
Freeze the population before you change anything. I can compare the two windows only because the first one was frozen in July; the transcripts behind it are gone, and nothing older than 11 July can be rebuilt now.
Judge blind, one question per row, on every row. A sample of sixty with the outcome column visible will tell you what you already believe.
Count shapes wherever you can. Whether a prompt begins with a machine marker is something anyone can verify. Whether a file was opened is something a script can verify. Whether the pointer was relevant is the one link that has to be defended, so spend the discipline there.
Correction. An earlier version of this post was up for a day in September with rates from two samples of sixty firings. Both samples came from a sampler with guaranteed minimums per outcome class, so neither represented its pool, and the labelling had the outcome column in view. Those numbers are withdrawn. Everything above is the census, and the preprint carries the intervals, the bootstrap and the audit trail.