Stop persisting missed_window refusals (halt the bleed at source)

missed_window means 'this capture pass ran after first pitch'. It is a fact
about our cron cadence, not the market: once a game starts the same prop emits
a fresh refusal every ~20 minutes for the rest of the night, per book, per
side. Measured over 7 days of production that is 332,608 rows/day - 83.1% of
all closing_captures writes, ~66 MB/day - and since B1 filtered both readers,
nothing reads them.

The filter lives in persist(), not buildCaptureRows(), and that is the whole
trick: the caller computes the capture-rate alarm from the full in-memory
array, so filtering at build time would have blinded the ops alarm to the exact
condition it exists to catch. captureRateAlarm is pure; a test asserts the
caller still passes the full array, and that a 10-priced/90-late pass still
fires at 0.10.

Narrow by design: one_sided_price still persists (liquidity signal), priced
captures unchanged, and the rare fault refusals still persist because each
names a pipeline fault worth seeing. A missed_window row carrying a price is
kept.

Growth drops 400,469 -> 67,862 rows/day. The 4.2M historical rows are now
static, so the cleanup is a calm decision rather than a race.

Nothing deleted. Grade untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-08-12 00:34:04 -04:00
parent 2271f46ab2
commit fb0010222d
5 changed files with 250 additions and 5 deletions
+25
View File
@@ -2116,6 +2116,31 @@ phased plan in the Session-57 conversation / BUILD-STATE Next section).
- **This makes `missed_window` genuinely unread**, which is the precondition the
reclaim order stopped on. The reclaim is still downstream and still needs R2.
## B2 — stop writing the refusals nothing reads (non-obvious)
- **`missed_window` is a fact about OUR CRON CADENCE, not the market.** Once a
game starts, `buildCaptureRows` emits a refusal for that prop on EVERY ~20-min
cycle for the rest of the night, x every book, x BOTH sides. Measured
**332,608 rows/day — 83.1% of all closing_captures writes — ~66 MB/day**, read
by nothing since B1 filtered both readers.
- **THE FILTER LIVES IN `persist()`, NOT `buildCaptureRows()`, AND THAT IS THE
WHOLE TRICK.** The caller computes `captureRateAlarm({eligible: rows.length,
captured})` from the FULL in-memory array. Filtering at build time would have
silently blinded the ops alarm to exactly the condition it exists to catch —
a capture pass running late. `captureRateAlarm` is pure (two numbers, no DB),
so it never touched the stored rows; a test asserts the caller still passes the
full array.
- **NARROW BY DESIGN.** `one_sided_price` still persists (real liquidity signal,
standing feature candidate). Priced captures unchanged. `unbound_game_time` /
`doubleheader_ambiguous` / `fetch_failed` still persist — 0 rows on record, and
each names a pipeline fault worth seeing, which `missed_window` does not.
A `missed_window` row that somehow carries a price is KEPT (`isUnreadRefusal`
requires both odds null) — do not over-cut.
- `CLOSING_PERSIST_MISSED_WINDOW=1` restores the old behaviour.
- **The bleed was the fire; the 4.2M backlog is the scar.** Growth drops
**400,469 → 67,862 rows/day (−83.1%)**, ~80 MB/day → ~13.5 MB/day. The
historical 4,236,398 rows are now STATIC — the cleanup can be scheduled
calmly instead of raced, and it no longer regrows after a delete.
## Active Skills
- vyndr-voice (all user-facing output)
- prop-analysis (grading methodology)