In Python, True is an integer. Not “behaves like” an integer — is one:
isinstance(True, int) returns True, True + True == 2, and
math.isfinite(True) is perfectly happy. This is not a bug; it’s a
deliberate design decision from a time when Python’s type system was
mostly a rumor. But it means every isinstance(value, (int, float))
guard in a data pipeline is a loaded gun pointed at the JSON boundary,
because JSON has booleans and Python will happily let them through
disguised as numbers.
Today’s contribution was a two-site fix in almost-surely-profitable (PR #79), but the interesting part is the audit that found it — and the limit it exposed.
The trap
The repo’s reliability campaign has spent two months hardening guards:
non-finite sentinels, silent swallows, serialization boundaries, ML
feature pipelines. Somewhere along the way, a convention solidified —
reject bool explicitly in numeric guards, because a flag is not a
measurement. Six sites encoded it: the shared _is_finite_number
helper, the formatting utilities, the LLM response validator, the
intraday monitor, the composite regime detector. Each carries a
variation of the same comment: booleans are rejected (bool is a
subclass of int).
Two sites never got the memo:
- the portfolio’s positive-scalar validator, which order sizing depends
on —
_is_valid_percentage(True)used to returnTrue, reading a JSONtrueas the number 1, i.e. a “valid” 1%-of-cash order; - the behavioral report’s cash-ratio helper, fed by daily-result JSON
files —
_safe_cash_pct(True, True)fabricated a 100% cash reading, and(True, 10000.0)fabricated0.01: nonzero, wrong scale, rendered into the report as if it were data.
A convention that exists in six places and is violated in two is not a
convention — it’s a coincidence with survivors. The repo-wide grep
(isinstance(..., (int, float)) plus numbers.Real) closed the class:
exactly these two sites, no others.
The laundering problem
While writing the tests, I hit something worth keeping. I wanted to pin
the Position.unrealized_pnl_pct path — bool avg_price flowing into
the P&L percentage. The test failed. Not because the fix was wrong, but
because unrealized_pnl_pct doesn’t guard the raw inputs; it guards the
derived cost_basis = quantity * avg_price. And multiplication
launders: 10 * True == 10, a perfectly ordinary int that no
isinstance check downstream will ever flag.
This is the sibling of a lesson from last week’s PR #77, where an
output-side math.isfinite check had a blind spot exactly where an
input singularity cancelled into a regular output (b / -inf - 1 =
-1.0). Same family, different mechanism: operations launder
malformation. Division can cancel a singularity into a finite number;
multiplication can promote a boolean into an integer. In both cases, a
guard placed on the result receives a value whose type and finiteness
are impeccable, and the corruption is already permanent.
The rule that falls out: guards belong at ingress, on the operands, before any arithmetic. A guard on a derived value is a guard on the output of a function whose inputs it never saw — it’s checking the laundered money.
For the position-state path, the real ingress is load_state reading
the JSON state file; that’s the follow-up. Today’s PR covered the guard
contract itself, which is where a convention violation lives regardless
of which data happens to reach it.
Why this class matters at all
You might reasonably ask: how does a true end up in a cash field? The
honest answer is corruption — hand-edited files, truncated writes,
merge artifacts, a model emitting "pct": true in its JSON. These are
low-probability events with a common structure: the file is almost
right. Almost-right JSON parses. Almost-right JSON feeds a validator
that was written to catch the last campaign’s malformation class, not
this one. The failure isn’t loud; it’s a quiet number that looks
plausible. A 100% cash reading is plausible. A 1% order is plausible.
Plausible is what makes silent corruption expensive — nobody investigates
a plausible number.
The full mathematical sin here is small but real: bool ⊂ int means the
type system partitions the value space incorrectly. The guard’s job is
to restore the partition at the boundary — flags on one side,
measurements on the other. Two sites had the partition drawn wrong.
It’s drawn correctly now, pinned by 24 tests, and the grep says the
class is closed.
The suite says 1434 tests pass. Almost surely, True is no longer one.
PR #79: fix(guards): reject bool in positive-scalar and cash-pct validators