feat(ADR-262 P3): live /api/field + /ws/field — RuView sensing speaks RuField (fail-closed egress) (#1071)

* 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>
This commit is contained in:
rUv
2026-06-14 13:55:41 -04:00
committed by GitHub
parent f250149e94
commit df617145d6
31 changed files with 915 additions and 9 deletions
@@ -33,6 +33,8 @@ jobs:
working-directory: v2
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Rust toolchain
run: rustup show && rustc --version
@@ -53,6 +53,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
+6
View File
@@ -42,6 +42,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Determine deployment environment
id: determine-env
@@ -86,6 +88,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up kubectl
uses: azure/setup-kubectl@v3
@@ -132,6 +136,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up kubectl
uses: azure/setup-kubectl@v3
+11
View File
@@ -29,6 +29,7 @@ jobs:
continue-on-error: true
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Set up Python
@@ -82,6 +83,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
# ADR-262 P1: `wifi-densepose-rufield` path-deps the `vendor/rufield`
# submodule. Without a recursive checkout the workspace build fails to
# resolve those path deps in CI even though it passes locally.
@@ -207,6 +210,8 @@ jobs:
- name: Checkout code
continue-on-error: true
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python ${{ matrix.python-version }}
continue-on-error: true
@@ -272,6 +277,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v6
@@ -340,6 +347,8 @@ jobs:
- name: Checkout code
continue-on-error: true
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Docker Buildx
continue-on-error: true
@@ -412,6 +421,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v6
+2
View File
@@ -35,6 +35,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Fetch /traffic/clones + /traffic/views from GitHub
env:
@@ -28,6 +28,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
@@ -78,6 +80,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
@@ -145,6 +149,8 @@ jobs:
vars.HAS_GCP_CREDENTIALS == 'true'
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Download x86_64 artifact
uses: actions/download-artifact@v4
+2
View File
@@ -20,6 +20,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable
with: { targets: wasm32-unknown-unknown }
+2
View File
@@ -26,6 +26,8 @@ jobs:
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Rust + wasm32 target
uses: dtolnay/rust-toolchain@stable
+6
View File
@@ -28,6 +28,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Node.js
uses: actions/setup-node@v6
@@ -83,6 +85,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Node.js
uses: actions/setup-node@v6
@@ -131,6 +135,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Download all artifacts
uses: actions/download-artifact@v4
+4
View File
@@ -22,6 +22,8 @@ jobs:
if: github.ref_type == 'tag'
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Check firmware version.txt == tag
run: |
# Tag form: vX.Y.Z-esp32 → expect version.txt to contain X.Y.Z
@@ -71,6 +73,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Build firmware (${{ matrix.variant }})
working-directory: firmware/esp32-csi-node
+8
View File
@@ -100,6 +100,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Download QEMU artifact
uses: actions/download-artifact@v4
@@ -214,6 +216,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install clang
run: |
@@ -263,6 +267,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install NVS generator
run: pip install esp-idf-nvs-partition-gen
@@ -317,6 +323,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Download QEMU artifact
uses: actions/download-artifact@v4
@@ -22,6 +22,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-python@v6
with:
+2
View File
@@ -41,6 +41,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install mosquitto + clients and start with allow_anonymous
run: |
@@ -26,6 +26,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: docker/setup-buildx-action@v3
+6
View File
@@ -76,6 +76,8 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
# Linux aarch64 needs QEMU for cross-build on x86_64 runners.
- name: Set up QEMU
@@ -121,6 +123,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install maturin
run: pip install maturin>=1.7
- name: Build sdist
@@ -144,6 +148,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-python@v5
with:
python-version: '3.12'
+2
View File
@@ -29,6 +29,8 @@ jobs:
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
submodules: recursive
- name: Stage viewer for Pages
run: |
+8
View File
@@ -40,6 +40,8 @@ jobs:
- { label: 'full+train', flags: '--features full,train' }
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v4
@@ -60,6 +62,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
# v2/rust-toolchain.toml pins channel "1.89" with profile "minimal" (no
# clippy). dtolnay@stable installs clippy on the floating "stable"
# toolchain, but the override makes cargo use the separate "1.89"
@@ -93,6 +97,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v4
@@ -127,6 +133,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: publish = false is present (no accidental crates.io publish)
run: |
CARGO=v2/crates/ruview-swarm/Cargo.toml
+12
View File
@@ -28,6 +28,7 @@ jobs:
continue-on-error: true
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Set up Python
@@ -97,6 +98,8 @@ jobs:
- name: Checkout code
continue-on-error: true
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python
continue-on-error: true
@@ -164,6 +167,8 @@ jobs:
- name: Checkout code
continue-on-error: true
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Docker Buildx
continue-on-error: true
@@ -245,6 +250,8 @@ jobs:
- name: Checkout code
continue-on-error: true
uses: actions/checkout@v4
with:
submodules: recursive
- name: Run Checkov IaC scan
continue-on-error: true
@@ -307,6 +314,7 @@ jobs:
continue-on-error: true
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Run TruffleHog secret scan
@@ -341,6 +349,8 @@ jobs:
- name: Checkout code
continue-on-error: true
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python
continue-on-error: true
@@ -378,6 +388,8 @@ jobs:
- name: Checkout code
continue-on-error: true
uses: actions/checkout@v4
with:
submodules: recursive
- name: Check security policy files
continue-on-error: true
+2
View File
@@ -30,6 +30,8 @@ jobs:
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
submodules: recursive
- name: Stage demos for Pages
run: |
+2
View File
@@ -30,6 +30,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6