wifi-densepose/v2/crates/wifi-densepose-bfld/tests
ruv e8b4fdbc8f feat(adr-118/p5.5): BfldPipelineHandle worker thread (177/177 GREEN)
Iter 25. Single-call operator surface: spawn() takes a BfldPipeline and
a Publish impl, returns a handle whose send() enqueues sensing inputs
into a worker thread. The worker drives pipeline.process() then
publish_event() per input. Drop or shutdown() joins cleanly.

Added (gated on `feature = "std"`):
- src/mqtt_topics.rs: impl<P: Publish> Publish for Arc<Mutex<P>>
  Lets a publisher owned by a worker thread remain inspectable from a
  test or operator post-shutdown.
- src/pipeline_handle.rs:
  * PipelineInput { inputs: SensingInputs, embedding: Option<...> }
  * BfldPipelineHandle { sender, worker: Option<JoinHandle<()>> }
  * spawn<P: Publish + Send + 'static>(pipeline, publisher) -> Self
      Worker loop: recv() → pipeline.process() → publish_event(); errors
      logged to stderr (single-frame failures must not kill the loop)
  * send(PipelineInput) -> Result<(), SendError<...>>
  * shutdown(self) — replaces sender with a dropped channel so worker
    recv() returns Err(RecvError); join propagates worker panics
  * Drop impl mirrors shutdown so forgotten handles still clean up
- pub use BfldPipelineHandle, PipelineInput from lib.rs

tests/pipeline_handle_worker.rs (8 named tests, all green):
  handle_publishes_single_input (5 topics for Anonymous + no zone)
  handle_publishes_multiple_inputs_in_order (3 × 5 = 15 topics)
  handle_send_after_shutdown_errors
    (compile-time witness: shutdown(self) consumes the handle so
     post-shutdown send() is structurally impossible)
  handle_drop_without_explicit_shutdown_joins_worker_cleanly
    (validates the Drop path completes without hanging)
  handle_honors_privacy_mode_toggle_via_pipeline_state
    (4 topics for Restricted; identity_risk absent)
  handle_drops_event_when_gate_rejects
    (5 topics from first Accept-state input + 0 from Reject)
  handle_with_zone_threads_through_to_published_topics
    (zone_activity payload = "\"kitchen\"")
  class_3_pipeline_baseline_produces_four_topics_per_input

Test publisher pattern: Arc<Mutex<CapturePublisher>> lets the test thread
read out the worker thread's publish log post-shutdown without needing
custom channel plumbing per test.

ACs progressed:
- ADR-118 §2.1 lib.rs entry point now has the "set up MQTT and walk away"
  operator surface promised in the implementation plan. Two lines:
      let handle = BfldPipelineHandle::spawn(pipeline, rumqttc_pub);
      handle.send(PipelineInput { inputs, embedding })?;
- ADR-122 §2.2 per-frame publish path is now structurally guarded by
  worker-thread isolation: even if a Publish::publish call panics, only
  the worker thread dies; the main thread sees a clean error on send().

Test config:
- cargo test --no-default-features → 72 passed
- cargo test                       → 177 passed (169 + 8)
- cargo test --features mqtt       → 186 (178 + 8 — handle is std-only,
  reachable in both feature configs)

Out of scope (next iter target):
- GitHub Actions workflow with mosquitto Docker service so the iter-24
  integration test actually runs in CI with BFLD_MQTT_BROKER set.
- HA discovery payload publisher (ADR-122 §2.1) — the auto-discovery
  config messages HA needs alongside the state topics this handle ships.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-24 17:27:48 -04:00
