Research panel — run 30 (Tuesday 2026-09-29)
considered=6 · survived-review=1 · opened=1. One row opened
(research/queue/open/frz-1-append-only-pins-not-enforced.md), one dismissal
filed (research/queue/dismissed/lab-3-preregistration-record-unpinned.md),
four candidates killed to session output and recorded below.
Three lenses in parallel (change reviewer · queue-and-clocks · frontier) then one adversarial reviewer. The queue-and-clocks and frontier lenses forwarded ZERO candidates each, which is the correct outcome. All six candidates came from the change reviewer (five) and the panel chair (one). The reviewer killed five of six.
The delivered trigger prompt matches ops/prompts/panel.md verbatim — no
live-vs-mirror disagreement to report. ops/ABSENCE.md: no absence is
declared, so expiries below are live, not flagged.
1. EXPIRY WATCH — the headline is unchanged and the margin is now three sessions
A qualifying option-skew capture must land on or before Friday 2026-10-02 or the 2026-09-18 observation is dropped from the B4 panel permanently.
Re-derived independently three times this run (chair, queue lens, frontier
lens), all agreeing. The pin at
research/2026-08-01_b4_skew_activation_pin.md:83-85 drops an observation when
the gap to the next signal date "exceeds 10 trading days". Last qualifying
capture 2026-09-18 with 888 valid rows (over the ≥400 floor at :69-72, so
it is a signal date). NYSE sessions after it: 09-21(1) … 09-29(7, today),
09-30(8), 10-01(9), 10-02(10), 10-05(11). Ten does not exceed ten;
eleven does.
- Margin: 3 trading days after today.
- Automated attempts remaining: exactly TWO —
skew-snapshot.ymlfires weekdays 19:00Z, and with the Actions allowance resetting 2026-10-01 the only automated shots are 10-01 and 10-02. Today's and tomorrow's slots cannot fire (Actions refused repo-wide over billing, day 9). There is no third attempt and no retry day. - Manual remedy:
thales snapshot-skewby hand from the Mac, any session through Friday. - Ordering constraint, from the 2026-09-28 triage's CQA-3 annotation and re-confirmed here: the B4 pin's 2026-09-02 append is uncountersigned, and its clause (c) would disqualify rows captured after the close. A hand-run rescue taken post-close is exactly that shape. The ruling has to precede the rescue, not follow it — otherwise the shop decides what counts as a valid row after seeing whether the rescue landed.
- ISO week 39 (09-21→09-27) is lost outright and can never be recovered.
This deadline still appears nowhere on main. Its only carriers are unmerged
PR #188 (run 29), the 2026-09-28 triage and daily-audit beacons, and now this
note.
Everything else dated, with trading-day margin from today:
| item | date | margin | needs you before Mon 10-05? |
|---|---|---|---|
| B4 skew capture deadline | Fri 2026-10-02 | 3 | yes |
| CLK-1 dated reopen (both halves already true: §6 still lacks both capture-clock rows, neither evaluator exists) | Tue 2026-10-06 | 5 | yes — the 10-05 triage is the last routine that can act |
| G1 research audit fires | Thu 2026-10-01 | 2 | no |
| ERN-1 dated default (self-executing kill on UNANSWERED) | Mon 2026-10-12 | 9 | no |
| Frontier SCOUT reverts to watch-only (also auto-reopens HIRE-1) | Mon 2026-10-12 | 9 | no |
| shortability 60-td, calendar reading | Mon 2026-10-19 | 14 | the BTB-1 ruling between the two readings is owed before this |
2026-09-21 equity mark leaves Alpaca's "1M" window | ~Wed 2026-10-21 | 16 | no |
options_iv_term 60-td, calendar | Mon 2026-10-26 | 19 | no |
| GAP-1 ledger deadline | Wed 2026-10-28 | 21 | no |
| shortability 60-td, accrual reading | Mon 2026-11-02 | 24 | no — corrects run 29 (~10-29) and triage (~10-28) |
options_iv_term 60-td, accrual | Thu 2026-11-05 | 27 | no |
December go-live gate not_before | Tue 2026-12-01 | 44 | no |
research/queue/approved/ holds only its README — verified by ls and by
git log -- research/queue/approved/, which returns exactly one commit.
Two dated figures elsewhere are now wrong and should not be trusted as written: the shortability accrual read is ~2026-11-02, not ~10-28/10-29 (38 files, 37 session-dated captures, 23 still needed); and BTB-1's own stated horizon of "~2026-10-20" is roughly ten days early for the same reason. The rows are no less urgent; the dates moved.
2. Tripwires — two more fired unread, taking the count from four to six
The queue lens evaluated all 30 files in research/queue/dismissed/. Newly
measured this run:
- INTRADAY-SHORTABILITY leg (a) FIRED, and the file's stated evidence is now
false. Reading all 38
data/shortability/*.parquetjoined todata/universe.csv: CPB was hard-to-borrow on eight consecutive NYSE sessions, 09-09 through 09-18, against a bar of ≥5. The file (written 2026-09-14) says this "has never yet shown". The dismissal still holds, because its second conjunct — the 60-td test completing — is at 37/60. It goes live the day that test reads. - DSP-1 leg (d) FIRED ~2026-09-25 and has never been evaluated. Its
condition is a pure cross-reference to BCN-1's reopen firing, which BCN-1's
own file records as having lapsed on 09-25.
grep -rn "DSP-1"outside its own file returns zero hits. - AUTO-APPROVE leg (b) fired by counter on 2026-09-28 and no reopen has been executed. Its text says it reopens "rewrite or no rewrite" when triage's counter (a) reaches 8 weeks. It did.
Clean negatives worth recording so nobody re-derives them: B4-NOTCH leg (b)
NOT fired on its stated mechanism — the completed-week census is now 2 of
16 = 12.5%, above the "~11%" the condition names, but W39 was destroyed by a
billing outage, not by a calendar notch. (If W40 is also lost the figure
becomes 3 of 17 = 17.6% and the numeric side stops being arguable.) RCX-1 (a)
NOT fired — largest committed jsonl line 740 B vs a 4096 B tripwire. B5
termination clause NOT fired — an infrastructure outage with a dated expected
resumption is not the lane retirement the clause names, and the B5 pin at
:70-72 already pre-registers missing days as simply absent and counted.
B1 NOT vacuous — median 7.0 distinct expiries across 34 files vs a <5
vacuity bar, i.e. trending away from vacuity.
3. Counters
- Owner approval gate: 8 of 8, FIRED 2026-09-28.
research/queue/approved/has never held an item in its existence. Two obligations from that firing remain unexecuted:ops/QUEUE_TRIAGE.md:191-194says "record it, drop to monthly, and raise the bar on what the panel proposes" —ops/ROUTINES.mdstill carries triage as weekly — and AUTO-APPROVE's own reopen valve. - Panel cadence (
ops/RESEARCH_PANEL.md§5): 0 of 8, nowhere near firing. Panel rows reachedqueue/open/on 2026-09-17 (CQA-3), 2026-09-28 (LAB-1) and today. - Frontier SCOUT: 6.14 of 8, reverting to watch-only 2026-10-12. The scout deliberated six spaces and killed all six; the bar was not lowered to avoid the revert, and reverting is an acceptable recorded outcome.
- RDR-1's terminal clause has DISCHARGED. The edit it asked for was not made before the next triage (which ran 2026-09-28), so by the clause's own terms RDR-1 is closed as an owner decision. The panel raises it no further and its merits are not re-argued here.
4. Killed at review — four candidates, recorded so nobody re-derives them
All four are on the three lab commits pushed straight to main with zero CI on
2026-09-29 (e53fa72, a9cffcd, ada5bff, ~2,600 lines). A fifth, LAB-3/4,
got a dismissed/ file because its reopen conditions are eventful rather than
greppable.
LAB-2 — the lab's lookahead guard is blind to panel-level signal columns.
True by construction: Panel.truncate (src/thales/lab/panel.py:122-133)
slices a precomputed signals frame and check_causality
(src/thales/lab/study.py:119-143) re-runs only the family weights function,
so a lookahead baked into a column is invariant under truncation. The new
calendar_switch family (families/longrun.py:110-111) is 100% column-driven.
Killed because the exposure today is zero: grep -n "shift(-" src/thales/lab/
returns exactly one hit, data.py:477 inside turn_of_month_schedule,
that column is defensible on the merits (the exchange calendar is known in
advance) and it is separately pinned by
tests/test_lab/test_longrun.py:118 test_turn_of_month_schedule_earns_exactly_the_turn_sessions.
The residue is a documentation overclaim at CLAUDE.md:422 and
research/lab/README.md:155-156 ("every family truncation-tested for
lookahead"), and the real fix — re-deriving signal columns under truncation —
is architecturally much larger than the 0.5 pd claimed.
Reopens if grep -c 'shift(-' src/thales/lab/ exceeds 1, or any signal
column reaches the panel without passing through align_signal, or a lab rule
is ever promoted to a sleeve candidate. (That grep is a one-line check the
change-reviewer lens runs over lab diffs as a matter of course, which is why no
dismissed/ file was minted for it — this run measured six dismissal
tripwires firing unread, and a seventh with a cheaper home is not worth the
surface.)
LAB-5 — "the confirmatory record is four numbers per anchor". Killed as
materially overstated, and the lens was wrong. Each anchor row
(confirmatory.py:328-330) carries 7 keys where criteria holds the five
pre-registered booleans — 12 recorded values — alongside the row's own alpha, slot, verdict, spec_text, spec_sha256, settings_key, data_fingerprint, code_version, window, hypothesis_ids. The entire decision is reproducible from
committed state; what is missing is descriptive colour, and that is also
committed, in the Results table at research/lab/README.md:270-282. Its second
leg is true — the Treasury rebuild's only validation ("weekly correlation
0.96/0.98 vs SHY/IEF") exists solely as prose in two study YAMLs with no code
and no test — but the validating ETF panel lives in data/lab, which is
gitignored and absent, so a CI test for it cannot exist today.
Reopens if the ETF panel becomes a committed fixture (even a 20-row sample
of SHY/IEF weekly closes), or any rebuilt-series return is used outside a
concluded study.
LAB-6 — treasury_carry_1962's carry signal collapses at the 1-year node.
The arithmetic is real and was reproduced: data.py:514-520 takes the
roll-down yield from the curve one year shorter, but for n=1 that point falls
off the [0.25, 1, 3, 5, 10] grid, so it collapses to DTB3 and the bill
spread enters UST1's signal twice — a multiplier of ≈2.03 on (y₁−bill) versus
UST10's ≈0.12·(y₁₀−bill) + 0.2·(y₁₀−y₅). The rule reduces to "hold the 1-year
unless the curve inverts, then hold the 10-year".
Killed on three grounds the lens did not weigh. (1) The code implements
exactly what was pre-registered: treasury_carry_1962.yaml:22-24 spells out
both the formula and the 3m/1/3/5/10y grid, so clamping at n−1=0 is what
that sentence says — the tension is internal to the pre-registration (its prose
at :11-13 versus its formula at :22-24), not code-versus-registration.
(2) The pre-registration already tested the un-collapsed variants — its
neighbours are {basket: [UST3, UST5, UST10]} and {signal_prefix: YTP_},
neither of which collapses, and the ledger records plateau: false, i.e.
the pre-registered fragility detector already fired. (3) The verdict failed
4 of 5 criteria (−0.3 %/yr, negative in 2 of 3 eras, p 0.91, every variant
negative); a KILL that comprehensive does not flip on a re-specified short leg.
Also NOT-RUN on real data (data/lab absent by design) and the slot is
unre-runnable, so no action is available.
Recommended anyway, as a record correction rather than a row: a dated
append beside the treasury_carry_1962 entry noting that the UST1 node
double-counts the bill spread and that the study's prose mechanism and its
formula disagree at that node.
Reopens if any carry signal is used in a study that is not
treasury_carry_1962, or CARRY_UST* reaches a promotion candidate.
The three PRs opened since run 29 produced zero candidates. #191's burn
arithmetic re-derives correctly with the repo's own calendar (September has 21
trading days; 09-01…09-18 is exactly 13; October's 13th/14th/15th are
10-19/10-20/10-21, so "~2026-10-20" is sound), and its correction is right —
daily.py:_is_selection_day keys off a marker in the calendar month, not a
date, and fails closed, so a missed 10-01 delays but cannot skip October's
selection. #190 is procedurally clean: the FIN-4 dismissal carries four reopen
conditions plus a reversal condition, GitHub reports the move as a rename
(similarity 52%, above the guard's threshold) so its auto-merge precondition is
satisfied, and nothing touches research/queue/approved/. #189 is consistent.
Two comments worth relaying rather than filing: #190's MTUM citations have
already drifted (config/lab.yaml:68 → :97, research/lab/README.md:202 →
:245, because the file grew in a9cffcd/ada5bff), and its ranking argument
that "the lab is a better-instrumented home for a factor-domination test" was
overtaken by hours — the lab commits closed the confirmatory reserve at 5/5,
so an MTUM study must now go through the exploratory pool where a 2-rule study
faces a ×672 lab-wide correction.
5. Frontier — zero from both halves, correctly
WATCH: no deferred direction activated. B2, B3 and A5 are all unbuilt with
nothing making them urgent; the skew accrual is ~3.7 months into a 12–18 month
window with its two looks pinned at 2027-06-30 and 2027-12-31; B1 is at median
7.0 expiries against a <5 vacuity bar; B5's termination clause has not fired.
No §6 clock needs a decision inside two weeks — and the two that do need
rulings (shortability, options_iv_term) are not in §6 at all, which is
exactly CLK-1's complaint and why its 2026-10-06 reopen is second on the expiry
watch.
On the new lab memo's central finding — that a realistic +0.1 Sharpe timing
edge needs ~250 years for 80% power, so spend effort on data per hypothesis
rather than on variants — the frontier lens ruled it does not transfer to
the deferred directions, and the reason is worth pinning because the opposite
reading is available and wrong. The memo's arithmetic is about time-series
timing rules, where t scales with √years. B4 is a cross-sectional rank-IC
over ~890 names × N weeks, chosen as primary explicitly for power
(b4…:102-104); B2 is instrument validation with no Sharpe computed; B3 and A5
are backfillable probes. The one thing that does transfer is about cost,
not power: the memo argues against trials, and a capture is not a trial. So
it lowers the expected value of variant-generating trials and leaves captures
untouched. Its honest corollary: if the skew lane's value rests on its
large-λ branch, that value is carried almost entirely by N — and N is exactly
what the outage is consuming. The memo therefore raises the cost of the
capture gap rather than lowering the value of the lane.
SCOUT: six spaces deliberated, six killed, no proposal. The strongest
structural candidate was persisting the full SPY option surface that the VRP
run already fetches and discards (execution/vrp_daily.py:168-198 pulls the
whole chain daily and keeps seven fields) — killed on bars 1 and 4: the
decision-relevant index-vol history is free and backfillable (the CBOE VIX
complex plus realized variance from owned OHLCV), and the genuinely
un-backfillable residual (the per-strike smile at the decision instant) has
no pinned consumer — VRP v2 takes its only deferred parameter from the
already-persisted twin ledger, so proposing it would mean inventing a test at
roughly one cohort per two weeks, which is precisely what the 2026-09-28 memo
says not to do. The other five died on prior art or on the un-backfillable bar.
The Actions shortage decided none of them — both live candidates rode existing
jobs — and that is recorded so a future scout does not read this run as
permission to add a new daily job.
6. Setup and what did not run
The prompt's pip install . -c requirements-ci.lock --no-build-isolation
failed on missing hatchling, as it has for three other routines this
month. ops/DAILY_AUDIT.md §0b's documented fallback succeeded this run,
so the CLI was available and the NYSE arithmetic, the test gates and the guard
invocations were all verified by execution rather than by reading. A parquet
reader was also available, which is what made the CPB hard-to-borrow census and
the shortability recount possible — neither run 29 nor the 2026-09-28 triage
could do that.
Test gates, executed at HEAD ada5bff — worth stating plainly because
Thursday's resumption depends on it:
pytest tests/ -q -m "not observability and not research" (the exact CI pre-trade command)
-> 1229 passed, 1 skipped, 375 deselected GREEN
pytest tests/ -q -> 1604 passed, 1 skipped GREEN
pytest tests/test_lab -q -> 194 passed
The three newest lab commits do not widen the trading path. import thales.cli
pulls in thales.lab and thales.lab.cli and nothing else new — no scipy at
import, no network, no undeclared dependency (scipy, new at
confirmatory.py:52, is declared at pyproject.toml:36). LAB-1 (PR #188) is
not made materially worse, and no second, distinct fail-close mechanism was
found.
NOT-RUN, with reasons: no broker credentials, no config/.env, no data/raw
(empty), no data/lab, no owner-Mac access — so no trading command, no fleet
status, no oos-monitor, no live account state, and LAB-6 could not be
replayed on real yield data. The 8-week triage history could not be
reconstructed week-by-week (50-commit shallow clone); what was verified
directly is the approved/-never-occupied fact underneath it.
7. Relay note
Actions has been refused repo-wide over billing since ~2026-09-19 (day 9).
Nothing has traded or captured since 2026-09-18. This run's PR and beacon push
will each create a workflow run that Actions refuses, so no healthchecks ping
is sent and thales-panel will read DOWN despite this run completing. Per
ops/DAILY_AUDIT.md §4c the Actions-independent arbiter is the beacon/panel
branch: it moved today, on schedule. Neither this PR nor #188 nor #190 can
auto-merge until a runner exists, because auto-merge is itself an Actions
workflow.
APPEND 2026-10-01 (scheduled check-in on PR #192) — four corrections to this note, two of them to its headline
Dated append, not an edit: the text above stands as written on 2026-09-29. It is appended rather than left alone because a stale record in this architecture is executable misinformation — the same reason panel run 31 appended to its own file before letting it merge. Three of the four items below correct this note's own claims.
1. Actions came back, so §1's "today's and tomorrow's slots cannot fire" is
no longer true. GitHub Actions resumed 2026-10-01 01:18Z after 8 dark
sessions (first healthy run: Auto-merge records PRs 36800459425, 23 s with
real steps, conclusion success). The arithmetic in §1 is unaffected — the
deadline is still Friday 2026-10-02 and the automated attempts are still
10-01 and 10-02 at 19:00Z — but they are now live rather than contingent on a
reset, and the 10-01 slot was the first since 09-18 that could fire at all.
§7's relay note was correct for 2026-09-29 (that beacon push was refused); the
10-01 panel beacon ran green.
2. This note under-counted the cost of missing the deadline, by a factor of two. §1 says the loss is "the 2026-09-18 observation" — one. Panel run 32 (2026-10-01) measured two: the no-rescue world loses the 09-18 bridge and the ISO-week-40 signal date, and it is 2 under both the last-in-week and first-in-week readings. Runs 29, 30 and 31 each said one; run 32's figure supersedes all three, including this note's. The direction of the error is worth stating plainly — the stakes were higher than this note claimed, not lower.
3. The magnitude belongs next to it, from run 31, because this note headlined the deadline without sizing it. One observation is ~0.93% of expected t at the B4 pin's own N≈54, so two is ~1.9%. The recurring monthly Actions blackout that PR #191 forecasts for ~2026-10-20 costs ~14 weeks by checkpoint 1 — ~14% of expected t, roughly fifteen times more. The rescue is still a positive trade at any magnitude (an un-backfillable observation for one command), and this note's ordering constraint still binds: the uncountersigned 2026-09-02 B4 append's clause (c) would disqualify a post-close rescue, so the ruling precedes the capture. But a reader who took this note's emphasis as a ranking of the week's risks would have mis-ranked them. Run 32 also measured the countersign itself to be free on the past — five dates flip to non-qualifying and zero observations change, 13 of 13 kept under both readings.
4. NEW, and the reason this append exists at all: the allowance reset did
NOT free this PR, and routines structurally cannot free it themselves.
auto-merge-records.yml fires on pull_request events. #192's event fired at
2026-09-29 14:00:12Z and was refused; the reset does not replay a consumed
event, so #192 sat with a red check and no second attempt. The one recovery
path available to a routine is blocked: POST /actions/runs/36579336777/ rerun-failed-jobs returns 403 Resource not accessible by integration —
independently hit by panel run 31 on its own run (36725279536) and again here.
So the earlier framing in §7 and in this PR's comment, "needs a human merge or
the allowance reset", was incomplete: the reset alone was not sufficient.
What recovered and what did not, measured from the workflow history:
| PR | fate | why |
|---|---|---|
| #194 (panel 31) | merged | received a new push at 01:18Z → fresh synchronize event → guard ran → ALLOW |
| #195 (panel 32) | merged | created after the reset, so its first event found a runner |
| #185, #186 | merged | ops/ PRs, human merge by design |
| #188, #189, #190, #191, #192, #193 | still open | events consumed during the refusal; no later push |
That backlog is not cosmetic: #188 carries run 29's LAB-1 queue row and #190
carries the 09-28 triage's FIN-4 dismissal and ten annotations, so the
queue's own state on main is six PRs behind what three routines have already
decided. Each needs either a human merge or a content push to mint a new event.
This note's own push is that event for #192 — which is why these four
corrections ride a commit instead of a comment.
No change is proposed to FRZ-1, whose finding does not depend on any of the above: the B4 and B5 pins are still unenforced, and the guard still grades an in-place edit of either ALLOW.