Releases: ruvnet/RuView
Release v1333
Automated release from CI pipeline
Changes:
fix(ci): use docker login --password-stdin (bypass login-action@v3)
docker/login-action@v3 kept emitting "malformed HTTP Authorization
header" against a fresh, known-good dckr_pat_* token (verified by
direct curl against hub.docker.com/v2/users/login). Replacing with
docker login --password-stdin — Docker's documented credential
ingestion path — sidesteps whatever encoding the action injects.
Refs #794.
Co-Authored-By: claude-flow ruv@ruv.net
Docker Image:
ghcr.io/ruvnet/RuView:c7488aeb7fc8f60987876c3c51dcfa2aa80e4de4
Release v1330
Automated release from CI pipeline
Changes:
fix(docker): include HA-DISCO MQTT + cog-ha-matter; restores #794
Three changes:
-
Dockerfile.rust now builds sensing-server with
--features mqtt
(ADR-115 HA-DISCO publisher) and also builds + ships the
cog-ha-matter binary (ADR-116 Home Assistant + Matter cog with
mDNS, embedded broker, RuVector-backed thresholds, Ed25519 witness).
Adds EXPOSE 1883 for the embedded MQTT broker. -
docker-entrypoint.sh routes
docker run <image> cog-ha-matter ...
(orha-matter) to /app/cog-ha-matter, defaulting --sensing-url to
http://127.0.0.1:3000 so a docker-compose deployment works out of
the box. The default entrypoint (no first arg) still launches
sensing-server unchanged. -
Workflow path filter now also fires on changes to
v2/crates/wifi-densepose-bfld/** and v2/crates/cog-ha-matter/**
so future iteration on those crates rebuilds the image.
DOCKERHUB_TOKEN rotated separately (was expired since 2026-05-13,
which is why the last 5 workflow runs failed at the Docker Hub login
step and latest on Docker Hub has stayed amd64-only despite #631
being merged). With this commit + rotated token, the next CI run
should land a multi-arch :latest with HA-DISCO + cog-ha-matter +
BFLD support.
Reproduced kutayozdur's pull failure on ruv-mac-mini (Apple Silicon,
Darwin arm64) via Tailscale before fixing.
Refs #794, #631, ADR-115, ADR-116, ADR-118.
Co-Authored-By: claude-flow ruv@ruv.net
Docker Image:
ghcr.io/ruvnet/RuView:2154b6931cd19181694ef382af5770097233e5e7
Release v1329
Automated release from CI pipeline
Changes:
chore(cogs): publish cog-ha-matter 0.3.0 + bump signal/sensing-server to 0.3.1
cog-ha-matter required wifi-densepose-sensing-server with the mqtt
feature exposed, which crates.io 0.3.0 did not expose. Chain:
- 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). - wifi-densepose-sensing-server 0.3.0 -> 0.3.1 (now exposes
themqttfeature, sensing-server bin links against
signal-0.3.1 cleanly). - 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
Docker Image:
ghcr.io/ruvnet/RuView:b9457220bd8f56d514f5c1670ad5a7ebb5802b51
Release v1328
Automated release from CI pipeline
Changes:
chore(cogs): publish cog-person-count + cog-pose-estimation 0.3.0 to crates.io
- cog-person-count: no path deps, clean publish.
- cog-pose-estimation: added explicit version="0.3.1" to the
wifi-densepose-train path dep (crates.io rejects path-only deps). - cog-ha-matter: keeps publish=false; the published
wifi-densepose-sensing-server@0.3.0 does not expose themqttfeature
this cog requires. Note added inline; republish sensing-server with the
feature exposed before dropping the flag.
Co-Authored-By: claude-flow ruv@ruv.net
Docker Image:
ghcr.io/ruvnet/RuView:22ca3da48c50da8823e8e1293ac43b3d37fb1ce5
Release v1327
Automated release from CI pipeline
Changes:
chore(security): allow .env reads + add rotate-npm-token.sh
Removes Read(./.env) / Read(./.env.*) from .claude/settings.json deny
list so utility scripts can read tokens from .env and push them into
GCP Secret Manager. .env itself remains gitignored.
scripts/rotate-npm-token.sh extracts NPM_TOKEN from .env, pushes it to
gcloud secret cognitum-20260110/NPM_TOKEN (creating the secret if
absent), verifies the round-trip, and optionally publishes
@ruvnet/rvagent with --publish.
Co-Authored-By: claude-flow ruv@ruv.net
Docker Image:
ghcr.io/ruvnet/RuView:2e0366c2141a73c68443e0858545025434a28d94
Release v1326
Automated release from CI pipeline
Changes:
feat(adr-124): SENSE-BRIDGE — @ruvnet/rvagent MCP server + 6 sensing tools (v0.1.0) (#791)
- feat(adr-118/p1.4): BfldFrame (header + payload + CRC32) — 24/24 GREEN
Iter 4. Lands the central wire-format primitive: complete frames with
header + arbitrary-length payload, protected by CRC-32/ISO-HDLC.
Added:
- crc = "3" dependency (CRC-32/ISO-HDLC, same poly as Ethernet / zlib)
- src/frame.rs: CRC32_ALG const and crc32_of_payload(&[u8]) -> u32
- src/frame.rs: BfldFrame { header, payload: Vec } (gated on
std)- BfldFrame::new(header, payload) — auto-syncs payload_len + payload_crc32
- BfldFrame::to_bytes() -> Vec — header LE bytes ‖ payload
- BfldFrame::from_bytes(&[u8]) -> Result<Self, BfldError>
- BfldError::TruncatedFrame { got, need } variant
- Doc strings on BfldError::Crc and BfldError::PrivacyViolation field names
- tests/frame_roundtrip.rs (7 named tests, gated on feature = "std"):
frame_roundtrip_preserves_header_and_payload
frame_new_syncs_payload_len_and_crc
frame_serialization_is_deterministic
frame_rejects_payload_crc_mismatch
frame_rejects_truncated_buffer_smaller_than_header
frame_rejects_truncated_buffer_smaller_than_payload
empty_payload_is_valid (CRC of empty payload is 0x00000000)
Test config:
- cargo test --no-default-features → 17 passed (frame_roundtrip cfg-out)
- cargo test (default features = std) → 24 passed (3+6+7+8)
ADR-119 ACs progressed:
- AC4 partial: bad-magic + bad-version + CRC-mismatch + truncation rejected
with typed errors; field-level masking lives in the privacy_gate iter. - AC5: BfldFrame round-trip preserves header + payload + CRC.
- AC6: Identical inputs produce bit-identical bytes (asserted explicitly).
Out of scope (next iter):
- Payload section parser (compressed_angle_matrix, amplitude_proxy, ...)
— only the byte buffer is opaque so far; sections need length prefixes. - BfldFrameRef<'_> for ESP32-S3 self-only mode (no-alloc, ADR-123 §2.5).
- PrivacyGate::demote(frame, target_class) transformer (ADR-120 §2.4).
Co-Authored-By: claude-flow ruv@ruv.net
- feat(adr-118/p1.5): payload section parser (BfldPayload) — 32/32 GREEN
Iter 5. Implements ADR-119 §2.2 payload layout: 4-byte LE length prefix
followed by section bytes, in this fixed order:
compressed_angle_matrix ‖ amplitude_proxy ‖ phase_proxy ‖ snr_vector
‖ csi_delta (iff flags.bit0)
‖ vendor_extension (length 0 allowed)
Added:
- src/payload.rs (gated on
feature = "std"):- BfldPayload struct with 6 fields (csi_delta: Option<Vec>)
- SECTION_PREFIX_LEN const (= 4)
- to_bytes(include_csi_delta: bool) -> Vec
- wire_len(include_csi_delta: bool) -> usize (predictive, no allocation)
- from_bytes(&[u8], expect_csi_delta: bool) -> Result<Self, BfldError>
- push_section / read_section helpers (private)
- BfldError::MalformedSection { offset, reason } variant
- pub use BfldPayload from lib.rs (cfg-gated mirror of BfldFrame)
tests/payload_sections.rs (8 named tests, all green):
payload_roundtrip_with_csi_delta
payload_roundtrip_without_csi_delta
wire_len_matches_to_bytes_length
empty_payload_has_five_zero_length_sections
parser_rejects_buffer_shorter_than_first_length_prefix
parser_rejects_section_body_running_past_buffer_end
parser_rejects_trailing_bytes_after_vendor_extension
csi_delta_flag_mismatch_with_payload_is_detectable_via_trailing_bytes
ACs progressed:
- AC5 ↑ — full section-level round-trip preservation (round-trip with and
without csi_delta both pass). - AC6 ↑ — deterministic section encoding (length prefixes use to_le_bytes,
body is byte-stable). - AC1 partial — section layout now parses with bounded errors; CBFR-specific
parsing (Phi/Psi Givens decoders) is a separate iter inside extractor.rs.
Test config:
- cargo test --no-default-features → 17 passed (payload module cfg-out)
- cargo test → 32 passed (3 + 6 + 7 + 8 + 8)
Out of scope (next iter target):
- Wire integration: feed BfldPayload bytes through BfldFrame::new so the
header.payload_crc32 covers the section-prefixed bytes per ADR-119 §2.2
("CRC32 covers all section bytes including length prefixes"). - A no_std-friendly BfldPayloadRef<'_> borrowing variant (ESP32-S3 path).
- Givens-rotation angle decoder (Phi/Psi extraction from compressed_angle_matrix).
Co-Authored-By: claude-flow ruv@ruv.net
- feat(adr-118/p1.6): BfldFrame <-> BfldPayload wire integration (39/39 GREEN)
Iter 6. Connects the typed payload parser (iter 5) to the framed
wire format (iter 4): the CRC32 now covers the section-prefixed
payload bytes per ADR-119 §2.2 ("CRC32 covers all section bytes
including length prefixes").
Added:
- BfldFrame::from_payload(header, &BfldPayload) -> Self
Auto-syncs header.flags HAS_CSI_DELTA bit from payload.csi_delta.is_some(),
serializes payload via to_bytes(), feeds BfldFrame::new() which computes
payload_len + payload_crc32 over the section-prefixed bytes. - BfldFrame::parse_payload(&self) -> Result<BfldPayload, BfldError>
Reads HAS_CSI_DELTA bit from header.flags and dispatches to
BfldPayload::from_bytes(&self.payload, expect_csi_delta).
tests/frame_payload_integration.rs (7 named tests, all green):
from_payload_then_parse_payload_is_identity
from_payload_autosets_has_csi_delta_flag
from_payload_clears_has_csi_delta_flag_when_csi_absent
(verifies the flag is cleared when csi_delta is None even if caller
pre-set the bit; other flag bits like PRIVACY_MODE are preserved)
frame_crc_covers_section_prefixed_bytes
(mutating a byte inside section body trips CRC, not magic/length)
frame_crc_covers_section_length_prefixes
(mutating a section length-prefix byte trips CRC before parser ever runs)
empty_typed_payload_roundtrips
end_to_end_wire_roundtrip_via_bytes
(BfldPayload -> from_payload -> to_bytes -> from_bytes -> parse_payload
is the identity function modulo flag auto-set)
ACs progressed:
- AC5 ↑ — full payload round-trip through the framed bytes (closes
the round-trip leg from BfldPayload through wire and back). - AC6 ↑ — same input produces same bytes through both layers.
- AC4 ↑ — CRC mismatch on tampered section bodies and tampered section
length prefixes both surface as BfldError::Crc, not as silent acceptance
or as a deeper parser error.
Test config:
- cargo test --no-default-features → 17 passed (integration tests cfg-out)
- cargo test → 39 passed (3 + 6 + 7 + 8 + 8 + 7)
Out of scope (next iter target):
- PrivacyGate::demote(frame, target_class) — ADR-120 §2.4 class transition
transformer with subtle::Zeroize on dropped fields. - IdentityEmbedding newtype with no Serialize impl (ADR-120 §2.5 / I2).
Co-Authored-By: claude-flow ruv@ruv.net
- feat(adr-118/p2.1): IdentityEmbedding newtype + zeroizing Drop — 44/44 GREEN
Iter 7. First structural enforcement of ADR-118 invariant I2 — the
identity embedding is in-RAM-only and cannot be serialized, cloned,
or copied. Lands the type itself; ring-buffer lifecycle is next.
Added:
- src/embedding.rs (no_std-compatible; lives in the lib regardless of features):
- IdentityEmbedding wrapping [f32; EMBEDDING_DIM=128]
- from_raw(values), as_slice() -> &[f32], l2_norm(), len(), is_empty()
- NO Serialize, NO Clone, NO Copy impl
- Custom Debug emits only dim + L2 norm + "" — never raw values
- Drop overwrites storage with 0.0 then core::hint::black_box(...) to defeat
dead-store elimination (DSE would otherwise let the compiler skip the write)
- Compile-time structural guards via static_assertions:
assert_impl_all!(IdentityEmbedding: Drop)
assert_not_impl_any!(IdentityEmbedding: Copy, Clone) - pub use IdentityEmbedding, EMBEDDING_DIM from lib.rs
tests/identity_embedding.rs (5 named tests, all green):
from_raw_preserves_values_through_as_slice
l2_norm_is_correct
debug_output_redacts_raw_values
(asserts the formatted output does NOT contain decimal text of values)
embedding_is_not_clonable
(runtime witness; compile-time assertion lives in src/embedding.rs)
drop_overwrites_storage_with_zeros
(Drop runs without panic; bit-level zeroization is asserted by the
black_box-guarded loop. Unsafe peek-after-free is intentionally avoided.)
ACs progressed:
- AC5 ↑ — even in
privacy_mode, the IdentityEmbedding type can't be reached
from any serialization path because the type system rejects the impl. - I2 ↑ — Drop, no Clone, no Copy, redacted Debug are all in place as
compile-time guarantees.
Test config:
- cargo test --no-default-features → 22 passed
- cargo test → 44 passed (3 + 6 + 7 + 8 + 8 + 7 + 5)
Out of scope (next iter target):
- EmbeddingRing — 64-entry FIFO ring buffer holding IdentityEmbeddings,
drained on coherence-gate Recalibrate (ADR-121 §2.4). - PrivacyGate::demote(frame, target_class) transformer (ADR-120 §2.4).
Co-Authored-By: claude-flow ruv@ruv.net
- feat(adr-118/p2.2): EmbeddingRing 64-entry FIFO buffer — 53/53 GREEN
Iter 8. Lands the lifecycle half of ADR-120 §2.5: a bounded, in-place,
no_std-compatible ring of IdentityEmbeddings. Insertion is O(1); when
full, push evicts the oldest entry, whose Drop runs and zeroizes the
f32 storage. drain() clears the ring on the coherence-gate Recalibrate
action (ADR-121 §2.4).
Added:
- src/embedding_ring.rs (no_std-compatible; no heap):
- EmbeddingRing struct with [Option; RING_CAPACITY=64]
backing array, head cursor, count - EmbeddingRing::new() / Default impl
- push(emb) -> Option (evicted oldest when full)
- len / is_empty / capacity / is_full / iter
- iter() returns occupied slots in insertion order (oldest first)
- drain() -> usize (empties the ring, returns count drained)
- EmbeddingRing struct with [Option; RING_CAPACITY=64]
- pub use EmbeddingRing, RING_CAPACITY from lib.rs
Uses [const { None }; RING_CAPACITY] (stable since 1.79) to initialize
the slot array for a non-Copy element type.
te...
Release v1316
Automated release from CI pipeline
Changes:
feat(adr-118): BFLD — Beamforming Feedback Layer for Detection (#789)
- feat(adr-118/p1.4): BfldFrame (header + payload + CRC32) — 24/24 GREEN
Iter 4. Lands the central wire-format primitive: complete frames with
header + arbitrary-length payload, protected by CRC-32/ISO-HDLC.
Added:
- crc = "3" dependency (CRC-32/ISO-HDLC, same poly as Ethernet / zlib)
- src/frame.rs: CRC32_ALG const and crc32_of_payload(&[u8]) -> u32
- src/frame.rs: BfldFrame { header, payload: Vec } (gated on
std)- BfldFrame::new(header, payload) — auto-syncs payload_len + payload_crc32
- BfldFrame::to_bytes() -> Vec — header LE bytes ‖ payload
- BfldFrame::from_bytes(&[u8]) -> Result<Self, BfldError>
- BfldError::TruncatedFrame { got, need } variant
- Doc strings on BfldError::Crc and BfldError::PrivacyViolation field names
- tests/frame_roundtrip.rs (7 named tests, gated on feature = "std"):
frame_roundtrip_preserves_header_and_payload
frame_new_syncs_payload_len_and_crc
frame_serialization_is_deterministic
frame_rejects_payload_crc_mismatch
frame_rejects_truncated_buffer_smaller_than_header
frame_rejects_truncated_buffer_smaller_than_payload
empty_payload_is_valid (CRC of empty payload is 0x00000000)
Test config:
- cargo test --no-default-features → 17 passed (frame_roundtrip cfg-out)
- cargo test (default features = std) → 24 passed (3+6+7+8)
ADR-119 ACs progressed:
- AC4 partial: bad-magic + bad-version + CRC-mismatch + truncation rejected
with typed errors; field-level masking lives in the privacy_gate iter. - AC5: BfldFrame round-trip preserves header + payload + CRC.
- AC6: Identical inputs produce bit-identical bytes (asserted explicitly).
Out of scope (next iter):
- Payload section parser (compressed_angle_matrix, amplitude_proxy, ...)
— only the byte buffer is opaque so far; sections need length prefixes. - BfldFrameRef<'_> for ESP32-S3 self-only mode (no-alloc, ADR-123 §2.5).
- PrivacyGate::demote(frame, target_class) transformer (ADR-120 §2.4).
Co-Authored-By: claude-flow ruv@ruv.net
- feat(adr-118/p1.5): payload section parser (BfldPayload) — 32/32 GREEN
Iter 5. Implements ADR-119 §2.2 payload layout: 4-byte LE length prefix
followed by section bytes, in this fixed order:
compressed_angle_matrix ‖ amplitude_proxy ‖ phase_proxy ‖ snr_vector
‖ csi_delta (iff flags.bit0)
‖ vendor_extension (length 0 allowed)
Added:
- src/payload.rs (gated on
feature = "std"):- BfldPayload struct with 6 fields (csi_delta: Option<Vec>)
- SECTION_PREFIX_LEN const (= 4)
- to_bytes(include_csi_delta: bool) -> Vec
- wire_len(include_csi_delta: bool) -> usize (predictive, no allocation)
- from_bytes(&[u8], expect_csi_delta: bool) -> Result<Self, BfldError>
- push_section / read_section helpers (private)
- BfldError::MalformedSection { offset, reason } variant
- pub use BfldPayload from lib.rs (cfg-gated mirror of BfldFrame)
tests/payload_sections.rs (8 named tests, all green):
payload_roundtrip_with_csi_delta
payload_roundtrip_without_csi_delta
wire_len_matches_to_bytes_length
empty_payload_has_five_zero_length_sections
parser_rejects_buffer_shorter_than_first_length_prefix
parser_rejects_section_body_running_past_buffer_end
parser_rejects_trailing_bytes_after_vendor_extension
csi_delta_flag_mismatch_with_payload_is_detectable_via_trailing_bytes
ACs progressed:
- AC5 ↑ — full section-level round-trip preservation (round-trip with and
without csi_delta both pass). - AC6 ↑ — deterministic section encoding (length prefixes use to_le_bytes,
body is byte-stable). - AC1 partial — section layout now parses with bounded errors; CBFR-specific
parsing (Phi/Psi Givens decoders) is a separate iter inside extractor.rs.
Test config:
- cargo test --no-default-features → 17 passed (payload module cfg-out)
- cargo test → 32 passed (3 + 6 + 7 + 8 + 8)
Out of scope (next iter target):
- Wire integration: feed BfldPayload bytes through BfldFrame::new so the
header.payload_crc32 covers the section-prefixed bytes per ADR-119 §2.2
("CRC32 covers all section bytes including length prefixes"). - A no_std-friendly BfldPayloadRef<'_> borrowing variant (ESP32-S3 path).
- Givens-rotation angle decoder (Phi/Psi extraction from compressed_angle_matrix).
Co-Authored-By: claude-flow ruv@ruv.net
- feat(adr-118/p1.6): BfldFrame <-> BfldPayload wire integration (39/39 GREEN)
Iter 6. Connects the typed payload parser (iter 5) to the framed
wire format (iter 4): the CRC32 now covers the section-prefixed
payload bytes per ADR-119 §2.2 ("CRC32 covers all section bytes
including length prefixes").
Added:
- BfldFrame::from_payload(header, &BfldPayload) -> Self
Auto-syncs header.flags HAS_CSI_DELTA bit from payload.csi_delta.is_some(),
serializes payload via to_bytes(), feeds BfldFrame::new() which computes
payload_len + payload_crc32 over the section-prefixed bytes. - BfldFrame::parse_payload(&self) -> Result<BfldPayload, BfldError>
Reads HAS_CSI_DELTA bit from header.flags and dispatches to
BfldPayload::from_bytes(&self.payload, expect_csi_delta).
tests/frame_payload_integration.rs (7 named tests, all green):
from_payload_then_parse_payload_is_identity
from_payload_autosets_has_csi_delta_flag
from_payload_clears_has_csi_delta_flag_when_csi_absent
(verifies the flag is cleared when csi_delta is None even if caller
pre-set the bit; other flag bits like PRIVACY_MODE are preserved)
frame_crc_covers_section_prefixed_bytes
(mutating a byte inside section body trips CRC, not magic/length)
frame_crc_covers_section_length_prefixes
(mutating a section length-prefix byte trips CRC before parser ever runs)
empty_typed_payload_roundtrips
end_to_end_wire_roundtrip_via_bytes
(BfldPayload -> from_payload -> to_bytes -> from_bytes -> parse_payload
is the identity function modulo flag auto-set)
ACs progressed:
- AC5 ↑ — full payload round-trip through the framed bytes (closes
the round-trip leg from BfldPayload through wire and back). - AC6 ↑ — same input produces same bytes through both layers.
- AC4 ↑ — CRC mismatch on tampered section bodies and tampered section
length prefixes both surface as BfldError::Crc, not as silent acceptance
or as a deeper parser error.
Test config:
- cargo test --no-default-features → 17 passed (integration tests cfg-out)
- cargo test → 39 passed (3 + 6 + 7 + 8 + 8 + 7)
Out of scope (next iter target):
- PrivacyGate::demote(frame, target_class) — ADR-120 §2.4 class transition
transformer with subtle::Zeroize on dropped fields. - IdentityEmbedding newtype with no Serialize impl (ADR-120 §2.5 / I2).
Co-Authored-By: claude-flow ruv@ruv.net
- feat(adr-118/p2.1): IdentityEmbedding newtype + zeroizing Drop — 44/44 GREEN
Iter 7. First structural enforcement of ADR-118 invariant I2 — the
identity embedding is in-RAM-only and cannot be serialized, cloned,
or copied. Lands the type itself; ring-buffer lifecycle is next.
Added:
- src/embedding.rs (no_std-compatible; lives in the lib regardless of features):
- IdentityEmbedding wrapping [f32; EMBEDDING_DIM=128]
- from_raw(values), as_slice() -> &[f32], l2_norm(), len(), is_empty()
- NO Serialize, NO Clone, NO Copy impl
- Custom Debug emits only dim + L2 norm + "" — never raw values
- Drop overwrites storage with 0.0 then core::hint::black_box(...) to defeat
dead-store elimination (DSE would otherwise let the compiler skip the write)
- Compile-time structural guards via static_assertions:
assert_impl_all!(IdentityEmbedding: Drop)
assert_not_impl_any!(IdentityEmbedding: Copy, Clone) - pub use IdentityEmbedding, EMBEDDING_DIM from lib.rs
tests/identity_embedding.rs (5 named tests, all green):
from_raw_preserves_values_through_as_slice
l2_norm_is_correct
debug_output_redacts_raw_values
(asserts the formatted output does NOT contain decimal text of values)
embedding_is_not_clonable
(runtime witness; compile-time assertion lives in src/embedding.rs)
drop_overwrites_storage_with_zeros
(Drop runs without panic; bit-level zeroization is asserted by the
black_box-guarded loop. Unsafe peek-after-free is intentionally avoided.)
ACs progressed:
- AC5 ↑ — even in
privacy_mode, the IdentityEmbedding type can't be reached
from any serialization path because the type system rejects the impl. - I2 ↑ — Drop, no Clone, no Copy, redacted Debug are all in place as
compile-time guarantees.
Test config:
- cargo test --no-default-features → 22 passed
- cargo test → 44 passed (3 + 6 + 7 + 8 + 8 + 7 + 5)
Out of scope (next iter target):
- EmbeddingRing — 64-entry FIFO ring buffer holding IdentityEmbeddings,
drained on coherence-gate Recalibrate (ADR-121 §2.4). - PrivacyGate::demote(frame, target_class) transformer (ADR-120 §2.4).
Co-Authored-By: claude-flow ruv@ruv.net
- feat(adr-118/p2.2): EmbeddingRing 64-entry FIFO buffer — 53/53 GREEN
Iter 8. Lands the lifecycle half of ADR-120 §2.5: a bounded, in-place,
no_std-compatible ring of IdentityEmbeddings. Insertion is O(1); when
full, push evicts the oldest entry, whose Drop runs and zeroizes the
f32 storage. drain() clears the ring on the coherence-gate Recalibrate
action (ADR-121 §2.4).
Added:
- src/embedding_ring.rs (no_std-compatible; no heap):
- EmbeddingRing struct with [Option; RING_CAPACITY=64]
backing array, head cursor, count - EmbeddingRing::new() / Default impl
- push(emb) -> Option (evicted oldest when full)
- len / is_empty / capacity / is_full / iter
- iter() returns occupied slots in insertion order (oldest first)
- drain() -> usize (empties the ring, returns count drained)
- EmbeddingRing struct with [Option; RING_CAPACITY=64]
- pub use EmbeddingRing, RING_CAPACITY from lib.rs
Uses [const { None }; RING_CAPACITY] (stable since 1.79) to initialize
the slot array for a non-Copy element type.
tests/embedding_ring.rs...
Release v1313
Automated release from CI pipeline
Changes:
docs(adr-124): RUVIEW-POLICY layer + Q4 cache resolution + multi-modal vision
Three additive sections per maintainer review of SENSE-BRIDGE
(the original 13-section draft is unchanged below; these are
inserts):
§4.1a — RUVIEW-POLICY governance layer (NEW). Five tools:
- ruview.policy.can_access_vitals(agent_id, node_id, vital)
- ruview.policy.can_query_presence(agent_id, scope, node_id?, zone?)
- ruview.policy.can_subscribe(agent_id, topic, duration_s)
- ruview.policy.redact_identity_fields(payload, agent_id)
- ruview.policy.audit_log(agent_id?, since_ts?)
Enforcement is server-side, not client-side — agents cannot bypass.
Default policy when no file exists: deny vitals + audit_log; allow
presence.now + node.list; allow primitives.list_active with
redact_identity_fields applied. "Explore safely" default.
Q4 — RESOLVED. The library MUST take continuous local cache +
event-driven invalidation + bounded freshness windows. Tools
never wait on the next CSI frame; cache hits return in <1 ms;
every tool accepts max_age_ms and returns
{ value: null, reason: "stale", last_seen_ms, threshold_ms }
when stale rather than blocking. Decouples agent orchestration
latency from RF acquisition jitter — required to scale to dozens
of concurrent Streamable HTTP sessions per Q8.
§11.3 — Strategic implication: ambient-sensing normalization
layer (NEW). The §4 tool catalog shape is modality-agnostic.
Same surface absorbs BLE / mmWave (already on COM4) / LiDAR /
thermal / camera / radar / UWB. Position as semantic-environment
API, not WiFi client. Follow-on ADR-13x RUVIEW-FUSION formalizes
per-modality adapter contract. Out of scope for 124; designed in.
§11.2 risk table — added the "sensing-tool surface becomes
surveillance API" row, mitigation = RUVIEW-POLICY layer + server-
side redaction.
Refs: docs/adr/ADR-124-rvagent-mcp-ruvector-npm-integration.md
Docker Image:
ghcr.io/ruvnet/RuView:efadeb3a73dd374e5db7599e30e1bbc49a0e9021
v0.9.0 — ADR-125 APPLE-FABRIC: native Apple Home / HomePod integration
ADR-125 (codename APPLE-FABRIC) lands: RuView advertises itself as a HomeKit Accessory Protocol (HAP-1.1) bridge on the LAN, so HomePod (acting as Home Hub) discovers it, Apple Home becomes the operator UX, and Siri voices presence/vitals by room — with zero Home Assistant intermediary.
Validated end-to-end on real hardware (ESP32-C6 on ruv.net, no mocks/sims, 8 iters tracked live on #796).
What's new
Native Apple Home integration (ADR-125)
- HAP-1.1 bridge
RuView Sensingwith N child accessories — one per room (ADR-125 §2.1.c topology). Single pairing covers all rooms; children remain individually addressable for Siri ("is anyone in the kitchen?") and Home automations. - Three services per room:
MotionSensor(immediate movement),OccupancySensor(sustained presence — "Unknown Presence"),StatelessProgrammableSwitch(BFLD anomaly drift — "Unrecognized Activity Pattern"). ADR-125 §2.1.d semantic-event naming, never security/threat framing. - Privacy gate enforced at FIVE boundaries: HAP characteristic, HTTP
/api/v1/bfld/..., semantic-events endpoint, MCP boundary, Python BFLD gate.identity_risk_score,soul_match_probability, andrf_signature_hashnever cross any of them.
Agentic capabilities
@ruvnet/rvagent 0.1.0(npm) — published in this release window — wired through a Python sensing-server bridge so any MCP-aware agent (Claude Code, Codex, custom LLM) can consume the BFLD-gated C6 stream through the standard 12-tool catalog.- New
scripts/rvagent-mcp-consumer.py— JSON-RPC 2.0 stdio MCP client that round-trips real C6 data throughnpx -y @ruvnet/rvagent.
HomePod announcements (Shortcuts-as-glue)
scripts/macos-shortcuts/— launchd + osascript glue that triggers operator-defined Shortcuts on RuView events, routing HomePod announcements via the iCloud Home graph instead of local Bonjour (works even when the AP doesn't reflect mDNS).
SOTA path
python/src/bindings/privacy_gate.rs— PyO3 binding over the publishedwifi-densepose-bfld 0.3.0Rust crate. Verifiedcargo checkgreen; wheel-deploy is ADR-117 P5 scope.
Docker
- Multi-arch image (
amd64+linux/arm64) restored. Apple Silicondocker pull ruvnet/wifi-densepose:latestworks again (closes #794). - Image now bundles
cog-ha-matterbinary + HA-DISCO MQTT publisher.
Documentation
- New ADR:
docs/adr/ADR-125-ruview-apple-home-native-hap-bridge.md(with §2.1.c and §2.1.d decisions captured during operator review) - New user guide:
docs/user-guide-apple-homepod.md(~4,500 words) — quickstart to HomePod-announces-RuView in 5 steps - HomePod Integration badge added to
README.md
Release artifacts
- crates.io:
cog-ha-matter 0.3.0,cog-person-count 0.3.0,cog-pose-estimation 0.3.0(published earlier this session) - crates.io:
wifi-densepose-signal 0.3.1,wifi-densepose-sensing-server 0.3.1(cascade for cog-ha-matter) - npm:
@ruvnet/rvagent 0.1.0 - Docker Hub:
ruvnet/wifi-densepose:latest(multi-arch)
Deferred to future releases
- HAP-python custom characteristic JSON-loader (runtime activation of the BFLD privacy class UUID)
- ADR-117 P5 wheel build for the PyO3 BFLD binding
- AirPlay 2 voice synthesis via pyatv (blocked on operator's Nighthawk MR60 Bonjour reflector)
- P2 Rust-native HAP via the
hapcrate (closes ADR-116 §P7) - P3 Matter Controller path (awaiting matter-rs stabilization)
References
- ADR-125 — RuView ↔ Apple Home native HAP bridge
- ADR-118 — BFLD beamforming feedback layer
- ADR-124 — @ruvnet/rvagent MCP/ruvector integration
- Issue #796 — live tracking thread (8 iter close-outs)
- Issue #794 — Docker Apple Silicon pull fix (closed)
Co-Authored-By: claude-flow ruv@ruv.net
Release v1261
Automated release from CI pipeline
Changes:
feat(adr-118/p1.3): Sink marker traits + PrivacyClass::try_from (17/17 GREEN)
Iter 3. Lands the structural enforcement of ADR-118 invariant I1
("raw BFI never exits the node") and ADR-120 §2.2 ("Sink marker types").
Added:
- src/sink.rs:
- Sink trait with MIN_CLASS and KIND associated constants
- LocalSink (Raw OK), NetworkSink (Derived+ only), MatterSink (Anonymous+)
- Hierarchy: MatterSink: NetworkSink (every Matter sink is a NetworkSink)
- check_class
(class) runtime gate, returns PrivacyViolation{reason:KIND} - Zero-sized kind tags: LocalKind / NetworkKind / MatterKind
- PrivacyClass::as_u8() const helper
- TryFrom for PrivacyClass (0..=3 valid; 4..=255 → InvalidPrivacyClass)
- BfldError::InvalidPrivacyClass(u8) variant
tests/sink_enforcement.rs adds 8 tests:
privacy_class_try_from_accepts_all_four_valid_bytes
privacy_class_try_from_rejects_out_of_range_bytes
privacy_class_byte_roundtrip_is_stable
local_sink_accepts_all_classes
network_sink_rejects_raw_frames
network_sink_accepts_derived_anonymous_restricted
matter_sink_rejects_raw_and_derived
matter_sink_accepts_anonymous_and_restricted
Out of scope (next iter):
- BfldFrame (header + payload + section length-prefixes + CRC32 over payload)
— needs thecrccrate dependency. - PrivacyGate::demote(frame, target_class) transformer (ADR-120 §2.4).
- compile-fail test that proves a sink-trait bound rejects Raw at compile
time — needstrybuildintegration; deferred to a separate iter.
cargo test -p wifi-densepose-bfld --no-default-features → 17 passed, 0 failed
(3 frame_header_size + 6 header_roundtrip + 8 sink_enforcement)
Co-Authored-By: claude-flow ruv@ruv.net
Docker Image:
ghcr.io/ruvnet/RuView:eb996294fb14b336a716f8dc961acb8de73a91c7