Commit Graph

2 Commits

Author SHA1 Message Date
arsen 47dafab42d feat(adr-104): phase-domain drift channel (script + server)
scripts/record-baseline.py and capture_baseline_to_disk now
compute per-subcarrier circular mean + variance of phases when the
WS stream carries them (ADR-106). Saved as per_subcarrier_phase_mean
+ per_subcarrier_phase_var in baseline.json.

Server loads them into PHASE_BASELINE_PER_SUB; phase_drift_update
computes a per-tick score (mean circular distance / π over
subcarriers with baseline variance < 0.30) and stores it in
PHASE_DRIFT. Surfaces as PerNodeFeatureInfo.phase_drift_score
(skip-if-none). Honesty contract: emits None below
PHASE_DRIFT_MIN_USABLE = 16 usable subcarriers.

Legacy baselines without phase fields fall back to amplitude-only
behaviour with no change.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-17 16:44:21 +07:00
arsen f411992435 feat(adr-103 v2): stable persistent baseline + NBVI quiet-window finder
Problem from ADR-103 v1: persisted NBVI-subset mean (19.86 in operator's
recording) drifted out of comparability after server restart because
NBVI re-selected a different top-12 subset, yielding a different mean
from the same channel. classifier saw current/baseline ratio > 1 even
in clearly empty room.

Fix:

1. Separate FULL-broadband mean (all non-zero subcarriers) from
   NBVI-subset mean in amp_presence_override. NBVI subset still drives
   CV / motion sensitivity. FULL is what gets compared to the
   persistent baseline — stable across NBVI re-selection.

2. baseline.json schema v2: full_broadband_{mean,p50,p95,std,cv_pct}
   replaces NBVI-only p95_amp/mean_amp. Loader prefers full_*; falls
   back to legacy fields for backward compat.

3. NBVI Step 1 quiet-window finder (ESPectre): nbvi_select_top_k now
   slides a window across the calibration history, picks the lowest-CV
   sub-window, and ranks subcarriers using only that. Robust to brief
   motion during the calibration buffer.

4. scripts/record-baseline.py v2: emits v2 schema, computes
   full-broadband stats per node, trims head/tail transients, picks
   cleanest 30-s sub-window, also saves per_subcarrier_mean for future
   subcarrier-level comparison.

Operator workflow now: step out → run script → restart server →
forget about the empty-room ritual forever.
2026-05-17 10:11:24 +07:00