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 — FOS-2 — FOS-1's NEW-vs-STANDING split reaches the per-sleeve digest but not THE fleet email

status: dismissed by TRIAGE 2026-09-14 · OVERFLOW — ranked 16 of 21 at the pre-registered 12-item cap · FOLDED INTO DGX-1 (same module, adjacent lines, same defect class) · raised 2026-09-09 · body verbatim below

Resolution — folded into DGX-1; PR #163 closed the detection half and halved what remains

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 one is a fold, not a kill: DGX-1 (kept, ranked 5) is the same defect — the digest's renderer and the digest's send-gate read different sources of truth — in the same file, within forty lines of this row's fix site.

Re-verified at HEAD 340bc5f; PR #163 (3fc2dba, 2026-09-12) changed this row materially and in the good direction:

  • Detection leg CLOSED. fleet_digest.py:1183-1187 now reads failed_standing and counts only n_failed - n_standing as NEW, so a fresh failure does force an off-cadence email under the new weekly policy. That is the only failed_standing reference in the module.
  • Rendering leg STILL OPEN. fleet_digest.py:781-785 still builds the line from d.get("failed_orders") alone, so the operator reading the mail still cannot tell a 16-session standing failure from a fresh one. The exception reason never reaches the body either — should_email returns it (:1205-1207) and it only prefixes the subject with ⚠️ (:1245).
  • The gather half of the filed fix is already done — digest.py:247-249 emits failed_orders / failed_new / failed_standing for weights sleeves, and fleet_digest.py:470 routes them there. So the remaining job is lifting digest.py:515-528 _failed_detail into a shared formatter.

Re-tiered by this run, on evidence: P0 → P2, effort 0.25 pd → ~0.1 pd. After #163 this is an operator-comprehension defect (a structural condition rendered as an event), not a loudness defect.

ARMED, unlike its fold partner. data/state/meanrev/order_log.jsonl holds 23 failed records, 15 of them AVB emergency-exit sells, 2026-08-17 → 2026-09-11 consecutive, cause APIError 40010001 asset AVB is not active. This renders on the next fleet email that includes meanrev. (DGX-1's three legs are all latent by measurement.)

Reopen condition. Reopen immediately if DGX-1 is dismissed, or if DGX-1 is built without a shared failed-order formatter reaching the fleet email body — the fold is only safe while DGX-1 carries it. FOS-2's negative control should survive into DGX-1's test set: revert the fleet renderer to the flat line and assert the test reds.


Body as filed (verbatim, untouched)

FOS-2 — FOS-1's NEW-vs-STANDING split was built into the per-sleeve digest only; THE daily email still prints the flat furniture line, so AVB's 14-session standing failure reads exactly like a fresh one-off

status: open · raised 2026-09-09 (owner-side interactive session, reviewing #154 in the panel's absence — the change reviewer that would normally catch this was rate-limited dead 09-07…09-09) · class: truth divergence between layers (the AI-native audit's recurring class) · judgement: YES · effort ~0.25 pd · horizon: live every day; it silently un-does a guard merged 2026-09-07


Plain-language summary for an owner reading one paragraph. FOS-1 was built to stop a repeating order failure from looking like a fresh one — a name that has failed to sell for fourteen sessions is a structural condition, a name that failed today is an event, and the flat line !! 1 FAILED order(s): AVB cannot tell them apart. The split was implemented in digest.py, which renders the single-sleeve digest (thales --sleeve meanrev portfolio digest). The message that actually reaches the operator is the fleet digest, and that renderer was not touched. So the guard is real, tested, merged — and invisible in the only channel anybody reads. The escalation half of FOS-1 (a fresh de-risking failure exits 4 and fires the workflow alert) is intact and is not what this row is about.

Mechanism — ground truth measured at HEAD ee68ebe

  • Rendered, not inferred. thales fleet digest run 2026-09-10 01:50 ET for session 2026-09-09 prints, in the MEANREV section: !! 1 FAILED order(s): AVB — no streak, no length, no cause. AVB's actual state that session: cause: emergency_exit, status: failed, fourteenth failed session since 2026-08-17, broker text APIError: {"code":40010001,"message":"asset AVB is not active"}.
  • src/thales/execution/fleet_digest.py:781-785 builds that line from d["failed_orders"] alone. grep -n "failed_streaks|split_new_and_standing|failed_standing" src/thales/execution/fleet_digest.py returns nothing — the module never imports FOS-1's machinery.
  • src/thales/execution/digest.py:120-132 gathers the split, :516-528 formats it, :590-591 renders the standing lines. That is the per-sleeve renderer, reached by a command a human must type.
  • The escalation half is fine and stays out of scope: cli.py:1176 raises typer.Exit(4) for de-risking orders failing for the first time. Both meanrev runs on 09-09 exited 0 correctly, because AVB is standing, not fresh.
  • The same divergence already happened once, and its scar is three lines above the defect. fleet_digest.py:777-779 carries the comment: "Failed submissions must be visible in THE daily email (2026-07-20: a broker-rejected SATS buy was invisible here; only the standalone single-sleeve digest rendered it)." A fix landed in the per-sleeve renderer and not the fleet one, was caught, was patched — and the next reporting feature took the identical wrong turn. That recurrence is the argument for a structural fix over a second patch.

Why it matters beyond the control sleeve

The renderers are shared across sleeves. On momentum the same silence is a stuck live position whose exit is standing-unable to execute, reported as an ordinary count. The December gate reads TCA and safety-HALT history on the assumption that exits execute; a standing exit failure that never distinguishes itself is exactly the input that assumption cannot survive.

Fix shape (propose-only)

One formatter, two callers — not a second copy of the logic. Lift the new/standing formatting into a single function (digest.py's _failed_detail is already almost it) and have fleet_digest.py call it with each sleeve's own order history and run summaries. A third renderer added later then cannot diverge silently, which is the actual defect class. The minimal alternative (duplicate the gather in fleet_digest.py) fixes today's symptom and re-arms tomorrow's.

Test design + negative control

Fixture order log with a ≥3-session streak on one symbol plus a fresh same-day failure on another → the rendered fleet email must name both distinctly (1 NEW: XYZ [selection]; 1 standing: AVB x14 [emergency_exit]). Negative control, shown in the PR: revert the fleet renderer to the flat line and assert the test fails. A control that passes against the bug is decoration — the FOS-1 build note says so in its own words, and this row exists because the thing it verified was not the thing the operator reads.

Kill criterion (pre-registered)

If a path already exists by which the standing/new split reaches the daily email — any path, including one this row missed — the item dies on that evidence and is filed dismissed with the path named. If the shared-formatter shape cannot be built without touching trading-path code, it drops to the render-only fix and says so; it does not grow.

Prior art

research/queue/built/fos-1-per-symbol-failed-streak.md (the guard this row says is half-landed) and research/queue/built/exq-1-batch-quote-fail-open.md (whose BUILT note first recorded the furniture line). The AVB position itself is not relitigated: the daily audit adjudicated it a broker-side owner chore (Alpaca 40010001 asset not active), that ruling stands, and nothing here touches it. This row is about the detector only.