Both offline consumers now screen through strategy.population.population_reject
instead of divergent, look-ahead admits:
- learn_params.load_sessions: replaces the `gap>=0.03 OR day_rvol>=2.0` admit,
whose day_rvol was a FULL-DAY volume sum (outcome-time look-ahead, the
2026-07-08 class). gap is already a FRACTION (open vs prev close) — passed
as-is; the OPEN price is passed for the price band. Stored daily bars expose
no open-knowable volume, so volume=None (fail-open on the day-volume floor) —
no day-end volume is smuggled into admission.
- eod_replay._daily_gappers: screens on the OPEN price + STATIC open gap (was
t_close look-ahead) via the shared gate; the local gap is converted to a
FRACTION before the call, so it inherits the >=150% squeeze exception. Deletes
the stale $1-5 / 3-60% WATCHER_/TRIGGER_ constants (KEEPS RISK_PCT). The
second-stage scan_gappers is made permissive (membership already decided by
the shared gate), removing the `gap_pct <= 60%` cap that wrongly dropped
squeezes. Prod thresholds passed explicitly (bare from_env would disable gap).
Unit test replaces the live-DB smoke run: $15/+12% qualifies, $4/+45% rejected
unless >=150%+rvol5. research/labeler.py deferred to plan 006 (no locatable
price/gap admission screen); sweep_exits/sweep_filters likewise deferred.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>