# 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.yml` fires
  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-skew` by 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/*.parquet` joined to
  `data/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-194` says "record it, **drop to
  monthly**, and raise the bar on what the panel proposes" — `ops/ROUTINES.md`
  still 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 reached `queue/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.
