Skip to content

Commit 42dcf49

Browse files
authored
fix(adr): resolve duplicate ADR numbers + close ADR-080 security + ADR-154 M1 signal backlog (#1051)
* fix(signal): circular phase variance for ghost-tap guard (ADR-154 §7.4 #1) `phase_variance` computed a LINEAR sample variance over phase angles that wrap at ±π, so a tightly-clustered set straddling the branch cut reported spuriously HIGH dispersion — false-tripping the `> TAU` ghost-tap guard on real, tightly-clustered CIR taps. Replace with Mardia's circular variance V = 1 − R̄, bounded [0,1] and invariant to where the cluster sits on the circle. Re-derive the guard against the bounded metric via a named const `GHOST_TAP_CIRCULAR_VARIANCE_MAX` (the old TAU-scaled threshold is meaningless on [0,1]). Grade: metric fix MEASURED; threshold value DATA-GATED — a clean single-path ramp also sweeps the circle, so V alone cannot separate clean from unsanitized without labelled frames. Conservative default (0.99) errs toward never false-rejecting, strictly more permissive at the wrap boundary than the buggy linear guard. Fails-on-old test: `phase_variance_circular_not_fooled_by_branch_cut` — inlines the old linear variance to show it exceeds TAU on wrap-straddling phases while circular V≈0 and the guard no longer trips. Plus `phase_variance_circular_is_bounded_and_extremal` (V∈[0,1], V≈0 identical, V≈1 uniform). cargo test -p wifi-densepose-signal --no-default-features --features cir --lib → 432 passed, 0 failed. Co-Authored-By: claude-flow <ruv@ruv.net> * fix(signal): pin Welford n=0/n=1 finiteness guard (ADR-154 §7.4 #10) The shared `WelfordStats` (field_model.rs, used by longitudinal.rs and others) relies on `count < 2` guards in `variance`/`sample_variance`/`std_dev`/ `z_score` to stay finite at the boundaries. The guards existed but the n=0 boundary was UNTESTED — exactly the §4 divide-by-(n−1) family the ADR groups this with. Add `welford_finite_at_n0_and_n1` asserting every statistic is finite and returns the documented sentinel (0.0) at n=0 and n=1, plus load-bearing doc comments on the two guards. Fails-on-old proof: with the `sample_variance` guard removed, the test FAILS with "attempt to subtract with overflow" at the `(self.count - 1)` underflow (0usize − 1); `variance` would similarly yield 0.0/0.0 = NaN. The guard is restored; the test pins it so a future regression is caught. Grade: MEASURED (boundary finiteness is asserted; the guard is the §4-family fix made testable). cargo test -p wifi-densepose-signal --no-default-features --lib field_model → 22 passed, 0 failed. Co-Authored-By: claude-flow <ruv@ruv.net> * refactor(signal): de-magic adversarial thresholds + boundary tests (ADR-154 §7.4 #13) Lift the bare numeric literals buried in `check`/`check_consistency` into named, documented module consts (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_* weights). VALUES UNCHANGED — each const equals the original literal; only names + pinning tests are new. Grade: DATA-GATED. The operating values stay empirical (defensible values need labelled spoofed/clean CSI — Wi-Spoof, §6.2/§7.3). The de-magicking + characterization tests are MEASURED: `tuning_consts_unchanged_from_literals`, `energy_ratio_high_boundary`, `energy_ratio_low_boundary`, `field_model_gini_boundary`, `consistency_active_fraction_boundary` pin the decision boundaries at/just-below/just-above each threshold, so a future data-driven retune is a visible, tested change. Fails-on-change proof: bumping ENERGY_RATIO_HIGH_VIOLATION 2.0→3.0 makes `energy_ratio_high_boundary` FAIL (restored). Operating values explicitly NOT changed. cargo test -p wifi-densepose-signal --no-default-features --lib ruvsense::adversarial → 20 passed, 0 failed. Co-Authored-By: claude-flow <ruv@ruv.net> * refactor(signal): de-magic coherence drift/gate thresholds (ADR-154 §7.4 #9) Lift the bare detection literals in `coherence.rs::classify_drift` (DRIFT_STABLE_SCORE=0.85, DRIFT_STEP_CHANGE_MAX_STALE=10) and the `coherence_gate.rs` Default impl (DEFAULT_ACCEPT_THRESHOLD=0.85, DEFAULT_REJECT_THRESHOLD=0.5, DEFAULT_MAX_STALE_FRAMES=200, DEFAULT_PREDICT_ONLY_NOISE=3.0) into named, documented consts. VALUES UNCHANGED. The gate already exposed these via GatePolicyConfig (config seam); this names + pins the defaults. Grade: DATA-GATED. Operating values stay empirical (defensible Z-score thresholds need labelled stable/drifting coherence traces). De-magicking + boundary tests are MEASURED: `classify_drift_stable_score_boundary`, `classify_drift_stale_count_boundary` pin the at/just-below/just-above decisions; `drift_consts_unchanged_from_literals` / `gate_default_consts_unchanged_from_literals` pin the values. Operating values explicitly NOT changed. cargo test -p wifi-densepose-signal --no-default-features --lib ruvsense::coherence → 40 passed, 0 failed. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(adr-154): mark §7.4 P1 backlog cleared — Milestone-1 (#1,#10 RESOLVED; #9,#13 DATA-GATED) Update ADR-154 §7.4 backlog rows #1, #9, #10, #13 with commit refs + grades, the §7.4 intro count (four P1 items cleared, ~41 P2/P3 remain), the Horizon-ledger one-liner (Milestone-1 DONE), and the §8 honest-limits #1 line (metric now correct; threshold still DATA-GATED). Add CHANGELOG [Unreleased] entry. Grades: #1 RESOLVED (MEASURED metric / DATA-GATED threshold), #10 RESOLVED (MEASURED), #9 & #13 RESOLVED-PARTIAL (DATA-GATED — de-magicked + boundary tested, operating values unchanged). Validation: cargo test --workspace --no-default-features → 2057 passed, 0 failed; wifi-densepose-signal lib → 442 passed (no-default + --features cir); python archive/v1/data/proof/verify.py → VERDICT: PASS, hash f8e76f21…46f7a UNCHANGED (CIR ghost-tap guard is not on the deterministic proof path). Co-Authored-By: claude-flow <ruv@ruv.net> * fix(sensing-server): stop leaking internal errors in HTTP responses (ADR-080 #2) Six handlers in `main.rs` serialized the internal error `Display` straight into the JSON response body, leaking server internals to any client (ADR-080 finding #2, CWE-209; reframed onto the Rust boundary by ADR-164 G11): - edge_registry_endpoint: a panicked spawn_blocking `JoinError` ("task … panicked") in a 500, and the raw upstream error in a 503 - delete_model / delete_recording / start_recording: std::io::Error strings carrying OS detail / filesystem paths - calibration_start / calibration_stop: the FieldModel error chain New `error_response` 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":…}`) — no `panicked`, no file paths, no Debug chain. The correlation id lets an operator join a client report to the exact server log line without ever shipping the detail. Pinned by 5 error_response tests, incl. a leak-substring guard (internal_error_body_does_not_leak_detail) verified to FAIL on the reverted old body (returns the panic message / path / "os error"). The HOMECORE sweep (ADR-161) covered homecore-server, not this crate. Co-Authored-By: claude-flow <ruv@ruv.net> * test(sensing-server): pin XFF-immunity + no-query-token (ADR-080 #1, #3) Findings #1 (XFF-spoofing bypass) and #3 (JWT-in-URL, CWE-598) were logged against the Python v1 API but are VERIFIED ABSENT on the current Rust sensing-server, so they get regression tests rather than redundant fixes: - #1 XFF: there is no IP-based rate-limiter or IP-allowlist to bypass, and neither security middleware reads a forwarded header. Added bearer_auth::xff_header_never_affects_auth_decision (spoofed X-Forwarded-For never flips a 401<->200 decision) and host_validation::forwarded_headers_never_bypass_host_allowlist (spoofed X-Forwarded-Host: localhost never lets Host: evil.com past the allowlist). - #3 JWT-in-URL: require_bearer reads the token only from the Authorization header; WS handlers take no query token; the sole Query extractor (EdgeRegistryParams) is a non-secret refresh flag. Added bearer_auth::query_string_token_is_never_accepted — ?token= / ?access_token= in the URL never authenticates (stays 401) while the header path still 200s. Verified to FAIL when a query-token path is injected into require_bearer. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(adr-080): mark P0 security findings #1-#3 RESOLVED; close ADR-164 G11 - ADR-080: Status note + per-finding closure (#1 XFF and #3 JWT-in-URL verified absent + regression-pinned; #2 leaked errors fixed via the error_response module). Records the v1-vs-Rust boundary distinction explicitly: v1 paths remain archived; this closure governs the shipped Rust sensing-server. - ADR-164: Gap Register G11 and the Open/Gated Backlog entry marked RESOLVED with the fix + branch reference. - CHANGELOG: [Unreleased] -> ### Security entry covering all three findings. Co-Authored-By: claude-flow <ruv@ruv.net> * docs(adr): renumber 6 displaced ADRs to resolve duplicate-number collisions (ADR-164 G1) Resolves the 5 duplicate ADR numbers (6 displaced files) flagged by ADR-164 Gap Register item G1. Canonical keeper per number = first file committed at that number (date tie-broken by inbound cross-reference count / parent-appendix relationship). Displaced files renumbered to the next free numbers (166-171): 050 keeps provisioning-tool-enhancements (5 refs vs 1) -> ADR-166-quality-engineering-security-hardening 052 keeps tauri-desktop-frontend (parent ADR) -> ADR-167-ddd-bounded-contexts (its appendix) 147 keeps nvidia-cosmos/OccWorld (the actual ADR, has Status header) -> ADR-168-benchmark-proof (proof companion, no Status) -> ADR-169-adam-mode-light-theme (was untracked) 148 keeps drone-swarm-control-system (committed #862) -> ADR-170-yoga-mode-pose-system (was untracked) 149 keeps public-community-leaderboard-huggingface (committed 16:47 vs 17:38) -> ADR-171-swarm-benchmarking-evaluation-methodology Updates in-file `# ADR-NNN` headers and intra-file self-references (yoga-modes * docs(adr): repoint inbound cross-references to renumbered ADRs (166-171) Follow-up to the ADR renumbering (ADR-164 G1). Updates every inbound reference that pointed at a displaced ADR, disambiguating shared numbers by title/slug so only references to the DISPLACED topic move and keeper references stay put. ADR-168 (was 147 benchmark-proof): README, CHANGELOG, user-guide, proof-of-capabilities, research docs 00/03 — all path/label refs updated. ADR-169 (was 147 adam-mode) / ADR-170 (was 148 yoga-mode): docs/adr/README index. ADR-171 (was 149 swarm-benchmarking): all ruview-swarm eval code+docs (Cargo.toml, evals/, eval_swarm.rs, metrics/mod/report/runner.rs), research doc 03 (every §-ref matched ADR-171 sections, not AetherArena), 00-system-review, series README, CHANGELOG, and ADR-148's forward/"open issues" pointers. ADR-166 (was 050 quality-engineering / security-hardening): disambiguated from the ADR-050 provisioning KEEPER by topic. The HMAC/secure_tdm, directory-traversal, bind-address, and OTA-PSK-auth references in code comments (wifi-densepose-hardware Cargo.toml + secure_tdm.rs, sensing-server main.rs) and in ADR-052-tauri / ADR-167 all describe the security-hardening ADR -> ADR-166. ADR-167 (was 052 ddd-appendix): inbound appendix references. Index/registry updates: docs/adr/README.md, gap-analysis/census.md (rows + header count), gap-analysis/lens-findings.md (collision table marked RESOLVED), and ADR-164 Gap Register G1 marked RESOLVED with the full renumber map. Keeper references deliberately untouched: all ADR-147 OccWorld code, all ADR-148 drone-swarm code/docs, all ADR-149 AetherArena refs (incl. ADR-150's SSL/resampling refs, which ADR-150 explicitly binds to the AetherArena benchmark), ADR-050 provisioning refs, ADR-052 tauri refs. The frozen GitHub blob URLs in docs/adr/.issue-177-body.md (pinned to an old branch) are left as historical. Comment-only code edits; no behavior change. wifi-densepose-hardware compiles clean; the sensing-server build's sole blocker is the pre-existing upstream midstreamer-temporal-compare@0.2.1 registry crate, unrelated to these edits. Co-Authored-By: claude-flow <ruv@ruv.net>
1 parent 74ecce3 commit 42dcf49

41 files changed

Lines changed: 1893 additions & 192 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 12 additions & 2 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ The separate **17-keypoint pose-estimation model** is now published at [`ruvnet/
194194
| **Efficiency frontier** | [`docs/benchmarks/wifi-pose-efficiency-frontier.md`](docs/benchmarks/wifi-pose-efficiency-frontier.md) | SOTA-beating WiFi pose in a 20 KB int4 edge model |
195195
| **Pretrained encoder** | [`ruvnet/wifi-densepose-pretrained`](https://huggingface.co/ruvnet/wifi-densepose-pretrained) | 82.3% held-out temporal-triplet, 8 KB int4 |
196196
| **Reproducible proof (Trust Kill Switch)** | [`archive/v1/data/proof/verify.py`](archive/v1/data/proof/verify.py) + [`expected_features.sha256`](archive/v1/data/proof/expected_features.sha256) | one-command deterministic pipeline replay (SHA-256 of output vs published hash) |
197-
| **Benchmark-proof ADR** | [ADR-147](docs/adr/ADR-147-benchmark-proof.md) | how the numbers are produced and verified |
197+
| **Benchmark-proof ADR** | [ADR-168](docs/adr/ADR-168-benchmark-proof.md) | how the numbers are produced and verified |
198198
| **Witness attestation** | [`docs/WITNESS-LOG-028.md`](docs/WITNESS-LOG-028.md) | 33-row capability attestation matrix with per-claim evidence |
199199

200200
```bash

docs/adr/ADR-052-tauri-desktop-frontend.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
| Status | Proposed |
66
| Date | 2026-03-06 |
77
| Deciders | ruv |
8-
| 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) |
8+
| 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) |
99
| Issue | [#177](https://github.com/ruvnet/RuView/issues/177) |
1010

1111
## Context
@@ -211,7 +211,7 @@ pub struct FlashProgress {
211211
// commands/ota.rs
212212

213213
/// Push firmware to a node via HTTP OTA (port 8032).
214-
/// Includes PSK authentication per ADR-050.
214+
/// Includes PSK authentication per ADR-166.
215215
#[tauri::command]
216216
async fn ota_update(
217217
node_ip: String,
@@ -801,7 +801,7 @@ Total estimated effort: ~11 weeks for a single developer.
801801
- ADR-039: ESP32 Edge Intelligence
802802
- ADR-040: WASM Programmable Sensing
803803
- ADR-044: Provisioning Tool Enhancements
804-
- ADR-050: Quality Engineering — Security Hardening
804+
- ADR-166: Quality Engineering — Security Hardening (renumbered from ADR-050)
805805
- ADR-051: Sensing Server Decomposition
806806
- `firmware/esp32-csi-node/` — ESP32 firmware source
807807
- `firmware/esp32-csi-node/provision.py` — Current provisioning script

docs/adr/ADR-080-qe-remediation-plan.md

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ADR-080: QE Analysis Remediation Plan
22

3-
- **Status:** Proposed
3+
- **Status:** Proposed — P0 security findings #1#3 **RESOLVED** on the shipped Rust sensing-server boundary (2026-06-13; closes ADR-164 G11)
44
- **Date:** 2026-04-06
55
- **Source:** [QE Analysis Gist (2026-04-05)](https://gist.github.com/proffesor-for-testing/a6b84d7a4e26b7bbef0cf12f932925b7)
66
- **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
1313

1414
Address the 15 prioritized issues from the QE analysis in three waves: P0 (immediate), P1 (this sprint), P2 (this quarter).
1515

16+
## Security P0 closure note (2026-06-13) — Rust sensing-server boundary
17+
18+
The three P0 security findings below were logged against the **Python v1** API
19+
(`archive/v1/src/…`). ADR-164 G11 re-scoped them to the *shipped* boundary:
20+
`wifi-densepose-sensing-server` (Rust). They were verified against the current
21+
Rust crate and closed on branch `fix/adr-080-sensing-server-security`. Each fix
22+
(or already-fixed finding) is pinned by a test that fails on the old behavior.
23+
**The Python v1 paths remain as-is** — v1 is archived and not the shipped
24+
surface; this closure governs the live Rust server only.
25+
1626
## P0 — Fix Immediately
1727

18-
### 1. Rate Limiter Bypass (Security HIGH)
28+
### 1. Rate Limiter Bypass / XFF spoofing (Security HIGH)**RESOLVED (verified absent on Rust boundary)**
1929

20-
- **Location:** `archive/v1/src/middleware/rate_limit.py:200-206`
30+
- **Original location (v1):** `archive/v1/src/middleware/rate_limit.py:200-206`
2131
- **Problem:** Trusts `X-Forwarded-For` without validation. Any client bypasses rate limits via header spoofing.
22-
- **Fix:** Validate forwarded headers against trusted proxy list, or use connection IP directly.
32+
- **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.
33+
- **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<SocketAddr>`) and only honor XFF from an explicit `--trusted-proxy` CIDR — captured as guidance in the test docstrings.
2334

24-
### 2. Exception Details Leaked in Responses (Security HIGH)
35+
### 2. Exception Details Leaked in Responses (Security HIGH, CWE-209) — **RESOLVED**
2536

26-
- **Location:** `archive/v1/src/api/routers/pose.py:140`, `stream.py:297`, +5 endpoints
27-
- **Problem:** Stack traces visible regardless of environment.
28-
- **Fix:** Wrap with generic error responses in production; log details server-side only.
37+
- **Original location (v1):** `archive/v1/src/api/routers/pose.py:140`, `stream.py:297`, +5 endpoints
38+
- **Problem:** Internal error/stack-trace detail serialized into client responses.
39+
- **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.
40+
- **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.
2941

30-
### 3. WebSocket JWT in URL (Security HIGH, CWE-598)
42+
### 3. WebSocket JWT in URL (Security HIGH, CWE-598)**RESOLVED (verified absent on Rust boundary)**
3143

32-
- **Location:** `archive/v1/src/api/routers/stream.py:74`, `archive/v1/src/middleware/auth.py:243`
44+
- **Original location (v1):** `archive/v1/src/api/routers/stream.py:74`, `archive/v1/src/middleware/auth.py:243`
3345
- **Problem:** Tokens in query strings visible in logs/proxies/browser history.
34-
- **Fix:** Use WebSocket subprotocol or first-message auth pattern.
46+
- **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.
47+
- **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.
3548

3649
### 4. Rust Tests Not in CI
3750

docs/adr/ADR-148-drone-swarm-control-system.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
| Relates to | ADR-134, ADR-136, ADR-139, ADR-140, ADR-143, ADR-144, ADR-146, ADR-147 |
1010

1111
> **Scope note:** ADR-147 deferred Cosmos WFM to "ADR-148" as an offline data generator.
12-
> That item is promoted to ADR-149. This ADR takes 148 to address the broader drone swarm
13-
> control architecture, which is the first consumer of ADR-147's OccWorld occupancy output.
12+
> That item is promoted to ADR-171 (the swarm-benchmarking/evaluation companion to this ADR;
13+
> renumbered from ADR-149 to resolve the ADR-149 duplicate-number collision). This ADR takes
14+
> 148 to address the broader drone swarm control architecture, which is the first consumer of
15+
> ADR-147's OccWorld occupancy output.
1416
1517
---
1618

@@ -874,9 +876,9 @@ validated; ITAR/EAR classification completed by export counsel.
874876
| GPS spoofing of full swarm simultaneously | Medium | Low | UWB mesh cross-check among all nodes; ≥ 3 nodes must agree on position to confirm |
875877
| 1000-UAV scale claims (not validated) | Low | High | SWARM+ demonstrated in simulation only; scale claims capped at 50 for production targets |
876878

877-
### 12.3 Open Issues (Forward to ADR-149)
879+
### 12.3 Open Issues (Forward to ADR-171)
878880

879-
- Cosmos WFM offline training data generation (deferred from ADR-147) — ADR-149
881+
- Cosmos WFM offline training data generation (deferred from ADR-147) — ADR-171
880882
- Fixed-wing hybrid platform support (endurance missions) — future ADR
881883
- Underwater-aerial cross-domain handoff protocol — future ADR
882884
- Quantum-enhanced task assignment (E6) — future ADR when hardware matures
@@ -998,4 +1000,4 @@ Implementation tracked at: https://github.com/ruvnet/RuView/issues/861
9981000

9991001
*ADR authored with research support from `ruflo-goals:deep-researcher` (2026-05-30).
10001002
Implementation progress tracked by `ruflo-goals:horizon-tracker`.
1001-
OccWorld integration basis: ADR-147. Next: ADR-149 (Cosmos WFM offline data generation).*
1003+
OccWorld integration basis: ADR-147. Next: ADR-171 (Cosmos WFM offline data generation; renumbered from ADR-149).*

0 commit comments

Comments
 (0)