yggvault ratatoskr-space connected via regular web
Color theme
also available via yggdrasil mesh http://[203:b338:2a84:a18f:986:47ae:1a4:d8d3]/pkg/ruview/v1256
vault / ruview / v1256

ruview @ v1256

integrity

size
59.8 MiB
downloaded
last checked
source https://github.com/ruvnet/RuView · available · github

release notes

Automated release from CI pipeline

Changes: feat(adr-117): pip wifi-densepose modernization (PIP-PHOENIX) + ruview sibling release (#786)

Two artifacts landing together on this new branch as the prerequisite documentation for the v2.0.0 Python wheel modernization work:

  1. docs/adr/ADR-117-pip-wifi-densepose-modernization.md (644 lines) — Plan to bring the 2025-published wifi-densepose PyPI package (last release v1.1.0, 2025-06-07, 11.5 months out of sync) up to the current Rust v2/ workspace SOTA. Recommends PyO3 + maturin with abi3-py310 (one binary covers Python 3.10–3.13 per OS/arch), first-wheel scope = core + vitals + signal crates (~5 MB), v1.99.0 tombstone + 90-day un-yank window for v1.1.0, v2.0.0 hard break. Open questions catalogued; phases P1–P6+ laid out with concrete acceptance criteria.

  2. docs/research/soul/ (5 files, ~1,450 lines) — Soul Signature research spec: 7-channel electromagnetic biometric fingerprint (AETHER 128-dim + cardiac HR/HRV + cardiac waveform morphology + respiratory pattern + gait timing + skeletal proportions + subcarrier reflection profile), fused into one RVF graph file. Includes 60s scanning protocol, 5-layer security model, threat-model + mitigations, references to existing ADRs (014, 021, 024, 027, 030, 039, 079, 106, 108, 109, 110, 115). Marked "Research Specification (Pre-Implementation)". Explicit "what this is NOT" disclaimers preempt pseudoscience drift; every discriminative-power claim either cites a measurement or is marked "open research; baseline TBD".

Branch off main at HEAD; ready for /loop 10m implementation iterations.

Co-Authored-By: claude-flow ruv@ruv.net

ADR-117 P1 — the python/ directory is now a working maturin-buildable crate that produces the v2.x replacement for the legacy pure-Python wifi-densepose==1.1.0 PyPI wheel.

What lands

What's deferred to P2+

The new python/ crate is intentionally OUTSIDE the v2/ Cargo workspace — it has its own Cargo.toml with [package] not [workspace.package] inheritance — to keep maturin's python-source

Refs ADR-117 §5 (Detailed design) and §6 (Phased migration). Refs #785 (tracking issue).

Co-Authored-By: claude-flow ruv@ruv.net

Three fixes to make maturin develop actually work locally:

  1. python/Cargo.toml removed *.workspace = true inheritance — the python/ crate is intentionally outside the v2/ workspace (ADR-117 §5.2) so it needs every [package] field local.

  2. python/pyproject.toml python-source = "python" was wrong because pyproject.toml lives at python/ — maturin was looking for python/python/. Changed to python-source = "." so the wifi_densepose/ package directory sibling-to-pyproject is found.

  3. python/src/lib.rs #[pymodule] fn wifi_densepose_native#[pymodule] #[pyo3(name = "_native")] fn wifi_densepose_native. PyO3 generates PyInit__native from the pyo3-name attribute, which must match the module-name in pyproject.toml's [tool.maturin] block ("wifi_densepose._native"). Without this attribute the wheel builds but import wifi_densepose._native fails with ModuleNotFoundError.

Local validation (P1 acceptance gate)

$ python -m venv .venv && .venv/Scripts/python -m pip install maturin pytest
$ VIRTUAL_ENV=… maturin develop --release
…
    Finished `release` profile [optimized] target(s)
📦 Built wheel for abi3 Python ≥ 3.10
🛠 Installed wifi-densepose-2.0.0a1

$ .venv/Scripts/python -c 'import wifi_densepose; print(wifi_densepose.__version__, wifi_densepose.__rust_version__, wifi_densepose.hello())'
2.0.0a1 2.0.0-alpha.1 ok

$ .venv/Scripts/python -m pytest tests/ -v
tests/test_smoke.py::test_package_imports PASSED
tests/test_smoke.py::test_version_string_well_formed PASSED
tests/test_smoke.py::test_rust_version_surfaced PASSED
tests/test_smoke.py::test_build_features_listed PASSED
tests/test_smoke.py::test_hello_returns_ok PASSED
tests/test_smoke.py::test_native_module_private PASSED
======================== 6 passed in 0.05s =========================

P1 closed. Moving to P2 (core type bindings).

Refs #785, ADR-117 §6.

Co-Authored-By: claude-flow ruv@ruv.net

Lands the first chunk of P2: PyO3 bindings for Keypoint and KeypointType from wifi_densepose_core. Bound types surface to Python as wifi_densepose.Keypoint / wifi_densepose.KeypointType.

Design choices that affect the API surface

  1. Confidence is NOT bound as a separate class. Users hate wrapping a float in a constructor. Python-side, confidence is just a float in [0.0, 1.0]; the binding validates on construction (ValueError for out-of-range, matching the Rust core error).

  2. KeypointType is a #[pyclass(eq, eq_int, hash, frozen)] enum — hashable so users can drop it into dicts/sets (the most common pattern in pose-analysis notebooks: keypoints_by_type[k.type] = k).

  3. Keypoint.__init__ keyword-only z so 2D users don't have to write None and 3D users get a clear named arg: Keypoint(KeypointType.LeftWrist, 0.2, 0.4, 0.8, z=0.1).

  4. Keypoint is #[pyclass(frozen)] — no in-place mutation. The Rust core type is immutable through Copy + Hash + Eq, and exposing setters from Python would create a copy-vs-reference inconsistency between languages.

Files

Local validation

``` $ cd python && .venv/Scripts/python -m pytest tests/ -v tests/test_smoke.py::test_package_imports PASSED tests/test_smoke.py::test_version_string_well_formed PASSED tests/test_smoke.py::test_rust_version_surfaced PASSED tests/test_smoke.py::test_build_features_listed PASSED tests/test_smoke.py::test_hello_returns_ok PASSED tests/test_smoke.py::test_native_module_private PASSED tests/test_keypoint.py::test_keypoint_type_all_returns_17 PASSED … ======================== 29 passed in 0.06s ========================= ```

Wheel size after both bindings: still well under the 5 MB ADR §5.4 budget (release build with --strip on Windows: ~340 KB).

Also adds python/.gitignore to prevent the .venv/ + target/ + _native.abi3.pyd artifacts from getting committed.

What's left in P2

CsiFrame + PoseEstimate bindings land in the next iteration. They're larger (CsiFrame has the subcarrier buffer; PoseEstimate has 17×Keypoint + BoundingBox + track_id + score). Pattern is now proven so they go faster.

Refs #785, ADR-117 §6.

Co-Authored-By: claude-flow ruv@ruv.net

Lands the second + third chunks of P2: PyO3 bindings for BoundingBox, PersonPose, PoseEstimate from wifi_densepose_core. Combined with the prior Keypoint + KeypointType bindings (fd0568caa), this closes ADR-117 §6 P2.

Coverage

Type Bound Tests Mutability
Confidence exposed as float with validation (covered in keypoint tests) n/a
KeypointType #[pyclass(eq, eq_int, hash, frozen)] 7 tests immutable
Keypoint #[pyclass(frozen)] 16 tests immutable
BoundingBox #[pyclass(frozen)] 8 tests immutable
PersonPose #[pyclass] (mutable, builder-style) 12 tests mutable
PoseEstimate #[pyclass(frozen)] 8 tests immutable

Smoke (P1) + new tests: 57/57 PASS locally on Windows.

What's deferred to P3

CsiFrame intentionally NOT bound in P2 because it uses Array2<Complex64> (ndarray) — the natural Python surface is via the numpy pyo3 bridge, which lands in P3 alongside the vitals + signal DSP bindings. Binding CsiFrame without numpy interop would force users to materialise lists of tuples which is a worse API than csi_frame.amplitude_array() returning an ndarray.

Design choices that affect the API surface

  1. PersonPose.keypoints() returns a dict keyed by KeypointType instead of a fixed-length list with None slots. Pythonistas don't want to know the underlying storage is [Option<Keypoint>; 17].

  2. PoseEstimate.id and .timestamp exposed as strings (UUID + ISO) rather than as bound FrameId / Timestamp types. Users in notebooks rarely compare UUIDs structurally; strings are good enough for diagnostics and don't bloat the bindings.

  3. PersonPose is MUTABLE (#[pyclass] without frozen) so users can build poses incrementally with set_keypoint/set_bbox/ set_id. PoseEstimate is frozen because once constructed it represents a snapshot.

Three PyO3 0.22 gotchas surfaced this iteration

  1. #[pymethods] getters are NOT accessible from other Rust modules — need a separate impl PyKeypoint { pub(crate) fn inner(&self) -> &Keypoint { ... } } block for cross-module use.

  2. PyDict::new(py) was removed in PyO3 0.21 → 0.22 in favour of PyDict::new_bound(py). (Confusing because Bound<'py, PyDict> is the return type either way.)

  3. dict.set_item(K, V) requires both K and V to impl ToPyObject. #[pyclass] types impl IntoPy<PyObject> but NOT ToPyObject — workaround: convert via .into_py(py) first, then set_item(py_object_k, py_object_v).

Saved as PyO3 0.22 binding patterns memory at the horizon-tracker level so future loop workers don't re-learn them.

Local validation

``` $ cd python && .venv/Scripts/python -m pytest tests/ -v … ======================== 57 passed in 0.24s ========================= ```

Wheel size: still ~340 KB on Windows release build.

Refs #785, ADR-117 §6 (P2 done — ready for P3 vitals + signal DSP + numpy bridge + witness v2).

Co-Authored-By: claude-flow ruv@ruv.net

Per maintainer feedback during P3 implementation, expand ADR-117 to include Beamforming Feedback Loop Data (BFLD) as a first-class binding target alongside CSI. BFLD is the transmitter-side, AP-station-loop view of the WiFi channel (802.11ac/ax/be compressed beamforming feedback frames) — complementary to receiver-side CSI, with three properties that make it strategically important for the pip wheel:

  1. Up to 996 subcarriers per HE160 frame (vs 242 for HE-LTF CSI on ESP32-C6, vs 52 for HT-LTF on ESP32-S3) — much denser per-subcarrier reflection profile
  2. Works on stock 802.11ac+ hardware — no Nexmon patch, no ESP32 monitor mode, no firmware drift. Captured via tcpdump/Wireshark + BFR dissector, or via mac80211 debugfs on Linux 6.10+
  3. Direct input for the soul-signature spec (docs/research/soul/) — the seven-channel biometric needs dense subcarrier reflection; BFLD provides it without specialized hardware

Three additions to ADR-117

§5.7a — New binding-target subsection

Comparison table CSI vs BFLD; binding strategy with forward-compat stub Rust impl pending the future wifi-densepose-bfld crate; the three Python types that ship in P3.5:

§6 P3.5 — Concurrent-with-P3 phase

Checkbox plan for the bindings module + stub Rust storage + numpy bridge for feedback_matrix (Complex64 ndarray, same approach as CsiFrame.amplitude from P3). Lands in the same wheel as P3, no schedule cushion needed.

§11.11/12 — Two new open questions

§12 — BFLD reference list

ADR line count: 644 → 807 (+163). Refs #785 (tracking issue).

The implementation work for P3.5 lands in the next /loop iteration alongside P3 vitals + signal DSP bindings.

Co-Authored-By: claude-flow ruv@ruv.net

P3 — Vital sign extraction bindings (wifi-densepose-vitals):

P3.5 — BFLD bindings (forward-compat surface, stub Rust):

Total Python test count: 93 (was 57, +36 P3+P3.5). All passing.

Refs: docs/adr/ADR-117-pip-wifi-densepose-modernization.md Refs: #785

Co-Authored-By: claude-flow ruv@ruv.net

New sub-package wifi_densepose.client (no PyO3, no Rust deps):

Test coverage (63 new tests, 156 total in Python suite):

Post-bridge wheel size: 238 KB (well under ADR §5.4 5 MB budget).

Refs: docs/adr/ADR-117-pip-wifi-densepose-modernization.md §5.6 Refs: docs/adr/ADR-115-home-assistant-integration.md §3.12 Refs: #785

Co-Authored-By: claude-flow ruv@ruv.net

P5 — .github/workflows/pip-release.yml:

P-tomb — python/tombstone/:

Both wheels are published by the same pip-release.yml workflow:

Per ADR-117 §7.3: tag and publish 1.99.0-pip FIRST so the tombstone claims the "current" slot in pip's resolver, THEN publish 2.0.0-pip.

Test count unchanged in main python/ suite (156/156). Tombstone sub-suite: 5 passing.

Refs: docs/adr/ADR-117-pip-wifi-densepose-modernization.md §5.4, §7 Refs: #785

Co-Authored-By: claude-flow ruv@ruv.net

Benchmarks (python/bench/, pytest-benchmark — opt-in via --benchmark-only):

Hot path Mean Ops/sec % of 100 Hz budget
BfldFrame HT20 1×1×52 800 ns 1.25 Mops 0.008%
BfldFrame HE20 2×1×242 1.3 μs 750 kops 0.013%
BfldFrame HE80 2×1×996 4.2 μs 236 kops 0.042%
BfldFrame HE160 2×2×1992 14 μs 71 kops 0.14%
BfldFrame.feedback_matrix() 2.8 μs 352 kops
WS edge_vitals decode 7.4 μs 134 kops 0.074%
WS pose_data decode (3 persons) 23 μs 42 kops 0.24%
BreathingExtractor.extract() 56sc 28 μs 35 kops 0.28%
BreathingExtractor.extract() 114sc 44 μs 23 kops 0.44%
BreathingExtractor.extract() 242sc 79 μs 13 kops 0.79%
HeartRateExtractor.extract() 56sc 105 μs 9.5 kops 1.05%

All hot paths well under the 100 Hz ESP32 frame budget (10 ms). Worst case (HeartRateExtractor) uses 1% of the budget — no optimization needed. Scaling on n_subcarriers is sub-quadratic (56→242 = 4.3× input, 2.8× time) — catches future O(n²) regressions.

Security & robustness tests (tests/test_security.py, +27 tests):

Test count: 156 → 183 (+27). All passing.

Bench results steady-state confirm no Rust-binding-layer optimization is needed before the v2.0.0 publish.

Refs: docs/adr/ADR-117-pip-wifi-densepose-modernization.md Refs: #785

Co-Authored-By: claude-flow ruv@ruv.net

Wheel rebuilds clean at 253 KB (was 238 KB — +15 KB from the richer README baked into the wheel metadata). Test suite unchanged at 183/183.

Refs: docs/adr/ADR-117-pip-wifi-densepose-modernization.md Refs: #785

Co-Authored-By: claude-flow ruv@ruv.net

Refs: docs/adr/ADR-117-pip-wifi-densepose-modernization.md Refs: #785

Co-Authored-By: claude-flow ruv@ruv.net

First v1.99.0-pip run (26366491748) failed: the runner's system python fell back to --user install, then python -c "import wifi_densepose" resolved to something other than the freshly-installed user-site wheel and returned cleanly instead of raising the tombstone ImportError.

Fixes:

No code changes to the tombstone source itself.

Co-Authored-By: claude-flow ruv@ruv.net

Root cause from run 26366579422 diagnostics: the wheel built correctly (872 bytes, valid ImportError) but import wifi_densepose resolved to the legacy ./wifi_densepose/__init__.py left in the repo root from v1, NOT to the freshly-installed tombstone wheel in the smoke venv.

Python places the cwd at sys.path[0] for python -c "...", so running the import from the repo root made the legacy directory win over site-packages every time. The "isolated venv" was not the problem — the cwd was.

Fix: copy the wheel to /tmp, cd /tmp before the import. Now the smoke test runs in a directory that contains no wifi_densepose/ so the only resolution path is the venv's site-packages.

The repo-root ./wifi_densepose/__init__.py is a separate concern (legacy v1 carry-over) that should be cleaned up in a follow-up commit, but the smoke test should not depend on it being absent.

Co-Authored-By: claude-flow ruv@ruv.net

Three PyPI artifacts now live (published from .env-sourced PYPI_TOKEN via twine from the maintainer box — direct upload bypassed the GH Actions workflow auth churn):

  1. wifi-densepose==1.99.0 — tombstone (raises ImportError with migration URL) https://pypi.org/project/wifi-densepose/1.99.0/

  2. wifi-densepose==2.0.0a1 — PyO3 wheel (win_amd64 cp310-abi3) + sdist https://pypi.org/project/wifi-densepose/2.0.0a1/

  3. ruview==2.0.0a1 — meta-package re-exporting wifi_densepose https://pypi.org/project/ruview/2.0.0a1/

New python/ruview-meta/ subdirectory:

End-to-end verified: fresh venv, pip install ruview, import ruview + import wifi_densepose both succeed, ruview.BreathingExtractor is wifi_densepose.BreathingExtractor → True.

Multi-platform wheels (manylinux x86_64+aarch64, macos x86_64+arm64) still pending — the cibuildwheel workflow path remains for that. Linux/macOS users today install via the sdist (requires rustup + maturin locally).

Refs: docs/adr/ADR-117-pip-wifi-densepose-modernization.md Refs: #785

Co-Authored-By: claude-flow ruv@ruv.net

Local check: all 25 markers pass.

Refs: docs/adr/ADR-117-pip-wifi-densepose-modernization.md Refs: #786

Co-Authored-By: claude-flow ruv@ruv.net

Docker Image: ghcr.io/ruvnet/RuView:0bffe272882676351aea7958f09f75a0eb06fbe7

download

unix · zip
curl -fL -o v1256.zip https://ratatoskr.space/pkg/ruview/v1256.zip
                    printf '%s  %s\n' '26506da829af8cc6f92ca691bd875a64150fb0cc9e5b1d92aa8d102e9c2ad5e2' 'v1256.zip' | sha256sum -c -
windows · zip
$url = "https://ratatoskr.space/pkg/ruview/v1256.zip"
$out = "v1256.zip"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "26506da829af8cc6f92ca691bd875a64150fb0cc9e5b1d92aa8d102e9c2ad5e2") { throw "sha256 mismatch" }
unix · tar.gz
curl -fL -o v1256.tar.gz https://ratatoskr.space/pkg/ruview/v1256.tar.gz
                    printf '%s  %s\n' 'b048952461dec03218aa315ce3ffbfc3605edd0dae1afb6baccb40d9a95bdd90' 'v1256.tar.gz' | sha256sum -c -
windows · tar.gz
$url = "https://ratatoskr.space/pkg/ruview/v1256.tar.gz"
$out = "v1256.tar.gz"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "b048952461dec03218aa315ce3ffbfc3605edd0dae1afb6baccb40d9a95bdd90") { throw "sha256 mismatch" }
download via yggdrasil mesh
unix · zip
curl -fL -o v1256.zip http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v1256.zip
                    printf '%s  %s\n' '26506da829af8cc6f92ca691bd875a64150fb0cc9e5b1d92aa8d102e9c2ad5e2' 'v1256.zip' | sha256sum -c -
windows · zip
$url = "http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v1256.zip"
$out = "v1256.zip"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "26506da829af8cc6f92ca691bd875a64150fb0cc9e5b1d92aa8d102e9c2ad5e2") { throw "sha256 mismatch" }
unix · tar.gz
curl -fL -o v1256.tar.gz http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v1256.tar.gz
                    printf '%s  %s\n' 'b048952461dec03218aa315ce3ffbfc3605edd0dae1afb6baccb40d9a95bdd90' 'v1256.tar.gz' | sha256sum -c -