..
coherence_gate.rs feat(adr-118/p3.3): CoherenceGate hysteresis + 5s debounce — 85/85 GREEN 2026-05-24 15:07:40 -04:00
embedding_ring.rs feat(adr-118/p2.2): EmbeddingRing 64-entry FIFO buffer — 53/53 GREEN 2026-05-24 14:37:03 -04:00
emitter_hasher.rs feat(adr-118/p4.3): wire SignatureHasher into BfldEmitter (123/123 GREEN) 2026-05-24 15:57:44 -04:00
emitter_pipeline.rs feat(adr-118/p4.2): BfldEmitter end-to-end pipeline (109/109 GREEN) 2026-05-24 15:37:23 -04:00
event_privacy_gating.rs feat(adr-118/p4.1): BfldEvent privacy-gated output + JSON (102/102 GREEN) 2026-05-24 15:27:49 -04:00
frame_header_size.rs feat(adr-118/p1): scaffold wifi-densepose-bfld crate + frame header (3/3 tests GREEN) 2026-05-24 13:34:05 -04:00
frame_payload_integration.rs feat(adr-118/p1.6): BfldFrame <-> BfldPayload wire integration (39/39 GREEN) 2026-05-24 14:16:54 -04:00
frame_roundtrip.rs feat(adr-118/p1.4): BfldFrame (header + payload + CRC32) — 24/24 GREEN 2026-05-24 13:58:26 -04:00
header_roundtrip.rs feat(adr-118/p1.2): header encode/decode + 6 round-trip tests (9/9 GREEN) 2026-05-24 13:38:11 -04:00
identity_embedding.rs feat(adr-118/p2.1): IdentityEmbedding newtype + zeroizing Drop — 44/44 GREEN 2026-05-24 14:27:28 -04:00
identity_features_encoder.rs feat(adr-118/p3.6): IdentityFeatures canonical-bytes encoder (137/137 GREEN) 2026-05-24 16:18:33 -04:00
identity_risk_score.rs feat(adr-118/p3.2): identity_risk score + GateAction enum — 72/72 GREEN 2026-05-24 14:57:08 -04:00
json_hash_format.rs feat(adr-118/p4.4): rf_signature_hash JSON as "blake3:<hex>" (128/128 GREEN) 2026-05-24 16:08:29 -04:00
mosquitto_integration.rs feat(adr-118/p5.4): mosquitto integration test (env-gated, 178/178 with mqtt) 2026-05-24 17:17:38 -04:00
mqtt_publish_loop.rs feat(adr-118/p5.2): Publish trait + publish_event free function — 169/169 GREEN 2026-05-24 16:57:05 -04:00
mqtt_topic_routing.rs feat(adr-118/p5.1): MQTT topic router (BfldEvent → Vec<TopicMessage>) — 162/162 GREEN 2026-05-24 16:47:11 -04:00
payload_sections.rs feat(adr-118/p1.5): payload section parser (BfldPayload) — 32/32 GREEN 2026-05-24 14:07:14 -04:00
pipeline_facade.rs feat(adr-118/p4.5): BfldPipeline facade + BfldConfig (146/146 GREEN) 2026-05-24 16:28:42 -04:00
pipeline_handle_worker.rs feat(adr-118/p5.5): BfldPipelineHandle worker thread (177/177 GREEN) 2026-05-24 17:27:48 -04:00
pipeline_to_frame.rs feat(adr-118/p4.6): BfldPipeline::process_to_frame wire-bytes path (152/152 GREEN) 2026-05-24 16:37:11 -04:00
privacy_gate_demote.rs feat(adr-118/p3.1): PrivacyGate::demote monotonic class transformer (60/60 GREEN) 2026-05-24 14:48:01 -04:00
rumqttc_publisher_smoke.rs feat(adr-118/p5.3): RumqttPublisher behind mqtt feature gate (176/176 GREEN with mqtt) 2026-05-24 17:09:05 -04:00
signature_hasher.rs feat(adr-118/p3.5): SignatureHasher (BLAKE3-keyed) — 117/117 GREEN 2026-05-24 15:47:21 -04:00
sink_enforcement.rs feat(adr-118/p1.3): Sink marker traits + PrivacyClass::try_from (17/17 GREEN) 2026-05-24 13:43:05 -04:00
soul_match_oracle.rs feat(adr-118/p3.4): SoulMatchOracle + Recalibrate exemption (93/93 GREEN) 2026-05-24 15:17:24 -04:00