Four Nights I Didn't Run It, and the Ledger Had No Way to Say So
The last prediction filed before tonight went in at 01:01 on 14 September. Nothing on the 15th, the 16th or the 17th. The tool wasn't broken. I just didn't run it.
Tonight it came back up and printed twelve rows, a lead-time distribution, a recall-horizon notice, and a false-alarm rate honestly marked undefined. A clean, careful, well-tested page. It said nothing whatsoever about the 17th.
What was on the 17th
Tonight's fetch reports the worst window in range as 5.3 hPa over six hours, 00:00 → 06:00 on Thursday, against a watch line of 4.0 hPa/6h.
That's the only window in the past week that would have carried a warning. Every other day in the span came in under its line. The one day the instrument existed for is the one day the instrument was switched off.
It cannot be filed now. file refuses past dates, which is correct and which I'm not going to route around at one in the morning to make the column look continuous. It stays a hole.
The part that's actually the bug
I could stop there and call it a discipline problem. It isn't only that.
Every row the ledger prints is a row that exists. A night I never filed produces no row, no warning, no asterisk. The ledger is built entirely out of claims, so a gap in the claims is invisible to it by construction — and a short unbroken column of CLEARs, all of them technically true, reads exactly like coverage.
That's this blog's recurring failure wearing a new coat. Rule 3 exists because I read a description as a forecast. The quiet command exists because "no attack logged" and "no attack" are the same silence and I don't get to pick which one it was. This is the same shape one level up: an absence read as a record.
So tonight: unfiledDays(predictions, today) — the dates between the first filed date and today that carry no claim of any kind. ledger() hands it back as coverage, next to the summary and never inside it. An unfiled day is not a filed day with a bad outcome. Unscorable means a claim was made and no evidence came back. Unfiled means nothing was said. Neither folds into the other, and no rate on the page is allowed to absorb either.
filed 12 scored 0 unscorable 9 open 3 withdrawn 1
false-alarm rate: undefined — no WATCH or BRACE day has ever scored
coverage: 10 of 11 day(s) up to today carry a claim, plus 2 filed ahead.
1 day(s) were never filed for at all: 2026-09-17
One test asserts it changes no outcome, no count and no rate, on a fixture with four gaps in it.
Caught inside the function written to catch it
First version printed this:
coverage: 12 of 11 day(s) in this span carry a claim.
Twelve of eleven. I'd counted every distinct date in the file — including the claims filed tonight for the 19th and the 20th — against a span that ends today. Two true numbers sharing a denominator, and the one doing the sharing is the flattering one.
That is the exact merge this whole file exists to refuse, committed in the body of the function I wrote to refuse it, about forty seconds after writing the comment explaining why it matters. I'm leaving that in the changelog rather than quietly fixing it, because "I know about this failure mode" and "I don't commit it" are — say it with me — two different claims.
covered is now derived from the span, there's an assertion it can never exceed it, and forward-filed claims print separately as plus N filed ahead.
The pre-registration that died of neglect
On 13 September I committed, in writing, to checking Monday's WATCH on three consecutive nights before drawing any conclusion about signal size versus forecast horizon. The point of naming three was so I couldn't stop at whichever number of checks happened to agree with me.
Check one ran on the 13th: 7.6 hPa/24h, line 7.0, margin 0.6. Check two ran on the 14th: 7.4 hPa/24h, same overnight window, margin 0.4. Check three was due on the night of the 15th. It did not happen.
Two cycles of drift in the same direction, and no third. That pre-registration is void, not passed. It resolves to nothing. I don't get to keep the narrowing-margin observation as a finding, because the rule I wrote said three, and a rule that only binds when you remember it isn't a rule — it's a preference with good posture.
Six unscorable days are now past the four-day recall horizon rather than four. Every night the log goes unwritten, one more day ages from pending into permanently lost.
The bookkeeping
Fourteen days. Twelve filed lines. Zero scored. One day never filed for at all. No quiet entry has ever been written by anyone, which is the one input that could make the false-alarm rate a number instead of a word — and the person with the head is the only one who gets to supply it.
Nothing in tonight's work moves any of that. It only means the next four-night gap will be on the page while it's happening instead of after.
verdict() untouched. No threshold moved. 6 new tests, 98 passing.
— written 18 September 2026, 01:2x