For Agents

Living index of themes for this project. Each H2 is a topic; bullets are wikilinks to related notes. Updated by obsidian-documenter when documenting work. Read by historian at bootstrap. Topics kept alphabetical.

Architecture & pmv2 Split

  • pmv2-rearchitecture — the pmv2 monolith (polymarket_fetch) retiring → split into independent service repos (producers → executor → notify), NATS JetStream spine, shared pmv2-contracts crate as the seam. Position Manager = the executor (consumes NATS directly, gates off/dry/live + caps + breaker, signs EIP-712, POSTs CLOB; Mode OFF). Trade wire-contract v2 (signed off by weather): weather emits a READY BUY entry-order to pmv2.order.weather.entry, pricing="limit" (limit_price≤0.95 + size_shares), entry-only, sizing moved to producers (notional/limit_price, default $50). source is a free String (wrong value → silent Mode::Off). Supersedes the old single-stream WEATHER_SIGNALS/docs/signal-schema.md v1 envelope
  • pmv2-reorg-and-weather-as-2026-06-22 — the same reorg from the Asian-weather (weather_as) side + the authoritative AS-lane current state. position_manager = the SINGLE execution authority (polymarket_fetch retires); producers (cohort_algo/crypto_algo/us_weather_algo/as_weather_algo) own strategy+sizing and publish READY entry-orders; pmv2-contracts envelope with a pricing discriminator (limit directive = weather/crypto vs derived = cohort), subjects pmv2.order.<source>.<entry|exit>, weather entry-only, TTR gate exempt for Directive. Repos: weather-bet → pmv2-weather-us-algo (/coding/pmv2/us_weather_algo, US+research) + new PRIVATE pmv2-weather-as-algo (/coding/pmv2/as_weather_algo, e9415dc). AS lane = source=weather_as on pmv2.order.weather_as.entry, modes OFF/dry, self-measure standalone, publish leg waits on pmv2-contracts. Companion to pmv2-rearchitecture
  • weather-as-babylon-identity-2026-06-22 — the AS lane’s babylon coordination identity: handle weather_as provisioned 2026-06-22, per-repo gitignored 0600 .mcp.json shadowing the global babylon MCP (no global BABYLON_TOKEN); split of the old weather handle into weather_as (Asian) + weather_us (US)
  • weather-as-deploy-pattern-b-2026-06-23AS-lane deploy convention: containerized Pattern B via the apps Ansible role (image→GAR, deploys/apps.yml entry + deploy-apps.yml dispatch, OTel env auto-injected). Run shape FLIPPED 2026-06-23 from two OnCalendar cron jobs to a continuous kind=service running python shadow_engine.py --loop (scan every SCAN_INTERVAL_SECS + daily settle at SETTLE_HOUR_UTC) — driven by the fleet-wide #354 boot-announce directive (#362 envelope to telegram.outbound, sha/title from GIT_SHA/GIT_TITLE build-args per #358) + symmetry with weather-US’s --loop. Lane is NO LONGER NATS-free: boot announce needs NATS_URL + weather_as NKey (NATS_NKEY_SEED, PUB telegram.outbound); orders stay OFF
  • weather-as-dry-publish-arm-2026-06-25AS-lane DRY publish ARMED 2026-06-25: pin 9a07960e5521c4 + WEATHER_AS_PUBLISH=1 env. New binary is env-gated default OFF; on gate_admit publishes the pmv2-contracts entry envelope to pmv2.order.weather_as.entry once per market/day (self-measure dedup). Money safety intact (weather_as=dry_run, global dry_run, PM gates at build+sign). NATS NKey + PMV2_ORDERS wildcard already in place from prior session. Kill switch = unset env + re-push secret + redeploy
  • pmv2-source-id-3way-invariant-2026-06-23the source_id 3-way invariant: position_manager keys on the wire source value, NOT the agent/repo handle (handle renames are free). Producer wire source == pmv2_autotrade_sources.source_id == NATS subject segment must stay equal or the lane silently fail-closes to Mode::Off. GOTCHA: US lane handle becomes weather_us but its wire source STAYS weather (intentional mismatch — do not “fix”); AS lane consistent (weather_as/weather_as). source renames = coordinated 3-way change, ping @positionmanager first

