Skip to content

fix(notice): one projection, one floor - #29

Merged
lroolle merged 1 commit into
mainfrom
fix/notice-flash-floor
Aug 24, 2026
Merged

fix(notice): one projection, one floor#29
lroolle merged 1 commit into
mainfrom
fix/notice-flash-floor

Conversation

@lroolle

@lroolle lroolle commented Aug 24, 2026

Copy link
Copy Markdown
Member

Patch on v0.32.0, from two live observations. Both are the same shape: a surface projecting from evidence it does not have, or judging a row by a measure that no longer means anything.

Row 3's gate was measuring the wrong thing

It required the flash to beat the pin by NOTICE_FLASH_MIN_GAIN columns. That was the right question in v0.31.0, when row 3 restated row 2 and the only issue was whether truncation had left anything extra to say. Since #28 the flash is a different notice, so the subtraction compares two unrelated sentences — and drops a short one whenever the pin happens to be long.

Measured at COLUMNS=30 on a 5h-85% / 7d-75% fixture:

row 2   ! 5h caps ~23:54        (16)
row 3   ! 7d caps ~Tue 07:22    (20)   gain 4 -> dropped

The second window lost its row for no reason a reader could name. The gate is now an absolute floor on the compacted flash itself — NOTICE_FLASH_MIN_CHARS=16 in a named, testable notice_flash_worth_row: did truncation leave a sentence, or a stub? 7d dry ~Wed says nothing actionable; 7d dry ~Wed 19:50 still carries the number.

Extracting the predicate was deliberate: enumerating the leading fact of every long form notice_collect can emit shows the shortest one reachable as a flash is budget last window (18) — the 9-char fb capped is rank 100 and therefore always the pin. So the floor cannot bite through the CLI today; it is a guard rail. An inline gate with no reachable failing case is one no test can pin down, which is exactly how it silently rotted.

A ten-minute-old 5h window drew ▮▯×××

One big prompt front-loads burn — 7% in ten minutes — and five_dry_cell (gated only on elapsed > 0) read that as a rate and walled off the rest of the window. On the same render the pace suffix hid itself (too young to judge) and the "5h caps" notice stayed silent (it waits 15 minutes). Three surfaces, one linear projection, only the loudest one speaking — the same phantom class as the 7d rollover guard in v0.32.0.

window_evidence_floor <length> is that rule in one place (5% of the window's own length) and now backs all three: the 5h trio agrees at 15 minutes, the 7d pace suffix keeps its ~8.4h. ADVISOR_PACE_MIN_ELAPSED=900 is retired into it — it was the same number as 18000/20 written a second way, which is drift waiting to happen.

week_dry_slot deliberately keeps SEVEN_DAY_YOUNG_SECS (a day) rather than length/20: a pace reading summarises what happened, a multi-day projection needs a day of evidence. Different questions, different floors, both now stated in the source.

Verification

  • 426 bats tests (was 423), shellcheck -S error clean
  • new: notice_flash_worth_row boundary pair, the narrow-width integration case the old rule dropped, and the young-5h pair (no × at 10 minutes, × returns past 900s)
  • the narrow-width test pins COLUMNS=30 on purpose — at 40 the assertion flipped depending on whether the reset gap rendered 40h or 39h59m

Known, not in this PR

The Pages demo still animates a pin and its own long form as rows 2+3 (docs/index.html, the PIN, FLASH and PIN_DRY, FLASH_DRY frames) — behaviour #28 removed. Two string constants plus their captions; rewriting that narrative mid-patch is how a demo starts lying differently, so it wants its own change.

🤖 Generated with Claude Code

Row 3's gate measured the wrong thing. It required the flash to beat the
pin by NOTICE_FLASH_MIN_GAIN columns — right in v0.31.0, when row 3
restated row 2 and the only question was whether truncation left
anything extra. Since v0.32.0 the flash is a DIFFERENT notice, so that
subtraction compared two unrelated sentences: at 30 columns
`! 5h caps ~23:54` (16) beside `! 7d caps ~Tue 07:22` (20) is a gain of
4, and the second window lost its row for no reason a reader could name.
The rule is now an absolute floor on the compacted flash itself
(NOTICE_FLASH_MIN_CHARS=16, in notice_flash_worth_row): did truncation
leave a sentence, or a stub?

A ten-minute-old 5h window drew ▮▯×××. One big prompt front-loads burn —
7% in ten minutes — and five_dry_cell read that as a rate and walled off
the window, while on the same render the pace suffix hid itself and the
"5h caps" notice stayed silent. Three surfaces, one linear projection,
only the loudest one speaking. window_evidence_floor is that rule in one
place — 5% of a window's own length — and now backs the 5h dry cells,
the pace suffix and the caps notice alike. ADVISOR_PACE_MIN_ELAPSED is
retired into it: a constant beside a formula is drift waiting to happen.
week_dry_slot keeps its own day-long floor on purpose — a pace reading
summarises what happened, a multi-day projection needs a day.

426 tests (was 423).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lroolle
lroolle merged commit 9a31d7c into main Aug 24, 2026
1 check passed
@lroolle
lroolle deleted the fix/notice-flash-floor branch August 24, 2026 06:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant