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.

RWG-1 — the runaway-order guard: phantom turnover-blend base on a HALT, and a blocked drawdown liquidation

status: open · raised panel run 11 (2026-08-20) · migrated 2026-08-24 (body verbatim below) · class: loudness & safety (live path) · judgement: YES · effort ~0.5–1 pd · leg 1 was resolved by the owner 2026-08-20 — see the dated triage note at the foot


15. RWG-1 — the runaway-order guard has halted the meanrev control sleeve two consecutive days (zero orders; the sell-count floor rose 44→149), the halt path advances its own turnover-blend base while nothing executes (unruled semantics), and a drawdown kill-switch liquidation of the current 196-name book would be blocked by the same guard — armed today, not latent. · judgement: YES · effort ~0.5–1 pd · horizon: live now (the control is not trading; the phantom blend base compounds each halted day)

Ground truth (data/state/meanrev/order_log.jsonl): 08-19 safety_halted: true, "205 orders exceeds max 150 (runaway guard)", n_targets 209, n_submitted 0; 08-20 same at 158 > 150, n_targets 161, n_submitted 0; selection_completed: false both days; equity marks continue on a frozen book (196 names, portfolio_snapshots 08-17→08-20: 173→185→196→196).

Leg 1 — calibration is a policy call, and it is the owner's. The guard counts the entire batch with no de-risking exemption (safety.py:245-250); the only de-risking exemption in the file (all_de_risking, safety.py:225) gates the daily-loss breaker alone — so CLAUDE.md's "de-risking is never blocked" is true only there and for per-order REJECTs. config/meanrev.yaml:128 pins max_orders_per_run: 150 as a bare copied value; the calibration comment behind 150 ("a full rebuild is ~50-100 orders") lives in momentum's config (settings.yaml:238) — a 50-name-sleeve number applied unmodified to a daily-churn sleeve holding 196 names against a 50-name selection (PC-1's unbounded growth, peak 2.92x). Options, none pre-decided: (a) exempt all-sell batches from the count guard (the guard's Knight-Capital rationale is amplification, mostly buy-side — but a runaway-sell loop is a real class; this trades that protection for guaranteed de-risking); (b) scale meanrev's max_orders_per_run to its book (config VALUE — owner-only, the panel proposes nothing here); (c) accept the wedge for the never-promotable control and record that it trades only on sub-150-order days. Every code path here is the fail-closed safety layer: the panel does not touch it.

Leg 2 — the blend base advanced while nothing executed; rule the semantics before the burst arrives. daily.py:1623-1634 persists last_targets (the next day's turnover-blend base) before execute_orders (:1692), guarded only by dry_run — no halt guard. Verified consequence: kelly_ledger.json last_targets is dated 2026-08-20 with 161 weights despite zero submissions, and weighted_turnover read exactly 0.5000 = the cap on both halted days — measured against the phantom base, so the intended book has walked two full cap-steps away from the real one; the clearing day's realized weighted churn against the REAL book can approach ~1.0, far past the daily cap. (The 205→158 order-count decline is phantom-target decay, not book convergence — sells rose 44→149 while resizes shrank.) This is a semantics divergence to rule, not a plain bug: the engine's own nearest analog retains the intended book in-cash and re-enters uncapped (engine.py:868-873, :657-666), and the live comment shows deliberate mimicry — but a safety HALT was unmodeled. Either gate the persist on execution success, or record intended-book semantics (and the post-halt burst) as deliberate; pin whichever ruling the owner records with a test.

Leg 3 — the same guard blocks the emergency exit, and on meanrev it is armed now. The drawdown kill-switch flat-to-cash path (daily.py:1089-1105) routes through pipeline.execute_orders_safety_gate → the count guard, which exempts nothing: a liquidation of any >150-name book HALTs. For meanrev's current 196-name book this is not latent — its own kill switch firing today would be blocked (momentum at 63 names is clear). This contradicts the safety layer's own stated intent ("blocking a de-risking trade during a crash would leave the book exposed, the opposite of fail-closed", safety.py:216-219) precisely in the emergency it exists for. Compounding, same persist-before-execute class: the kill-switch path wipes the Kelly holding-return snapshot at daily.py:1101-1103 before execute_orders — a halted liquidation clears the snapshot while the book remains fully held. Legs 2+3 resolve in the same fix-sitting as leg 1's policy call.

Test design. Leg 1 (if (a)): a 196-sell batch passes the count guard, a 196-mixed batch still halts; revert → red; regression: a simulated kill-switch liquidation of a >150-name book reaches the broker. Leg 2: pin the recorded ruling (halt → last_targets unmoved, or intended-book semantics asserted with the burst documented); revert → red. Leg 3's snapshot wipe: on a halted liquidation, the Kelly snapshot survives.

Kill criterion — pre-registered. If by 2026-09-04 (10 td) meanrev has resumed submitting with NO config or code change, the wedge-persistence claim for this instance is falsified — record that, and the row narrows to legs 2+3, which stand independently of whether this halt self-clears. If the owner rules (c) and the leg-2 ruling is pinned, the row closes with the wedge accepted-as-recorded. Zero registry rows, zero DSR debt, no pinned value touched.

Coupling flags (for triage, not edits): PC-1's 08-31 dismissal default rests on pre-halt evidence (expiry watch above). The AVB/BRK-B failed-pair day-3 tripwire is masked, not resolved — with n_submitted 0 the two per-symbol failures cannot occur; it re-arms the day the halt clears.

Triage note — 2026-08-24 (annotation only; the body above is untouched)

Leg 1 is DECIDED and the row narrows to legs 2+3. The owner took option (b) on 2026-08-20 in 955db93: meanrev's safety.max_orders_per_run 150→300 (config + keystone, the deliberate two-touch); momentum and vrp stay at 150. Re-verified from data/state/meanrev/order_log.jsonl: 08-19 n_submitted 0 of 209 targets, 08-20 0 of 161, then 08-21 133 of 149 and 08-24 96 of 73 — the sleeve is submitting again.

The item's own 09-04 falsifier is therefore already answered, and in the direction that CONFIRMS the row, not the one that narrows it. The pinned wording was "if by 2026-09-04 meanrev has resumed submitting with NO config or code change, the wedge-persistence claim is falsified". A config change was required to clear the wedge, so wedge-persistence stands as claimed. Legs 2 and 3 stand independently either way, as the item said.

Leg 2 has since been measured, not just predicted. The daily audit of 2026-08-21 recorded the clearing-day churn against the phantom base — realized cap-accounted churn 67.2% two-sided vs the 0.5/day cap (97.4% gross incl. cap-exempt emergency exits), with last_targets dated 2026-08-20 carrying 161 weights against n_submitted 0. Durable record: research/2026-08-21_meanrev_clearing_day_churn.md (PR #113, merged 9b2e268). Attribution — legal frequency-scaled catch-up vs phantom-base artifact — was deliberately left to this row and is still open.

Leg 3 is temporarily de-armed on meanrev and re-arms on its own. With the cap at 300 a liquidation of today's book passes the count guard; the book is 219 names as of 2026-08-24 and rising (173→185→196→196→196→219 since 08-17), so the guard re-arms against the kill-switch path as the book approaches 300. The structural point the item makes — the count guard exempts nothing, including an all-sell de-risking batch — is unchanged by the recalibration.


Triage note — 2026-09-07 (annotation only; the body above is untouched)

Read this first: PR #154's "leg 3" is IGD-1's leg 3, not this row's. #154 (ee68ebe, 2026-09-06) is titled "IGD-1 leg 3 (owner ruling) + FOS-1" and rules the failed-order escalation shape (exit 4 on a NEW de-risking failure). It does not touch the runaway-order count guard. Recorded because the titles collide and a reader scanning git log would reasonably conclude otherwise.

Leg 2 — STILL LIVE, verbatim. daily.py:1795-1803 persists ledger["last_targets"] guarded only by if not self.pipeline.dry_run: (:1796); execute_orders runs at :1862. No halt guard, no recorded semantics ruling, no pinning test.

Leg 3 — the core is STILL LIVE; only its compounding facet closed. KSP-1 (#151, 47ea024) fixed the side-effects this row named: _liquidate_to_cash now reads the halt state (daily.py:1222-1223) and on a halt advances nothing, the kill-switch active: True publish is deferred through _kill_switch_pending (:1103-1106:1227-1230), and the Kelly snapshot is cleared only in the non-halt branch (:1231-1234). The blockage itself is untouched: safety.py:261-267 still counts len(orders) with no exemption, and the only all_de_risking exemption remains scoped to the daily-loss breaker (safety.py:225-232). The code's own comment at daily.py:1213 names "RWG-1 leg 3: the count guard blocks any >150-name book" as a still-live premise — so the fix that landed knows it did not close this.

Arming status re-measured, and it moved the wrong way. The 08-24 note predicted leg 3 would re-arm as the book approached the 300 cap; the 08-31 note recorded the book falling. It then climbed: 203 (08-28) → 233 → 250 (09-01) → 248 → 243 → 239 (09-04). On 2026-09-01 meanrev submitted 241 orders against its 300-order cap — the closest it has come since the 08-19/20 halt that needed an owner config change to clear — and a kill-switch liquidation of that day's 250-name book would have been 250 sells, 50 short of a HALT. Momentum is clear (67 names against 150).

Consequence for sequencing. The row's own text says legs 2 and 3 resolve in one sitting with a ruling; SAC-1's cap note asks to be sequenced first so the ruling is made once. Triage carries both into this week's decide-list as one item on that basis, and makes no ruling itself — every path here is the fail-closed safety layer.