windows · tar.gz
$url = "http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v1256.tar.gz"
$out = "v1256.tar.gz"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "b048952461dec03218aa315ce3ffbfc3605edd0dae1afb6baccb40d9a95bdd90") { throw "sha256 mismatch" }
artifact format size hashes
v1256.zip zip 59.8 MiB
blake3-24 5592b82b9b390c4205fcde7b166694d1e5dba4c4368bffa8
sha256 26506da829af8cc6f92ca691bd875a64150fb0cc9e5b1d92aa8d102e9c2ad5e2
sha1 c538fea80226c245f3c2ee53476a3698c15a15a4
v1256.tar.gz tar.gz 58.3 MiB
blake3-24 3df3bf22c62eacb05ad63ec8bd81099f899df2c006b0dd8b
sha256 b048952461dec03218aa315ce3ffbfc3605edd0dae1afb6baccb40d9a95bdd90
sha1 72101f9d7123844d859d09cb35158166af9c5581

install

bazel
http_archive(
    name = "ruview",
    urls = ["https://ratatoskr.space/pkg/ruview/v1256.tar.gz"],
    integrity = "sha256-sEiVJGHewDIYqjFc4/+/w2Be3Q2uGvtrrMtA2alb3ZA=",
    strip_prefix = "ruview-v1256",
)
zig
.url = "https://ratatoskr.space/pkg/ruview/v1256.tar.gz",
install via yggdrasil mesh
bazel
http_archive(
    name = "ruview",
    urls = ["http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v1256.tar.gz"],
    integrity = "sha256-sEiVJGHewDIYqjFc4/+/w2Be3Q2uGvtrrMtA2alb3ZA=",
    strip_prefix = "ruview-v1256",
)
zig
.url = "http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v1256.tar.gz",
← v1258v1251 →