# Panel run 29 — 2026-09-28

Records for the daily research panel's run of Monday 2026-09-28. One row
opened (`queue/open/lab-1-collection-error-fail-closes-trading-gate.md`); this
note carries everything that was judged worth recording but **not** worth a
queue slot, plus the run's dated escalations.

**Run record: considered=7 · survived-review=1 · opened=1.**

Standing context, because it governs most of what follows: GitHub has refused
every Actions job repo-wide since 2026-09-19 (the account's free allowance is
spent; it resets **2026-10-01**). Re-verified this morning — the two lab-commit
runs at 06:00Z and 06:21Z today died in 3 seconds with zero steps, the billing
signature per `ops/DAILY_AUDIT.md` §4c. Nothing has traded since **2026-09-18**;
five trading days are permanently lost (09-21 … 09-25) with 09-28 in progress.

---

## 1. THE ONE THING WITH A DEADLINE — Friday 2026-10-02, zero margin

**This is the headline of the run.** It is recorded nowhere else in the
repository — not in a memo, not in the ledger, not in a queue file.

`research/2026-08-01_b4_skew_activation_pin.md:83-85` is hash-frozen and reads:

> If s′ − s exceeds **10 trading days** (a dead-cron stretch), the observation
> is dropped rather than becoming one giant-horizon week.

The last option-skew capture is **2026-09-18**, carrying **888 valid rows** —
comfortably over the pin's ≥400 floor, so it *is* a signal date. Counting NYSE
trading days forward from it:

| next qualifying signal date | trading days from 09-18 | the 09-18 observation |
|---|---|---|
| 2026-10-01 (Thu) | 9 | survives |
| **2026-10-02 (Fri)** | **10** | **survives — "exceeds 10" is false, by exactly zero margin** |
| 2026-10-05 (Mon) | 11 | **destroyed, permanently** |
| 2026-10-09 (Fri) | 15 | **destroyed, permanently** |

So: **a qualifying skew capture (≥400 valid rows) must land on or before
Friday 2026-10-02**, or the 2026-09-18 observation is dropped from the B4
panel for good. ISO week 2026-W39 (09-21 → 09-27) is already lost outright —
it has no qualifying date and never will.

The allowance resets 10-01, so the automated path *just* makes it. If anything
slips — the reset lands late in the day, the first run fails, the dispatcher
misfires — the remedy is to run `thales snapshot-skew` **by hand from the Mac**
on 10-01 or 10-02. This is un-backfillable either way, on the one live forward
experiment this shop has.

## 2. Escalations with dates — none of these is buildable work

- **#185 and #186 are the only lever on whether this outage recurs, and
  auto-merge cannot land them.** The outage memo records that the owner has
  decided to stay on the free tier, so the remedy is "fit inside the
  allowance"; those two PRs are exactly that fix (~250 Actions minutes a
  month). But `auto-merge-records.yml` is itself an Actions workflow, refused
  until the reset. **A human merge is the only path**, and if they land after
  October's allowance is already burning at September's pace, the outage
  repeats late in the month.
  One caveat on sizing, worth stating because nobody has: both PRs were
  measured against *this repository's* minutes, while the allowance is
  account-wide across repositories. Nobody currently knows the account-level
  number, and nothing in the tree computes it (`grep` for billing/minutes/
  allowance finds only `ops/DAILY_AUDIT.md:380`, a post-hoc diagnosis recipe).
- **#177 and #176 republish known-wrong numbers publicly on 10-01.** Both are
  still unmerged and both defects are still live on `main`:
  `src/thales/execution/fleet_digest.py:169` is still `"gate_days":
  len(equity_hist)` (the VRP forward clock, inflated by 11 days because it
  counts from activation rather than the pinned reset, and counts marks rather
  than returns), and `forward_gate.py` still sums ledger rows the ledger's own
  annotation marks basis-contaminated (~2× overstated twin). The first
  post-outage fleet digest runs 2026-10-01 and `export-public` commits to the
  public site in the same workflow.
