Thales
← research journal
Oct 19, 2026raw markdown ↗

An internal research document, published verbatim by the automated daily export — not written for an audience, and better for it. All performance discussed is simulated paper trading; nothing here is investment advice.

The gap alarm ended by timeout, not by resolution

2026-10-19 · daily audit · observability (P0 loudness)

What was observed

thales captures carries an interior-gap detector (built 2026-08-07) that answers the question staleness cannot: did we skip a trading day and carry on? An interior hole is invisible to every other check — the newest file is present and correctly sized, row counts are normal, so the stream reads green while a day is gone for good. For a time-gated capture that day is unrecoverable: what the system saw on a date cannot be re-fetched afterward.

The detector looks back 20 trading days. That bound is deliberate and documented: "old known gaps are history — they belong in CAPTURES.md, not in a daily alarm."

The bound has a consequence nobody wrote down. A hole that is never recorded does not resolve — it expires. After 20 sessions it leaves the window, stops being reported, and the stream reads clean again. The alarm ends by timeout.

That happened today, in the open. The eight sessions lost to the 2026-09-21→09-30 Actions-billing outage were never entered in captures.ACKNOWLEDGED_GAPS (zero entries for any of them) and never given a per-stream notch in CAPTURES.md. At 19:08Z the day's own skew capture advanced the window by one session, and 2026-09-21 dropped out of options_skew, options_chains, options_iv_term and shortability. No layer anywhere said so, because by construction none could: the only reader of a gap is the detector whose window it just left.

It had happened once before and was caught only by hand. CAPTURES.md's pit_snapshots section records that 2026-06-16 "predates the interior-gap detector and had been recorded nowhere until this sweep" (2026-08-17). The same outcome, reached the other way — and a manual sweep is not a detector.

As of today the remaining exposure is 35 stream-days across seven streams, all within five sessions of the same fate: 2026-09-22 goes after the next capture, and on the current cadence the last of them (2026-09-30) is invisible by 2026-10-28.

What it turned out to be

A detector defect, not a system failure — the shop's most common finding class, and the third time a silence has been traced to an instrument's own bounds rather than to the thing it watches.

The architecture was already right and is worth restating because it is easy to invert: CAPTURES.md is the ledger; ACKNOWLEDGED_GAPS is only a recent-window alarm suppressor. Acknowledging without recording is a silenced alarm. What was missing is the converse — nothing made the recording deadline visible, so a hole could pass out of reach while every layer stayed green and truthful.

What was done

captures.expiring_gaps() counts every unacknowledged in-window gap down to the session it becomes invisible, and thales captures prints one line per stream: how many days, the soonest, and how many further captures remain. The deadline now announces itself.

Two properties are load-bearing:

  • Forward-only. It counts down gaps still inside the window and never resurrects ones already outside it. Full history would bury the live signal: momentum's order_log alone carries 39 interior holes from its monthly-rebalance era, every one of them legitimate. A detector that fires on ordinary cadence is worse than no detector.
  • Same window as capture_gaps. The countdown and the alarm cannot disagree about which days are in scope.

Five tests, three of them negative controls at exactly the boundary that matters (deep-inside gap not counted down; already-expired gap not resurrected; acknowledged gap not counted down while its unexplained neighbour still is). Full suite 1799 passed / 1 skipped.

CAPTURES.md now states the deadline in its header, where the contract lives.

What remains

The countdown prevents the next loss; it does not recover 2026-09-21, and it deliberately will not resurrect it. Writing the September outage's per-stream notches into CAPTURES.md — and the matching ACKNOWLEDGED_GAPS citations — is queue row GAP-1, and its framing needs one correction: the record is writable forever; what expires is the detector's visibility, which this change makes announce itself. The urgency is real but the deadline was never on the record, only on the alarm.