From 5cf60ec8882da1aa968c13285865ffa47087f421 Mon Sep 17 00:00:00 2001 From: ruvnet Date: Sat, 13 Jun 2026 18:45:04 +0000 Subject: [PATCH] deploy: 42dcf49f4dbc7de9c203690c6bc7be03316054af --- .../adr/ADR-052-tauri-desktop-frontend.md | 6 +- api-docs/adr/ADR-080-qe-remediation-plan.md | 35 +- .../adr/ADR-148-drone-swarm-control-system.md | 12 +- .../adr/ADR-154-signal-dsp-beyond-sota.md | 16 +- .../adr/ADR-164-adr-corpus-gap-analysis.md | 12 +- ...quality-engineering-security-hardening.md} | 2 +- ...xts.md => ADR-167-ddd-bounded-contexts.md} | 8 +- ...rk-proof.md => ADR-168-benchmark-proof.md} | 2 +- api-docs/adr/ADR-169-adam-mode-light-theme.md | 226 ++++++ api-docs/adr/ADR-170-yoga-mode-pose-system.md | 643 ++++++++++++++++++ ...rm-benchmarking-evaluation-methodology.md} | 2 +- api-docs/adr/README.md | 4 +- api-docs/adr/gap-analysis/census.md | 24 +- api-docs/adr/gap-analysis/lens-findings.md | 32 +- api-docs/proof-of-capabilities.md | 2 +- .../ruview-beyond-sota/00-system-review.md | 6 +- .../03-benchmark-validation-methodology.md | 50 +- .../research/ruview-beyond-sota/README.md | 4 +- api-docs/user-guide.md | 6 +- 19 files changed, 991 insertions(+), 101 deletions(-) rename api-docs/adr/{ADR-050-quality-engineering-security-hardening.md => ADR-166-quality-engineering-security-hardening.md} (98%) rename api-docs/adr/{ADR-052-ddd-bounded-contexts.md => ADR-167-ddd-bounded-contexts.md} (98%) rename api-docs/adr/{ADR-147-benchmark-proof.md => ADR-168-benchmark-proof.md} (99%) create mode 100644 api-docs/adr/ADR-169-adam-mode-light-theme.md create mode 100644 api-docs/adr/ADR-170-yoga-mode-pose-system.md rename api-docs/adr/{ADR-149-swarm-benchmarking-evaluation-methodology.md => ADR-171-swarm-benchmarking-evaluation-methodology.md} (99%) diff --git a/api-docs/adr/ADR-052-tauri-desktop-frontend.md b/api-docs/adr/ADR-052-tauri-desktop-frontend.md index f0aad85e..74da6f5e 100644 --- a/api-docs/adr/ADR-052-tauri-desktop-frontend.md +++ b/api-docs/adr/ADR-052-tauri-desktop-frontend.md @@ -5,7 +5,7 @@ | Status | Proposed | | Date | 2026-03-06 | | Deciders | ruv | -| Depends on | ADR-012 (ESP32 CSI Mesh), ADR-039 (Edge Intelligence), ADR-040 (WASM Programmable Sensing), ADR-044 (Provisioning Enhancements), ADR-050 (Security Hardening), ADR-051 (Server Decomposition) | +| Depends on | ADR-012 (ESP32 CSI Mesh), ADR-039 (Edge Intelligence), ADR-040 (WASM Programmable Sensing), ADR-044 (Provisioning Enhancements), ADR-166 (Security Hardening, renumbered from ADR-050), ADR-051 (Server Decomposition) | | Issue | [#177](https://github.com/ruvnet/RuView/issues/177) | ## Context @@ -211,7 +211,7 @@ pub struct FlashProgress { // commands/ota.rs /// Push firmware to a node via HTTP OTA (port 8032). -/// Includes PSK authentication per ADR-050. +/// Includes PSK authentication per ADR-166. #[tauri::command] async fn ota_update( node_ip: String, @@ -801,7 +801,7 @@ Total estimated effort: ~11 weeks for a single developer. - ADR-039: ESP32 Edge Intelligence - ADR-040: WASM Programmable Sensing - ADR-044: Provisioning Tool Enhancements -- ADR-050: Quality Engineering — Security Hardening +- ADR-166: Quality Engineering — Security Hardening (renumbered from ADR-050) - ADR-051: Sensing Server Decomposition - `firmware/esp32-csi-node/` — ESP32 firmware source - `firmware/esp32-csi-node/provision.py` — Current provisioning script diff --git a/api-docs/adr/ADR-080-qe-remediation-plan.md b/api-docs/adr/ADR-080-qe-remediation-plan.md index c0863c01..eddcb9c1 100644 --- a/api-docs/adr/ADR-080-qe-remediation-plan.md +++ b/api-docs/adr/ADR-080-qe-remediation-plan.md @@ -1,6 +1,6 @@ # ADR-080: QE Analysis Remediation Plan -- **Status:** Proposed +- **Status:** Proposed — P0 security findings #1–#3 **RESOLVED** on the shipped Rust sensing-server boundary (2026-06-13; closes ADR-164 G11) - **Date:** 2026-04-06 - **Source:** [QE Analysis Gist (2026-04-05)](https://gist.github.com/proffesor-for-testing/a6b84d7a4e26b7bbef0cf12f932925b7) - **Full Reports:** [proffesor-for-testing/RuView `qe-reports` branch](https://github.com/proffesor-for-testing/RuView/tree/qe-reports/docs/qe-reports) @@ -13,25 +13,38 @@ An 8-agent QE swarm analyzed ~305K lines across Rust, Python, C firmware, and Ty Address the 15 prioritized issues from the QE analysis in three waves: P0 (immediate), P1 (this sprint), P2 (this quarter). +## Security P0 closure note (2026-06-13) — Rust sensing-server boundary + +The three P0 security findings below were logged against the **Python v1** API +(`archive/v1/src/…`). ADR-164 G11 re-scoped them to the *shipped* boundary: +`wifi-densepose-sensing-server` (Rust). They were verified against the current +Rust crate and closed on branch `fix/adr-080-sensing-server-security`. Each fix +(or already-fixed finding) is pinned by a test that fails on the old behavior. +**The Python v1 paths remain as-is** — v1 is archived and not the shipped +surface; this closure governs the live Rust server only. + ## P0 — Fix Immediately -### 1. Rate Limiter Bypass (Security HIGH) +### 1. Rate Limiter Bypass / XFF spoofing (Security HIGH) — **RESOLVED (verified absent on Rust boundary)** -- **Location:** `archive/v1/src/middleware/rate_limit.py:200-206` +- **Original location (v1):** `archive/v1/src/middleware/rate_limit.py:200-206` - **Problem:** Trusts `X-Forwarded-For` without validation. Any client bypasses rate limits via header spoofing. -- **Fix:** Validate forwarded headers against trusted proxy list, or use connection IP directly. +- **Rust verification (2026-06-13):** The Rust sensing-server has **no XFF-trusting control to bypass** — there is no IP-based rate-limiter and no IP-allowlist, and neither security middleware reads a forwarded header. `bearer_auth.rs` authenticates on the token alone (`require_bearer` inspects only the `AUTHORIZATION` header); `host_validation.rs` decides on the `Host` header only. A repo-wide grep for `x-forwarded-for|forwarded|peer_addr|client_ip|real-ip` over `wifi-densepose-sensing-server` returns nothing. The only "rate limiter" is the MQTT *sample-rate* gate (`mqtt/state.rs`), a per-entity publish throttle with no IP/header input. +- **Resolution:** No code change needed (no vulnerable surface). Regression tests pin the immunity: `bearer_auth::tests::xff_header_never_affects_auth_decision` (spoofed XFF never flips a 401↔200 decision) and `host_validation::tests::forwarded_headers_never_bypass_host_allowlist` (spoofed `X-Forwarded-Host: localhost` never lets a foreign `Host: evil.com` past the allowlist). Residual: if an IP-based control is ever added, it must derive the peer from the socket (`ConnectInfo`) and only honor XFF from an explicit `--trusted-proxy` CIDR — captured as guidance in the test docstrings. -### 2. Exception Details Leaked in Responses (Security HIGH) +### 2. Exception Details Leaked in Responses (Security HIGH, CWE-209) — **RESOLVED** -- **Location:** `archive/v1/src/api/routers/pose.py:140`, `stream.py:297`, +5 endpoints -- **Problem:** Stack traces visible regardless of environment. -- **Fix:** Wrap with generic error responses in production; log details server-side only. +- **Original location (v1):** `archive/v1/src/api/routers/pose.py:140`, `stream.py:297`, +5 endpoints +- **Problem:** Internal error/stack-trace detail serialized into client responses. +- **Rust finding (2026-06-13):** Six handlers in `wifi-densepose-sensing-server/src/main.rs` serialized the internal error `Display` into the JSON body: `edge_registry_endpoint` returned a panicked `spawn_blocking` `JoinError` (`"task … panicked"`) in a `500` and the raw upstream error in a `503`; `delete_model`/`delete_recording`/`start_recording` returned `std::io::Error` strings (OS detail / path); `calibration_start`/`calibration_stop` returned the `FieldModel` error chain. +- **Fix:** New `src/error_response.rs` module — `internal_error` / `internal_error_json` / `upstream_unavailable` log the full detail **server-side only** (tagged with a correlation id) and return a generic body (`{"error":"internal_error","correlation_id":…}`) with no `panicked`, no file paths, no Debug chain. All six call-sites rewired. Pinned by `error_response::tests::internal_error_body_does_not_leak_detail` (leak-substring guard, verified to fail on the reverted old body) + 4 sibling tests. -### 3. WebSocket JWT in URL (Security HIGH, CWE-598) +### 3. WebSocket JWT in URL (Security HIGH, CWE-598) — **RESOLVED (verified absent on Rust boundary)** -- **Location:** `archive/v1/src/api/routers/stream.py:74`, `archive/v1/src/middleware/auth.py:243` +- **Original location (v1):** `archive/v1/src/api/routers/stream.py:74`, `archive/v1/src/middleware/auth.py:243` - **Problem:** Tokens in query strings visible in logs/proxies/browser history. -- **Fix:** Use WebSocket subprotocol or first-message auth pattern. +- **Rust verification (2026-06-13):** The Rust sensing-server never reads a token from the URL. `require_bearer` (`bearer_auth.rs`) inspects only the `Authorization` header; the WebSocket handlers (`ws_sensing_handler`/`ws_introspection_handler`/`ws_pose_handler`) take a bare `WebSocketUpgrade` with no `Query` extractor; the single `Query` in the crate (`EdgeRegistryParams`) is a non-secret `refresh` flag. +- **Resolution:** No code change needed (no query-token path exists). Regression test `bearer_auth::tests::query_string_token_is_never_accepted` proves `?token=`/`?access_token=` in the URL never authenticates (stays `401`) while the same token in the header succeeds (`200`) — verified to fail if a query-token path is re-introduced. ### 4. Rust Tests Not in CI diff --git a/api-docs/adr/ADR-148-drone-swarm-control-system.md b/api-docs/adr/ADR-148-drone-swarm-control-system.md index b3f5a536..a66a012d 100644 --- a/api-docs/adr/ADR-148-drone-swarm-control-system.md +++ b/api-docs/adr/ADR-148-drone-swarm-control-system.md @@ -9,8 +9,10 @@ | Relates to | ADR-134, ADR-136, ADR-139, ADR-140, ADR-143, ADR-144, ADR-146, ADR-147 | > **Scope note:** ADR-147 deferred Cosmos WFM to "ADR-148" as an offline data generator. -> That item is promoted to ADR-149. This ADR takes 148 to address the broader drone swarm -> control architecture, which is the first consumer of ADR-147's OccWorld occupancy output. +> That item is promoted to ADR-171 (the swarm-benchmarking/evaluation companion to this ADR; +> renumbered from ADR-149 to resolve the ADR-149 duplicate-number collision). This ADR takes +> 148 to address the broader drone swarm control architecture, which is the first consumer of +> ADR-147's OccWorld occupancy output. --- @@ -874,9 +876,9 @@ validated; ITAR/EAR classification completed by export counsel. | GPS spoofing of full swarm simultaneously | Medium | Low | UWB mesh cross-check among all nodes; ≥ 3 nodes must agree on position to confirm | | 1000-UAV scale claims (not validated) | Low | High | SWARM+ demonstrated in simulation only; scale claims capped at 50 for production targets | -### 12.3 Open Issues (Forward to ADR-149) +### 12.3 Open Issues (Forward to ADR-171) -- Cosmos WFM offline training data generation (deferred from ADR-147) — ADR-149 +- Cosmos WFM offline training data generation (deferred from ADR-147) — ADR-171 - Fixed-wing hybrid platform support (endurance missions) — future ADR - Underwater-aerial cross-domain handoff protocol — future ADR - Quantum-enhanced task assignment (E6) — future ADR when hardware matures @@ -998,4 +1000,4 @@ Implementation tracked at: https://github.com/ruvnet/RuView/issues/861 *ADR authored with research support from `ruflo-goals:deep-researcher` (2026-05-30). Implementation progress tracked by `ruflo-goals:horizon-tracker`. - OccWorld integration basis: ADR-147. Next: ADR-149 (Cosmos WFM offline data generation).* + OccWorld integration basis: ADR-147. Next: ADR-171 (Cosmos WFM offline data generation; renumbered from ADR-149).* diff --git a/api-docs/adr/ADR-154-signal-dsp-beyond-sota.md b/api-docs/adr/ADR-154-signal-dsp-beyond-sota.md index 240fedb3..c0cfaa06 100644 --- a/api-docs/adr/ADR-154-signal-dsp-beyond-sota.md +++ b/api-docs/adr/ADR-154-signal-dsp-beyond-sota.md @@ -195,13 +195,15 @@ The §2–§5 fixes are **ACCEPTED and committed**: dead CIR gate fixed, NaN byp - Evaluate the **diffusion CIR prior** (public weights, MEASURED) as an offline quality ceiling — *not* an edge target. - Bayesian multi-AP fusion (2512.02462, CLAIMED) — comparison only, pending released code. -### 7.4 Deferred Milestone-0 review findings (the ~45 not fixed here — explicit backlog) +### 7.4 Deferred Milestone-0 review findings (explicit backlog) Catalogued so nothing is silently dropped. Priority: **P1** correctness-adjacent, **P2** perf, **P3** clarity/style. +**Milestone-1 update (2026-06-13):** the **four P1 backlog items** (#1, #9, #10, #13) are now cleared — #1 and #10 **RESOLVED (MEASURED)**, #9 and #13 **RESOLVED-PARTIAL (DATA-GATED:** de-magicked + boundary-tested, operating values unchanged**)**. ~41 P2/P3 items remain deferred. Each fix is pinned by a regression test that fails on the old behaviour (commits `fd32f094a`, `4a9f2bcf4`, `d672fa602`, `5193f6369`); workspace `--no-default-features` green, Python proof unchanged (bit-exact). + | # | Module | Finding | Pri | Why deferred | |---|--------|---------|-----|--------------| -| 1 | cir.rs ~937 | `phase_variance` uses **linear** variance on **wrapped** angles (doc says "variance of phase angles") — spuriously inflates near ±π | P1 | Used as the `> TAU` ghost-tap *guard*; a correct circular variance is bounded [0,1] and would need the threshold re-derived. Semantic change — defer with a real recalibration, don't risk a silent gate regression in a perf/correctness pass. | +| 1 | cir.rs ~937 | `phase_variance` uses **linear** variance on **wrapped** angles (doc says "variance of phase angles") — spuriously inflates near ±π | P1 | **RESOLVED (`fd32f094a`) — metric MEASURED, threshold DATA-GATED.** Replaced with Mardia's circular variance V = 1 − R̄ ∈ **[0,1]**, invariant to the cluster's position on the circle (branch-cut artefact gone). Guard re-derived against the bounded metric via named const `GHOST_TAP_CIRCULAR_VARIANCE_MAX = 0.99` (fires only when R̄ ≤ 0.01 — essentially uniform phase). The **threshold value is DATA-GATED**: a clean single-path ramp also sweeps the circle, so V alone can't separate clean from unsanitized without labelled frames — the default is deliberately conservative (strictly more permissive at the wrap boundary than the buggy linear guard). Fails-on-old: `phase_variance_circular_not_fooled_by_branch_cut` (old linear variance > TAU on wrap-straddling phases while circular V≈0, guard no longer trips), `phase_variance_circular_is_bounded_and_extremal`. | | 2 | calibration.rs ~311 | `subtract_in_place` had a vacuous `if active_input {ki} else {ki}` branch implying a full-FFT→bin remap that didn't exist | P3 | **Resolved here** (branch removed, sequential-convention documented to match the sibling `extract_first_stream`). Listed for visibility — behavior unchanged. | | 3 | spectrogram.rs / bvp.rs | FFT planner built once-per-call (already amortized across frames) | P2 | Marginal vs the per-frame PSD site; cache if these become hot. | | 4 | features.rs ~347 | Doppler FFT planner planned once per call, reused across subcarriers | P2 | Already amortized within the call. | @@ -209,11 +211,11 @@ Catalogued so nothing is silently dropped. Priority: **P1** correctness-adjacent | 6 | tomography.rs | ISTA L1 solver re-allocates voxel buffers per solve | P2 | Bench first. | | 7 | pose_tracker.rs | Kalman gain matrices reallocated per update | P2 | Bench first. | | 8 | field_model.rs | SVD recomputed on every perturbation extract | P2 | Incremental SVD is a real project, not a micro-fix. | -| 9 | coherence.rs / coherence_gate.rs | Z-score thresholds are magic constants, untested at boundaries | P1 | Needs labelled data to set defensible thresholds. | -| 10 | longitudinal.rs | Welford update not numerically guarded for n=0 | P1 | Add `n>=1` guard + test (same family as §4). | +| 9 | coherence.rs / coherence_gate.rs | Z-score thresholds are magic constants, untested at boundaries | P1 | **RESOLVED-PARTIAL (`5193f6369`) — DATA-GATED.** De-magicked `classify_drift` (`DRIFT_STABLE_SCORE=0.85`, `DRIFT_STEP_CHANGE_MAX_STALE=10`) and the `coherence_gate.rs` defaults (`DEFAULT_ACCEPT_THRESHOLD`/`…REJECT…`/`…MAX_STALE_FRAMES`/`…PREDICT_ONLY_NOISE`) into named, documented consts marked EMPIRICAL DEFAULT; added at/just-below/just-above boundary tests (`classify_drift_*_boundary`) + `*_consts_unchanged_from_literals`. **Operating values explicitly NOT changed** — defensible values still need labelled stable/drifting traces. The gate already exposed these via `GatePolicyConfig` (config seam). | +| 10 | longitudinal.rs | Welford update not numerically guarded for n=0 | P1 | **RESOLVED (`4a9f2bcf4`) — MEASURED.** The shared `WelfordStats` (`field_model.rs`, consumed by longitudinal.rs) `count < 2` guards already prevent the n=0 NaN / n=1 div0 / `(count−1)` underflow, but the boundary was untested. Added `welford_finite_at_n0_and_n1` (finite + documented 0.0 sentinel at n=0/n=1). Fails-on-old proof: removing the `sample_variance` guard makes the test panic with "attempt to subtract with overflow" at the `(count − 1)` underflow. | | 11 | cross_room.rs | Fingerprint hash collisions unhandled | P2 | Low collision prob; needs design. | | 12 | gesture.rs | `euclidean_distance` no length-mismatch guard | P3 | Caller-enforced; add `debug_assert`. | -| 13 | adversarial.rs | Gini/consistency thresholds are magic constants | P1 | Same labelled-data dependency as #9. | +| 13 | adversarial.rs | Gini/consistency thresholds are magic constants | P1 | **RESOLVED-PARTIAL (`d672fa602`) — DATA-GATED.** Lifted the bare literals in `check`/`check_consistency` (`FIELD_MODEL_GINI_VIOLATION=0.8`, `ENERGY_RATIO_HIGH_VIOLATION=2.0`, `ENERGY_RATIO_LOW_VIOLATION=0.1`, `CONSISTENCY_ACTIVE_FRACTION_OF_MEAN=0.1`, `SCORE_W_*`) into named, documented consts marked EMPIRICAL DEFAULT; added at/just-below/just-above boundary tests (`energy_ratio_high_boundary`, `energy_ratio_low_boundary`, `field_model_gini_boundary`, `consistency_active_fraction_boundary`) + `tuning_consts_unchanged_from_literals`. **Operating values explicitly NOT changed** — defensible values still need labelled spoofed/clean CSI (Wi-Spoof, §6.2/§7.3). Bumping a const fails a boundary test (verified). | | 14 | cir.rs | `fft_operator` path changes the witness hash (documented) — no test that it's *numerically close* to dense | P2 | Add a tolerance test. | | 15 | multistatic.rs | `cir_gate_coherence` only estimates the **first** node/channel; multi-node CIR consensus unused | P2 | Design item (which node's CIR is authoritative?). | | 16 | phase_align.rs | Iterative LO offset estimation has no convergence cap test | P2 | Add iteration-cap test. | @@ -223,12 +225,12 @@ Catalogued so nothing is silently dropped. Priority: **P1** correctness-adjacent | 20 | spectrogram.rs | `compute_multi_subcarrier_spectrogram` re-plans per subcarrier via `compute_spectrogram` | P2 | Hoist the planner (relates to #3). | | 21–45 | (assorted) | Remaining clarity/doc/magic-constant/missing-boundary-test findings across `ruvsense/*`, `features.rs`, `motion.rs` | P3 | Bulk-addressable in a dedicated "test-the-boundaries + de-magic-constant" follow-up; not high-leverage individually. | -> **Horizon-ledger one-liner.** Milestone-0 DONE: dead CIR gate (FIXED+proved), NaN/inf adversarial bypass (FIXED+proved), divide-by-(n−1) window trio (FIXED+proved), calibration dead-branch (FIXED), PSD FFT-planner cache (MEASURED), DTW band (MEASURED). DEFERRED to follow-up: the ~45 findings in §7.4 (P1: phase_variance circular bug #1, Welford guard #10, threshold magic-constants #9/#13; P2/P3: the rest) — none silently dropped. +> **Horizon-ledger one-liner.** Milestone-0 DONE: dead CIR gate (FIXED+proved), NaN/inf adversarial bypass (FIXED+proved), divide-by-(n−1) window trio (FIXED+proved), calibration dead-branch (FIXED), PSD FFT-planner cache (MEASURED), DTW band (MEASURED). **Milestone-1 DONE (2026-06-13): all four P1 backlog items cleared — circular phase variance #1 (RESOLVED/MEASURED metric, DATA-GATED threshold), Welford n=0 guard #10 (RESOLVED/MEASURED), threshold magic-constants #9 & #13 (RESOLVED-PARTIAL/DATA-GATED — de-magicked + boundary-tested, values unchanged).** DEFERRED to follow-up: the ~41 remaining P2/P3 findings in §7.4 — none silently dropped. --- ## 8. Consequences - **Positive:** the ADR-134 CIR gate is alive for the first time in production; the adversarial detector can no longer be NaN-bypassed; three latent divide-by-zero NaN sources are gone; the per-frame PSD path and gesture DTW are measurably faster with bit-identical output; the SOTA landscape and a concrete LISTA-for-CIR roadmap are graded and recorded. -- **Negative / honest limits:** `canonical56()` models the canonical grid as a contiguous 56-tone band — a reasonable physical interpretation of a *resampled* grid, but not a literal hardware tone map; the CIR gate still uses only the first node's CIR (#15); the `phase_variance` circular bug (#1) remains until it can be re-thresholded with data. +- **Negative / honest limits:** `canonical56()` models the canonical grid as a contiguous 56-tone band — a reasonable physical interpretation of a *resampled* grid, but not a literal hardware tone map; the CIR gate still uses only the first node's CIR (#15). The `phase_variance` **metric** is now correct (Mardia circular variance, Milestone-1 #1), so the branch-cut false-trip is gone — but its ghost-tap **threshold** (`GHOST_TAP_CIRCULAR_VARIANCE_MAX = 0.99`) is a conservative DATA-GATED default, not a calibrated operating point, and still awaits labelled sanitized/unsanitized frames to tune. Likewise the de-magicked coherence/adversarial thresholds (#9/#13) keep their pre-existing empirical values pending labelled calibration. - **Neutral:** no public API removed; `with_cir_ht20()` kept (warned); files stay scoped; new bench is additive. diff --git a/api-docs/adr/ADR-164-adr-corpus-gap-analysis.md b/api-docs/adr/ADR-164-adr-corpus-gap-analysis.md index 9ca7174a..e8e114db 100644 --- a/api-docs/adr/ADR-164-adr-corpus-gap-analysis.md +++ b/api-docs/adr/ADR-164-adr-corpus-gap-analysis.md @@ -7,7 +7,7 @@ ## Context -The corpus has grown to **162 ADR entries across 156 distinct files** (ADR-001 through ADR-163, plus 6 duplicate-number collisions). It now spans nine subsystems — signal/DSP, NN/training, ESP32 firmware, RuvSense multistatic, RuView desktop, Cognitum cogs, HOMECORE (HA reimplementation), BFLD privacy, and the streaming engine — written over roughly a year by many agent-driven sessions. +The corpus has grown to **162 ADR entries across 156 distinct files** (ADR-001 through ADR-171; the 5 duplicate-number collisions / 6 displaced files originally noted here were RESOLVED by renumbering the displaced files to ADR-166…171 — see Gap Register G1). It now spans nine subsystems — signal/DSP, NN/training, ESP32 firmware, RuvSense multistatic, RuView desktop, Cognitum cogs, HOMECORE (HA reimplementation), BFLD privacy, and the streaming engine — written over roughly a year by many agent-driven sessions. Two forces motivate a corpus-wide gap analysis *now*: @@ -39,7 +39,7 @@ Counts are approximate (`~`) where a status string is non-canonical or dual-valu | Proposed (incl. conditional/research-only) | ~88 | partial | ~50 | | Superseded | 1 (ADR-002) | proposed-only | ~64 | | Rejected | 1 (ADR-098) | stale-or-contradicted | 3 (029/030/031) | -| Missing / no Status header | 3 (ADR-147-proof, ADR-052-ddd, ADR-134) | unknown | 5 (034/044/052-ddd/147-proof/…) | +| Missing / no Status header | 3 (ADR-168-proof [was 147], ADR-167-ddd [was 052], ADR-134) | unknown | 5 (034/044/167-ddd/168-proof/…) | | Mixed/dual status in one ADR | 3 (115, 149×2, 133) | superseded | 1 (ADR-002) | **Headline:** ~114 of 162 ADRs (≈70%) are decisions that never fully landed (proposed-only + partial + stale + unknown). The dominant failure mode is **stale Status headers**, not abandoned work. @@ -50,8 +50,8 @@ Severity: CRITICAL (corpus integrity / tooling-breaking / life-safety / security | ID | Gap | Severity | Affected ADRs | Recommended action | |----|-----|----------|---------------|--------------------| -| G1 | 6 duplicate ADR numbers (two ADRs answer to one number; breaks index/`/adr` tooling) | CRITICAL | 050×2, 052×2, 147×3, 148×2, 149×2, 134 (identity split) | renumber 2-of-3 at 147, 1 each at 050/148/149; demote 052-ddd to appendix; resolve 134 identity | -| G2 | 3 files with no Status header (cannot triage) — **INVESTIGATED in `docs/adr-gap-remediation-1`: only 2 genuinely lack one, both owner-gated** | CRITICAL | 147-benchmark-proof, 052-ddd-appendix, ~~134-CIR~~ | add canonical `## Status`; relocate 147-proof to `benchmarks/`; label 052-ddd as appendix — **NOTE: ADR-134-CIR DOES have a Status (`\| Status \| Proposed \|` in its header table) — mislabeled here. The two real misses (147-benchmark-proof, 052-ddd) are both inside owner-gated duplicate-number collisions (147×3, 052×2), so left untouched pending owner. The early ADRs (048/049/068/070 etc.) use `\| Status \|` not `\| **Status** \|` — different-format-but-present, not missing. Net: 0 headers added.** | +| G1 | ~~6 duplicate ADR numbers (two ADRs answer to one number; breaks index/`/adr` tooling)~~ **RESOLVED (duplicate-number item)** | CRITICAL | 050×2, 052×2, 147×3, 148×2, 149×2; 134 (identity split, separate) | ~~renumber 2-of-3 at 147, 1 each at 050/148/149; demote 052-ddd to appendix; resolve 134 identity~~ **DONE: displaced files renumbered to the next free numbers (166–171), keepers = first-committed file per number (date ties broken by inbound-ref count / parent-appendix relationship): 050 keeps provisioning-tool-enhancements → quality-engineering-security-hardening = ADR-166; 052 keeps tauri-desktop-frontend → ddd-bounded-contexts appendix = ADR-167 (still linked to parent 052); 147 keeps nvidia-cosmos/OccWorld → benchmark-proof = ADR-168, adam-mode-light-theme = ADR-169; 148 keeps drone-swarm-control-system → yoga-mode-pose-system = ADR-170; 149 keeps public-community-leaderboard-huggingface → swarm-benchmarking-evaluation-methodology = ADR-171. In-file headers, intra-file self-refs, all inbound cross-references (README index, census, lens-findings, user-guide, CHANGELOG, proof-of-capabilities, research docs), and this register updated. `ls docs/adr/ADR-*.md | … | uniq -d` is now EMPTY. The ADR-134 identity split is NOT a filename collision; resolved separately under G3 (→ ADR-165).** | +| G2 | 3 files with no Status header (cannot triage) — **INVESTIGATED in `docs/adr-gap-remediation-1`: only 2 genuinely lack one, both owner-gated** | CRITICAL | ADR-168-benchmark-proof (was 147), ADR-167-ddd-appendix (was 052), ~~134-CIR~~ | add canonical `## Status`; relocate ADR-168-proof to `benchmarks/`; label ADR-167-ddd as appendix — **NOTE: ADR-134-CIR DOES have a Status (`\| Status \| Proposed \|` in its header table) — mislabeled here. The two real misses (ADR-168-benchmark-proof [was 147], ADR-167-ddd [was 052]) were inside the owner-gated duplicate-number collisions (147×3, 052×2); those collisions are now resolved (G1) but the missing Status headers themselves remain owner-gated, so left untouched pending owner. The early ADRs (048/049/068/070 etc.) use `\| Status \|` not `\| **Status** \|` — different-format-but-present, not missing. Net: 0 headers added.** | | G3 | ~~Shipped crates cite a non-existent or wrong-identity governing ADR~~ **RESOLVED in `docs/adr-gap-remediation-1`** | CRITICAL | homecore-recorder→"ADR-132" (no file); homecore-migrate→"ADR-134" (file is CIR) | ~~write-missing-ADR (HOMECORE-RECORDER, HOMECORE-MIGRATE)~~ DONE: wrote ADR-132 (recorder, Accepted) + ADR-165 (migrate, Accepted — P1 scaffold); repointed migrate's ADR-134 refs → ADR-165 | | G4 | Anti-slop retractions: accuracy/security/function provably false until sweep landed | CRITICAL | 155, 154, 079, 161 (see Contradictions) | already fixed in-code by 154/155/161/162; this ledger records the retraction | | G5 | ~~10 streaming-engine ADRs marked `Proposed` while §Impl-Status reports Built + commits + tests~~ **RESOLVED in `docs/adr-gap-remediation-1`** | HIGH | 136–145 | ~~mark-stale → "Accepted — partial (integration glue pending)" (one batch)~~ DONE: all 10 (136–145) flipped to "Accepted — partial"; each retains its commit-pinned Implementation-Status note. NB: notes describe *building blocks built + tested*, **not** live-path integration — "partial" is the honest label, not full "Accepted" | @@ -60,7 +60,7 @@ Severity: CRITICAL (corpus integrity / tooling-breaking / life-safety / security | G8 | ADR-002 supersession not reciprocated by successors; 5 children stranded | HIGH | 002→016/017; children 003/007/008/009/010 | reconcile-docs (add reciprocal language or downgrade); split 002 to "partially superseded" | | G9 | Streaming-engine integrator crate has no governing ADR (composition/back-pressure/live-path seam) | HIGH | wifi-densepose-engine (composes 135–146) | write-missing-ADR | | G10 | CLAUDE.md doc-vs-header drift (doc says one status, header another) | HIGH | 017, 024, 027, 072, 152 | reconcile-docs | -| G11 | Open security HIGH findings, gate FAILED, never marked done | HIGH | 080 (XFF bypass, leaked stack traces, JWT-in-URL CWE-598) | implement (sensing-server boundary — NOT covered by HOMECORE sweep 161/162) | +| G11 | ~~Open security HIGH findings, gate FAILED, never marked done~~ **RESOLVED (2026-06-13, branch `fix/adr-080-sensing-server-security`)** | HIGH | 080 (XFF bypass, leaked stack traces, JWT-in-URL CWE-598) | ~~implement (sensing-server boundary — NOT covered by HOMECORE sweep 161/162)~~ DONE: verified all three against the *current Rust* `wifi-densepose-sensing-server`. **#2 leaked errors** was the one live exposure — 6 `main.rs` handlers serialized internal `Display`/`JoinError` into response bodies; fixed via a new `error_response` module (generic body + correlation id, detail logged server-side only). **#1 XFF** and **#3 JWT-in-URL** were verified *absent* on the Rust boundary (no IP-rate-limit/allowlist reads XFF; token is header-only, WS handlers take no query token) and pinned with regression tests that fail if either is re-introduced. ADR-080 P0 §1–3 marked RESOLVED. | | G12 | ADR-052→054 edge unacknowledged by successor; likely mis-modeled (impl, not replacement) | MEDIUM | 052-tauri, 054 | reconcile-docs (054 is the impl plan *for* 052, not a replacement) | | G13 | Capability governed only by remediation/deploy ADR, no creation/architecture ADR | MEDIUM | wasm-edge (only 160/163); occworld-candle (147 blessed Python path only); pointcloud (094 = viewer deploy only) | write-missing-ADR (taxonomy/ABI for wasm-edge; Candle backend swap; pointcloud data contract) | | G14 | Conflicting decisions on one topic, none superseding the others | MEDIUM | person-count 037/075/103; PQ-sign 007/109; fed key-exchange 107/108; provisioning 050/060/052; audit 010/028; RVF-WASM 009-vs-shipped | reconcile (pick one, supersede the rest) | @@ -104,7 +104,7 @@ The ADR-154–163 sweep was narrowly scoped. The two largest **capability** gaps - **CRITICAL — Camera-teacher training validation (ADR-079 / 072 / 150).** P7–P9 Pending; blocker is a real synchronized camera+ESP32 paired-capture session + GPU training on the fleet (ruvultra RTX 5080). Cross-subject collapse (11.6%) is data-gated on a heterogeneous multi-subject CSI dataset, per ADR-150 §F3 / ADR-152 F3 (the lever is *more data*, not capacity). Accepted-on-paper, not proven. - **HIGH — Federation + BFLD privacy chains (ADR-105–109, 118–125).** All Proposed-only, ACs unchecked. Blockers: KIT BFId dataset (121), Pi5/Nexmon CBFR capture hardware (123 — ESP32 structurally cannot sniff CBFR), Soul-Signature + cog-ha-matter (122/125). The privacy control *plane* (ADR-141) is built; the *capture/scoring* chain it gates is not. -- **HIGH — Sensing-server security (ADR-080).** Distinct from the HOMECORE boundary the sweep fixed; XFF bypass / stack-trace leakage / JWT-in-URL remain open. +- ~~**HIGH — Sensing-server security (ADR-080).** Distinct from the HOMECORE boundary the sweep fixed; XFF bypass / stack-trace leakage / JWT-in-URL remain open.~~ **RESOLVED (2026-06-13, G11):** verified against the current Rust sensing-server — stack-trace leakage was the one live finding (fixed via `error_response` generic bodies); XFF bypass and JWT-in-URL were verified absent and regression-pinned. See ADR-080 P0 §1–3. - **MEDIUM — gold-standard deferrals (model to follow):** ADR-163 (ESP32 on-hardware latency UNMEASURED), ADR-160 (medical/affect/weapon NOT validated, relabelled), ADR-158 (RF-through-rubble + learned counter DATA-GATED). Code is real, the claim is withheld pending absent hardware/labelled data — labels are honest. - **MEDIUM — purely hardware/data-gated Proposed decisions (no overreach):** ADR-023, 027, 042, 063/064, 065/066, 070, 073/078, 083, 086, 091, 103, 110 (HE-CSI needs ESP-IDF ≥5.5), 113, 114, 134/135, 143-v2, 144. *needs verification* where flags rely on downstream prose rather than direct file inspection. diff --git a/api-docs/adr/ADR-050-quality-engineering-security-hardening.md b/api-docs/adr/ADR-166-quality-engineering-security-hardening.md similarity index 98% rename from api-docs/adr/ADR-050-quality-engineering-security-hardening.md rename to api-docs/adr/ADR-166-quality-engineering-security-hardening.md index c37145eb..b1c13827 100644 --- a/api-docs/adr/ADR-050-quality-engineering-security-hardening.md +++ b/api-docs/adr/ADR-166-quality-engineering-security-hardening.md @@ -1,4 +1,4 @@ -# ADR-050: Quality Engineering Response — Security Hardening & Code Quality +# ADR-166: Quality Engineering Response — Security Hardening & Code Quality | Field | Value | |-------|-------| diff --git a/api-docs/adr/ADR-052-ddd-bounded-contexts.md b/api-docs/adr/ADR-167-ddd-bounded-contexts.md similarity index 98% rename from api-docs/adr/ADR-052-ddd-bounded-contexts.md rename to api-docs/adr/ADR-167-ddd-bounded-contexts.md index 39093fca..ac7ef7bb 100644 --- a/api-docs/adr/ADR-052-ddd-bounded-contexts.md +++ b/api-docs/adr/ADR-167-ddd-bounded-contexts.md @@ -1,4 +1,8 @@ -# ADR-052 Appendix: DDD Bounded Contexts — Tauri Desktop Frontend +# ADR-167 Appendix: DDD Bounded Contexts — Tauri Desktop Frontend + +> Appendix to [ADR-052](ADR-052-tauri-desktop-frontend.md). Renumbered from ADR-052 +> to ADR-167 to resolve the ADR-052 duplicate-number collision (per ADR-164 Gap Register +> G1); the parent decision remains ADR-052. This document maps out the domain model for the RuView Tauri desktop application described in ADR-052. It defines bounded contexts, their aggregates, entities, @@ -158,7 +162,7 @@ Represents an over-the-air firmware update to a running node. | `target_node` | `MacAddress` | Target node MAC | | `target_ip` | `IpAddr` | Target node IP | | `firmware` | `FirmwareBinary` | The binary being pushed | -| `psk` | `Option` | PSK for authentication (ADR-050) | +| `psk` | `Option` | PSK for authentication (ADR-166) | | `phase` | `OtaPhase` | Uploading / Rebooting / Verifying / Done / Failed | | `progress` | `Progress` | Upload progress | diff --git a/api-docs/adr/ADR-147-benchmark-proof.md b/api-docs/adr/ADR-168-benchmark-proof.md similarity index 99% rename from api-docs/adr/ADR-147-benchmark-proof.md rename to api-docs/adr/ADR-168-benchmark-proof.md index 6405e424..e913c9fe 100644 --- a/api-docs/adr/ADR-147-benchmark-proof.md +++ b/api-docs/adr/ADR-168-benchmark-proof.md @@ -1,4 +1,4 @@ -# ADR-147 Benchmark Proof — OccWorld on RTX 5080 +# ADR-168 Benchmark Proof — OccWorld on RTX 5080 Date: 2026-05-29 Hardware: NVIDIA GeForce RTX 5080 (15.47 GB VRAM), CUDA 12.8 Model: OccWorld TransVQVAE (random weights — pre-domain-fine-tuning baseline) diff --git a/api-docs/adr/ADR-169-adam-mode-light-theme.md b/api-docs/adr/ADR-169-adam-mode-light-theme.md new file mode 100644 index 00000000..d62462dd --- /dev/null +++ b/api-docs/adr/ADR-169-adam-mode-light-theme.md @@ -0,0 +1,226 @@ +# ADR-169: adam-mode — light theme toggle for the three.js realtime demo + +| Field | Value | +|-------|-------| +| **Status** | Proposed | +| **Date** | 2026-06-02 | +| **Deciders** | ruv | +| **Codename** | **adam-mode** | +| **Scope** | `examples/three.js/demos/05-skinned-realtime.html` (primary), demos 01–04 (follow-on) | +| **Relates to** | ADR-019 (sensing-only UI), ADR-035 (live sensing UI accuracy) | +| **Tracking issue** | none yet | + +--- + +## 1. Context + +`examples/three.js/demos/05-skinned-realtime.html` (build stamp `2026-05-15-fps-tune`) is the live MediaPipe → Mixamo retargeting + ESP32 CSI overlay demo. It currently ships a single, opinionated **dark theme**: + +- Body `--bg: #050507` (near-black), `--text: #d8c69a` (warm beige). +- Amber accents (`--amber: #ffb840`, `--amber-hot: #ffe09f`) on panels and controls. +- Two full-screen overlays: a radial-vignette `.overlay-frame` and a 50%-opacity CRT-style `.scanlines` layer. +- Three.js scene matches: `scene.background = new THREE.Color(0x050507)` and `scene.fog = new THREE.FogExp2(0x050507, 0.06)` (lines 269–270). + +The dark/amber CRT aesthetic is intentional for screen-recording and "command-centre" feel, but it has real failure modes: + +1. **Daylight visibility** — Demoing the live capture on a laptop in a sunlit room is unreadable; the dark background absorbs ambient glare and the amber-on-dark contrast disappears. +2. **Recording for embedded/print contexts** — When the demo's screen is captured for documentation, blog posts, or HA blueprints, the dark theme bleeds into surrounding white content and looks heavy. +3. **Accessibility** — A subset of users with light-sensitive retinas (the inverse of typical photophobia) report the high amber-on-near-black combination strains them; high-contrast light themes are easier. +4. **Operator pairing with a light-mode IDE** — Many operators run a light-mode browser alongside a dark-mode IDE and want the demo to match the browser, not the IDE. + +A toggle is the right answer because none of these reasons are universal — some sessions and some users want each mode. + +### 1.1 What this ADR is *not* + +- Not a redesign. The amber accent stays; only the surface colours and overlays swap. The information density, panel layout, and three.js scene geometry are unchanged. +- Not a multi-theme system. We add exactly two themes: the existing dark (default, unnamed) and **adam-mode** (light). Future themes would need a new ADR. +- Not a backend / data-model change. Pure presentation. +- Not yet propagated to demos 01–04. Those follow-on after adam-mode lands on demo 05 and is validated. + +## 2. Decision + +Add a **client-side theme toggle** to `05-skinned-realtime.html` that switches between the existing dark theme and a new light theme called **adam-mode**, driven by a `data-theme="adam"` attribute on `` plus a sibling `:root[data-theme="adam"]` CSS block that re-defines the existing custom properties. A new toggle button in the existing `#helpers` panel switches between modes and persists the choice in `localStorage` under the key `ruview.theme`. + +### 2.1 CSS — the colour swap + +Add immediately after the existing `:root { ... }` block in `