- **The December go-live gate — a correction to the standing record.** The
  gate could never have PASSED on 2026-12-01: NYSE days 2026-06-11 →
  2026-12-01 inclusive = 120, so `n_live` maxes at ~119 against
  `min_live_trading_days: 126`. `not_before` is a **floor, not a target**, and
  `config/settings.yaml:353` already says so ("clean clock 2026-06-11 + 126 td
  lands ~Dec 10").
  More materially: the daily audit of 2026-09-24 concluded that "each further
  lost day moves that date one further." **That is wrong.** All three trading
  workflows run `thales portfolio backfill-equity --days 14` on every run
  (`paper-trading.yml:276`, `-meanrev.yml:165`, `-vrp.yml:111`), and
  `AlpacaBroker.get_equity_history` maps `days <= 30` to Alpaca period `"1M"`
  and **does not truncate back to `days`** (`alpaca_broker.py:558-584`,
  verified). All eight lost sessions fall inside one month of 2026-10-01, so
  the first post-outage run should heal the clock in full and restore the
  126th return to ~2026-12-10. Lost days are not cumulative.
  **The new exposure this creates:** the `"1M"` window means the earliest lost
  mark (09-21) stops being recoverable around **2026-10-21** — and the outage
  memo says allowance exhaustion "will recur, most likely late in a month",
  i.e. the next outage would land in the window where the first one is still
  un-healed. Worth confirming the backfill actually fired on 10-01 rather than
  assuming it.
- **Triage's kill-counter (a) fires 8 of 8 today**, and fires into nothing:
  `git log -- research/queue/approved/` returns exactly one commit ever (the
  README), so the owner-approval directory has never held an item, and the
  pre-committed remedy (AUTO-APPROVE) was itself dismissed 2026-08-31 as
  unbuildable. Eight weeks, thirteen proposals, zero approvals. See §3.
- **Five older PRs carry new head commits no panel has reviewed.** #168, #171,
  #174, #176 and #177 all gained new heads on 2026-09-26 (07:27–07:36Z), each
  a response to a prior panel review; **three touch `src/thales/execution/`**.
  Checked cheaply because the file is keystone-pinned on a live sleeve:
  #177's `config/vrp.yaml` change is comment-only, no keystone value moves.
  Flagged for the next change-review lens; reviewing five PRs' new heads was
  not this run's scope.
- **The panel itself was dark on Friday 2026-09-26** — no beacon commit, no
  PR, no session artifact. The scheduler's control plane exposes only the most
  recent run, so from here the cause is **not establishable**; recorded rather
  than guessed.

## 3. RDR-1 — the third decline, and the last one

`research/queue/dismissed/rdr-1-no-reader-for-dismissed-reopen-conditions.md`
records the finding that `queue/dismissed/` holds 30 files, every one carrying
a reopen condition, and nothing reads them on their date. Run 28 declined to
reopen it and pre-registered: *"the next panel to find a fourth instance
should open the row without further deliberation."*

**The fourth instance exists, and both of run 28's conditions are satisfied.**

- **The "dies permanently" clause is unsatisfied.** `grep -n "dismissed"` over
  `ops/QUEUE_TRIAGE.md`, `ops/prompts/triage.md`, `ops/RESEARCH_PANEL.md` and
  `ops/prompts/panel.md` returns only write-side and structural hits. No
  routine is instructed to *evaluate* reopen conditions.
- **CQA-2 is the fourth fired-unevaluated dismissal** (after LCK-1, ERN-1,
  BCN-1). Its leg (a) —
  `research/queue/dismissed/cqa-2-latest-file-only-qa.md:45`, *"a capture
  stream loses a day and **no instrument reds** … reopen immediately and rank
  it P0"* — has both conjuncts measurably true during this outage, and
  `grep -rn "CQA-2"` returns only fold cross-references. Nothing evaluated it.

**It is nonetheless recorded here rather than opened as a row**, for three
reasons, and this is the third time the panel has declined:

1. **The row is not buildable, and building it would do harm.** The ask is
   prose in `ops/QUEUE_TRIAGE.md` *and* `ops/prompts/triage.md`. The prompt
   mirrors carry a standing rule that any prompt edit lands in the mirror and
   is pushed to the live control plane **in the same sitting** — and the
   implementer's rails forbid touching the control plane. An implementer
   building this row would land the mirror and leave the live trigger prompt
   stale: precisely the invisible prompt-rot the rule exists to prevent, and
   which has already happened twice in this repo.
2. **A note reaches the owner by the same channel a row does.** A dated file
   in `research/` is a PR, and PRs surface in the fleet digest's
   research-pipeline section — the same visibility, at zero slot cost and zero
   eviction. Run 28's own annotation reached the owner exactly this way.
3. **`open/` is at its 12-file cap**, and the one row this run opened (LAB-1)
   already spends the eviction.

**New material that changes the owner ask itself.** `dismissed → open` is
**not a sanctioned transition for any routine**. Triage owns `open→dismissed`;
the implementer owns `approved→built|dismissed`; nobody is granted
`dismissed→open`. Yet all 30 dismissed files carry reopen conditions, and
CQA-2's own text says *"Reopening costs one `git mv`"* — a move no routine may
make. So the two-sentence fix as previously drafted is **incomplete**: it
commissions a sweep whose only remedy nobody is permitted to execute. The
corrected ask is two things, not one:

> (a) Add to `ops/QUEUE_TRIAGE.md` §1 **and** `ops/prompts/triage.md`'s sweep
> step: *"Also sweep `research/queue/dismissed/` for reopen conditions dated
> within the next 14 days or whose triggering event has already occurred;
> report each one."*
> (b) Name who may act on a fired condition — either grant triage the
> `dismissed→open` move, or state that reopening is the owner's move alone.
> Without (b), (a) produces a report nobody can act on.

**Terminal clause, and it binds this panel, not the next one.** If the owner
does not make that edit before the next triage run, the panel **stops raising
RDR-1 entirely** and it is recorded as an owner decision. A panel that
annotates the same unactioned item a fourth time is manufacturing output —
`NORTHSTAR.md` §2's degenerate strategy in slow motion — and a chain of panels
each minting an obligation for its successor is a ratchet, not discipline. The
ratchet is cut here, on the record.

**One argument deliberately not used.** `ops/RESEARCH_PANEL.md:21-22` says
reopen conditions become "standing tripwires future panels **EVALUATE** (did Y
become true?)", and the panel prompt orders that section be followed exactly —
so it is arguable that a routine *is* already instructed, and RDR-1's "dies
permanently" clause is already satisfied. RDR-1's author quoted that exact line
and ruled it non-operative preamble. Re-reading it now, in the direction that
retires the file, is the after-the-fact reinterpretation this shop treats as a
cardinal sin. Recorded as **contested**, not used to close. It has one
consequence worth carrying: if that line binds, the defect class is
*non-compliance with an instruction the panel already has*, not a missing
instruction — and no queue row has ever fixed non-compliance.