Backtesting & Research Data

  • intraday-drift-study-naive-no-go — 112 market-days × 1,224 buckets intraday-price + forecast + realized-high dataset (research/data/, report research/report.md); drift is real but naive buy-open/sell-+50% = −27% net (survivorship bias); v2 re-analysis off same cache: forecast-conditioned (NEAR ±1°C of lead-2 forecast) + 18:00 exit = +9.6% blended → CANDIDATE
  • weather-netcost-precheck-2026-06-19 — net-of-cost EV pre-check on the gate’s OOS test: gated (P≥0.68) +0.140/ pessimistic, gate lift +0.317/ is the convergence-gated number, later shown to be an optimistic ceiling by the win-label re-score
  • weather-gate-winlabel-2026-06-19the WIN-label re-score (discharge of shadow-rehearsal caveat #1): selecting on P(WIN) not P(convergence) collapses EV ~+0.14/** at matched 8.5% admit, and the model loses to a per-city win-rate lookup (+0.0939) / ex-ante-only (+0.0772) / ties ungated (+0.0553) — entry_price alone (AUC 0.7136) beats it (0.6838). Disguised price-picker; CONDITIONAL-GO → LIVE-MEASUREMENT-ONLY (no capital, no Rust). True-resolution OOS coverage 74.7%, biased with the signal. research/gate/{build_win_gate.py,winlabel_ev.json,gate_win_metrics.json}
  • weather-lag-distribution-2026-06-19unbiased full-population reprice-lag study, N=28,664 (every cheap-Yes 0.55–0.95, no highest-paid cap), all 6 shards, critic-reviewed. Realized ex-ante hit rate ~34.4% (not 63–67% — ~49% never reprice to 0.97); converged lag median 37.1min (p25 8.2 / p75 81.6 / p90 127.6). research/reports/weather-lag-distribution-2026-06-19.md + lag_distribution.json
  • wx-lag-pipeline-bug — data-integrity backstory behind the repricing-lag distribution study (research/copytrade/top50/lag/): the published 180-min convergence numbers were re-derived at analysis time but never persisted (lag_distribution.json showed stale 120-min); 180-min is fully recoverable from reprice_lag_min without CLOB re-pull; shard_2 (16.7% of population) missing, concentrated in headline cities → per-city n and city ranking are provisional
  • weather-lookahead-trap-2026-07-02the look-ahead trap (binding backtest rules): proxy-timed entries (gate at 15:30 modal price, enter earlier prints) inflated EV ~2–3× — AS +0.455→+0.145/; overlays (0.90 cap / flatten / PT rejection) were test-set-selected. Rules: entries at-or-after ALL gating info exists; never tune a parameter on its scoring window. The clean causal n=67 +29.9%/trade backtest survives

Bug Fixes & Debugging

  • fix-resolve-twc-primary-source — TWC as primary resolution source; IEM demoted to cross-check fallback; fixed ~50% silent skip rate on market resolution
  • wx-lag-pipeline-bug — convergence-horizon baking bug in the 6-shard reprice-lag pipeline: converged baked at 120-min while reprice_lag_min measured at 180-min (the 120/180 mismatch); report’s 180-min numbers never written back to lag_distribution.json. Fix = compute convergence in aggregate_lag.py via is_converged() against CONVERGE_HORIZON_MIN (aggregator authoritative, immune to stale per-shard baked flags). Gotcha: no lib.py — fetch logic inlined & drifted per shard
  • weather-as-selfmeasure-pnl-fixes-2026-06-23 — resolves HANDOVER PART 2 M7/M8/M9 + L7 self-measure P&L bugs (TDD, 44 tests Python 3.14). M7 reject fills above producer’s actual limit min(0.95, current_at_lock+PRICE_BUMP) (limit_price threaded lock→realizable_fill); M8 never infer void from price — _resolve_payoff binary-only-when-closed&binary, settle force-voids as STAKE-REFUND (net_pnl=-cost) only after MAX_SETTLE_AGE_DAYS; L7 _RES_CACHE re-keyed per (slug,token) to stop sibling-token poisoning; M9 records gross+net with mode-aware slippage (live=0, replay SLIPPAGE_REPLAY half-spread). 2026-06-24 (@ 7939da2): live --loop crash — Python %Z strptime CANNOT parse abbreviated zone names (PDT/JST/CST); a strftime→strptime round-trip in lock_from_trace ValueError’d on every locked city and aborted the whole scan tick. Fix: reconstruct local datetime from lock_ts + IANA zone (fromtimestamp(ts, utc).astimezone(ZoneInfo(zone))). Coverage lesson: tests mocked lock_from_trace itself so the round-trip went uncovered — added real JST+PDT regressions; surfaced by the deploy-mode-OFF dry-run (46 tests green)

Coordination & Babylon

  • weather-as-babylon-identity-2026-06-22 — the lane’s babylon identity (weather_as), the weatherweather_as/weather_us handle split, channel subscriptions (general, pmv2, weather-shadow, weather-jetstream), the inherited coordination thread (#302/#306/#313/#315 → 337), and open coordination blockers (#302 deploy convention, PMV2_ORDERS + pmv2_autotrade_sources row source_id=weather_as, OTel before live)
  • pmv2-source-id-3way-invariant-2026-06-23 — @positionmanager pmv2 #340: the wire source is a 3-way contract (producer emit == pmv2_autotrade_sources.source_id == NATS subject) — any drift silently fail-closes the lane to Mode::Off. Renaming a source_id = coordinated 3-way change → ping @positionmanager FIRST. Acked AS-side in pmv2 #344; weather_us must coordinate before touching its source

Copy-Trade & Wallet Reconnaissance

  • copytrade-top-weather-tradersv1: on-chain recon off the small weatherbot.fi board; real money = late near-certain settlement, buy-cheap/sell-pop = the worst (huskyvs LAST)
  • weather-top50-decypher-2026-06-19v2: official leaderboard 87→65→50, critic-corrected. “Capacity, not return” (1.52M, ROI inversely correlates with capital); ≤0.95 fillable edge plausible but NOT proven (3/10 wallets clear latency, lag right-skewed, winners paid ABOVE book 11–13/15); doctrine = “detect, don’t mirror” (Tier A copyable vs Tier B confirmation-only carry whales); design-only copytrade NATS source spec; all PnL FIFO-inferred
  • weather-lag-distribution-2026-06-19 — the unbiased follow-up that settles v2’s fillability open items → weak-GO. Full-population (N=28,664, no highest-paid cap): realized hit rate ~34.4% (not 63–67%, ~49% never converge); thesis FLIP — winners pay a ~2-tick PREMIUM not a discount (fair_gap +0.02, 75% above same-instant book) → convergence-timing edge bought slightly rich; binding gate = net-of-cost recovery (live rehearsal); per-wallet unbiased lag medians HondaCivic 8.6 / vip68 10.7 / Happening9014 20.6

Deployment & Infrastructure

  • weather-as-deploy-pattern-b-2026-06-23pmv2-weather-as-algopolymarket-infra via containerized Pattern B (apps Ansible role; no host venv/systemd). Image → GAR europe-west3-docker.pkg.dev/<project>/levandor-apps/pmv2-weather-as-algo:<sha>; deploy = deploys/apps.yml entry + dispatch deploy-apps.yml; role auto-injects OTel env (zero-config once Python OTLP SDK init’d in main()). Run shape FLIPPED 2026-06-23 from two OnCalendar crons (--scan/--settle) to a continuous kind=service running python shadow_engine.py --loop (cohort scan every SCAN_INTERVAL_SECS default 1800 + daily settle at/after SETTLE_HOUR_UTC default 12, both env-tunable). Reason: fleet-wide #354 boot-announce directive (continuous service announces cleanly ONCE on boot, cron can’t) + weather_as NKey PUB telegram.outbound (#366) + symmetry with weather-US’s --loop. Boot announce = canonical #362 envelope component: pmv2-weather-as-algo started with version: <sha> <title>, sha/title from GIT_SHA/GIT_TITLE build-args (#358). Lane is NO LONGER NATS-free — announce needs NATS_URL + weather_as NKey seed (NATS_NKEY_SEED, PUB telegram.outbound); orders stay OFF (no --live); announce best-effort (missing creds → startup_announce:false, never crashes). Built TDD, 22 tests green on Python 3.14 (shadow_engine.py, publisher.py DRY _connect_and_publish, Dockerfile ARG/ENV + CMD --loop, build-push.yml gitmeta). Container artifacts in-repo: Dockerfile (python:3.13-slim), .dockerignore, build-push.yml (WIF→GAR). WARNING: accumulated P&L validates plumbing not edge (open bugs M7/M8/M9). Pending from deploy/Andras: WIF provider + SA literals for CI, the weather_as NKey, Andras’s terraform ci_github_repos grant

Forecasting & Edge Thesis

  • weather-strategy-validated-plan — the validated edge: fixed 12:00Z close lands pre-peak for US/London → forecast game; quantified 6-8c at first 0.90 crossing decaying to ~0.4c at 1h; >=0.90 in final 2-3h = 100% YES (113 market-days)
  • polymarket-weather-market-structure — why next-day station Tmax (whole °C, 1°C buckets) is a skill-rich, validatable edge surface
  • weather-top50-decypher-2026-06-19 — the honest fillability re-read: 99c carry NOT fillable; candidate edge = repricing-lag layer (cheap-Yes post-METAR-lock) but unproven — right-skewed lag means a 20-min gate rarely fires, and winners paid ABOVE the book (risk-taking vs lag-harvest unresolved); best slice = highest-temp buckets, post-local-peak, ~4–9h pre-resolution, London + Asian-hours + NYC/Atlanta, select by observed lag
  • weather-lag-distribution-2026-06-19the unbiased study that resolves the above → weak-GO. Winners pay a ~2-tick PREMIUM (fair_gap +0.02, 75% above book) — convergence-timing edge bought rich, NOT discount-to-fair; realized hit ~34.4% (~49% never converge); gate must be EX-ANTE (city/session/METAR — observed lag is look-ahead); robust tier-A city universe = london/nyc/seoul/hong-kong/atlanta/paris/wuhan; binding gate = net-of-cost recovery of the premium

Maker & Liquidity Rewards

  • weather-prior-corrections-2026-07-02LR pools on weather EXIST (prior belief wrong): 467 temp markets with rewardsDailyRate>0, ~55/mkt/day; top pools seoul-lowest 1,010 / munich 703; min 20–50 shares inside 4.5c spread; 25% maker rebate (weather is fee-bearing)
  • weather-strategy-verdicts-2026-07-02 — the EU maker/LR pilot spec: madrid+paris quoting vacuums (12–18c spreads), blocked on GTD/GTC + cancel() live-test + quote manager/self-cross guard; inventory cap $150–200, no modal-bucket quoting 15:00–18:00; pass = LR accrual ≥2× inventory losses, cancel p95 <5s, zero self-crosses

Market Structure & Resolution

  • weather-strategy-validated-plan — fixed 12:00:00Z book close (the why-an-edge-exists insight); resolution-mirror spec (Wunderground daily-history, named ICAO, whole °F/°C, local midnight-midnight, UMA ~2h liveness/$750 bond); wrong-station traps (Dallas=KDAL, Houston=KHOU, Denver=KBKF, Moscow=UUWW, Chicago=KORD, skip Hong Kong); global=Wunderground vs US/CFTC=NWS CLI
  • polymarket-weather-market-structure — 11 neg-risk 1°C buckets, Wunderground ICAO airport resolution, whole-°C precision, endDate timing
  • fix-resolve-twc-primary-source — TWC is the authoritative Polymarket resolution source; IEM is cross-check only
  • weather-as-selfmeasure-pnl-fixes-2026-06-23 — void economics: a Polymarket void/invalid market is a STAKE REFUND (P&L≈0), NOT a mid payout; closed:true can coexist with mid prices during dispute/settlement so don’t infer resolution from outcomePrices alone; multi-market events poison sibling-token caches unless keyed per (event,token)
  • weather-prior-corrections-2026-07-02stations pinned engine-critical (paris = LFPB Le Bourget NOT LFPG; london = EGLC City NOT Heathrow; denver = Buckley SFB; istanbul/moscow/tel-aviv via NOAA weather.gov; HK = HKO 0.1°C); universe now 48 cities + a lowest-temperature series; US 2°F buckets/0.01 tick vs non-US 1°C/0.001 tick; °C boundary gate fixable via national tenths feeds (DWD/FMI/IMGW/JMA-AMeDAS/SYNOP-OGIMET) for 7/8 EU cities; api.weather.com public key polls the WU resolution truth directly (~30-min, ToS-gray)

NATS & Signal Publishing

  • pmv2-rearchitecture — the v2 transport: stream PMV2_ORDERS over pmv2.order.<source>.<entry|exit>; PM binds ONE durable pull consumer on pmv2.order.> (explicit ack, Nats-Msg-Id=id dedup, duplicate_window). Subject builders + envelope live in the shared pmv2-contracts crate. Supersedes the single WEATHER_SIGNALS/weather.signals stream
  • wx-signal-nats-nkey-authwx-signal SignalPublisher NATS/JetStream publisher; async-nats 0.49 NKey auth (ConnectOptions::with_nkey(seed) sync+infallible; nkeys default feature; lazy seed validation at connect → ConnectError Authentication); auth precedence NATS_NKEY_SEED > NATS_CREDS > no-auth; dormant (None) unless NATS_URL set; no secret logging
  • weather-top50-decypher-2026-06-19 — design-only copytrade signal source (not a new stream): rides existing WEATHER_SIGNALS, source-routed source="copytrade", cohort-first-mover K-of-N in window W, max_price ≤0.95 (cohort worst-entry clamped), UUIDv5 Nats-Msg-Id dedup aligned with the live engine; independent confirmer not a mirror
  • pmv2-source-id-3way-invariant-2026-06-23 — the NATS subject segment pmv2.order.<source>.entry is one leg of the 3-way source invariant (producer emit == operator source_id == subject); locked subjects: US pmv2.order.weather.entry, AS pmv2.order.weather_as.entry. A drifted subject → PM sees an unknown source → lane silently fail-closes to Mode::Off

Polymarket API

  • polymarket-weather-market-structure — Gamma public-search vs /events?slug=, field-availability quirks (volume vs liquidity), clobTokenIds
  • wx-market-prices-history — CLOB /prices-history endpoint: interval=max (caps ~410 pts) and startTs/endTs windowed variants; YES-token ID required; fidelity in minutes
  • wx-market-trades-client — data-api /trades endpoint: per-token trade history, asset = YES-token ID; note different base URL from CLOB
  • weather-prior-corrections-2026-07-02 — Gamma clobRewards/rewardsDailyRate fields carry the LR pools; feeType=weather_fees + true fee formula 0.05·p·(1−p); api.weather.com public-web-key endpoint as a direct WU-truth poll

Project Overview

  • weather-bet — project entry point, status, the pmv2 split context; repo now pmv2-weather-us-algo at /Users/levander/coding/pmv2/us_weather_algo/
  • weather-bet-live-status — ⭐ authoritative current state: live engine, pmv2 split, v2 wire contract, dry-test gate saga, repo rename/move, open items
  • pmv2-rearchitecture — the multi-service design + trade wire-contract v2
  • wx-research-cratewx-research workspace crate added for intraday-drift research work

Research Pipeline & Data Clients

  • wx-market-prices-historyClobPriceHistory in wx-market: read-only CLOB price-history client; history() interval-capped, history_window() unix-timestamp windowed; keeper component for research + live trader
  • wx-market-trades-clientTradesClient in wx-market: read-only data-api trades client; all_by_asset() auto-paginator (PAGE=500, cap 20k); de_flex_i64 flex deserializer; Side enum (Buy/Sell)

Rust Gotchas & Tooling

  • wx-signal-nats-nkey-authedition-2024 gotcha: std::env::set_var/remove_var are unsafe fn, and [workspace.lints.rust] unsafe_code = "forbid" makes them uncompilable in tests (even unsafe {}forbid overrides #[allow]) → can’t mutate env in tests; workaround = assert read-order side effects instead. Also clippy-pedantic used_underscore_binding fires when a _-prefixed binding is actually used. Plus async-nats sync/infallible with_nkey vs async/fallible with_credentials_file

Strategy Research

  • weather-strategy-validated-planVALIDATED GO plan: late-settlement / repricing-lag. Fixed 12:00Z book close lands before the afternoon peak for US/London → winning bucket only 0.40-0.57 at close (0.43-0.61 gap to 1; rank on LIVE PRICE not volume. Tier-1 NYC/Atlanta/Chicago/Miami. Validate-first: OOS Python backtest of the 6-8c edge → then live Rust nowcaster. Full report research/reports/weather-trader-deep-dive.md
  • intraday-drift-study-naive-no-go — naive buy-open/sell-pop REJECTED (−27% net, survivorship bias); v2 forecast-conditioned (buy buckets AT/just-ABOVE forecasted high, hold to 18:00 convergence) = +9.6% mean, 80% LCB −6.9%, n=125 → live CANDIDATE not GO; two levers (forecast-proximity + exit-timing) stack; confirms 2026-06-11 nowcast/information-timing pivot
  • copytrade-top-weather-traders — copy-trade recon of the top Polymarket weather traders (on-chain trades via data-api/trades?user=): real money = late near-certain settlement (HondaCivic 1); 2nd winner = early forecast value (Railbird/dpnd); buy-cheap/sell-pop is ONLY huskyvs+ShyGuy1 and huskyvs is LAST in PnL → independently confirms the nowcast/information-timing edge (trade the repricing lag on a bucket you can see has won)
  • weather-top50-decypher-2026-06-19v2 copy-trade decypher (official leaderboard, 87→65→50, critic-corrected): “capacity, not return” — whales hold 70% of capital at the LOWEST ROI (6.2%); the ≤0.95 own-edge is plausible but NOT proven and the dry-rehearsal is reframed as the deciding experiment (discount-to-fair vs residual risk); “detect, don’t mirror” copy doctrine
  • weather-lag-distribution-2026-06-19unbiased full-population settlement of the fillability question → weak-GO. Sizes the repricing-lag layer honestly: realized hit ~34.4% (size for ~65% miss), winners pay a ~2-tick PREMIUM (not a discount), binding gate = net-of-cost recovery in the live rehearsal; gate ex-ante on city/session/METAR (tier-A: london/nyc/seoul/hong-kong/atlanta/paris/wuhan), never on observed lag
  • weather-gate-winlabel-2026-06-19 — the honest-criterion verdict on the whole Asian-afternoon chain: re-scoring the gate on P(WIN) gives only +0.0604/$, loses to a per-city win-rate lookup, ties ungated → CONDITIONAL-GO downgraded to LIVE-MEASUREMENT-ONLY. Treat the strategy as “favorite-buying with a cost cushion,” not a weather-timing model; no capital / no Rust until a live loop beats an ungated/per-city baseline
  • pmv2-reorg-and-weather-as-2026-06-22 — the Asian chain packaged as the weather_as producer: validated edge (gated net-of-cost EV ~+0.14/$ beats per-city +0.09 / ungated +0.06 on backtest) but NEVER RUN LIVE — single-season (~11d June), thin (n 14–19/latency), convergence-selected (optimistic ceiling), book reprices past us ~40–50%. End-of-stage-3; the forward dry-run (self-measure, NATS-independent) is the next milestone
  • weather-strategy-verdicts-2026-07-02⭐ the post-skeptic lane verdicts (2026-07-02 synthesis): US taker PILOT NOW (100 clips, gate ≥0.60, cap 0.95, hold-to-settle, fee-fix first, pass n≥50 & roi_lcb_80>0); EU taker **DROP** (8 D3c depth, 1-min whale sweeps; reopen via a 16:00–20:30-local depth cron); EU maker PILOT after cancel()/GTD; Asia RESEARCH-MORE (Seoul fold at 65% lockout; CN/Tokyo decided by a lock-timed replay); cheap tails + adjacent-flip-tail DEAD. Taker capacity ceiling $1–3k/day US-wide
  • weather-lookahead-trap-2026-07-02 — why today’s headline EVs (+0.455 AS / +0.191 US per $) were fantasy: proxy-timed entries + test-set-selected overlays; the binding causal-backtest rules; what survives (gate direction, hold-to-settle, the n=67 +29.9%/trade clean backtest)

Storage & Infrastructure

  • weather-bet-supabase-migration — SQLite → Supabase Postgres migration; dedicated weather_bet schema (fleet convention), idempotent embedded schema.sql bootstrap (not sqlx::migrate!), SQLite→Postgres strict-typing gotchas, session-pooler-:5432-not-:6543 connection rule, testcontainers/testkit-feature test harness, @deploy handoff

Workspace & Crates

  • wx-research-cratewx-research crate skeleton (branch research/intraday-drift): 7 module stubs, plotters+csv workspace deps, local deps on wx-core/wx-market/wx-data
  • wx-signal-nats-nkey-authwx-signal crate’s NATS publisher (crates/wx-signal/src/nats.rs): NKey/creds-file/no-auth precedence + the edition-2024 set_var test gotcha for this workspace (edition = "2024", unsafe_code = "forbid")