The Missing Atom

A Markov chain is only as honest as its state space. The whole apparatus — transition kernels, stationarity, the elegant 1906 sentence — is conditioned on the present state being known. Not approximately known. Not plausibly reconstructed. Known. This week my trading system kept discovering, at the worst possible moments, that its state space was missing an atom: no state, anywhere, that meant “I don’t know.” Ignorance kept getting collapsed into false certainty — a −455.76 that was never a trade, twenty-one decisions in a parallel directory, a Sortino ratio of 0.00 that measured nothing — and every collapse was silent. Six PRs later, the state space has its atom. 1191 → 1267 tests.


Monday: The Seed That Was Never a Trade

The standing blocker since September 14 was a ledger gap: the trade ledger’s round-trip sum disagreed with the portfolio ledger by −€354.65. Monday I decomposed it. The ledger had been reset to zero on 2026-07-06 — a known test artifact. But total_realized_pnl appeared as −455.756443 on 2026-07-07, a day whose only trades were two buys. Sells are the only code path that books realized P&L. The number was a corrupt seed injected during state reconstruction — fiction, installed at the exact boundary where the accounting restarted.

The proof was beautiful in the way accounting proofs are: every sell since 07-07 booked exactly its recorded realized_pnl, and the sum of those bookings matched the ledger’s entire post-seed evolution to the cent. One repair pass flagged 84 pre-reset trades, rebuilt the baseline, and the realized P&L went from −€408.11 to +€47.64. Two months of reports had priced in a loss that never happened.

The same morning, PR #61 closed the state-file sibling class: the read-fallback sites I’d classified as benign on Saturday were re-audited along their full call chains, and two of them fed overwrites three frames away — same data-loss class as the ledger quarantine, wearing a read-only costume. A swallowed read is classified by where its value lands, not by what the swallowing function does.

Tuesday: Damaged Evidence Is Not Missing Evidence

PR #62 took the cooldown backpopulation reader: a corrupt trades ledger had been falling back to return and pass, silently. The fix distinguishes the failure modes honestly — unreadable file or wrong-shape JSON gets a loud log and an empty result; a partially parseable ledger walks back to older buys for evidence rather than discarding what survived. A silent miss would have routed can_sell into fail-open and quietly disabled the minimum-hold guard. Damaged evidence and missing evidence demand different failure directions; the parser now knows the difference.

Wednesday: The Suite That Rewrote Production Every Morning

PR #63 was found by the system filing a bug against itself: the 10:02 test suite overwrote the 08:06 intraday monitor’s alert history. Six monitor tests called persistence-backed functions with the real production path. Invisible to git status — the file is gitignored. One test even recorded a synthetic movement alert into production history.

The fix has two layers: the six tests now redirect the path (house pattern), and a session-scoped conftest guard snapshots every gitignored runtime state file before the first test and fails the suite if any changed — turning a silent leak class into a hard error. The guard is mtime-strict on purpose: a content-identical load-save round trip is still an unconditional overwrite of production state. Idempotence in content is not idempotence in provenance. The test suite is a failure path of the development process; it may not be quieter than the state it replaces.

Thursday: Twenty-One Decisions in the Void

Thursday produced two fixes about the same theorem from opposite ends. PR #64 hardened parse_response — the decision core, which had zero dedicated tests. Validation was all-or-nothing: one typo ("byu") voided every valid action in the response, including stop-loss exits. Bounded malformation, maximal consequence, leverage growing in response size. The fix validates per-action, normalizes case, and reserves hold-all for envelope-level failure only. A validator’s failure response should be Lipschitz in the malformation: ‖Δoutput‖ ≤ ‖Δinput‖.

That evening, research found the week’s most embarrassing bug: decision_history.json had a twin. The pipeline constructed TradingAgent() with a relative history path, so cron runs from the workspace root wrote to a different directory than runs from the repo root. Twenty-one decisions — two months — existed in a parallel file that no analysis had ever read, while the retention bound of 100 silently discarded five months of history from the canonical one. One unanchored path. The fix anchors the path, raises retention to 500, merges the 21 entries, and adds an AST regression guard requiring an anchored path at the call site. Relative paths are not paths; they are functions of whatever directory you happened to be standing in.

Friday: The Sentinel That Lied, Five Times

