# 2026-10-09 — the cutover landed, and the one email arrived cut

*Daily audit record. Window 2026-10-08 22:30Z → 2026-10-09 22:30Z (Friday).*

Three things happened today that are worth keeping: the tbx-win cutover
succeeded and the fleet traded off GitHub Actions for the first time; the first
Friday digest under the owner delegation was delivered with all five of its
owner decisions cut mid-sentence; and a position the risk layer has been trying
to exit since August turned out to be un-exitable, with no alarm ever raised.

---

## 1. The cutover happened, and the evidence is a commit trailer

`scripts/win/cutover.sh` squash-merged its own docs PR #206 at
**2026-10-09 01:00:22Z** (HEAD `c680133`) and then removed its own launchd
agent. From that point the Windows box runs the day locally and pushes state,
rather than dispatching GitHub workflows.

Today's state commits carry `Runner: tbx-win`:

| leg | commit | time (UTC) | trailer |
|---|---|---|---|
| vrp | `6b9029d` | 14:37:04 | `Runner: tbx-win` |
| momentum | `f179461` | 14:56:11 | `Runner: tbx-win` |
| meanrev | `fc572e7` | 15:02:04 | `Runner: tbx-win` |
| skew capture | `4688879` | 19:08:20 | `Runner: tbx-win` |

**4 of 4 legs, on the first day, including the skew slot** — the one that missed
5 of its last 16 sessions (31%) from the Mac. The GitHub backup crons fired
later and behaved as designed: meanrev and vrp stopped at their cheap bash
early-exit in 16 and 18 seconds, and momentum ran full-fat for 13 minutes (by
design — it is the leg that keeps the daily pre-trade test gate) and committed
one macro parquet, no trades, the idempotency gate holding.

Two residuals worth naming rather than assuming away:

- **The box's real `commit()`/`push()` path and a non-dry `thales run` executed
  for the first time ever today.** The three practice gates certified dry runs
  only (`scripts/win/session.py:332`, `live_only` steps skipped under
  `--dry-run`), as panel run 38 re-verified. Today's four clean commits are the
  first evidence of the live path, not a continuation of it.
- **A session that trades and then fails `persist` leaves real broker positions
  with no state commit**, which is observationally identical to "the box never
  ran". `thales portfolio reconcile` is the discriminator; the `Runner:` trailer
  alone is not. No cloud routine can run it (no broker credentials).

## 2. The one email the owner reads cut every decision in it

Since 2026-10-07 the Friday fleet digest is the owner's only email, and the
`NEEDS-OWNER:` lines in it are the decisions the steward may not make for him.
Both renderers cut each one at `n[:240]` — `fleet_digest.py:828` (text) and
`:1051` (HTML).

`ops/STEWARD.md` pins the field order `<decision> — <recommendation> —
<deadline>` (`:243`) and requires a one-paragraph recommendation (`:64`). The
cap therefore discards exactly the payload. Measured on the real reports behind
today's 20:25:54Z send:

| ask | chars | what the owner did not receive |
|---|---|---|
| 1 tbx-win cutover | 655 | `My recommendation: spend ten minutes before 10-16 reading the cutover log…` |
| 2 B4 post-close append | 590 | `My recommendation: countersign with a 60-minute grace past the close…` |
| 3 the 60-day unit | 408 | `My recommendation is captured sessions, because the criterion's own test…` |
| 4 FRZ-1 | 263 | `…1's ruling waits on it.` |
| 5 VRP twin total | 344 | `Record the choice as a dated append before the gate matures (~2027-01).` |

**5 of 5 truncated; the word "recommendation" appears zero times in the email,
though three of the five asks contain one.** Fixed in #233 (both twins, four
observability-tier tests, both caps negative-controlled independently).

Found by panel run 38 (`research/queue/open/nol-1-owner-decision-lines-
truncated.md`, #232) hours before the send; verified here against the live
function and the real reports.

**The lesson is not the cap.** It is that yesterday's fix (#228, the inclusive
lower edge) made each report reach exactly one Friday digest — which also makes
every rendering defect in that path *permanent* rather than merely late. Ask 1's
recommendation concerned a deadline of 10-16; it had one delivery and lost it.
Exactly-once delivery raises the cost of every bug downstream of it, and nothing
in #228 or in this system's records said so.

## 3. #224's refresh could never fast-forward — and today was its first exercise

#224 (merged yesterday, to stop the false `SLEEVE DID NOT RUN — meanrev` of
10-07) gave the digest job two halves: wait for an in-flight sleeve run, then
`git pull --ff-only` so the detector reads that run's state. The second half
cannot work: the `Restore data cache` step writes into `data/macro`, which is
**tracked**, so the tree is dirty on every run, and git refuses a fast-forward
that would overwrite a modified file. **Every momentum state commit carries a
`data/macro` parquet** (8 of the last 8), so the refresh fails on precisely the
commits it exists to pick up.

Today, run `37986381726`: the wait worked (7 polls), then
`error: Your local changes to the following files would be overwritten by
merge: data/macro/credit.parquet` → `could not fast-forward main`.

It passed on 10-08 only because there was nothing to fast-forward to, which
prints `state already current` and is indistinguishable from success. **A green
you cannot believe is worse than a red.** Fixed in #234, with the real failure
reproduced in a scratch clone and the same pull then succeeding.

No harm today. The live harm case is momentum pushing after the digest's
checkout: the wait succeeds, the pull refuses, and the no-run canary falsely
names momentum — the 10-07 incident with a different sleeve.

## 4. meanrev's largest position cannot be sold, and nothing has ever said so

`AVB` was bought on **2026-08-14**. That is its last successful order. Since
**2026-08-17** every attempt to exit it has failed at the broker with
`APIError: {"code":40010001,"message":"asset AVB is not active"}`:

