Files
ruv 3620abff12 fix(toolkit): integrate capture and device corrections for 0.9.2
Integrate reviewed source PRs #2201-#2223, #2225-#2231, #2235-#2240, #2242 and #2243 (firmware README hunks remain in #2247). Regenerate one package manifest after integration, validate native Windows test execution, bound MTK listener startup, support official C6 release bundles, and retain stronger release metadata gates.
2026-10-12 11:49:52 -04:00

376 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `npx @ruvnet/ruview` — RuView WiFi-sensing operator harness
An AI agent harness that knows how to operate **RuView** (WiFi-DensePose): onboard a
newcomer, provision an ESP32 CSI node, calibrate a room, train pose models, and —
crucially — **refuse to overstate accuracy**. Minted from the RuView monorepo via
[`metaharness`](https://www.npmjs.com/package/metaharness) and hardened per **ADR-182**.
WiFi sensing infers *coarse* pose/presence/breathing from Channel State Information.
It is **not a camera**. Every accuracy number this harness emits must be MEASURED
against a baseline — that rule is enforced in code (`ruview_claim_check`).
## Quick start
```bash
npx @ruvnet/ruview # onboard — pick a setup path
npx @ruvnet/ruview claim-check --file REPORT.md # the honesty guardrail (non-zero exit on untagged claims)
npx @ruvnet/ruview verify # run the deterministic proof (VERDICT: PASS)
npx @ruvnet/ruview doctor # structured diagnostics with fixes (ADR-372)
npx @ruvnet/ruview ports # find the ESP32 serial port
npx @ruvnet/ruview flash --port COM7 --bundle ./esp32-csi-node-s3-8mb # plan only
npx @ruvnet/ruview train-plan --mode pose-smoke
npx @ruvnet/ruview train-gate --file eval-report.json
npx @ruvnet/ruview devices # ESP32 / mmWave / LiDAR on this machine
npx @ruvnet/ruview hosts run --host lab-pi --tool ruview_devices_scan
npx @ruvnet/ruview guidance --topic homecore --query "Wasmtime plugins"
npx @ruvnet/ruview spaces --resource spaces
npx @ruvnet/ruview spaces --resource events --limit 25
npx @ruvnet/ruview --help
```
The operator tools are pure Node and the published package has no runtime
dependencies (ADR-263 O3). MetaHarness, Darwin and Flywheel are exact-pinned
development dependencies used only for scoring, evolution proposals and
replay verification.
## Tools (`ruview_*`)
Exposed both as CLI verbs and as an MCP server (`npx @ruvnet/ruview mcp start`):
| Tool | What it does |
|------|--------------|
| `ruview_onboard` | Pick docker-demo / repo-build / live-esp32; print the next command |
| `ruview_claim_check` | Lint text for untagged / overstated accuracy claims (guardrail) |
| `ruview_verify` | Run `verify.py` deterministic proof → VERDICT |
| `ruview_node_monitor` | Assert CSI is flowing on an ESP32 (read-only) |
| `ruview_calibrate` | ADR-151 room pipeline (baseline→enroll→train-room→room-watch) |
| `ruview_node_flash` | Cross-platform esptool flash with checksum + chip preflight and boot-log evidence (mutating, guarded) |
| `ruview_firmware_plan` | Verify a firmware bundle and return the exact esptool command (read-only) |
| `ruview_firmware_ports` | Enumerate serial ports via pyserial (read-only) |
| `ruview_doctor` | Structured diagnostics: runtime, integrity, hosts, repo, Rust, Python/esptool, bundles, serial, kernel |
| `ruview_train` | Run pose-smoke / pose / room training from the checkout (writes checkpoints, guarded) |
| `ruview_train_plan` | Resolve a training command without running it (read-only) |
| `ruview_train_gate` | Mean-pose-baseline + leakage gate that returns the only publishable claim wording |
| `ruview_devices_scan` / `ruview_esp32_capture` / `ruview_mmwave_read` / `ruview_lidar_read` | Host device access: USB discovery, ESP32 UDP stream, 60/24 GHz mmWave, RPLIDAR/iPhone LiDAR (`device-access` grant) |
| `ruview_host_list` / `ruview_host_run` | SSH remote hosts, read-only tools only (`remote-host` grant) |
| `ruview_guidance` | Source-cited code map, capability maturity, validation commands, and limitations |
| `ruview_spaces_list` | OAuth-only paging for sites/buildings/floors/spaces/zones/entities/events/alerts (guarded over MCP) |
| `ruview_memory_search` | Search the reviewed, source-cited contributor brain |
| `ruview_kernel_selftest` | SYNTHETIC self-test of the optional `@ruvnet/ruview-kernel` WASM/napi-rs compute package (ADR-368) |
Every tool is **fail-closed**: missing repo / python / binary / port → an honest
negative, never a fabricated success.
### MCP authority model
Read-only tools need no grant. Over MCP, `ruview_calibrate` and `ruview_train`
need the `workspace-write` grant plus `confirm: true`; `ruview_node_flash`
needs `hardware-write` plus `confirm: true`; `ruview_spaces_list` needs
`credential-use`. The plan tools (`ruview_firmware_plan`, `ruview_train_plan`)
cannot take `confirm`, so a read-only client can review exactly what would run.
The MCP doctor cannot probe a board or contact a network host; those are
CLI/SDK options (`--probe`, `--url`).
## Device and host access (ADR-373, ADR-374)
Run these on the machine the hardware is plugged into. A cloud agent cannot
reach your USB ports; use a local session or an SSH host.
```bash
npx @ruvnet/ruview devices # classify USB serial devices by VID:PID
npx @ruvnet/ruview esp32 --seconds 10 # ESP32 / Realtek RAC1 node UDP stream (default :5005)
npx @ruvnet/ruview esp32 --seconds 45 --analyze # ...and run live CSI through @ruvnet/ruview-kernel
npx @ruvnet/ruview monitor --port COM10 --baud 1500000 # serial console without resetting the node
npx @ruvnet/ruview mmwave --port /dev/ttyUSB0 # MR60BHA2 60 GHz / LD2410 24 GHz, auto-detected
npx @ruvnet/ruview lidar --source rplidar --port /dev/ttyUSB1 # RPLIDAR scan summary
RUVIEW_LIDAR_TOKEN=… npx @ruvnet/ruview lidar --source iphone --url ws://HOST:8787/ws/lidar
```
| Tool | Access | MCP grant |
|---|---|---|
| `ruview_devices_scan` | pyserial port list + VID:PID roles (opens no port) | `device-access` |
| `ruview_esp32_capture` | receive-only UDP; CSI/vitals/feature packets per node, loss, RSSI | `device-access` |
| `ruview_mmwave_read` | serial; firmware-identical MR60BHA2/LD2410 parsers | `device-access` |
| `ruview_lidar_read` | RPLIDAR SCAN over serial, or iPhone relay WebSocket (stats only) | `device-access` |
| `ruview_host_list` | configured SSH hosts | none |
| `ruview_host_run` | one read-only tool on an SSH host | `remote-host` + the tool's grant |
Remote hosts live in `~/.config/ruview/hosts.json` (mode 0600) and are added
only from the CLI:
```bash
npx @ruvnet/ruview hosts add --name lab-pi --ssh ruv@lab-pi.local
ssh ruv@lab-pi.local true # pin the host key once; BatchMode + StrictHostKeyChecking are enforced
npx @ruvnet/ruview hosts run --host lab-pi --tool ruview_esp32_capture --args-json '{"seconds":10}'
```
The remote side runs `npx -y @ruvnet/ruview@<this version> call <tool>
--read-only`. Every argument is validated against the tool schema on both
ends and single-quoted for the remote shell. Flash, train, calibrate,
credentialed reads, and host hopping are never forwarded.
## Firmware flashing (ADR-370)
Works on Windows, macOS, and Linux through `python -m esptool` (`pip install
esptool pyserial`). The bundle directory is an extracted release flash bundle,
`firmware/esp32-csi-node/release_bins/<variant>`, or an ESP-IDF `build/`.
```bash
npx @ruvnet/ruview ports
npx @ruvnet/ruview flash-plan --port /dev/ttyUSB0 --bundle ./bundle --variant s3-8mb
npx @ruvnet/ruview flash --port /dev/ttyUSB0 --bundle ./bundle --variant s3-8mb --confirm
```
Before writing, every image is checked against `SHA256SUMS(.txt)` (a mismatch
is always refused), and `esptool chip_id` must report the chip the variant
targets. After writing, a 15 s serial capture is summarized. The result sets
`hardwareValidated: true` only when that log shows CSI callbacks without a
panic. A successful write alone is not hardware validation. WiFi provisioning
stays with `provision.py` (see the `provision-node` skill) so credentials never
pass through the harness.
## Training (ADR-371)
```bash
npx @ruvnet/ruview train-plan --mode pose-smoke # review the command
npx @ruvnet/ruview train --mode pose-smoke --confirm # SYNTHETIC smoke run (needs libtorch)
npx @ruvnet/ruview train --mode pose --data-dir data/mmfi --confirm
npx @ruvnet/ruview train --mode room --enrollment enrollment.json --confirm
npx @ruvnet/ruview train-gate --file eval-report.json # PASS/FAIL + publishable wording
```
Pose training builds `wifi-densepose-train` with `tch-backend`. It needs
libtorch: tch 0.24 expects torch 2.11.0, via `LIBTORCH` or
`LIBTORCH_USE_PYTORCH=1`. Checkpoints and trainer logs go under
`v2/target/ruview-train/<mode>` unless `--checkpoint-dir` names another path
inside the checkout. The gate refuses any score that has no mean-pose baseline,
uses a random-frame split, shares subjects across a grouped-subject split,
overlaps in time, or fails to beat the baseline.
## Debugging doctor (ADR-372)
```bash
npx @ruvnet/ruview doctor # all groups, human output
npx @ruvnet/ruview doctor --json --group python,serial,firmware
npx @ruvnet/ruview doctor --port COM7 --probe # esptool chip_id (resets the board, writes nothing)
npx @ruvnet/ruview doctor --url http://localhost:3000 # sensing-server /health
```
Each check is `pass`, `warn`, `fail`, or `skip`, and every non-passing check
carries a `fix:`. The exit code is non-zero only for failures.
## SDK (ADR-369)
```js
import { createRuView } from '@ruvnet/ruview/sdk'; // typed: src/sdk.d.ts
const ruview = createRuView(); // { strict: true } throws RuViewError on ok:false
const report = await ruview.doctor({ groups: ['python', 'serial'] });
const plan = await ruview.firmware.plan({ port: 'COM7', bundle: './bundle', variant: 'c6' });
const flashed = await ruview.firmware.flash({ port: 'COM7', bundle: './bundle', variant: 'c6', confirm: true });
const gate = await ruview.training.gate({ model_score: 0.595, baseline_score: 0.501, split: 'chronological',
train_end: '2026-03-01', test_start: '2026-03-02', n_test: 800, reproducer: 'python eval.py' });
const kernel = await ruview.kernel.load(); // optional @ruvnet/ruview-kernel
const radar = await ruview.devices.mmwave({ port: '/dev/ttyUSB0', model: 'auto' });
const nodes = await ruview.devices.esp32({ seconds: 10 });
const remote = await ruview.hosts.run('lab-pi', 'ruview_lidar_read', { source: 'rplidar', port: '/dev/ttyUSB1' });
```
The SDK calls the same policy-checked registry as the CLI and MCP server.
Mutating calls still need `confirm: true`.
### Cognitum Spaces OAuth
Activate the additional read scope through the Rust CLI, then use the same
validated client through the metaharness:
```bash
wifi-densepose login --spaces
wifi-densepose whoami
npx @ruvnet/ruview spaces
npx @ruvnet/ruview spaces --resource sites --limit 50
npx @ruvnet/ruview spaces --resource events --cursor '<opaque-next-cursor>'
```
The metaharness never accepts a bearer token or API key and removes
`COGNITUM_SPACES_API` from the child environment, so this surface cannot
silently fall back to the compatibility API-key path. The API origin is fixed
to `https://api.cognitum.one`, and the credentialed adapter requires an
installed `wifi-densepose` binary rather than running Cargo build scripts from
an auto-detected checkout. It returns only the bounded P2/P3 semantic
projection. `--resource` selects one of `sites`, `buildings`, `floors`,
`spaces`, `zones`, `entities`, `events`, or `alerts`; `--limit` is 1–100 and
`--cursor` is the opaque value from the prior page. An empty list is a valid
authenticated result, not sensing-quality evidence. An expired session may
rotate the stored refresh credential before the read completes.
MCP use is denied unless the server operator starts it with
`RUVIEW_MCP_GRANTS=credential-use`. Set `RUVIEW_CREDENTIALS_PATH` in the MCP
server environment when a non-default store is needed; MCP calls cannot choose
an arbitrary credential file or URL. `spaces:read` grants no write, pairing,
command, policy-approval, spending, or actuator authority. See the bundled
`cognitum-spaces` skill for the full playbook.
### Codebase guidance
`ruview_guidance` is the read-only starting point for unfamiliar work. Filter
by `architecture`, `sensing`, `hardware`, `training`, `homecore`,
`integrations`, `deployment`, `community`, or `testing`, and optionally add a
free-text query:
```bash
npx @ruvnet/ruview guidance --topic sensing --query "UDP CSI ingestion"
npx @ruvnet/ruview guidance --topic homecore --query "restore migration voice"
```
Each result separates implementation maturity from evidence, cites current
repository paths, names focused validation commands, and states known
limitations. In a RuView checkout, cited paths are checked before the result
passes. Outside a checkout, the tool labels them as a reviewed packaged
catalog. Related shared-brain records are bounded, reviewed, and treated only
as evidence.
## Skills
Host-neutral playbooks in `skills/` (`onboard`, `provision-node`, `calibrate-room`,
`train-pose`, `verify`, `cognitum-spaces`). `npx @ruvnet/ruview skill <name>`
prints one.
## Use as a Claude Code MCP server
The bundled `.claude/settings.json` registers the `ruview` MCP server
(`npx -y @ruvnet/ruview mcp start`). Drop this package's `.claude/` into a repo, or run
`npx @ruvnet/ruview install --host claude-code`.
## Claude Code mod: live sensing pane (ADR-377)
This package ships **`ruview-live`**, a Claude Code mod: a plugin whose behaviour is a function-hooks module. `/ruview` opens a pane beside the transcript, refreshed on a timer, with:
- your CSI nodes (ESP32, Realtek): rate, loss, RSSI and CSI shape;
- an optional ESPHome radar kit: presence, distance, and device-reported heart/breathing;
- alerts, such as a board that sends heartbeats but no CSI.
It also keeps a one-line status: `RuView · 2 nodes · radar present · 1 alert`.
**Showcase views (ADR-378).** The pane has three views, switched with keys `1`, `2` and `3`. The two live views are drawn in terminal cells (24-bit colour) and animated in place:
| Key | View | What you see |
|---|---|---|
| `1` | Overview | the cards above |
| `2` | CSI waterfall | per-subcarrier amplitude over time (`esp32 --spectrum`), replayed at the frames' arrival rate and labelled **MEASURED** or **SYNTHETIC** from the packets' own flag; `n` cycles nodes |
| `3` | Radar | a 120° range fan with a sonar ping out to the measured distance; heart and breathing charts, and a ♥ and breathing gauge that pulse at the device-reported rates (a metronome, not a waveform) |
The radar kit reports range, not bearing, so the arc covers every bearing at that range. Desktop and IDE surfaces show the overview, and a note in the live views.
```bash
npx @ruvnet/ruview mod # where the mod is in this install, and how to load it
claude --plugin-dir "<path printed above>" # try it for one session
# or from the RuView marketplace:
# /plugin marketplace add ruvnet/RuView
# /plugin install ruview-live@ruview
```
In Claude Code:
| Command | What it does |
|---|---|
| `/ruview` | Open or close the pane |
| `/ruview refresh` | Run one capture now; the result goes in the status line |
| `/ruview off` | Close the pane |
| `/ruview waterfall`, `/ruview radar` | Open the pane on that view |
**Settings** (`claude plugin configure ruview-live`):
- `udpPort` (default 5005);
- `radarHost`, an ESPHome kit on your private network;
- `refreshSeconds` (default 15, minimum 5);
- `liveRefreshSeconds` for the waterfall and radar views (default 4, range 2–60);
- `captureSeconds` (default 3).
**How it reads:**
- The mod only runs this package's CLI, for read-only `esp32` and `mmwave --source esphome` captures, with `--json`, so the pane shows exactly what those tools return.
- It never flashes, provisions or writes to a device.
- Like every mod, it runs inside Claude Code with Claude Code's access, so install it only from a source you trust.
Mods are early access in Claude Code. If `/ruview` is missing, start Claude Code with `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1`.
## ChatGPT and MCP Apps (ADR-375)
`ruview_esp32_capture`, `ruview_devices_scan` and `ruview_doctor` render in a
self-contained console widget (`ui://ruview/console-v1.html`) in ChatGPT and
other MCP Apps hosts, with a Refresh button that re-runs the tool. Results also
carry `structuredContent`.
```bash
# MCP over HTTP: loopback, token-authenticated, read tools only
export RUVIEW_MCP_TOKEN="$(node -e "console.log(require('crypto').randomBytes(24).toString('base64url'))")"
RUVIEW_MCP_GRANTS=device-access npx @ruvnet/ruview mcp start --http --port 8790
```
Clients that can send headers use `Authorization: Bearer <token>` on
`http://127.0.0.1:8790/mcp`. ChatGPT connectors cannot, so expose the port over
HTTPS (a tunnel or reverse proxy) and add `https://<host>/mcp/<token>` as the
connector URL. Write grants (`hardware-write`, `workspace-write`) are never
honoured over HTTP: flashing and calibration stay on the CLI and stdio. Malformed request targets return HTTP 400
with `invalid_request_target`; the transport continues serving subsequent requests.
In a terminal, commands print a formatted view; pipes and `--json` print JSON.
`ruview esp32 --watch` redraws live capture windows with per-node trends.
## Hosts
Claude Code and Codex are implemented directly and tested with the local,
non-interactive CLIs:
```bash
npx @ruvnet/ruview agent run --host claude-code --repo . --prompt "Map the sensing-server startup path"
npx @ruvnet/ruview agent run --host codex --repo . --prompt "Find the nearest tests for HomeCore restore state"
```
Prompts travel over stdin, never through a shell. Both adapters are read-only by
default (`claude -p --safe-mode` in plan mode; `codex exec` in its read-only
sandbox with user config and exec rules ignored), use a scrubbed environment,
bound output/time, redact secrets, and require a trusted RuView checkout.
Workspace writes require both `--allow-write` and `--confirm`; dangerous bypass
flags are never emitted.
## Shared contributor brain
The committed `brain/corpus/core.jsonl` is a small, reviewable source of
repository facts. Every record has a source citation, evidence tier, tags, and
review state:
```bash
npx @ruvnet/ruview brain search --query "darwin community memory"
npx @ruvnet/ruview brain verify --repo .
npx @ruvnet/ruview brain propose --id finding-id --title "Finding" \
--content "Source-bound observation" --sourcePath README.md --sourceLine 1 \
--tags onboarding,docs --contributor github-user
```
Proposals are unreviewed JSONL for a normal pull request. Local vector indexes,
private overlays, raw agent transcripts, CSI/person data, and credentials are
never part of the shared corpus. Retrieved text is quoted evidence, not an
instruction or authority grant.
## Ruflo + Darwin/Flywheel
Development tooling is exact-pinned in `devDependencies`: `metaharness@0.4.1`,
`@metaharness/darwin@0.8.0`, and `@metaharness/flywheel@0.1.7`. Ruflo remains an
optional contributor coordinator rather than cold-start weight for the
dependency-free published MCP server:
```bash
claude mcp add --scope project ruflo -- npx -y ruflo@3.32.26 mcp start
codex mcp add ruflo -- npx -y ruflo@3.32.26 mcp start
```
`npm run flywheel:plan` is read-only. Darwin execution is human-triggered with
`node flywheel/run.mjs --confirm`; it writes only an untrusted
`.metaharness/` proposal archive. The protected gate requires frozen-anchor
retention, holdout lift, security and legacy-test success, verified provenance,
and human approval. No contributor run can directly replace or publish the
champion.
## License
MIT © ruvnet