The first-mover publish lane mode (off / dry / live) is computed once at process boot in cohort_algo. Flipping the mode in the DB (e.g. off → dry → live) does NOT take effect until the firstmover service is restarted. Verified against the code 2026-06-25.
Operational Gotcha
After changing autotrade mode in the DB you MUST restart the firstmover service. Until then the running process keeps the lane state (publisher present/absent) it had at boot. There is no hot-reload of signal mode.
Where the lane decision is made
Computed once at boot in crates/cohort_algo/src/pmv2/watch/mod.rs → run_watch() → build_first_mover_lane() (~line 85-101). Not re-evaluated while the process runs.
Decision formula:
lane_active = signal_lane_active(effective_mode(global_mode, source_mode), source_enabled)
global_mode=pmv2_autotrade_config.mode(source_mode, source_enabled)= thepmv2_autotrade_sourcesrow for SOURCE_IDfirst_mover_coreeffective_mode(global, source)(crates/cohort_algo/src/pmv2/signals/config.rs:44) takes the MORE CONSERVATIVE of global and source mode.signal_lane_active(mode, source_enabled) = source_enabled && mode != Off(crates/cohort_algo/src/pmv2/signals/sources.rs:11).
So both enabled = true AND effective mode ≠ Off are required for the NATS publisher to be built.
What the lane state controls
The NATS publisher (build_cohort_publisher, crates/cohort_algo/src/pmv2/positions.rs:224) is built once from lane_active:
- Lane inactive → publisher is
None→ DEBUG log"cohort publisher dormant (lane inactive)"(positions.rs:234). DEBUG is usually not exported to SigNoz, so this is effectively silent. - Lane armed but NATS env unset (
NATS_URL/NATS_COHORT_NKEY_SEED) → WARN"cohort lane armed but NATS publisher env (NATS_URL/NATS_COHORT_NKEY_SEED) unset; entries dropped this run"(positions.rs:239) plus a notify.
What hot-reloads vs not
| Thing | Hot-reloaded? |
|---|---|
| Cohort watchlist | Yes — refreshed periodically (spawn_cohort_refresh) |
breaker_tripped | Yes — re-read live per-event (breaker_tripped_live, crates/cohort_algo/src/pmv2/watch/cohort.rs:106) |
| Signal config / mode (off/dry/live) | No — fixed at boot |
Confirming the change took effect
After restart, look for the INFO log "first_mover_core armed" (target/scope pmv2_watch, watch/mod.rs:192) appearing after the DB change. It carries the cohort count.
Observability gap — can't tell dry vs live from logs
There is NO INFO log that announces the effective mode (off / dry / live) at startup.
"first_mover_core armed"only prints the cohort count. You cannot confirm dry-vs-live from logs alone — you must check thepmv2_autotrade_config/pmv2_autotrade_sourcesDB rows. A startup line printing effective mode would make dry/live verifiable from SigNoz; worth adding.
Absence of publish logs ≠ broken lane
First-mover ENTRY signals are inherently rare and mostly silently filtered (tier eligibility, position-select, candidate skip, gates) before any log line is emitted. So the absence of
"first-mover published"/"first-mover: signal gate skipped publish"logs is NOT evidence the lane is broken or misconfigured.
SigNoz / OTEL identity
- service.name:
pmv2-cohort-algo-firstmover - k8s.namespace.name:
apps - host:
polymarket-infra.taild4189d.ts.net(prod)
Quick triage when "first-mover isn't firing"
- Check
pmv2_autotrade_config.modeand thefirst_mover_corerow inpmv2_autotrade_sources(mode + enabled). Effective mode = more conservative of the two.- Was the firstmover service restarted after the last mode change? If not → restart.
- Confirm
first_mover_core armedINFO appeared post-change.- Confirm
NATS_URL/NATS_COHORT_NKEY_SEEDare set (else WARN + entries dropped).- Remember: no entries in logs may just mean no eligible signals — not a fault.