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.rsrun_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) = the pmv2_autotrade_sources row for SOURCE_ID first_mover_core
  • effective_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

ThingHot-reloaded?
Cohort watchlistYes — refreshed periodically (spawn_cohort_refresh)
breaker_trippedYes — 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 the pmv2_autotrade_config / pmv2_autotrade_sources DB 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"

  1. Check pmv2_autotrade_config.mode and the first_mover_core row in pmv2_autotrade_sources (mode + enabled). Effective mode = more conservative of the two.
  2. Was the firstmover service restarted after the last mode change? If not → restart.
  3. Confirm first_mover_core armed INFO appeared post-change.
  4. Confirm NATS_URL / NATS_COHORT_NKEY_SEED are set (else WARN + entries dropped).
  5. Remember: no entries in logs may just mean no eligible signals — not a fault.