Thales
← research journal

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.

RAD-1 — "a file exists" is the unit for every capture count, and one of the 45 sessions feeding the options_iv_term retire-or-keep criterion is a 33-row stub; the evaluator that will read them does not exist yet, so the input-set question gets decided after the data is in

status: open · raised panel run 39 (2026-10-16, queue-and-clocks candidate C1, narrowed and re-evidenced at the chair's adversarial pass) · class: registration hygiene (G5 — a pinned criterion whose input set is still unruled) with a G4 measurement leg · judgement: YES · effort ~0.1 pd (the ruling) + ~0.3 pd (optional predicate) · horizon: the ruling must land before ~2026-11-05, the earlier of the two accrual dates it governs

Plain-language summary

Several of this shop's pinned criteria say "wait 60 trading days, then decide". On 2026-10-15 the owner ruled what "60 trading days" counts: captured sessions, not elapsed ones — defined as "trading days with a file in data/options_iv_term". That ruling was pinned before the criterion was computed, which is exactly right.

It leaves one question unanswered: what makes a file a session? Today the answer is "it exists". A file can exist and be worthless. One of the 45 files now counting toward that 60 holds 33 rows against a median of 7,928 — four tenths of one percent of a normal day. The criterion those 45 files feed is a per-name median of captured expiries, and a 33-row day cannot supply a per-name median for more than a handful of names.

The evaluator that will compute this does not exist yet. It has to be written in the next ~three weeks. Whoever writes it will decide — in code, silently, with the data already on disk — whether that stub counts. That is the post-hoc decision the 10-15 ruling existed to prevent, one level down.

Mechanism — why it would pay, and who is on the other side

Nobody is on the other side; this is not an alpha claim. It pays in TRUST (NORTHSTAR.md §1 asset 1, gauge G5): a criterion whose inputs are chosen after its data exists cannot be quoted as pre-registered, and this stream's criterion is a retire-or-keep decision on a moat asset. The bias direction is the part that matters: a near-empty day dragging a per-name median downward pushes the verdict toward VACUOUS, i.e. toward retiring a capture that may not be vacuous at all. A false VACUOUS is permanent — the write is removed from the run and the stream stops accruing.

Evidence — all re-measured this run

The criterion, verbatim (CAPTURES.md:299-307):

at 60 trading days of accrual (~late 2026-10), compute each name's median-per-day count of distinct captured expiries; if the cross-name median is < 5, the capture is recorded VACUOUS … and the stream is RETIRED

The unit ruling, verbatim (CAPTURES.md:307-318):

RULED 2026-10-15 (owner): "60 trading days" means 60 CAPTURED sessions. These are trading days with a file in data/options_iv_term … 45 were captured through 10-15 … It was pinned before the criterion was computed. Capture-qa's daily line prints only the newest day's median (7–8 expiries/name this week), not each name's median across its captured days. The ruling sets when the check runs, not what passes it.

The stub. 45 files in data/options_iv_term; row counts, measured:

filerows
2026-08-0733
2026-08-057,592
2026-08-037,595
2026-08-047,609
median of all 457,928

The stub is not a borderline case: the next-thinnest file is 230× larger.

Why it is there, and why it is the shop's own precedent that forces the question. The 2026-09-06 re-session ruling's ledger (CAPTURES.md:605-613) records, in the same list:

  • options_chains 2026-08-07: 82 rows stayed (captured in-session)
  • options_iv_term 2026-08-07: 33 rows stayed (captured in-session)
  • options_skew 2026-08-07: 5 rows stayed (captured in-session)
  • totals: relabelled 239179, quarantined 85028, dropped 0

That ruling quarantined 85,028 rows of thin holiday re-serves as not sessions — and left these three stubs in place, because its test was capture instant, not row adequacy. One ledger, two unit rules, and only the first was ever ruled on. The remedy is already built and already used: data/quarantine/, excluded by the loaders by default.

No evaluator exists. grep -rniE "median.*expir|expir.*median|vacuous" src/thales/ --include="*.py" returns only captures.py:750 — capture-qa's newest-day breakage check, which CAPTURES.md:315-317 explicitly distinguishes from the kill criterion — and backtest/dtc_vacuity.py, which is the A4 DTC battery, a different stream. So the 60-session evaluator is unwritten with ~20 days to go, against §3 G5's own rule that "no forward clock may reach maturity without its evaluator already pinned."

The same unit gap, second instance, already recorded in prose and unimplemented. CAPTURES.md:425-430 rules on the two truncated PiT sessions: "2026-10-12 = 49 rows and 2026-10-13 = 1 row, against an 886-row norm … both mislabel themselves as sessions, and both are permanent … Treat them as missing." No instrument does. thales captures --json reports pit_snapshots gaps=8; the true unusable-session count is 10. ACKNOWLEDGED_GAPS (captures.py:295-317) structurally cannot hold them — capture_gaps (:412) tests d not in have, and both dates are in have. PIT-1 (built 2026-10-15) closes the forward hole at the writer and cannot reach backwards. So a human instruction exists with no machine reader, and gauge G4 under-reports permanent notches by two.

Scope — measured before anyone asks, and it is in this row's disfavour

A rows >= 0.5 × median(trailing 10 same-stream files) predicate flags:

  • options_iv_term: 1 of 45 (2026-08-07)
  • shortability: 0 of 48 — thinnest 14,137 vs a ~14,400 norm = 98%

So shortability's ~2026-11-02 date is unaffected, and options_iv_term's ~2026-11-05 moves by at most one day (to 11-06) if the stub is excluded. On the cross-name median itself the stub touches roughly a dozen of ~880 names, by one datum each. This is a one-day, one-clock unit question plus a two-day census correction — not a build. It is proposed because it is cheap and must be pinned before 11-05, not because it is large.

Test design — how it gets falsified on this apparatus

  1. Define rows_adequate(f) = rows(f) >= 0.5 × median(rows of the trailing 10 same-stream files) — reusing capture-qa's existing floor convention (captures.py:595-606), introducing no second tunable number. This is the same reuse discipline PIT-1 adopted and the same floor that correctly red the skew workflow on 10-12/13/14.
  2. Run it over every committed file in pit_snapshots, options_iv_term, shortability, options_skew, options_chains.
  3. Compare the flagged set against the files already named in CAPTURES.md's own disposition ledgers.

Kill criterion — pre-registered

If the predicate flags only the files already named in CAPTURES.md's disposition ledgers — options_iv_term 2026-08-07, pit_snapshots 2026-10-12 and 2026-10-13 — then the defect is confined to days the record already knows about, the remedy is one owner ruling plus one quarantine move, and this row DIES as bookkeeping with no code change. It converts to a P2 detector row only if it flags a fourth file nobody has recorded.

Written down because the narrow outcome is the one I expect: half the predicate is already run above, and it found exactly the three known files.

Second, independent kill: if the owner rules that file presence alone is the unit — a legitimate call, and the conservative one for keeping a stream alive — the row closes immediately with that ruling recorded, and the PiT census correction becomes a separate ~0.1 pd G4 bookkeeping item or nothing.

Effort · horizon · whose job

  • ~0.1 pd — the ruling: does a present-but-row-inadequate file count as a captured session for an accrual count, and do the three named files move to data/quarantine/?
  • ~0.3 pd — optional: wire a reusable rows_adequate into capture_gaps/capture_status so thales captures stops under-counting G4 by two.
  • Horizon: before ~2026-11-05. Deciding it after that date, with the verdict computable, is the gate-shopping the 10-15 ruling was pinned to prevent. The clock is the same shape and the same reason.
  • Whose job: the unit choice is the owner's — it moves a pinned criterion's input set, and the ruling is the whole deliverable. The quarantine move and the predicate are implementer work once ruled. The panel proposes only.

Why no existing slug covers this

  • grep -rliE "row.?adequa|present.?but.?worthless|presence is not" over research/, CAPTURES.md, TECH_DEBT.md, RESEARCH.md and killlist.json → zero hits. grep -rn "33 rows\|33-row" → one hit, CAPTURES.md:611, the ledger line quoted above.
  • GAP-1 (dismissed) covers missing days and the 20-td trailing window's re-blinding; its instrument structurally cannot represent present-but-worthless, and its reopen clause already fired and was escalated in runs 28 and 32 with a third annotation killed by run 38. Not duplicated here, and deliberately not annotated a fourth time.
  • CLK-1 (dismissed, fired 2026-10-06) covers the absence of a §6 row and an evaluator for these two clocks, generically. This row is the distinct question of which inputs the evaluator must count when it is written. CLK-1's remedy is owner-side in agent-denied NORTHSTAR.md; this one is a ruling plus an optional predicate.
  • PIT-1 (built) is writer-side and forward-only, by its own text.
  • CQA-2 (dismissed) is latest-file-only QA plus alert-subject attribution.
  • RSN-1 (dismissed) covers quarantine counters, not the admission rule.
  • BTB-1 (open) covers the shortability degeneracy claim, not the unit.
  • CQA-3 (open) covers the capture instant — the 2026-09-06 ruling's test. This row is the orthogonal axis that ruling left open, and the two together are the reason the stub survived.

Reopen / resurrection note

If this row is dismissed on the "presence is the unit" ruling, it reopens if any accrual criterion is ever computed on a panel containing a file flagged by the predicate above, or if a stream is retired as VACUOUS on a panel including one.


Annotation — panel run 40, 2026-10-19: the stub moves the kill statistic by exactly zero, and a second, dated input defect sits on the axis this row says CQA-3 owns

Two measurements, both taken from the committed store at HEAD 6c8d2d0. Neither changes this row's judgement; the first narrows it and makes the ruling cheaper, the second widens the ruling's scope by one clause.

(a) The 33-row stub's effect on the kill criterion is zero, measured

The criterion this row protects is "at 60 captured sessions, cross-name median of per-day distinct expiries < 5 → VACUOUS, stream RETIRED". Computed today over 898 names:

panelcross-name median expiries/namenames below 5
all 46 options_iv_term files7.0221 of 898 (24.6%)
the 45 excluding the 33-row stub7.0221 of 898

The stub holds 33 rows across 7 underlyings, so it contributes one datum to 7 of 898 names and moves no median. This row's estimate of "roughly a dozen names" was high; the measured figure is 7.

What this does and does not change. The harm mechanism stated above is real in form and measures zero on the kill statistic today. Its live effect is on the count — 46 files vs 45 row-adequate sessions, i.e. the check date moves by one session (~11-05 vs ~11-06). So the expected outcome of the ruling is the narrow one this row already anticipated, and the owner can make it knowing the decision does not currently turn on it. That is an argument for ruling cheaply and soon, not for leaving it unruled: the whole point is that the ruling must pre-date the computation, and a criterion whose input set is still open on the day it is computed is unpinned regardless of which way the number happens to fall.

(b) 2026-10-16 is disqualified for the skew stream by the owner's own signed grace, and silently counts for these two

This row delegates the capture instant to CQA-3. CQA-3's gate is the skew stream's B4 signal-date rule. So for options_iv_term and shortability — whose unit is file presence — no row owns the instant, and there is now a dated instance on disk:

datecaptured_atnamesmedian exp/namemean exp/namemin dte
10-1319:00Z (15:00 ET, box)8957.09.181
10-1419:00Z8957.09.152
10-1519:00Z8958.09.541
10-1621:59Z = 17:59 ET8957.08.713

The 10-16 files were committed at 22:04Z by skew: snapshot 2026-10-16 — GitHub's late backup leg, past the 60-minute post-close grace the owner countersigned on 2026-10-15 (CAPTURES.md). The 2026-10-16 steward report calls 10-16 "permanently lost as a skew signal date" and is right. Yet the options_iv_term and shortability files for that day exist, so under the 10-15 unit the same instant the shop has already ruled disqualifying for one stream is admitted for the other two.

Direction of the bias, which is the part that matters. 10-16 was the third Friday — monthly expiration. A post-close snapshot loses the front expiries: min dte 1→3, and mean expiries/name 8.71, the lowest of the four days. The statistic is a median against a floor, and the floor retires the stream. So an admitted post-close day pulls the number toward VACUOUS — toward retiring the options stream, the one surviving experiment candidate in the shop and the asset whose entire thesis is 12–18 months of un-backfillable accrual. The bias runs the same direction as the stub's and, unlike the stub's, has a mechanism large enough to measure.

Effort: still ~0. captured_at is already column 11 of the parquet. This asks for one additional clause in the ruling this row already requests — whether a session captured outside the signed grace counts toward the options_iv_term and shortability accrual — not a build and not a new row.

Kill criterion for this leg specifically. It dies if the owner rules that presence alone is the unit for these two streams with the OPEX measurement above in front of him (a ruling made on the evidence is pinned; that is the whole requirement), or if the evaluator, when written, filters captured_at against the signed grace, which closes it in code.

Prior art re-checked for (b): cqa-3 (skew stream only), rsn-1 (quarantine counters), kbm-1 (dateless booking), b4-notch-skew-row-floor (the W27/W40 notches and the ≥400-valid-row floor — a skew-side rule). None states this. Raised by the queue-and-clocks lens as candidate C1 and the frontier lens independently; kept as an annotation, not a row, because the remedy is the same single owner ruling.


Triage annotation — 2026-10-19 (annotation only; the body above is untouched) — A LIVE INSTANCE OF THIS ROW'S EXACT QUESTION, AND THE ROW'S OWN KILL CRITERION DOES NOT FIRE AS WRITTEN

1. The naive unit over-counts the shortability clock by one TODAY, and the only reason nobody has noticed is that three humans each excluded the file by hand

data/shortability/ holds 50 files. One of them, 2026-07-26.parquet, is dated a Sunday (verified: date(2026,7,26).weekday() == 6). The stream's registration date is a non-trading day. The owner's 2026-10-15 ruling (CAPTURES.md:375-377) defines the unit as "trading days with a file in data/shortability" — which excludes it. Nothing in code does.

That single file reconciles three published numbers that otherwise disagree:

sourcecount= disk minus the Sunday?
raw ls data/shortability (the "a file exists" unit)50—
panel run 40, 2026-10-194950 − 1 ✓
the owner's pin, CAPTURES.md:381-382, "48 captured through 10-15"4849 files ≤10-15, − 1 ✓

So both published readings are correct and mutually consistent because each author dropped the Sunday by hand, while the mechanical unit the ruling's words imply — and that any evaluator written from them would implement — gives a number one higher and reaches 60 one session early. This row says the admission test gets decided in code, silently, by whoever writes the evaluator first; here is that decision already pending, on the clock that matures first (~2026-11-02, 11 captured sessions away). options_iv_term has no weekend-dated file: 46 files, all session-dated, 45 through 10-15, matching its pin exactly.

Re-measured ground truth for the row's own figures: the stub is options_iv_term/2026-08-07 at 33 rows; the median of the 45 files through 10-15 is 7,928 exactly as claimed (46 files at HEAD → 7,925.5); next thinnest 7,592. "45 sessions" is now 46 (10-16 landed after the row was written).

2. The row's pre-registered kill criterion does not fire, and the direction is MORE work than claimed

The criterion dies "if the predicate flags only the files already named in CAPTURES.md's disposition ledgers" and converts to a P2 detector "if it flags a fourth file nobody has recorded". The body states that half of it was already run and "found exactly the three known files" — but it was run over two of the five streams the row declares. Run over all five, the declared predicate (rows >= 0.5 x median(trailing 10 same-stream files)) flags 22 files:

  • options_iv_term 1 of 46 — 2026-08-07 (33 vs 7,602) ✓ as claimed
  • shortability 0 of 50 — thinnest 14,137 vs median 14,276 = 99%. So the 11-02 clock is unaffected on row adequacy; its only defect is the Sunday above
  • pit_snapshots 4 of 80 — 08-27 (123), 08-28 (58), 10-12 (49), 10-13 (1)
  • options_chains 9 of 81 — 08-07 (82) plus eight ~25-28k coverage notches
  • options_skew 11 of 83 — 08-07 (5 rows) plus ten ~281-283-row days

Every one of the 22 is recorded somewhere in CAPTURES.md — pit 08-27/08-28 at :409-413 ("partial, non-representative samples"), the thin skew/chains class as the calendar coverage notch at :108-121 ("reduced-coverage days, not missing days"). So the criterion is satisfied on its clause and falsified on its enumeration. It needs re-grounding before this row is picked up, or a builder will read "three known files" and find twenty-two.

3. Nothing has been written, and thales captures was run this session to confirm it

Neither 60-session criterion has an evaluator. thales captures executed here prints staleness and gap columns and no 60-session column at all; captures.py:751-752 explicitly disclaims its daily median as "a breakage bar, not the pre-registered <5-median kill criterion"; the only VACUOUS evaluator in src/ is backtest/dtc_vacuity.py, a different stream (A4 DTC). There are zero consumers of the borrow flags outside the capture/QA path. And NORTHSTAR.md §6 (:192-202), whose own header reads "every entry must have a pinned evaluator before maturity", contains neither clock — which is dismissed/clk-1's reopen condition, fired on its own date 2026-10-06 and still true 13 days later.

Live state: horizon ~2026-11-05 (shortability's sibling clock ~2026-11-02 arrives first) · blocked on one owner/steward ruling (~0.1 pd) · the ruling is now cheaper than when it was filed (panel run 40 measured the stub moving the cross-name median by exactly zero: 7.0 either way, 221 of 898 names below the <5 bar either way) · and the ruling should settle the non-trading-day case in the same sentence, because that one is already live.