Per ADR-115 §9.4 (maintainer ACK on #776), v0.7.0 ships **3 starter
blueprints**. This commit goes further: all **8** of the catalog
proposed in §3.12.2 land as standalone YAML files under
`examples/ha-blueprints/`, ready to import into HA.
## Blueprints
1. Notify on possible distress → possible_distress
2. Dim hallway when sleeping → someone_sleeping
3. Wake routine on bed exit → bed_exit (time-window-gated)
4. Alert on elderly inactivity → elderly_inactivity_anomaly
(with optional escalation chain)
5. Meeting lights + presence mode → meeting_in_progress
(activates a HA scene)
6. Bathroom fan while occupied → bathroom_occupied
(privacy-mode-safe; zone-derived)
7. Escalate on fall-risk crossing → fall_risk_elevated
(numeric_state trigger)
8. Auto-arm security when not active → group(room_active) + no_movement
(composed; multi-room sense)
Each blueprint:
- Uses HA's blueprint schema (https://www.home-assistant.io/docs/blueprint/schema/)
- Declares typed `selector:` for every input (entity-domain-constrained
where applicable)
- Carries a `source_url` for HACS-style re-import
- Includes `mode: single` + `max_exceeded: silent` where appropriate
so transient retriggers don't spam
- Includes a `cooldown_minutes` / `confirm_minutes` / `ack_timeout_min`
parameter where time-debouncing matters
## Validator (`scripts/validate-ha-blueprints.py`)
Pure-Python validator that:
- Registers no-op constructors for HA's `!input` and `!secret` YAML tags
(PyYAML doesn't know them)
- Asserts every file has a top-level `blueprint:` mapping with
`name`/`description`/`domain`
- Asserts `domain` is `automation` or `script`
- Asserts at least one declared `input`
- Asserts at least one of `trigger`/`action`/`sequence` is present
Exits 0 only when all 8 validate. Local run:
python scripts/validate-ha-blueprints.py
All 8 HA Blueprints validate OK
## CI integration
`.github/workflows/mqtt-integration.yml` gains a new
`Validate HA Blueprints` step that runs the Python validator before
the cargo test phases — fails the workflow on any malformed blueprint
in a PR.
## Privacy-mode coverage table
5 of 8 blueprints are unconditionally privacy-mode-safe (no biometric
dependency in the state derivation). The other 3 depend on inferred
states that themselves derive from biometrics — the inferred state
still publishes under `--privacy-mode` (per ADR §3.12.3) but the
operator should audit the use case in regulated contexts. Full table
in `examples/ha-blueprints/README.md`.
Refs #776, PR #778.
Co-Authored-By: claude-flow <ruv@ruv.net>
The mqtt-integration workflow's first cargo-test step was passing three
filters in one invocation:
cargo test ... --lib mqtt:: semantic:: cli::tests
cargo test treats positional args after --lib as ONE TESTNAME and
errored with 'unexpected argument semantic::'. Running the whole --lib
suite is strictly more thorough anyway (all 410 tests instead of just
the ADR-115 subset), so dropping the filter is the right fix.
Refs PR #778.
## Two CI failures on PR #778 fixed
### 1. Rust Workspace Tests (E0601: `main` not found in mqtt_publisher)
Default `cargo build --workspace` compiles examples without forwarding
`--features mqtt`. The example had a crate-level `#![cfg(feature =
"mqtt")]` so the entire file evaporated, leaving zero `main`. Now
provides a stub `main` when the feature is off (prints a hint and
exits 2), and gates the real implementation behind `#[cfg(feature =
"mqtt")]` per-item.
Local verification:
cargo check --no-default-features --examples → clean
### 2. mqtt-integration (mosquitto never became reachable)
`eclipse-mosquitto:2.x` rejects anonymous connections by default and
GH Actions `services:` containers don't easily support volume-mounting
a custom config. Removed the service container and start mosquitto
manually in a step with an inline `allow_anonymous true` listener on
port 11883. Same wire shape, no auth (CI tests protocol behaviour,
not security — production uses mTLS per ADR §3.9).
## Benchmark numbers captured (`docs/integrations/benchmarks.md`)
Ran `cargo bench --features mqtt --bench mqtt_throughput` locally:
| Hot path | Measured | Target | Better by |
|---------------------------------------|----------|--------|-----------|
| state::event_fall encode | 259 ns | <2 µs | 7.7× |
| rate_limiter::allow_first | 49.7 ns | <100 ns| 2× |
| rate_limiter::allow_within_gap | 62.1 ns | <100 ns| 1.6× |
| privacy::decide_hr_strip | 0.24 ns | <50 ns | 208× |
| privacy::decide_presence_keep | 0.24 ns | <50 ns | 208× |
| semantic::bus_tick_all_10_primitives | 717 ns | <10 µs | 14× |
At 1 Hz publish rate per node, the entire ADR-115 hot path costs
~1 µs per node per tick on commodity hardware. A Cognitum Seed
hosting 100 nodes would burn 100 µs/sec — 0.01% load floor. Memory:
~30 KB total FSM state for 10 primitives × 100 nodes.
The numbers exceed every target by ≥1.6×, several by 100×+. No need
to optimise further for v0.7.0.
Refs #776, PR #778.
Co-Authored-By: claude-flow <ruv@ruv.net>
Adds three integration tests (`v2/crates/wifi-densepose-sensing-server/
tests/mqtt_integration.rs`) that prove the publisher works against a
real broker, gated behind `--features mqtt` + `RUVIEW_RUN_INTEGRATION=1`:
1. `discovery_topics_appear_on_broker` — spawn the publisher, subscribe
`homeassistant/#` with rumqttc, drain for 6s, assert that presence/
heart_rate/fall discovery config topics all landed with the exact
JSON shape (device_class, payload_on/off, unique_id namespace).
2. `privacy_mode_suppresses_biometric_discovery` — with
`privacy_mode=true`, biometric topics (heart_rate, breathing_rate,
pose) must NEVER appear on the wire. Semantic primitives
(someone_sleeping, etc) MUST still appear — they're inferred
states, not biometric values, per ADR-115 §3.12.3.
3. `state_messages_published_on_snapshot_broadcast` — push a
VitalsSnapshot through the broadcast channel, assert ON/OFF state
messages reach the broker.
Plus `.github/workflows/mqtt-integration.yml` — spins up Mosquitto
2.0.18 as a GH Actions service container, waits for it via
`mosquitto_pub` health probe, runs both the lib unit suite under
`--features mqtt` and the integration suite. Dumps broker logs on
failure for debugging.
Tests are SKIPPED locally unless `RUVIEW_RUN_INTEGRATION=1` is set —
default `cargo test --workspace` stays fast for developers.
Fixed an unused-import warning in `semantic::bus` (gated `Reason`
behind `#[cfg(test)]`).
Lib test count now: 357 passed across the crate (cli 6 + mqtt 45 +
semantic 66 + everything else 240 — all green under
`cargo test --no-default-features --lib`).
Refs #776.
Co-Authored-By: claude-flow <ruv@ruv.net>