- published as github.com/ruvnet/worldgraph (3-crate workspace, history via git-filter-repo)
- replace the 3 in-tree crates with one submodule at v2/crates/worldgraph
- parent workspace: drop the 3 members, exclude the submodule (it is its own workspace),
repoint workspace.dependencies(worldmodel) + engine/sensing-server path-deps into it
- cargo metadata resolves clean (geo/worldgraph/worldmodel consumed from the submodule)
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(ADR-262 P3): live RuField surface — RuView sensing speaks RuField on /api/field + /ws/field
Wire the P1 `wifi-densepose-rufield` bridge into the live
`wifi-densepose-sensing-server` so the governed sensing cycle emits real
signed RuField `FieldEvent`s on two additive endpoints.
- Cargo: add the `wifi-densepose-rufield` path dep (the single coupling
point, ADR-262 §5.4 — no new RuView-internal coupling).
- New `src/rufield_surface.rs` (kept out of the 8k-line main.rs):
`FieldSurface` holds a dedicated ed25519 `Signer` + a bounded ring of
recent events + the `/ws/field` broadcast topic; `GET /api/field` and
`GET /ws/field` handlers; a standalone `router()` for isolated testing.
- Signer (defers the P2 key decision, ADR-262 §8 Q1): a STANDALONE
dev/sensing key from `WDP_RUFIELD_SIGNING_SEED`, else a deterministic
dev default with a logged WARN. Reusing the `cog-ha-matter` Ed25519
key is the deferred P2 call — P3 does not pre-empt it.
- Tap: at the ESP32 governed-trust cycle (`main.rs` ~5886 observe_cycle
/ ~5938 SensingUpdate build), `emit_rufield_event` joins the cycle's
features/classification/signal_field with the engine's
effective_class/demoted trust state into a `SensingSnapshot` and
surfaces it via the bridge. Existing endpoints (`/ws/sensing` etc.)
are unchanged — purely additive.
- Privacy egress: `network_egress_allowed` is fail-closed for an
unattended live surface — only P1/P2 leave the box; P0 raw and
P3/P4/P5 (identity/biometric/aggregate) are held edge-local. A
`Derived` cycle maps to P4/P5 and never surfaces.
- No-phantom: `emit` drops no-presence cycles (no fabricated events).
Gates (tests/rufield_surface_test.rs, tower::oneshot, 4/0): well-formed
signed event (WifiCsi, P2 not P1, is_fusable, real timestamp); empty
cycle → no phantom; Derived trust never surfaces; mixed stream surfaces
only egress-safe events.
Honesty (ADR-262 §0/§6): real plumbing on a live endpoint, NOT accuracy.
Single-link CSI with its existing caveats (no validated room-coordinate
accuracy); dedicated dev signing key pending the P2 ownership decision;
no accuracy claim.
Co-Authored-By: claude-flow <ruv@ruv.net>
* docs(ADR-262 P3): mark P1+P3 implemented; document /api/field + /ws/field; CHANGELOG
- ADR-262 Status → "P1 + P3 implemented"; add a P3 implementation-status
block (tap site, endpoints, dedicated dev signer deferring the §8 Q1
key decision, fail-closed egress, gates). Keep the honesty framing:
real plumbing on a live endpoint, not accuracy.
- CHANGELOG [Unreleased]: add the ADR-262 P3 entry.
- user-guide: add `/api/field` to the REST table + a "RuField surface
(ADR-262 P3)" section covering `/api/field` + `/ws/field`, the
fail-closed P1/P2-only egress, the WDP_RUFIELD_SIGNING_SEED dev key,
and the no-accuracy honesty note.
Co-Authored-By: claude-flow <ruv@ruv.net>
* ci: checkout submodules everywhere + Dockerfile copies vendor/rufield
Making wifi-densepose-rufield (ADR-262 bridge) a v2 workspace member means
EVERY cargo-on-workspace context must have the vendor/rufield submodule
present (cargo loads all member manifests). P1 only fixed the rust-tests
job; this adds `submodules: recursive` to all workflow checkouts that run
cargo (mqtt-integration was failing on the missing submodule manifest), and
makes Dockerfile.rust COPY vendor/rufield/ to /vendor/rufield (matches the
bridge's ../../../vendor/rufield path-dep under the collapsed Docker layout).
update-submodules.yml left alone (it manages submodules itself).
Co-Authored-By: claude-flow <ruv@ruv.net>
---------
Co-authored-by: ruv <ruvnet@gmail.com>
* docs(research): add RuView beyond-SOTA system review (00)
First document of the beyond-SOTA research series: capability audit of
the current RuView engine with role-to-crate maturity matrix, ruvsense
module inventory, gap analysis, and risk register.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* docs(research): add beyond-SOTA architecture design (02, in progress)
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* docs(research): finalize beyond-SOTA architecture (02)
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* docs(research): add benchmark/validation methodology snapshot (03)
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* docs(research): add beyond-SOTA series index with validation results; changelog
README index ties the 5 research docs together with the session's
measured validation evidence: 2,797 workspace tests / 0 failed, Python
proof PASS (bit-exact), and paired pre/post criterion CIR benchmarks.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* perf(signal): precompute CIR warm-start system; hoist tomography solver allocs
Exact, determinism-safe optimizations (bit-identical float results):
- cir.rs: diag(PhiH Phi)+lambda*I and its CSR matrix depend only on Phi
and lambda (fixed at CirEstimator::new) but were rebuilt every frame
(O(K*G) pass + CSR allocation). Now built once in new() via
build_warm_start_system; summation order unchanged.
- tomography.rs: ISTA gradient buffer hoisted out of the 100-iteration
loop (fill(0.0) reset) and the Frobenius Lipschitz bound moved from
per-reconstruct to construction.
Verified: signal 456 tests green; engine 11/11 green including
cycle_is_deterministic and witness-stability tests. Criterion paired
pre/post: cir_estimate/he40 -3.9% (p<0.01), multiband -1.2/-1.4%.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* fix(worldgraph): bound SemanticState growth with deterministic retention
StreamingEngine::process_cycle appended one SemanticState belief per cycle
with no eviction — ~1.7M nodes/day at 20 Hz (beyond-SOTA roadmap finding #6).
Add WorldGraph::prune_semantic_states(max): deterministic eviction of the
oldest beliefs by (valid_from_unix_ms, id); structural nodes (rooms, zones,
sensors, anchors, tracks, events) are never eligible. Wire it into the
engine after each belief append (DEFAULT_SEMANTIC_RETENTION = 7,200, ~6 min
at 20 Hz; set_semantic_retention to tune). The WorldGraph holds current
beliefs; durable history is the recorder's job, so no audit data is lost.
3 new tests: end-to-end bounded growth, oldest-only eviction, deterministic
equal-timestamp tie-break. Workspace gate: 2,865 passed, 0 failed.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* feat(sensing-server): route live frames through the governed StreamingEngine
Closes the live-trust-path gap (ADR-136 section 8, beyond-SOTA system review):
the running server fused live CSI with the bare MultistaticFuser, while the
privacy/provenance/witness control plane (ADR-135..146) only ever ran on
synthetic in-test frames. The privacy control plane was therefore bypassable
on the real path.
New engine_bridge module drives StreamingEngine::process_cycle from the
server's live NodeState map, reusing the existing NodeState -> MultiBandCsiFrame
conversion. It lazily wires each contributing node as a WorldGraph sensor
(idempotent), bounds belief growth via the retention cap, and forwards explicit
timestamps/calibration ids so the path stays deterministic and replayable.
Wired additively into both live ESP32/WiFi fusion sites in main.rs via a
split-borrow off the write guard, so person-count behavior is unchanged; the
latest BLAKE3 witness is stored on AppState. Every published belief now carries
evidence + model + calibration + privacy decision and a deterministic witness.
Adds wifi-densepose-engine/-worldgraph/-bfld/-geo deps. 6 new bridge tests
(witnessed belief with full provenance, cross-run determinism, idempotent node
registration, retention bound, privacy-mode propagation). sensing-server suite
430+128 green; workspace gate 2,904 passed / 0 failed.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* feat(train): falsifiable occupancy benchmark with anti-overfitting gate
Makes the presence/person-count "beyond SOTA" claim falsifiable in code
instead of aspirational (the unfalsifiability gap from the beyond-SOTA system
review). occupancy_bench grades predictions vs ground truth and gates a SOTA
claim behind one claim_allowed invariant requiring ALL of:
- DataProvenance::Measured — synthetic/mock data is scorable for regression
but never claimable (anti-mock-contamination; the CLAUDE.md Kconfig-bug
lesson made structural).
- A leak-free EvalSplit — validate() refuses any split where a subject OR
environment id appears in both train and test (subject leakage /
per-environment overfitting).
- n_test >= min_test_samples (small-N guard).
- Presence F1 whose bootstrap-CI lower bound (deterministic seeded splitmix64)
clears the threshold — not the point estimate.
- Count MAE within threshold.
The claim string is unreadable except through the gate (NO_CLAIM otherwise),
same discipline as the ruview-gamma acceptance gate. What remains is data, not
method: a frozen, SHA-pinned, subject/environment-disjoint measured replay set
turns the claim into a passing/failing test.
Lives in wifi-densepose-train (the eval bounded context, alongside ablation/
eval/metrics). 10 tests cover each refusal path; warning-clean under the
crate's missing_docs lint. Workspace gate 2,914 passed / 0 failed. Doc 03
updated.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* feat(engine): per-room adapter provenance + drift-to-recalibration advisor
Closes the trust-chain gap where an ~11 KB per-room LoRA adapter (ADR-150
section 3.4) could silently change inference without the witness noticing:
provenance carried only "rfenc-v<N>" with no notion of adapter identity.
- StreamingEngine::set_room_adapter(AdapterInfo): pins the adapter's
content-derived id into provenance model_version
("rfenc-v1+adapter:<id>") — and therefore into the BLAKE3 witness — so
swapping or clearing adapter weights always shifts the witness. Engine test
proves base -> adapter -> other-adapter -> cleared all witness differently
and cleared == base.
- RecalibrationAdvisor: recommends re-running the ADR-135 empty-room baseline
/ refitting the room adapter on sustained low fusion coherence (streak
threshold, default 60 cycles ~ 3 s at 20 Hz) or an ADR-142 change-point.
Surfaced as TrustedOutput::recalibration_recommended, stored on the
sensing-server AppState alongside the witness at both live fusion sites.
- Bridge plumbing: EngineBridge::{set_room_adapter, clear_room_adapter} +
live-path test that the adapter id flows into the live witness.
Scope note (honest): this is the deployable provenance/trigger half of the
"retrained model" roadmap item. Fitting the adapter itself runs in the
existing external calibration service (aether-arena/calibration/); a trained
RF-encoder checkpoint still does not exist in-tree.
Engine 15 tests, bridge 7 tests. Workspace gate: 2,918 passed / 0 failed.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* fix(mat): gate api module behind its feature — standalone no-default-features builds
pub mod api was unconditional while its only dependency, serde, is optional
behind the 'api' feature, so any build without default features failed with
101 unresolved-serde errors (masked in --workspace runs by feature
unification). The api module and its create_router/AppState re-export are now
cfg(feature = "api")-gated with docsrs annotations.
All combos compile: bare --no-default-features (was 101 errors, now 0),
--no-default-features --features api, and full default (177 tests pass).
Workspace gate: 2,918 passed / 0 failed.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* perf(signal): opt-in FFT operator for the CIR ISTA solver (8-14x measured)
Phi is a sub-DFT, so each ISTA mat-vec can run as one length-G FFT
(O(G log G)) instead of a dense O(K*G) product — the dominant-latency-hazard
finding from the beyond-SOTA optimization roadmap.
New CirConfig::fft_operator, default FALSE: the dense path stays the
bit-exact witness default. The FFT evaluates the same sums in a different
order, so enabling it shifts float results in the last bits and requires
regenerating any pinned witness — strictly opt-in per deployment.
FftOperator (rustfft, planned once at CirEstimator::new, scratch buffers
reused across the ISTA loop) dispatches inside ista_solve:
Phi x = scale * forward-FFT(x) sampled at bins (k_idx mod G)
Phi^H v = scale * unnormalised inverse-FFT of v scattered into those bins
Warm-start and Lipschitz estimation stay dense at construction.
Measured (criterion, same run, same machine):
ht20: 2.22 ms -> 265 us (8.4x)
ht40: 10.26 ms -> 717 us (14.3x)
The real HE40 grid (K=484, G=1452) scales further per the O(K*G)/O(G log G)
ratio.
3 new tests: FFT<->dense matvec equivalence to float tolerance on ht20 and
he40 grids; end-to-end dominant-tap agreement on a single-path frame; all
default configs keep FFT off. New cir_estimate_fft bench group.
Workspace gate: 2,921 passed / 0 failed (default path bit-exact, witnesses
unchanged).
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* feat(core): canonical frame decoder — capture-to-claim replay (ADR-136)
The encode half of the ADR-136 frame contract existed (ComplexSample,
to_canonical_bytes, witness_hash) but there was no decoder: a captured
canonical frame could be witnessed but never reconstructed, blocking
replay-from-capture.
CsiFrame::from_canonical_bytes is the exact inverse: same id, metadata,
complex payload, and witness hash (tested as the round-trip law AC7 — the
replayed frame re-encodes byte-identically). Amplitude/phase are recomputed
from the payload (projections, not independent state). Every malformed-input
class fails closed (AC8): header truncation -> Truncated, payload truncation
-> PayloadMismatch, unknown discriminants, non-UTF-8 device id, trailing
bytes. Nil calibration uuid decodes as None per the documented encoding.
Core: 36 tests pass. Workspace gate: 2,937 passed / 0 failed.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* feat(engine): dynamic min-cut mesh partition guard (ruvector-mincut)
Maintains an exact min-cut over the live mesh coupling graph — nodes are
sensing nodes, coupling is the product of fusion attention weights — and
surfaces per cycle, as TrustedOutput::mesh:
- cut value: the global "how close is the array to partitioning" number,
a structural measure per-node heuristics miss;
- weak side: which specific nodes would split off (failure/jamming triage,
feeds ADR-032 posture);
- at-risk flag: counts as a structural event for the drift->recalibration
advisor (alongside ADR-142 change-points).
Degenerate cases fail toward risk: a node with zero coupling is reported as
already partitioned (cut 0, that node as the weak side).
Measured cost policy (criterion, 12-node mesh — the honest part):
- weights quantized (1/64) + change-gated: steady-state cycles do ZERO graph
work and reuse the cached cut (~7.3 us, ~23x cheaper than building);
- on any real change a full exact rebuild (~171 us) is used, because ONE
DynamicMinCut delete+insert measured ~240 us — the subpolynomial machinery
amortizes on much larger graphs, so rebuild-on-change is the measured
optimum at mesh scale (one-edge case -28% after switching policy);
- full process_cycle with the guard: ~33 us for 4 nodes vs the 50 ms budget.
9 mesh_guard tests (weak-node detection, steady-state zero updates,
sub-quantum gating, join/drop rebuild, determinism, disconnection) + an
engine-level wiring test (down-weighted node -> weak side -> recalibration).
Engine 24 tests; workspace gate 2,946 passed / 0 failed.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* feat(engine): mesh partition risk demotes privacy + enters the witness (ADR-032)
Completes the mesh-guard integration: its at_risk signal was advisory-only
(fed the recalibration advisor). It now also contributes to the ADR-141
privacy demotion alongside fusion- and array-level contradictions — a mesh
close to partitioning makes the fused belief less trustworthy, so the cycle
emits at a more restricted class (monotonic; information only removed).
Because effective_class feeds the BLAKE3 witness, a fragmenting array now
shifts the witness: partition risk is auditable, not just logged. The mesh
computation moved ahead of the demotion step in process_cycle; mesh_guard_mut
exposes risk-threshold tuning.
Test: a forced-risk 3-node cycle demotes PrivateHome Anonymous->Restricted
and shifts the witness vs a clean baseline. Engine 25 tests; workspace gate
2,947 passed / 0 failed.
https://claude.ai/code/session_01MjBucx95K4BuUxZi8NWwRH
* fix: public-PR review findings — privacy-path honesty, gate holes, mesh-guard cliff
- sensing-server: engine errors logged+counted (no silent swallow), trust
state exposed via status surface, privacy-demotion claims aligned with
the actual parallel-audit-path behavior
- occupancy_bench: vacuous-F1 hole closed (degenerate test sets fail with
their own criterion); CI-lower-bound test made probative
- mesh_guard: quantization scaled to observed coupling range — >=65-node
balanced meshes no longer permanently at_risk (regression test)
- engine: both wiring tests made probative (same-topology witness compare,
deterministic risk-crossing fixture)
- mat: axum/tokio optional behind api; real serde feature (api enables it)
- core: canonical decoder strict (non-zero reserved bytes and nil UUID
rejected — injective on accepted domain, forged-bytes tests)
- CHANGELOG: un-spliced the FFT/adapter bullet mangle
Co-Authored-By: claude-flow <ruv@ruv.net>
* chore: strip private-track references for public PR
Reword the occupancy-benchmark changelog bullet to drop a cross-reference
to the private research track, and restore the WorldGraph retention bullet
header that was glued onto the preceding MAT bullet.
Co-Authored-By: claude-flow <ruv@ruv.net>
* chore: lockfile refresh for cherry-picked feature set
Co-Authored-By: claude-flow <ruv@ruv.net>
---------
Co-authored-by: Claude <noreply@anthropic.com>
cog-ha-matter required wifi-densepose-sensing-server with the `mqtt`
feature exposed, which crates.io 0.3.0 did not expose. Chain:
1. wifi-densepose-signal 0.3.0 -> 0.3.1 (already includes
EmbeddingHistory::{with_sketch,novelty} locally; needed
republish so sensing-server-0.3.1 can compile against it).
2. wifi-densepose-sensing-server 0.3.0 -> 0.3.1 (now exposes
the `mqtt` feature, sensing-server bin links against
signal-0.3.1 cleanly).
3. cog-ha-matter sensing-server dep bumped to ^0.3.1; publish=false
dropped. cog-ha-matter@0.3.0 published.
Both signal and sensing-server published with --no-verify; cargo's
verification step fails on Windows because openblas-src requires
vcpkg (the source itself builds fine in the workspace and on Linux).
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(edge-registry): ADR-102 — surface Cognitum cog catalog via /api/v1/edge/registry
Adds a new sensing-server endpoint that fetches and caches the canonical
Cognitum app registry at
https://storage.googleapis.com/cognitum-apps/app-registry.json (105 cogs
across 11 categories as of v2.1.0). RuView previously had no live
awareness of the catalog — the README's capability table was hand-
curated and went stale as Cognitum shipped new cogs (the registry was
last updated 6 days ago).
ADR:
* docs/adr/ADR-102-edge-module-registry.md — full design, response
shape, configuration flags, failure modes, and a 12-row security
review covering SSRF, response inflation, ?refresh abuse, stale-serve
semantics, TLS, cache poisoning, JSON-panic resistance, etc.
Code:
* v2/.../edge_registry.rs — EdgeRegistry struct + UreqFetcher +
MockFetcher trait + 7 unit tests. RwLock<Option<CachedEntry>> with
stale-on-error fallback. MAX_PAYLOAD_BYTES=8 MiB, 10s wire timeout.
* v2/.../main.rs — constructs Option<Arc<EdgeRegistry>> at startup,
registers GET /api/v1/edge/registry handler, wires Extension layer.
Handler runs the blocking ureq fetch via tokio::task::spawn_blocking
so the async runtime stays free.
* v2/.../cli.rs / main.rs Args — three new flags (per user request to
"allow the registry to be disabled or changed"):
--edge-registry-url <URL> (env RUVIEW_EDGE_REGISTRY_URL)
--edge-registry-ttl-secs <N> (env RUVIEW_EDGE_REGISTRY_TTL_SECS)
--no-edge-registry (env RUVIEW_NO_EDGE_REGISTRY)
When --no-edge-registry is set or the URL is empty, the endpoint
returns 404.
Cargo.toml: adds ureq (rustls), sha2, thiserror as direct deps.
README:
* New collapsed "🧩 Edge Module Catalog" section with the full 105-cog
table generated from the registry, grouped by category with practical
one-line descriptions (e.g. "Spots irregular heartbeats and abnormal
heart rhythms", "Detects walking problems and scores fall risk").
Links to https://seed.cognitum.one/store and the local appliance
/cogs page. Sits between the HF model section and How It Works.
Tests (7/7 pass):
first_call_hits_upstream_and_caches
ttl_expiry_triggers_refetch
force_refresh_bypasses_fresh_cache
stale_serve_on_upstream_failure_after_cached_success
no_cache_no_upstream_returns_error
upstream_invalid_json_is_treated_as_error
upstream_sha256_is_deterministic
Security highlights (full review in ADR-102 §"Security review"):
- The registry is metadata-only; per-cog binary signatures (ADR-100)
remain the trust root for installs. A compromised registry can
mislead a human reader but cannot ship malicious binaries.
- 8 MiB cap + 10s timeout + Option<Arc<...>> via Extension layer means
the endpoint can't be used to exhaust memory or pin tokio threads.
- Stale-on-error responses carry an explicit `stale: true` field so
upstream outages are visible to consumers rather than silently
masked.
- Endpoint sits behind the existing RUVIEW_API_TOKEN bearer gate when
set, otherwise unauthenticated (registry contents are public anyway).
* chore: refresh Cargo.lock for ureq/sha2/thiserror deps added by ADR-102
Closes#520, #514, #443.
## #520 / #514 — stale Docker image, missing UI assets
`ruvnet/wifi-densepose:latest` was published before `ui/observatory*` and
`ui/pose-fusion*` were added; users see /app/ui missing those files and the
v0.6+ packet format doesn't reach the server. Two fixes:
1. `docker/Dockerfile.rust` now `RUN`s a build-time guard after `COPY ui/`
that fails the build if `index.html` / `observatory.html` / `pose-fusion.html`
/ `viz.html` (or the `observatory/` / `pose-fusion/` / `components/` /
`services/` directories) are missing, plus an exec-bit check on
`/app/sensing-server`. A stale image can never be silently produced again.
2. New `.github/workflows/sensing-server-docker.yml` rebuilds + pushes on
every change to the Dockerfile, the server crate, the signal/vitals/
wifiscan crates, the workspace manifests, the `ui/` tree, or itself —
plus `v*` tags and manual dispatch. Pushes to both `docker.io/ruvnet/
wifi-densepose` AND `ghcr.io/ruvnet/wifi-densepose` with `latest` +
`vX.Y.Z` + `sha-<short>` tags, then post-push smoke-tests the artifact:
/health, /api/v1/info, the observatory + pose-fusion HTML, AND the
bearer-auth path (no token → 401, wrong → 401, correct → 200). Uses the
`DOCKERHUB_USERNAME`/`DOCKERHUB_TOKEN` repo secrets; ghcr.io rides on
the workflow's GITHUB_TOKEN.
## #443 — sensing-server REST API auth model
QE security audit raised that 40+ /api/v1/* routes have no auth layer with
a default `0.0.0.0` bind. New `wifi_densepose_sensing_server::bearer_auth`
module + middleware:
- Env-var-gated: `RUVIEW_API_TOKEN` unset/empty ⇒ middleware is a no-op
(current LAN-mode behaviour preserved — **no default change**); set ⇒
every `/api/v1/*` request must carry `Authorization: Bearer <token>`
or the server returns 401.
- Constant-time byte compare via local `ct_eq` (no new dep).
- `/health*`, `/ws/sensing`, and `/ui/*` are intentionally never gated
(orchestrator probes + local browsers).
- Startup logs which mode is active and warns when auth is ON with a
`0.0.0.0` bind.
- 8 unit tests on the middleware via `tower::ServiceExt::oneshot`
(sensing-server lib tests 191 → 199, 0 failures).
Verified locally: `cargo build --workspace --no-default-features` ✓,
`cargo test -p wifi-densepose-sensing-server --no-default-features` ✓.
Co-Authored-By: claude-flow <ruv@ruv.net>
The Rust port lived two directories deep (rust-port/wifi-densepose-rs/)
without any sibling under rust-port/ that warranted the extra level.
Move the whole workspace up to v2/ to match v1/ (Python) at the same
depth and shorten every cd / build command across the repo.
git mv preserves history for all tracked files. 60 files updated for
path references (CI workflows, ADRs, docs, scripts, READMEs, internal
.claude-flow state). Two manual fixes for relative-cd paths in
CLAUDE.md and ADR-043 that became wrong after the depth change
(cd ../.. → cd ..).
Validated:
- cargo check --workspace --no-default-features → clean (after target/
nuke; the gitignored target/ was carried by the OS rename and had
hard-coded old paths in build scripts)
- cargo test --workspace --no-default-features → 1,539 passed, 0 failed,
8 ignored (same totals as pre-rename)
- ESP32-S3 on COM7 → still streaming live CSI (cb #40300, RSSI -64 dBm)
After-merge follow-up: contributors should `rm -rf v2/target` once and
let cargo regenerate from the new path.