Skip to content

Releases: ruvnet/RuView

Release v1333

25 May 19:49

Choose a tag to compare

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

25 May 19:37

Choose a tag to compare

Automated release from CI pipeline

Changes:
fix(docker): include HA-DISCO MQTT + cog-ha-matter; restores #794

Three changes:

  1. 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.

  2. docker-entrypoint.sh routes docker run <image> cog-ha-matter ...
    (or ha-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.

  3. 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

25 May 15:17

Choose a tag to compare

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:

  1. 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).
  2. wifi-densepose-sensing-server 0.3.0 -> 0.3.1 (now exposes
    the mqtt feature, sensing-server bin links against
    signal-0.3.1 cleanly).
  3. 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

25 May 14:59

Choose a tag to compare

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 the mqtt feature
    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

25 May 14:39

Choose a tag to compare

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

25 May 03:06
a91004e

Choose a tag to compare

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)
  • 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...

Read more

Release v1316

25 May 00:28
faecee9

Choose a tag to compare

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)
  • 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...

Read more

Release v1313

25 May 00:22
faecee9

Choose a tag to compare

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

25 May 21:37
2bccdf5

Choose a tag to compare

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 Sensing with 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, and rf_signature_hash never 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 through npx -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 published wifi-densepose-bfld 0.3.0 Rust crate. Verified cargo check green; wheel-deploy is ADR-117 P5 scope.

Docker

  • Multi-arch image (amd64 + linux/arm64) restored. Apple Silicon docker pull ruvnet/wifi-densepose:latest works again (closes #794).
  • Image now bundles cog-ha-matter binary + 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 hap crate (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

24 May 17:50

Choose a tag to compare

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 the crc crate 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 — needs trybuild integration; 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