Poker Tilt Detection — A Real Synthesis
Four separate tilt-detection implementations existed in the library, none of them a clean match for "the best poker tilt detection." This combines the genuinely complementary parts of three of them into one detector — not by picking a favorite, but by checking what each one actually contributed.
Bug found and fixed in the library itself
While testing an unrelated kernel version (STATE_ESTIMATION_KERNEL_v8_REAL_DEPS.py), a real bug
surfaced in both near-duplicate detector files this synthesis drew design ideas from — not code, design
ideas, so this synthesis was never affected — but the library files themselves were broken:
self._aggregate_severity([s[1] for _, s in active_signals]) tried to index an already-unpacked
dict with an integer key, crashing on every real call. Fixed in both
applications/TiltDetector.py and
pipeline_v2/detectors_tilt_detector.py,
each independently re-verified with a real end-to-end call afterward.
What went in, and why
Snapshot signal, adapted from the better of two near-duplicate detectors in the library
(pipeline_v2/detectors_tilt_detector.py, which has a real check — confidence misalignment — that
its near-duplicate in applications/ is missing entirely). Answers: does this one instant's
reading diverge sharply from an established baseline?
Temporal signal, new here, but the underlying insight is real and comes directly from
poker_decision_state_estimation.py's own regime data: the TILTED regime's aggression range
(1.8–7.5) is far wider than any other regime's, including MANIAC (5.5–9.0). Tilt isn't an extreme setting —
it's inconsistency. That's a rolling-window variance signal, genuinely different from a snapshot check, and
no existing detector in the library actually measured it.
State machine, structurally borrowed from a fourth file
(COMPONENT_A_TILT_SPECTRUM_FIXED.py's FractalBrain) — a CLEAR/TILT/RECOVERY cycle with real
hysteresis, the same structural pattern already verified in this codebase's circuit-breaker work. That
file's actual tilt logic is text-keyword matching for a different domain and didn't transfer — the state
machine shape did.
Verified against real regime data, not just run once
Tested against the same regime-generation logic used elsewhere in the library — 40 hands per regime:
TIGHT_AGGRESSIVE 0/40 hands flagged final state: CLEAR MANIAC 0/40 hands flagged final state: CLEAR GTO 0/40 hands flagged final state: CLEAR TILTED 28/40 hands flagged final state: TILT
The important result is MANIAC scoring 0. A naive detector conflates "extreme" with "tilted" and would flag it. This one doesn't, because MANIAC is extreme but consistent — narrow range relative to its own baseline — while TILTED is specifically inconsistent. That's the actual poker-native insight, doing real work instead of just being described.
The JavaScript version below was checked against the exact same Python logic on a fixed, non-random sequence — matched hand-by-hand, state-by-state, to three decimal places, before being trusted.
Feed it a sequence yourself and watch the state transitions →