**On CQA-2 itself: it should NOT be reopened.** Its proposed fix is a
missing-today check *inside* `capture_qa`, and `capture_qa` did not run at all
during the outage — the remedy is counterfactually inert against the event
that tripped its own trigger. It counts as a fired-unevaluated instance (which
is what RDR-1's leg (b) measures) without being a valid reopening. Those are
two different questions and were being run together.

## 4. Hypothesis lab — two findings folded here rather than opened as rows

The lab's trading-safety exposure is the row (LAB-1). Three further findings
were raised against its honesty guarantees; one survives, as a fold
instruction.

- **FOLD INSTRUCTION — `lab confirm` prints the vault verdict before it
  charges the ledger.** `src/thales/lab/cli.py:505-528` echoes the p-values,
  the bar and `CONFIRMED / NOT CONFIRMED`; the `confirm` row is appended at
  `:529`, the **last** statement in the function. A Ctrl-C, a full disk or an
  append error after the answer is on screen leaves `k` un-incremented, the id
  absent from `ledger.confirmations`, and the rule re-openable at α/2 with the
  holdout answer already known. This is not an accusation of cheating — it is
  that the operator would not know the vault had been opened.
  `src/thales/lab/study.py:480-487` states the correct principle in its own
  docstring (*"Charge the ledger FIRST, then write the artifacts … the trials
  are still counted (the safe direction)"*); `confirm` is the one path that
  does not follow it. **When anyone next opens `src/thales/lab/cli.py`, move
  the append above the first `typer.echo` of any result.** Three lines, with
  the module's own precedent as the spec.
- **Provenance, worth one sentence: both committed study runs are stamped
  `+lab-dirty`.** The lab's ledger records `code_version` as
  `18a510b+lab-dirty` and `547950a+lab-dirty` — i.e. the two closures CLAUDE.md
  now cites as pre-registered NULLs (`cross_index_v1`, `comprehensive_v1`) were
  produced by code that was not committed at the time of the run. Recorded, not
  escalated; the verdicts were NULL in both cases, so nothing rests on them.
- **Struck at review:** the claim that the lab's vault date and never-shrink
  floor are currently unguarded because their pinning test runs only in
  `fleet-digest.yml`. True, but it is a strict subset of "no CI has run in nine
  days", which is already the headline incident — and the runtime backstop is
  not vacuous in the real repo, since `results/lab/ledger.jsonl` holds four
  records all sealed under `2023-01-01` for `check_vault` to refuse against.
- **Also struck:** that trial charging is blind to the lab's own code version
  (`settings_key` hashes settings but not source; `code_version` is written
  five times and compared never). The gaming channel is genuine but exotic, and
  the naive fix — folding `code_version` into `settings_key` — charges a trial
  for every unrelated commit, which is worse. Recorded as known, not as work.

## 5. Capture continuity — considered and not filed, deliberately

All six time-gated capture streams are delivered by one workflow on one
billing-gated account, and the incident memo's remedy section is framed
entirely as *cost-fitting inside the allowance*, never as *capture
continuity*. It is a real structural observation and it is **not** a
relitigation of `queue/dismissed/cron-delay-capture.md` (that dismissal
rejects capturing runner-latency telemetry as a data stream; this is a
delivery-path question, a different object).

It is not filed as a row because there is no experiment in it: the remedy set
is "pay GitHub", "move the capture crons to the Mac launchd dispatcher that
already exists", or "accept the risk" — owner decisions with no falsifiable
design and no kill criterion, and `ops/RESEARCH_PANEL.md` §4 says a suggestion
without a kill criterion does not ship. A budget detector is not buildable by
any routine either: the binding number is account-wide and reachable only with
owner-side billing credentials no routine container holds.

**And no dismissal file was written for it**, which is this run's one original
procedural point. A `dismissed/` file's entire promised function is to become a
standing tripwire a future panel evaluates — and this run has just measured
that **four such tripwires have fired unread**. Minting a fifth record whose
mechanism is demonstrably not working, on a topic the owner may act on this
week, adds surface and buys nothing. Its one concrete consequence already has a
louder carrier: §1's Friday deadline *is* capture-continuity risk, dated,
quantified, and attached to the only live experiment.

## 6. Clean negatives — recorded so nobody re-derives them

- **B4-NOTCH leg (b) has NOT fired.** Cumulative accrued-week loss measured at
  6.7% today (15 accrued ISO weeks, 1 with no qualifying day), rising to
  ~11.8% once W39 is counted — marginally above, not "materially exceeding",
  the ~11% its reopen condition names, and by an outage rather than a calendar
  notch.
- **B5's termination clause has NOT fired.** An 8-session outage is not the
  lane being retired; the clause cites a two-consecutive-DEGRADED-verdict rule.
  Recorded now so nobody reaches for it after seeing Δ.
- **B5's lost days are already pre-registered as backfillable** — the pin says
  so explicitly and names `portfolio backfill-equity` as allowed. One residual:
  if the marks are *not* backfilled, returns are computed by consecutive-mark
  differencing with no date-gap awareness, so 09-18 → 10-01 becomes a single
  9-session compounded return — and 2026-10-01 is a turn day under the pinned
  phase. The single most contaminated return in the record would be classified
  as a turn day. The backfill removes this entirely, which is one more reason
  to confirm it fired rather than assume it.
- **RCX-1 leg (a) has NOT fired** — the largest line in any committed `.jsonl`
  is 740 B against its 4096 B tripwire.
- **NORTHSTAR §6's G1 row is correct** and the stale "to build" line has not
  returned; §7's contrary line is the dated 2026-08-02 appendix, superseded by
  §7b. No live defect.
- **The live trigger prompt matches `ops/prompts/panel.md` verbatim.** Read
  from the scheduler's control plane and compared against the mirror's fenced
  body; prompt-rev `2026-08-21-panel-r2` on both. No disagreement to report.
- **Frontier: zero from both halves, correctly.** No deferred direction
  activated (B1 at 34 of 60 with median 7.0 expiries against its <5 vacuity
  bar — trending *away* from vacuity; B2/B3/A5 unbuilt and owner-gated). Six
  scout idea-spaces deliberated, four dead on prior art — including the one
  genuinely attractive candidate, IBKR's public `shortstock` fee file, killed
  because the 2026-07-25/26 probes found the anonymous FTP dead and
  iBorrowDesk bot-blocking, which puts it in the banned fragile-scrape class.
- **Scout cadence: 6.00 of 8 weeks.** Revert-to-watch-only fires 2026-10-12 —
  the same date as ERN-1's dated default. Two clocks, one date, two weeks out.

## 7. Slipped clocks — the outage moved these

| clock | was | now, assuming 2026-10-01 resumption |
|---|---|---|
| `options_iv_term` 60-td vacuity read (B1) | ~2026-10-26 | ~2026-11-05 |
| shortability 60-td read (BTB-1 is pinned to land before it) | ~2026-10-19 | ~2026-10-29 |
| GAP-1's ledger deadline (lost capture-days must reach `ACKNOWLEDGED_GAPS` + `CAPTURES.md` before `capture_gaps()`'s trailing 20-td window rolls past them) | ~2026-10-23 | ~2026-10-28 |
| momentum go-live 126th return | ~2026-12-10 | ~2026-12-10 **if the backfill fires**, ~2026-12-22 if not |
| VRP forward gate 126th return | ~2027-01-26 | ~2027-01-26 **if the backfill fires**, ~2027-02-05 if not |

GAP-1 remains the cheapest dated item in the tree (~0.2 pd, implementer-
buildable) and it sits in `dismissed/`, where no implementer can reach it —
which is §3's problem wearing its most expensive hat. Note it needs three new
`ACKNOWLEDGED_GAPS` keys created (`options_skew`, `options_chains`,
`options_iv_term` have none), not merely lines appended.

---

## Appended 2026-10-01 from the scheduled check-in on this PR

This PR was stranded for three days (see the mechanism in panel run 30's
append: auto-merge fires on `pull_request` events, #188's was consumed and
refused during the outage, and an allowance reset does not replay it;
`rerun-failed-jobs` returns 403 to a routine). It is merging now with a
correction it must not merge without, because §2's December-gate paragraph
makes a prediction that is **partly falsified**.

### The backfill healed 6 of 8 lost marks, not 8 — and the same two are missing on all three sleeves

§2 said the first post-outage run "should heal the clock in full". Measured on
`main` at `3d4f432`, after all three sleeves ran on 2026-10-01:

| sleeve | outage-window trading days | missing after backfill |
|---|---|---|
| momentum (`data/processed/`) | 10 | **2026-09-21, 2026-09-28** |
| meanrev (`data/state/meanrev/`) | 10 | **2026-09-21, 2026-09-28** |
| vrp (`data/state/vrp/`) | 10 | **2026-09-21, 2026-09-28** |

So the mechanism I described does work — 09-22, 09-23, 09-24, 09-25, 09-29 and
09-30 all came back from broker truth — but **two sessions did not**, the two
Mondays inside the outage, identically on all three sleeves. Identical across
three independent Alpaca accounts means this is systematic, not a per-sleeve
fluke.

Momentum now stands at **76 marks since the pinned clean clock 2026-06-11 →
`n_live` 75**, against 78 NYSE trading days in that range. 126 returns needs
127 marks.

**I am deliberately not naming a cause.** Both obvious candidates — Alpaca's
`period="1M"` portfolio history omitting those points, or
`select_backfill_entries` rejecting them — are untestable from this container
(no broker credentials: **NOT-RUN**), and this run's sibling panel (run 31)
spent its headline on a record whose named cause was false. The measurement is
solid; the explanation is open. Note also that it is *not* a general Monday
defect: 13 of the 15 Mondays in the clean-clock window carry marks.

**The dated consequence, which is the reason this is worth your attention.**
`AlpacaBroker.get_equity_history` maps `days <= 30` to period `"1M"`, so
2026-09-21 leaves the recoverable window around **2026-10-21** — and PR #191
independently forecasts the next allowance exhaustion at **~2026-10-20**. The
chance to recover that mark closes at almost exactly the moment the next
blackout is forecast to begin. If those two marks are wanted, the window is
roughly the next three weeks, and it is narrower than it looks.

Also worth correcting in the frozen archive's direction: `RESEARCH_QUEUE.md`
records `equity_history.jsonl` as "BACKFILLABLE — **0** — healed by
`backfill-equity`". That is now false for two sessions on three sleeves. The
archive is frozen and must not be edited; this note is the correction of
record.

### The other three check-in items

- **The skew deadline is still live and still unmet.** `data/options_skew.jsonl`
  on `main` still ends at **2026-09-18** (888 valid rows). The last four Skew
  Snapshot runs all failed — 09-29 19:11Z, 09-29 22:53Z, 09-30 19:11Z,
  09-30 22:53Z — and the most recent died in 3 seconds with zero steps, i.e.
  the billing refusal, not a new defect. Today's 19:00Z attempt had not fired
  when this was written (15:20Z). Two automated attempts remain (10-01, 10-02).
  Panel run 32's correction stands and matters for any hand-run rescue: the
  rescue is worth **two** observations, not one, and a *post-close* rescue
  would fail clause (c) of the B4 pin's still-uncountersigned 2026-09-02
  append — so the countersign ruling has to precede the rescue, not follow it.
- **Trading resumed on all three sleeves** (state commits `3d4f432`,
  `a4597f0`, `18f72a0`), so momentum's October alpha selection was not missed.
- **#185 and #186 both merged** 2026-10-01 07:31Z and 07:32Z, by hand as
  predicted. §2's note that they needed a human merge is discharged. Its other
  claim — that nobody had the account-level number — was closed the same day by
  PR #191, which measures the trims at **~8% of the account** and forecasts
  recurrence **~2026-10-20**. That is a far larger loss than the skew deadline
  this run headlined (#191 and run 31 size it at roughly fifteen times), and my
  run put its emphasis on the smaller of the two.