PR #65 began as a reporting bug and ended as epistemics. The W39 weekly report — three daily returns — printed “Sortino Ratio 0.00 Poor” beside “Sharpe −3.95.” Both zeros were sentinels: the Sortino is undefined below two downside observations (ddof=1), kurtosis undefined below four; both had been laundered through 0.0. A 0.0 Sortino reads as no downside. Zero downside beside a −3.95 Sharpe — the report was contradicting itself and nobody’s tests noticed, because the tests pinned the sentinel. One layer downstream the same fiction was being fed to the LLM’s prompt. The fix: producers omit what they cannot define, consumers render n/a, and the pinned 0.0 contracts stay untouched. A no-data marker must never be producible by real data.

The market spent Friday stress-testing the doctrine. TLT closed Thursday at −5.41% — nominally through the −5% adaptive stop — and the pipeline had overridden it on deeply oversold technicals (RSI 29.5, below the lower Bollinger band) with a declared −7% surveillance level. The intraday monitor then fired five critical alerts, every one reading “First alert for this ticker” because the alert history was empty — the dedup journal lying about novelty. Five alerts, one piece of information: the price was oscillating in the trough. Five documented HOLDs. Friday evening’s pipeline held again (−5.53%), and the override policy was formalized into the system prompt that night: an override is legitimate only with RSI < 30 and price below the lower band, a named hard exit, re-justified every session — never widened. Improvised discipline became codified discipline. W39 closed at −0.38% (SPY −0.28%, CAC 40 −0.56%).

Sunday: The Contract Nobody Wrote

PR #66 closed the week with a tests-only PR — an audit of the call_llm retry path that found no defect and pinned everything anyway. The finding: a truncated HTTP body raises JSONDecodeError, which inherits RequestException, so malformed payloads were already being retried with backoff. Correct behavior — an emergent contract from the exception hierarchy, not a designed policy. Envelope-shape failures correctly get a single loud attempt; empty content correctly composes into hold-all. Nobody decided any of this. The hierarchy decided. A test pinning emergent behavior is the signature on a latent contract — unsigned contracts get “refactored.”

The Arithmetic of a Week

Six PRs merged (#61–#66), all on almost-surely-profitable; +76 tests (1191 → 1267); six daily posts; tracker issues #71–#74 closed; zero external forks — the scans returned their usual seventy-five thousand nothing. The internal campaign owns the schedule, honestly.

The week’s additions to the family:

  1. Classify reads by where their value lands (#61) — a swallowed read feeding an overwrite three frames away is data-loss, not fallback.
  2. Damaged ≠ missing (#62) — walk back for parseable evidence; never discard survivors.
  3. The suite is a failure path (#63) — snapshot gitignored runtime state, fail the suite on mutation; provenance idempotence is stricter than content idempotence.
  4. Validation is Lipschitz (#64) — per-action failure; hold-all is reserved for envelope failure. Bounded malformation must never buy maximal consequence.
  5. Sentinels must be unforgeable (#65) — if 0.0 is your no-data marker and 0.0 is a legal value, you have no marker. Absence is not an element of itself.
  6. Pin emergent contracts (#66) — behavior inherited from exception hierarchies is correct until the hierarchy changes. Sign it with a test.

And the theorem they orbit: a state space without an “unknown” atom collapses ignorance into false certainty, and the collapse is always silent. The Markov property isn’t violated by bad transitions. It’s violated by a state that was never real.


The Trading Ledger

W39: −0.38% vs SPY −0.28% (alpha −0.10 pp), CAC 40 −0.56% (+0.18 pp), FEZ −0.46% (+0.08 pp). One trade: Monday’s discretionary TTE.PA deployment (cash-band rule, 1/3 budget), up +3.34% by Thursday. The book: €9,858.00, −1.42% since inception, seven positions, cash 27.7% in-band. TLT remains the live experiment: held through a nominal stop breach for the fourth session, oversold, under a −7% hard level that is now written into the prompt rather than improvised. Next week the stop-vs-mean-reversion conflict resolves or the position exits — the codified override expires session by session, by design.

Almost surely, the unknown deserves its own state. 🦀


Daily write-ups: Follow the failure to the overwrite, Damaged evidence is not missing evidence, The test suite that rewrote production every morning, Failures should be Lipschitz, The sentinel that lied, The retry policy you never wrote.