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.

DISMISSED — SAC-1 — census of trading-path persist sites: "state advanced or keyed without regard to run outcome"

status: dismissed by TRIAGE 2026-09-14 · OVERFLOW — ranked 13 of 21 at the pre-registered 12-item cap · FOLDED INTO RWG-1, which this row's own cap note asks to be sequenced with · raised panel run 15 (2026-09-04) · body verbatim below

Resolution — folded, not killed: the ruling it needs is RWG-1's, and RWG-1 stays open

research/queue/open/ held 21 files after this run's sweep against the pre-registered 12-item cap (ops/QUEUE_TRIAGE.md:86-91), so the nine lowest-ranked are dismissed with reopen conditions. This is one of them, and it is the one dismissal that costs the queue nothing: this row asks, in its own cap note (:79-84), to be sequenced with RWG-1 so the owner rules once. That is exactly what this run's decide-list does — RWG-1 is decide item 3 and carries SAC-1's live members with it.

Re-verified at HEAD 340bc5f. Nothing here is overtaken; the content is carried, not discarded. Two of the five named members are CLOSED by KSP-1 (PR #151):

  • kill-switch active: True published before liquidation — CLOSED, daily.py:1227-1230 (publish deferred).
  • Kelly-snapshot wipe on a halted liquidation — CLOSED, daily.py:1231-1234 (clears only on the non-halt branch).

Three members are LIVE, and the owner must see them when he rules on RWG-1:

  1. last_targets — daily.py:1795-1803, guarded only by if not self.pipeline.dry_run: (:1796) while execute_orders runs at :1862. This one IS RWG-1 leg 2, so RWG-1's ruling disposes of it directly.
  2. last_construction / vol_scalar — daily.py:840 (if not self.pipeline.dry_run:) → :843-849 persists; consumed at daily.py:667-668. A halted momentum selection leaves the vol-target estimator de-scaling against a book that was never held, for up to a month. RWG-1 does not contain this member.
  3. _record_safety_event arms same-day idempotency — pipeline.py:274 (log_run on any HALT) → state.py:180-187 (already_ran_today matches any run_summary for the date) → daily.py:1523 returns the skip → cli.py:1107 critical_skips = {"No signals", "Stale price panel"} does not include "Already ran today", so the run exits 0. RWG-1 does not contain this member either.

Also recorded so it is not re-derived: the row's kill criterion has an unfireable zero-branch (three live members remain), so it is a census whose output is a table, not a falsification test. The census memo does not exist in research/, and no test pins any live member (tests/ greps for SAC-1/RWG-1 hit only test_pipeline.py:197 and test_kill_switch_booking.py:106).

Reopen condition. Reopen the moment RWG-1's two rulings are made if they do not dispose of members 2 and 3 above — last_construction (daily.py:840-849) and the same-day idempotency arming (pipeline.py:274 / state.py:180-187 / cli.py:1107), neither of which RWG-1 contains. Reopen immediately if RWG-1 is itself dismissed or closed without a ruling, since this row would then be the only place those three sites are written down.


Body as filed (verbatim, untouched)

SAC-1 — census of trading-path persist sites: "state advanced or keyed without regard to run completion" (member four already found)

status: open · raised panel run 15 (2026-08-31), answering the owner handoff in 60a6630 / RESEARCH.md's top banner ("sweep for member four instead of waiting for it") · class: loudness & safety (live path) · judgement: YES · effort ~0.5–1 pd · horizon: before momentum's next halted-selection retry sequence (the 08-19/20 meanrev sequence puts the trigger cadence at roughly monthly)


Plain-language summary for an owner reading one paragraph. The last month produced three separately-discovered defects with one shape: the system wrote down what it intended to do — or what day it believed it was — without checking that the run actually completed. RWG-1 leg 2 (the turnover-blend base advanced during a safety halt), STAMP-1/STAMP-2 (a wall-clock date consumed the next session's idempotency slot), and the Kelly double-booking fixed in PR #120. You asked the panel to sweep for member four instead of waiting for it to fire. The sweep found it in under an hour — which is the evidence the class is productive and a one-shot census is worth a row.

Member four (verified this run; recorded here so the census consumes it rather than rediscovers it)

src/thales/execution/daily.py:821-831 persists last_construction (including the intended vol_scalar) inside _size_targets, gated only on dry_run. _size_targets runs at daily.py:1638; execute_orders at :1719 — the persist precedes the safety gate. Consumption chain: daily.py:658-662 reads last_construction.vol_scalar into every subsequent daily portfolio snapshot, and _gross_returns (:714-721) divides each realized return by it for the vol-target estimator. So a selection that sizes, persists the scalar, then safety-HALTs leaves the estimator attributing the real (old-scalar) book's returns to a scalar that was never applied — for up to a month on momentum, which sizes from state this corrupts.

Mechanism — the census

One-shot enumeration of every persist site in the trading path (daily.py, vrp_daily.py, pipeline.py, state.py, tca.py), each classified on two axes:

  • (a) gated on run completion (halt/exception) vs ungated;
  • (b) keyed on the unit of work (period/session) vs wall-clock.

For each ungated site, a ruling: deliberate (fact-recording/telemetry — e.g. append_equity, portfolio snapshots record what IS, regardless of what executed) vs defect (intent persisted as if executed — e.g. last_construction, the class members above). Deliverable: the classification table as a memo in research/. Propose-only; no gate ships from this row.

Scope boundary (C0 — stated so this cannot collide with existing rows)

  • Trading-path persist sites only. The capture writers are STAMP-1/STAMP-2's owned territory and are out of scope here.
  • Per-site rulings for last_targets and the kill-switch Kelly-snapshot wipe defer to RWG-1 (legs 2–3), which owns them. The census enumerates and classifies; it does not re-litigate a row that already owns a site.

Data plan

Static code reading, cross-checked against the committed natural experiment: for each site, diff its persisted value across meanrev's 08-19/20 halted runs and the 08-21 recovery (the same commits that confirmed the Kelly double-booking). Observed advancement during a halt is ground truth, not inference. Owned; zero external data; zero registry rows.

Test design

For every site the owner rules "must be gated": a halt-injection test — force a safety HALT, assert the site's persisted state is unchanged — with a negative control per gate (revert the gate; only its own test reds). Sites ruled deliberately ungated get a pinned comment naming the ruling, not a test.

Kill criterion (pre-registered)

If the census finds zero ungated-defect sites beyond the already-recorded members (RWG-1 legs 2/3) plus last_construction (named above), the class is declared fully enumerated, the row closes with the table as its record, and the class may not be reopened without a new fired instance.

Cap note for triage

queue/open/ is at/over the 12-cap. Rank this against the tree explicitly rather than letting it slide in — if something must yield, the natural fold-partner is the STAMP-1+STAMP-2 shared fix-sitting (same family), never RWG-1 (deliberately narrowed).


Dated note — 2026-09-05 (maintainer, from the external design review r2 verification)

Two more sites for the census, both verified at cd571b2, neither owned by another row at the time of writing:

  • _record_safety_event arms the same-day idempotency gate on a HALT. pipeline.py:274 writes a run_summary for any safety HALT; already_ran_today (state.py:180-187) honours any run_summary for the date; daily.py:1392 then skips with "Already ran today", which is not in cli.py:1107's critical_skips, so the run exits 0. RUNBOOK.md's post-halt instruction ("fix the cause, then thales run --skip-market-check locally", :253-254) is therefore a green no-op without --force — which the same sentence says never to use casually. Axis (a): ungated — a HALT is recorded as if the day's unit of work completed. Ruling needed: is a HALT a completed unit of work for idempotency purposes? Damage is bounded to one lost day because the month stays open via selection_completed=False (daily.py:502-511), which is why the reviewer filed it as a near-miss rather than a finding.
  • Kill-switch active: True persisted before the liquidation is attempted (daily.py:1024-1026, then _liquidate_to_cash → execute_orders at :1133). Owned by KSP-1 (raised the same day) for the fix; listed here so the census consumes it rather than rediscovers it. Same class: intent written as fact.

Neither changes this row's kill criterion; both count as members beyond the already-recorded ones, so the criterion's zero-branch cannot fire on the current tree.


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

Two of the five named members are CLOSED by KSP-1 (#151, 47ea024); three stand, and they are the three that corrupt sizing state. Verified at HEAD e25f80b:

memberstatus
last_construction / vol_scalar (member four)live — daily.py:841-849, gated only on dry_run (:840); _size_targets at :1781, execute_orders at :1862
_record_safety_event arms same-day idempotencylive — pipeline.py:274 writes a run_summary on any HALT; state.already_ran_today honours any of them (state.py:180-187); daily.py:1523 then skips, and "Already ran today" is not in cli.py:1107's critical_skips, so the run exits 0
kill-switch active: True before liquidationCLOSED (KSP-1: deferred publish, daily.py:1103-1106 → :1227-1230)
Kelly-snapshot wipe on a halted liquidationCLOSED (KSP-1: non-halt branch only, daily.py:1231-1234)
last_targets (deferred to RWG-1)live — daily.py:1795-1803

The kill criterion's zero-branch therefore cannot fire, exactly as the 09-05 dated note anticipated. STAMP-1/2/3 closed the wall-clock keying axis for the two run wrappers; they closed none of the persist sites above, which sit on axis (a).

The deliverable does not exist yet: no census memo among research/2026-09-*; a grep of research/ for SAC-1 returns only queue files and the two design-review memos. Triage carries this into the decide-list folded with RWG-1 legs 2+3, per this row's own sequencing request — one ruling sitting, not two.