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.

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.