ADR-081: Layer 3 mesh plane + Rust mirror trait — all 5 layers landed

Fully implements the remaining deferred pieces of the adaptive CSI mesh
firmware kernel. All 5 layers (Radio Abstraction, Adaptive Controller,
Mesh Sensing Plane, On-device Feature Extraction, Rust handoff) are
now implemented and host-tested end-to-end.

Layer 3 — Mesh Sensing Plane (firmware/esp32-csi-node/main/rv_mesh.{h,c}):
  * 4 node roles: Unassigned / Anchor / Observer / FusionRelay / Coordinator
  * 7 message types: TIME_SYNC, ROLE_ASSIGN, CHANNEL_PLAN,
    CALIBRATION_START, FEATURE_DELTA, HEALTH, ANOMALY_ALERT
  * 3 auth classes: None / HMAC-SHA256-session / Ed25519-batch
  * Payload types: rv_node_status_t (28 B), rv_anomaly_alert_t (28 B),
    rv_time_sync_t (16 B), rv_role_assign_t (16 B),
    rv_channel_plan_t (24 B), rv_calibration_start_t (20 B)
  * 16-byte envelope + payload + IEEE CRC32 trailer
  * Pure rv_mesh_encode()/rv_mesh_decode() plus typed convenience encoders
  * rv_mesh_send_health() + rv_mesh_send_anomaly() helpers

Controller wiring (adaptive_controller.c):
  * Slow loop (30 s default) now emits HEALTH
  * apply_decision() emits ANOMALY_ALERT on transitions to ALERT /
    DEGRADED
  * Role + mesh epoch tracked in module state; epoch bumps on role
    change

Layer 5 — Rust mirror (crates/wifi-densepose-hardware/src/radio_ops.rs):
  * RadioOps trait mirrors rv_radio_ops_t vtable
  * MockRadio backend for offline tests
  * MeshHeader / NodeStatus / AnomalyAlert types mirror rv_mesh.h
  * Byte-identical IEEE CRC32 (poly 0xEDB88320) verified against
    firmware test vectors (0xCBF43926 for "123456789")
  * decode_mesh / decode_node_status / decode_anomaly_alert / encode_health
  * 8 unit tests, including mesh_constants_match_firmware which asserts
    MESH_MAGIC/VERSION/HEADER_SIZE/MAX_PAYLOAD match rv_mesh.h
    byte-for-byte
  * Exported from lib.rs
  * signal/ruvector/train/mat crates untouched — satisfies ADR-081
    portability acceptance test

Tests (all passing):
  test_adaptive_controller:   18/18   (C, decide() 3.2 ns/call)
  test_rv_feature_state:      15/15   (C, CRC32 87 MB/s)
  test_rv_mesh:               27/27   (C, roundtrip 1.0 µs)
  radio_ops::tests (Rust):     8/8
  --- total:                 68/68 assertions green ---

Docs:
  * ADR-081 status flipped to Accepted
  * Implementation-status matrix updated; L3 + Rust mirror both
    marked Implemented
  * Benchmarks table extended with rv_mesh encode+decode roundtrip
  * Verification section updated with cargo test invocation
  * CHANGELOG: two new entries for L3 mesh plane + Rust mirror

Remaining follow-ups (Phase 3.5 polish, not blocking):
  * Mesh RX path (UDP listener + dispatch) on the firmware
  * Ed25519 signing for CHANNEL_PLAN / CALIBRATION_START
  * Hardware validation on COM7
This commit is contained in:
Claude 2026-04-19 03:57:18 +00:00
parent d53e29506e
commit 8dfb031cb3
No known key found for this signature in database
12 changed files with 1633 additions and 39 deletions

View file