- **22 failed orders over 22 attempted sessions** (08-17 → 09-18, then 10-09 —
  the gap is the September Actions outage plus days the exit rule did not fire).
- **21 of 22 were sells, 19 of them `cause: emergency_exit`** — i.e. the risk
  layer's forced-exit rule, firing correctly and unable to execute.
- As of today's live broker read it is still **meanrev's #1 holding at 1.1%** of
  the sleeve.
- `WBD` joined today with the same error on a `selection` buy (first failure in
  11 orders since July).

**What never happened:** no alarm, in two months. The surfacing is a
`##[warning]meanrev: 2 NEW failed order(s)` annotation on a green run, plus a
`!! 2 FAILED order(s)` line in the digest body. The annotation is keyed on
*newness*, so a symbol that fails every day is new once and silent thereafter,
and the digest line was in a daily email that is now weekly. Nothing anywhere
tracks **persistence**: a position the book has decided to exit and cannot.

Two reasons this matters despite meanrev being the never-promotable negative
control:

1. **Kind, not size.** "An order failed" is routine. "The risk layer decided to
   exit and the broker refuses, every session, for two months" is a different
   class, and it is the class a kill-switch depends on being able to act in.
   Momentum shares the same broker path (its one lifetime failure, `SATS` on
   2026-07-01, was the same shape).
2. It silently distorts the control arm's own record — the placebo it exists to
   provide — alongside the known min-notional residue (today: 184 sells wanted,
   37 submitted; 287 broker positions against 131 targets).

**Deliberately not fixed tonight.** The remedy divides cleanly: dropping
broker-deactivated assets from a tradable universe is **trading-path logic**, not
an observability change, and so is outside this routine's rails entirely. The
*detector* — raise an exception when a symbol's exit has failed N consecutive
sessions — is in the rails, but its threshold and its escalation path are design
choices, and a badly-calibrated new alarm trains the operator to ignore the
class. That belongs in a pre-registered queue row ruled on by triage, not
improvised by a routine at 22:30Z. Recommended to Monday's triage; escalated to
the owner for the universe half.

## 5. What else held

The trading day was clean. No halt file at any sleeve or fleet level; both
weights sleeves' kill-switches inactive and stamped `2026-10-09`; momentum
drawdown 7.78% and meanrev 2.94% against 20% triggers; gross 0.28× (binding
constraint `vol_target`) and 1.03× against a 3.0 cap; zero safety HALTs or
REJECTs. VRP opened a new cohort (SPY 764/763P, 2026-11-06, 28 DTE, $0.11/sh
credit, $89 max loss). Every capture stream passed QA on today's data. All 138
config keystones match. Full suite 1,749 passed / 1 skipped with both of today's
fixes in. The go-live clock is **83 / 126** live trading days; the VRP forward
gate **53 / 126**. September's 8-day capture notch across 7 streams is
unchanged and permanent.

The outbox channel reconciled cleanly for the first time end-to-end under the
new policy: yesterday's report pushed 22:25:33Z, the relay logged
`1 report(s) withheld from email by owner policy (weekly_digest_only) -- this
branch is the record; the steward reads it`, and the steward then merged #228
and #176 — both of yesterday's escalations — within 40 minutes.

---

## Addendum, same evening — the two skew legs are complementary, not duplicates

Written after the report went out, because the fact is easy to misread and the
misreading touches a decision the owner has open.

A **second** `skew: snapshot 2026-10-09` commit landed at **23:09:01Z**
(`634fa7b`), four hours after the box's in-session capture at 19:08:20Z
(`4688879`). The obvious reading — GitHub's backup cron re-ran the capture and
overwrote an in-session snapshot with a post-close one — is **wrong**, and it
matters because "post-close disqualifies" is exactly the B4 question awaiting
the owner's ruling.

What the two commits actually touched:

| | 19:08Z, `Runner: tbx-win` | 23:09Z, GitHub backup cron |
|---|---|---|
| `options_chains/2026-10-09` | ✅ written | — |
| `options_iv_term/2026-10-09` | ✅ written | — |
| `options_skew.jsonl` | ✅ 883 records | — |
| `shortability/2026-10-09` | ✅ written | — |
| `short_volume/2026-10-08` | ✅ written | — |
| `short_interest/2026-09-30` | — | ✅ written (new file) |

**Nothing was overwritten.** The backup leg added exactly one file the box had
not: the **bi-monthly consolidated short-interest** parquet, which is on a
different cadence from the daily time-gated streams and was genuinely missing.
Every daily stream kept its in-session capture.

So the post-cutover arrangement for this workflow is two legs with different
jobs, not one leg running twice:

- **the box, 12:00 PT / ~19:00Z** — the daily time-gated streams, captured
  *in session*, which is the whole reason the slot exists;
- **GitHub's cron, ~23:00Z** — a late net that writes only what is still
  absent, so it contributes the off-cadence files and no-ops on the rest.

**Two consequences worth holding.** First, a 23:0xZ `skew: snapshot` commit is
**not** evidence of a post-close capture of the time-gated streams, and must not
be counted as one in the B4 census — check the commit's file list, not its
timestamp. Second, the `options_iv_term` post-close count that ask 2 of today's
digest cites ("15 of the 37 term-stream days were captured entirely after the
close") is a **pre-cutover** statistic: from 10-09 forward the box captures that
stream in session, so the ratio stops growing. That strengthens the owner's
option of countersigning a grace window rather than disqualifying post-close
days — the population it would disqualify is now closed, not open-ended.

This also corrects one phrase in the report sent at 22:30Z, which called the
pending backup legs "pure duplicates anyway". They are not duplicates; they are
a complement. No check or conclusion in that report changes — captures were PASS
on either reading.
