Commit 89e354e on main of wowjeeez/pmv2-crypto-algo. Second discrete pre-live hardening pass targeting the migrate, binance, and emit crates. All gates clean: fmt + build + clippy -D warnings + 7 new tests pass.

Blocker Fix

migrate/src/main.rs — Compile-Time Migration Embedding

Replaced a runtime fs::read_to_string loop that searched three candidate paths for each .sql file with a compile-time constant:

const MIGRATIONS: &[(&str, &str)] = &[
    ("0001", include_str!("../migrations/0001_....sql")),
    // ... all five
];

All five migrations (0001–0005) are now embedded via include_str!. There is no CWD dependency at runtime, making the binary safe for systemd units where the working directory is not guaranteed.

High-Severity Fixes

emit.rs — NATS NKey Authentication

NATS_URL now requires a companion NATS_NKEY_SEED environment variable. Connection uses:

async_nats::ConnectOptions::with_nkey(seed).connect(url).await

Hard error at startup if NATS_URL is set but NATS_NKEY_SEED is absent. Shadow mode (when NATS_URL is unset) is unchanged.

async-nats 0.37 NKey API

async_nats::ConnectOptions::with_nkey(seed: String).connect(url: &str).await

binance.rs — Staleness Gate Clock Source Fix

In stream_once, after parse_trade succeeds, the receive timestamp is now stamped via pp.as_of = Utc::now(). The staleness gate in emit.rs ((now - bt).num_seconds() <= 30) therefore compares local clock to local clock — previously it mixed exchange-reported milliseconds with a local Utc::now() comparison.

Also fixed: .unwrap() on timestamp_millis_opt replaced with .single().unwrap_or_else(Utc::now) to avoid a panic on ambiguous DST-fold timestamps (rare but possible after leap-second adjustments in test environments).

Medium-Severity Fixes

emit.rs — Cached Active Market (Hot-Path Latency)

discover_active_market() has been removed from the event loop. A background tokio task polls every 30 seconds and writes results into a tokio::sync::watch channel. The fire path reads market_rx.borrow().clone() — zero HTTP latency on the critical path.

polymarket.rs — Outcome Label Hardening

Confirmed live labels from Gamma API (btc-updown-5m-1781901900) are ["Up", "Down"]. Two defensive fixes applied:

  • .trim() called before .to_lowercase() on each label.
  • warn! log with outcomes = ?outcomes on any no-match, making mislabelled markets loud.
  • token_down validated as a parseable u128 (parity with the existing token_up validation).

polymarket.rs — HTTP Hardening

  • User-Agent header "crypto-shortterm-emitter/1.0" set on both the Gamma and CLOB reqwest clients.
  • 5-second timeout added to the poll_book reqwest client (was unbounded).

Low-Severity Fixes

emit.rs — RUST_LOG Default

EnvFilter::new(filter) with a "info" fallback when RUST_LOG is unset. No std::env::set_var call (avoids the Clippy set_var warning in multi-threaded code).

emit.rs — ON CONFLICT Confirmed Correct

INSERT ... ON CONFLICT (window_id) DO NOTHING was already present in the emitted_signals insert. Confirmed correct; no change needed.

New Tests (7 Added)

TestCrateVerifies
migrate::tests::migration_sequence_includes_all_fivemigrateinclude_str! const covers migrations 0001–0005
observe::tests::binance_staleness_uses_receive_timeobservepp.as_of set to local clock after parse_trade
observe::tests::cached_market_window_mismatch_skipobservewatch-channel market skipped if window_id mismatches
observe::tests::label_match_trimmed_and_casedobserve" Up " and "UP" both resolve to the correct token
observe::tests::label_no_match_is_noneobserveunrecognised label returns None (no silent wrong token)
observe::tests::on_conflict_window_id_semanticsobserveDO NOTHING path is idempotent for duplicate window_id
observe::tests::nats_url_without_seed_is_hard_errorobservestartup hard-errors when NATS_URL set but NATS_NKEY_SEED absent

Live Outcome Label Reference

Confirmed from Gamma API market btc-updown-5m-1781901900:

["Up", "Down"]

Index 0 = Up (YES). The outcome-label matching introduced in crypto-shortterm-emit-guardrails (commit bc38734) now handles whitespace and case variation correctly.

Pre-live Discipline Pattern

This is the second in a series of pre-live code review passes on money-path code. Pattern:

  1. Build — code written, tests written, CI clean.
  2. Review — explicit severity-tagged audit of every code path that touches money, IO, or state.
  3. Fix — changes committed with full test coverage and gate check.
  4. Delta review — planned for the moment before the live flip to catch any regressions introduced by the fixes themselves.