2026-09-05 — External design review (r2): maintainer-side verification
Provenance. An advocate-contested external design review of this repo at
cd571b2 arrived 2026-09-05 and is committed verbatim as
research/2026-09-05_external_design_review.md. It is the second review of
the day at that commit: the first ran without a repo-advocate and was not
kept; the second re-derived every figure against an advocate and says its
predecessor's headline number was wrong by ~10× in the repository's favour.
This memo is the maintainer-side verification: every load-bearing claim
re-checked against the code at cd571b2 (source unchanged through 3779020,
this PR's base — only two queue rows landed in between), against committed
state, and — where the reviewer explicitly could not look — against broker
truth pulled read-only on 2026-09-05.
Verdict up front. Findings 1–4 and 6 are real and reproduce exactly;
findings 5 and 7 are real but smaller than written; the reviewer's stated
residual uncertainty on finding 1 is now measured: 0 refused cancels in
544 checked; one of its two open questions closes (the TCA median is not
biased by the replacement path); and the broker pull surfaced one finding the
review did not have (OSR-1). Nothing ships code from this PR — records only:
four queue rows, one row moved to built/, one dated census note on SAC-1,
and the RESEARCH.md banner.
What the reviewer could not reach, measured
Finding 1 — the inert cancel guard has been exposed 546 times and has never fired
The review reproduced by execution that broker.cancel_order returning
False (what every implementation does) still leads to a full-size market
replacement at daily.py:1252, counted 546 cancel-replacements in the
meanrev order log, and wrote: "the residual uncertainty is the broker's
cancel-refusal rate, which I cannot measure without broker logs." Measured
here with thales --sleeve meanrev portfolio orders --days 45 --json
(3,255 broker orders, 07-23 → 09-04) joined to the local log on order id:
| quantity | value |
|---|---|
local replacements (market_replace_of set) | 546 (544 in window; 07-15 and 07-20 predate it) |
| original limit orders — terminal status | canceled 544 / 544, filled_qty 0 on every one |
| cases where original + replacement fills exceed intended qty | 0 |
| observed cancel-refusal rate | 0 / 544 |
| replacement terminal statuses | filled 412 · rejected 130 · expired 2 · 2 out of window |
So the exposure count is right, the mechanism is right, and the consequence
has never occurred. The review's p_manifests 0.5 is the probability that a
replacement happens; the probability that a refused cancel is followed
by one is much lower. The fix is unchanged and trivial (IGD-1). The 130
rejected replacements are a different defect entirely — see "New" below.
Finding 2 — magnitude reproduced independently
Recomputed from data/processed/kelly_ledger.json (5,000 observations; the
production window is the last rolling_window_months 24 × momentum_sleeve_size 50 = 1200, construct.py:129-141 → kelly.py:91-92) with the production
fraction: 0.5, appending one hypothetical crash-period return per held name
(66 names in the live snapshot):
| pooled sample | half-Kelly f | today's f is |
|---|---|---|
| as-is (crash period lost) | 0.1089 | — |
| +66 × −20% | 0.03796 | 2.9× larger |
| +66 × −30% | 0.01148 | 9.5× larger |
| +66 × −40% | 0.0 — fail-closed zero-deployment branch | unbounded |
Identical to the review's figures through build_target_weights and to
thales inspect kelly (f 0.109, 90% CI [0.017, 0.191]). Mechanism confirmed
by reading: daily.py:1421-1423 returns into _liquidate_to_cash upstream
of :1485, the only call site of _update_kelly_ledger_returns;
_liquidate_to_cash clears the snapshot at :1130 before execute_orders
at :1133; the next selection day hits :929 (if not prev_prices: return). The engine books the period (engine.py:711, :936). RWG-1 leg 3
already owns the halted-liquidation snapshot wipe; the new mechanism — a
successful liquidation also never books the crash — is in no row, hence
KSP-1. Plain language: the sizer's memory of the crash is erased on the
day of the crash, and the live book would re-enter roughly three to nine
times larger than the validated strategy.
The review's TCA open question — closed
It asked whether the go-live gate's median |slippage| 47.3 bps (against a
binding 50 bps threshold, go_live.py:309-314, settings.yaml:363) is
inflated by the replacement path. It is not. Decomposed by fill date:
| selection day | fills | median |slippage| bps |
|---|---|---|
| 2026-07-01 | 51 | 43.5 |
| 2026-08-03 | 66 | 79.3 |
| 2026-09-01 | 45 | 15.4 |
| pooled since 2026-06-11 | 162 | 47.3 |
Momentum's only 39 replacements were on 2026-06-11, and there are no TCA records dated 06-11 in the window, so zero replacement fills enter the median. The 2.7 bps margin is real and is one bad selection day pooled since inception with no rolling window — recorded for the December reading (the interpretation memo already says the gate is operational, not alpha proof); not a defect.
Per-finding verification
| # | review's claim | verification | disposition |
|---|---|---|---|
| 1 | cancel_order returns a bool and never raises, so the except at daily.py:1236 is unreachable and a False falls through to a market replacement; same shape at :1190 (mid-run halt), cli.py:3641/:3696/:4405 (send_email returns False, discarded), and cli.py:1130-1138 exits 0 on failed orders | Confirmed by reading all five sites, all three broker implementations (alpaca_broker.py:421-427, simulated.py:195-200, broker.py:125-127), notifications.py:74-76, cli.py:1105-1138. Broker truth: 0/544 refused cancels | Queued — IGD-1 (with finding 3) |
| 2 | kill-switch day never books the crash period; snapshot cleared before execute_orders; engine books it; magnitude 187% / 848% | Confirmed by reading; magnitude reproduced to five significant figures | Queued — KSP-1 (with gap 9) |
| 3 | the test pinning the cancel guard gives the mock a RuntimeError no broker raises | Confirmed: test_daily.py:569 | folded into IGD-1 |
| 4 | 2,068 skew rows on 2026-06-19, 07-03, 08-29 (Saturday); no reader filters; GHC/NVR 0 rows in 62 days | Reproduced exactly: 892 / 283 / 893 against get_trading_days; 0 duplicate (date, symbol) keys in 50,438; MASI 1, ASGN 18, HOLX 18, PSTG 19 | Queued — SKW-4 |
| 5 | skew-snapshot.yml is the only scheduled state-writer without a concurrency: group; launchd 12:00 PT and cron 19:00Z collide under PDT; the loser permanently loses a capture day | Premise confirmed (four workflows carry groups; this one does not). Consequence not: the two firings on 09-03 and 09-04 were ~2 h apart (19:00Z dispatch, 21:36Z / 21:19Z cron) and both green; the JSONL writer is idempotent per (date, symbol); on a true same-minute collision the winner's data lands and only the loser reds. The real second-firing defect is the panel's SKW-3 (term rows silently overwritten), already queued | hygiene: add the four-line block; no row |
| 6 | queue/open/skw-1 still says open; the fix shipped in 29c6f23 | Confirmed; RESEARCH.md's 09-01 banner already says "SKW-1 built" — notebook and queue disagreed | Moved open → built this PR |
| 7 | the public subscribe route shares the alerting Gmail credential behind a per-instance limiter | Confirmed by reading route.ts:16-23, :49-50; blast radius is a paper account's alerting, as the review itself narrowed | no row; separate credential when convenient |
| — | near-miss: the RUNBOOK's post-halt recovery is a green no-op (_record_safety_event writes a run_summary, already_ran_today honours it) | Confirmed: pipeline.py:274 → state.log_run (type: run_summary) → daily.py:1392 skips; "Already ran today" is not in critical_skips → exit 0 | dated note on SAC-1 (a census member) |
| — | 1,356 / 1,358 tests pass in a clean copy | not re-run here (records-only PR); the publish-set scrub test was run before commit | — |
Corrections to the review (it holds up; these sharpen it)
- Finding 5's consequence is wrong, and the plist comment is wrong for a different reason than the review gives. The firings do not land in the same minute in practice because GitHub's schedule events run hours late (the self-audit's M4), so there is no collision to lose; and even a collision loses no data, only the loser's push. Meanwhile the plist's claim that "the idempotency gate makes the second run a no-op" is true for the skew JSONL and false for the term stream — SKW-3 found the second firing overwriting in-session term rows with post-close quotes. Add the concurrency block as hygiene; the defect that matters is SKW-3's.
- Finding 1's "up to 2× intended quantity" needs a refused cancel, and the observed rate is 0 / 544. By this shop's ranking the top two swap: KSP-1 first — its manifestation is certain the first time the kill switch fires, which is the scenario the risk layer exists for — then IGD-1. Both fixes are cheap; the order only decides what lands first.
- "A failed run exits 0" omits a mitigation. The fleet digest renders
!! N FAILED orders(digest.py:550-551, PR #41/#42). Partial — it keys on local submit-time status, which is exactly OSR-1's blind spot — but the run is not fully silent. - The review counts "15 of 33 meanrev trading days"; the order log holds 35 days with orders. Immaterial.
- Finding 4's phrasing "in the dataset this project treats as its moat, and which it publishes" is accurate, and the STAMP-1 built-row footer already records the relabel-vs-quarantine ruling as the owner's open decision — SKW-4 routes the 2,068 rows into that ruling rather than inventing a new one.
New here — outside the review's scope (OSR-1)
Of the 544 in-window replacements, 130 were rejected by Alpaca at the
next session's 07:00 ET pre-market processing: 45 from the 08-28 batch
(broker submitted_at 2026-08-31T11:00Z), 85 from the 08-31 batch
(2026-09-01T11:00Z); 90 buys, 40 sells, all fractional market orders
submitted after the close on the displaced-cron sessions. The local order log
records OrderStatus.ACCEPTED for all 130 — statuses are write-once at
submit. The digest counted zero failures (digest.py:114 counts local
failed only); reconcile reported no drift (reconcile.py:135-160 compares
ID sets, never status); FOS-1's streak logic reads the same log; the daily
audit, whose procedure does say to check rejected orders against broker
truth (ops/DAILY_AUDIT.md:146), was quota-starved on those days. The 08-31
displaced-cron memo assumed the batches would "fill at the next open". The
positions self-healed because each run recomputes from broker positions, so
the cost to the never-promotable control was a one-day delay — but the
blindness is sleeve-agnostic and would look identical on a refused momentum
exit. This is the truth-divergence-between-layers class the AI-native audit
named as the recurring failure; the cause of the rejections is not exposed by
the orders endpoint and is OSR-1's first data-plan item.
The family observation, extended
The 08-29 memo's class — state advanced or keyed without regard to run
completion — gains two members from this review, both now listed on SAC-1's
census: the kill-switch active: True persisted before the liquidation is
attempted (KSP-1 leg 3), and the HALT run_summary that arms the same-day
idempotency gate (the RUNBOOK no-op). The review contributes the
complementary class: a guard that exists and cannot fire. Its advocate's
observation is worth adopting verbatim — every finding the reviewer drafted
was a missing guard; every one of the advocate's best findings was an
inert one — and on a codebase this dense with guards a sweep for absence is
structurally blind to the actual defect class. Its two-stage AST sweep (a
function whose except returns a value and never raises, called as a bare
statement inside a try) returned exactly the five IGD-1 sites with zero
false positives on ~17k lines. The dual — a MagicMock given an exception
side_effect for a method no concrete implementation can raise — is an hour
and belongs in this repo's test suite, not only in canon's.
What shipped vs. queued (all owner-directed, recorded as such)
- Shipped in this PR: nothing in code. Records only — the review verbatim, this memo, four rows (KSP-1, IGD-1, SKW-4, OSR-1), SKW-1 moved open → built with its footer, the SAC-1 dated note, the RESEARCH.md banner.
- Queue arithmetic:
queue/open/goes 13 → 16 against the pre-registered 12-item cap. Monday's triage overflow-dismisses mechanically with reopen conditions; each new row names its fold-partner (KSP-1 → SAC-1, never RWG-1; IGD-1 → RPL-1; SKW-4 → SKW-2 / the STAMP-1 ruling; OSR-1 → FOS-1). - Direct fixes the owner can make without a row: the
skew-snapshot.ymlconcurrency block (four lines the other three scheduled writers already carry); a separate sending credential for the web form. - Routing note: drafted in an owner-directed interactive session, not via
queue/approved/; the PR contains a rename, which the records fast path denies by design, so a human merges — and merging publishes both documents to the site on the next daily export (scrub-checked: 0 BLOCK hits, 0 redactions).
Verification commands (re-runnable)
# finding 1 exposure + broker truth
python -c "import json,collections; r=[json.loads(l) for l in open('data/state/meanrev/order_log.jsonl')]; print(len([x for x in r if x.get('market_replace_of')]))"
thales --sleeve meanrev portfolio orders --days 45 --json > /tmp/mr.json # then join on id / client_order_id
# finding 2 magnitude
python - <<'EOF'
import json, numpy as np
from thales.portfolio.kelly import kelly_fraction
pool=[float(x) for x in json.load(open('data/processed/kelly_ledger.json'))['returns']]
def f(p):
a=np.array(p[-1200:]); w=a[a>0]; l=a[a<0]
return 0.0 if not len(w) or not len(l) else max(0.0, kelly_fraction(len(w)/len(a), w.mean()/abs(l.mean()))*0.5)
print(f(pool), [f(pool+[c]*66) for c in (-0.2,-0.3,-0.4)])
EOF
# finding 4
python -c "import json,collections,pandas as pd; from thales.utils.calendar import get_trading_days; r=[json.loads(l) for l in open('data/options_skew.jsonl')]; c=collections.Counter(x['date'] for x in r); td={str(d)[:10] for d in get_trading_days(pd.Timestamp('2026-01-01'),pd.Timestamp('2026-12-31'))}; print([(d,c[d]) for d in sorted(c) if d not in td])"
# finding 5
gh run list --workflow skew-snapshot.yml -L 8 --json createdAt,event,conclusion
# TCA decomposition
python -c "import json,numpy as np,collections; r=[json.loads(l) for l in open('data/processed/tca_log.jsonl') if l.strip()]; r=[x for x in r if x['date']>='2026-06-11']; by=collections.defaultdict(list); [by[x['date']].append(abs(x['slippage_bps'])) for x in r]; print({d:(len(v),round(float(np.median(v)),1)) for d,v in sorted(by.items())}, round(float(np.median([abs(x['slippage_bps']) for x in r])),1))"
Addendum — built the same night (2026-09-05, owner-directed)
After the records PR (#150) merged, the owner asked to keep going. Built in a second, human-merge PR (#151), with the negative controls the shop requires:
- KSP-1 legs 1–3 (
daily.py): the in-flight holding period is booked before the liquidation (_book_kelly_returns_before_liquidation, fail-soft, marker-idempotent); the trigger-dayactive: Trueis staged and published — together with the snapshot clear — only afterexecute_ordersreturns without a HALT; the snapshot'skill_switchflag is intent ANDgross_leverage <= 0.05, withkill_switch_intentrecorded alongside. Leg 4 (HWM reset) confirmed at parity by reading (engine.py:652). Five new tests intest_kill_switch_booking.py; the trigger-day contract intest_kill_switch.pyupdated. - IGD-1 legs 1, 2, 4 (
daily.py,cli.py, tests): both cancel sites read the bool and log at ERROR; the three alert-email sites report a False send; the pinning test is parametrized over the real failure signal and the transport branch, plus two tests against a queryableSimulatedBroker(confirming cancel → remainder replaced; refusing cancel → nothing). - Held: IGD-1 leg 3 (exit non-zero on failed orders) — meanrev's standing AVB failure would red the workflow daily; the owner rules between "any failed order", "failed de-risking sell only" (recommended), and "keep green". IGD-1 leg 5 (the dual-sweep lint) not built.
- Verification: full suite 1,359 passed / 1 skipped; with the two source
files stashed and the tests kept, the new tests red on exactly the
predicted assertions (the refusing-fake test buys the double fill; the
halted-liquidation test finds the snapshot wiped and
active: Truepublished). Engine untouched — no re-baseline. - Queue: KSP-1 and IGD-1 moved to
built/with footers (the rename denies the records fast path, so a human merges);open/returns to 14 rows.
Addendum 2 — the STAMP-1 ruling executed, SKW-3 built (2026-09-06, owner-directed; PR #152)
- The ruling. Asked for a recommendation, the maintainer found the 2,068
non-session rows were two classes (the capture commit instants decide it):
holiday re-serves → quarantine; STAMP-class post-close firings →
re-session per row to the session
market_session_date(captured_at)names (08-07 → 08-06, 08-28 → 08-27, 08-29 → 08-28; the 08-07 files hold both firings, so 82 chain rows / 33 term rows / 5 skew rows stay). The owner accepted. Executed by the newthales capture-resession— never a hand edit — with the per-row ledger appended to CAPTURES.md ("Data-disposition rulings"): 239,179 rows relabelled, 85,028 quarantined, 0 dropped. - Recorded honestly: the first
--applydropped 892 rows. The JSONL step resolved legacy rows' instants by re-reading the chains files AFTER the parquet rewrite; for 08-28 — both a source (→ 08-27) and a target (← 08-29) — that handed the 08-29 firing's instant to the 08-28 rows, which then stayed and collided with the incoming ones. The command's owndroppedcount made it visible in the same line; the store was restored from git (plusgit cleanof the six new target files the checkout does not remove), the plan now carries the instants it resolved, a chained source-and-target case is pinned intests/test_data/test_resession.py, and the re-apply reporteddropped 0. Lesson for the next migration command: compute every input at plan time, apply from the plan only. - Bookkeeping:
captures.ACKNOWLEDGED_GAPSnow carries the two true shortability holes (08-07, 08-28) the writer's file-idempotency caused; the deliberately-open 08-06 note it replaced is the ruling's citation.capture-qagreen; the gap detector's only unexplained entries are the pre-existing 08-26 pit / 08-27 order_log + portfolio_snapshots holes of the no-run day, outside this PR's scope.pit_snapshotsdeliberately untouched. - Also in the PR: SKW-3 (the re-fire collects only the streams a name is
missing from; workflow hint corrected; commit message stamps the session),
the
concurrency:group onskew-snapshot.ymlwith a lint that every scheduled state-writer has one, andload_skew(sessions_only=True). SKW-3 and SKW-4 moved tobuilt/(SKW-4 leg 3, the per-name coverage census, re-routed to SKW-2).
Addendum 3 — IGD-1 leg 3 ruled; FOS-1 built (2026-09-06, owner-directed; PR #154)
- The ruling. Neither "red on any failed order" nor "red on any failed
sell" survived contact with the facts: AVB's standing failure IS a
de-risking sell (an emergency exit), so both would red meanrev daily. The
owner accepted the third shape — escalate NEW de-risking failures, report
standing streaks — and it is one build with FOS-1 because both need the
same per-symbol streak computation (
execution/failed_streaks.py, sessions = the days the sleeve ran). - What ships:
thales runexits 4 on a fresh sell/close failure (a fresh buy leaves the book under-invested, not exposed — not escalated); the digest names NEW and STANDING failures distinctly, gives an emergency-exit streak its own line, and turns 🔴 only for a new de-risking failure; every failed order record carries the broker's rejection text. Diagnosing AVB is now a matter of reading tomorrow's record — no order for it has reached the broker in ten days, so the failure is at submit, and the text will say why. - Verification: streak/rule unit tests, digest render tests (streak vs fresh; fill resets; standing alone is amber), pipeline test for the error text, and a source-pinned contract for the run command's exit code (the CLI builds a live broker and is not unit-invokable).
- Queue: FOS-1 →
built/; IGD-1's footer records the ruling; the dual-sweep lint (IGD-1 leg 5) stays open as the one unbuilt leg.