fix(run): drop first_accepted gate from catchup synth-arm
Catchup branch gated on first_accepted, but an earlier accepted gray tick consumes the flag before a dark_* arrives, so the real prime-phase color falls through to noise classification and no alert fires. Gate on IDLE + dark_* alone — self-sufficient and correct. Regression: 2 unit tests for _handle_tick + 1 integration test feeding run_live a scripted gray→gray→dark_red→dark_red→light_red sequence. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -218,3 +218,41 @@ def test_handle_tick_first_turquoise_no_catchup_label():
|
||||
# Decision 3A: cannot distinguish fresh arm from catchup-at-arm-phase
|
||||
assert "catchup" not in notif.alerts[0].title.lower()
|
||||
assert "catchup" not in notif.alerts[0].body.lower()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Regression: user bug 2026-04-16. Catchup must fire whenever IDLE + dark_* is
|
||||
# observed, regardless of first_accepted. Prior gray noise tick consumed
|
||||
# first_accepted and the subsequent dark_red was gated out of catchup.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_handle_tick_catchup_dark_red_when_not_first_accepted():
|
||||
fsm = StateMachine(lockout_s=60)
|
||||
notif = FakeNotifier()
|
||||
audit = FakeAudit()
|
||||
|
||||
tr = _handle_tick(fsm, "dark_red", 1.0, notif, audit, first_accepted=False)
|
||||
|
||||
assert tr is not None
|
||||
assert tr.next == State.PRIMED_SELL
|
||||
assert len(notif.alerts) == 2
|
||||
assert notif.alerts[0].kind == "arm"
|
||||
assert notif.alerts[0].direction == "SELL"
|
||||
assert "catchup" in (notif.alerts[0].title + notif.alerts[0].body).lower()
|
||||
assert notif.alerts[1].kind == "prime"
|
||||
assert notif.alerts[1].direction == "SELL"
|
||||
|
||||
|
||||
def test_handle_tick_catchup_dark_green_when_not_first_accepted():
|
||||
"""Mirror — BUY side."""
|
||||
fsm = StateMachine(lockout_s=60)
|
||||
notif = FakeNotifier()
|
||||
audit = FakeAudit()
|
||||
|
||||
tr = _handle_tick(fsm, "dark_green", 1.0, notif, audit, first_accepted=False)
|
||||
|
||||
assert tr is not None
|
||||
assert tr.next == State.PRIMED_BUY
|
||||
assert len(notif.alerts) == 2
|
||||
assert notif.alerts[0].direction == "BUY"
|
||||
assert notif.alerts[1].direction == "BUY"
|
||||
|
||||
Reference in New Issue
Block a user