@ -2,7 +2,7 @@
| Field | Value |
|-------------|-----------------------------------------------------------------------|
| **Status** | Accepted (partial — Layers 1/2/4 landed; L3 mesh plane and Rust trait tracked in Phase 3/4) |
| **Status** | Accepted — Layers 1/2/3/4/5 implemented and host-tested; mesh RX path and Ed25519 signing tracked as Phase 3.5 polish |
| **Date** | 2026-04-19 |
| **Authors** | ruv |
| **Depends** | ADR-018, ADR-028, ADR-029, ADR-031, ADR-032, ADR-039, ADR-066, ADR-073 |
@ -331,16 +331,20 @@ Artifacts delivered alongside the ADR:
| L1 Mock (QEMU) binding | `firmware/esp32-csi-node/main/rv_radio_ops_mock.c` | Implemented |
| L2 Controller FreeRTOS plumbing | `firmware/esp32-csi-node/main/adaptive_controller.c` | Implemented |
| L2 Pure decision policy (testable) | `firmware/esp32-csi-node/main/adaptive_controller_decide.c` | Implemented |
| L3 Mesh-plane types + encoder/decoder | `firmware/esp32-csi-node/main/rv_mesh.{h,c}` | Implemented |
| L3 HEALTH emit (slow loop, 30 s) | `adaptive_controller.c:slow_loop_cb()` | Implemented |
| L3 ANOMALY_ALERT on state transition | `adaptive_controller.c:apply_decision()` | Implemented |
| L3 Role tracking + epoch monotonicity | `adaptive_controller.c` (`s_role`, `s_mesh_epoch`) | Implemented |
| L4 Feature state packet + helpers | `firmware/esp32-csi-node/main/rv_feature_state.{h,c}` | Implemented |
| L4 Emitter from fast loop (5 Hz) | `adaptive_controller.c:emit_feature_state()` | Implemented |
| L1 Packet yield + send-fail accessors | `csi_collector.c:csi_collector_get_pkt_yield_per_sec()` + send fail | Implemented |
| Host unit tests (18 + 15 assertions) | `firmware/esp32-csi-node/tests/host/` | Passing |
| L5 Rust mirror trait + mesh decoder | `crates/wifi-densepose-hardware/src/radio_ops.rs` | Implemented |
| Host C unit tests (60 assertions) | `firmware/esp32-csi-node/tests/host/` | **60/60 ✓** |
| Rust unit tests (8 assertions) | `crates/wifi-densepose-hardware` (`radio_ops::tests`) | **8/8 ✓** |
| QEMU validator hooks (3 new checks) | `scripts/validate_qemu_output.py` (check 17/18/19) | Passing |
| L3 mesh-plane message types | — | Deferred |
| L3 role-assignment FSM | — | Deferred |
| Rust-side mirror trait | `crates/wifi-densepose-hardware/src/radio_ops.rs` | Deferred |
Deferred items remain in the Roadmap table below (Phase 3 / Phase 4).
| L3 mesh RX path (receive + dispatch) | — | Phase 3.5 |
| Ed25519 signing for CHANNEL_PLAN etc. | — | Phase 3.5 |
| Hardware validation on COM7 | — | Pending |
## Measured performance
@ -350,11 +354,12 @@ a modern CPU; on-target ESP32-S3 Xtensa LX7 at 240 MHz is ~510×
slower for bit-by-bit CRC and broadly comparable for the decide
function after inlining.
| Operation | Cost per call | Notes |
|--------------------------------------------|---------------------|-------------------------------------|
| `adaptive_controller_decide()` | **3.2 ns** (host) | O(1) policy, 9 branches evaluated |
| `rv_feature_state_crc32()` (56 B hashed) | **614 ns** (host) | 87 MB/s — bit-by-bit IEEE CRC32 |
| `rv_feature_state_finalize()` (full) | **616 ns** (host) | CRC-dominated |
| Operation | Cost per call | Notes |
|---------------------------------------------|---------------------|-------------------------------------|
| `adaptive_controller_decide()` | **3.2 ns** (host) | O(1) policy, 9 branches evaluated |
| `rv_feature_state_crc32()` (56 B hashed) | **612 ns** (host) | 87 MB/s — bit-by-bit IEEE CRC32 |
| `rv_feature_state_finalize()` (full) | **592 ns** (host) | CRC-dominated |
| `rv_mesh_encode_health()` + `_decode()` | **1010 ns** (host) | Full roundtrip, hdr+payload+CRC |
Projected on-target cost at 5 Hz cadence:
@ -385,11 +390,21 @@ Projected on-target cost at 5 Hz cadence:
**Tests:**
| Suite | Assertions | Result |
|-------------------------------------|-----------:|------------|
| `test_adaptive_controller` | 18 | **PASS** |
| `test_rv_feature_state` | 15 | **PASS** |
| QEMU validator (`ADR-061` pipeline) | +3 checks | hooked |
| Suite | Assertions | Result |
|---------------------------------------------|-----------:|------------|
| `test_adaptive_controller` (host C) | 18 | **PASS** |
| `test_rv_feature_state` (host C) | 15 | **PASS** |
| `test_rv_mesh` (host C) | 27 | **PASS** |
| `radio_ops::tests` (Rust) | 8 | **PASS** |
| **Total** | **68** | **68/68** |
| QEMU validator (`ADR-061` pipeline) | +3 checks | hooked |
Cross-language parity: the Rust `crc32_ieee()` is verified against the
same known vectors used by the C test (`0xCBF43926` for `"123456789"`,
`0xD202EF8D` for a single zero byte), and the `mesh_constants_match_firmware`
test asserts `MESH_MAGIC`, `MESH_VERSION`, `MESH_HEADER_SIZE`, and
`MESH_MAX_PAYLOAD` match the C header byte-for-byte. Any drift between
the two implementations fails CI.
## New components this ADR authorizes
@ -458,11 +473,19 @@ Projected on-target cost at 5 Hz cadence:
## Verification
```bash
# Host-side unit tests (no ESP-IDF, no QEMU required)
# Host-side C unit tests (no ESP-IDF, no QEMU required)
cd firmware/esp32-csi-node/tests/host
make check
# → test_adaptive_controller: 18/18 pass, decide() = 3.2 ns/call
# → test_rv_feature_state: 15/15 pass, CRC32(56 B) = 614 ns/pkt
# → test_rv_feature_state: 15/15 pass, CRC32(56 B) = 612 ns/pkt
# → test_rv_mesh: 27/27 pass, HEALTH roundtrip = 1.0 µs
# Rust-side radio_ops trait + mesh decoder tests
cd rust-port/wifi-densepose-rs
cargo test -p wifi-densepose-hardware --no-default-features --lib radio_ops
# → 8 passed; verifies MockRadio, CRC32 parity with firmware vectors,
# HEALTH encode/decode roundtrip, bad-magic/short/CRC rejection,
# and that MESH_MAGIC/VERSION/HEADER_SIZE match rv_mesh.h
# QEMU end-to-end (requires ESP-IDF + qemu-system-xtensa, see ADR-061)
bash scripts/qemu-esp32s3-test.sh
@ -470,7 +493,7 @@ bash scripts/qemu-esp32s3-test.sh
# adaptive_ctrl boot line, rv_radio_mock binding registration, and
# slow-loop heartbeat.
# Full workspace (Rust) — unchanged, ADR-081 introduces no Rust changes
# Full workspace
cargo test --workspace --no-default-features
```