From 4329f53a2b85165a3121c235f4bb0b11c35b52a9 Mon Sep 17 00:00:00 2001 From: ruv Date: Sun, 24 May 2026 19:37:21 -0400 Subject: [PATCH] feat(adr-118/p6.10): public API surface snapshot (308/308 GREEN) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Iter 45. Compile-time witness that every `pub use` re-export from lib.rs survives refactors. A future PR removing one fires a named test failure instead of producing a silent SemVer break. Added (in tests/public_api_snapshot.rs): - 5 named tests across feature flags: always_available_types_are_re_exported (no_std-compatible) Witnesses PrivacyClass, GateAction, MatchOutcome, BfldFrameHeader, CoherenceGate, NullOracle, EmbeddingRing, SignatureHasher, IdentityEmbedding + 11 const re-exports + 5 flag bits. sink_trait_hierarchy_re_exported (no_std-compatible) Witnesses Sink, LocalSink, NetworkSink, MatterSink, LocalKind, NetworkKind, MatterKind + check_class function. Trait bounds asserted via fn assert_sink() etc. so missing impls fire here too. soul_match_oracle_trait_re_exported (no_std-compatible) Witnesses SoulMatchOracle trait + NullOracle impl. bfld_error_re_exported_with_all_named_variants (no_std-compatible) Constructs every BfldError variant — removing one fires. std_only_types_are_re_exported (gated on `std`) BfldConfig, BfldPipeline, BfldEmitter, PrivacyGate, CapturePublisher, BfldPipelineHandle, PipelineInput, SensingInputs, IdentityFeatures, BfldEvent, BfldFrame, BfldPayload, TopicMessage + 12 free-function re-exports (identity_risk_score, availability_topic, online_message, offline_message, publish_availability_*, publish_discovery, publish_event, render_*, with_privacy_gating) + PAYLOAD_AVAILABLE, PAYLOAD_NOT_AVAILABLE, RISK_FACTOR_BYTES. mqtt_publisher_types_are_re_exported (gated on `mqtt`) RumqttPublisher type + with_lwt free function signature. ADR-124 status (iter step 0 sibling check): - docs/adr/ADR-124-rvagent-mcp-ruvector-npm-integration.md unchanged at 431 lines. SENSE-BRIDGE scope remains orthogonal. ACs progressed: - ADR-118 §2.1 public-API stability — every documented re-export has a named-symbol regression test. Accidental removal fires loudly at build time rather than as a silent SemVer break on downstream consumers (cog-ha-matter, wifi-densepose-sensing-server, pip wifi-densepose, sibling-agent SENSE-BRIDGE crate). Test config: - cargo test --no-default-features → 101 passed (97 + 4 no_std-compat — the std-only mod test is cfg-out) - cargo test → 308 passed (303 + 5) Out of scope (next iter target): - PR-readiness pivot still pending: CHANGELOG batch across iters 1-45, witness bundle regeneration, AC closeout table for the PR description. External-resource-gated work (KIT BFId, Pi5/Nexmon) still skipped. Co-Authored-By: claude-flow --- .../tests/public_api_snapshot.rs | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 v2/crates/wifi-densepose-bfld/tests/public_api_snapshot.rs diff --git a/v2/crates/wifi-densepose-bfld/tests/public_api_snapshot.rs b/v2/crates/wifi-densepose-bfld/tests/public_api_snapshot.rs new file mode 100644 index 00000000..b24d90d1 --- /dev/null +++ b/v2/crates/wifi-densepose-bfld/tests/public_api_snapshot.rs @@ -0,0 +1,197 @@ +//! Public API surface snapshot. Compile-time witness that every `pub use` +//! re-export from `lib.rs` survives refactors. A future PR that removes +//! one of these breaks the build with a specific named-symbol error, +//! which is a much louder signal than a silent SemVer-breaking removal. +//! +//! Two feature configurations are exercised: +//! - Always available (no_std-compatible core) +//! - `feature = "std"` items behind a cfg guard +//! +//! `feature = "mqtt"` items have their own snapshot test below. + +// --- always-available exports (work under `--no-default-features`) ---- + +use wifi_densepose_bfld::frame::{flags, BFLD_HEADER_SIZE, BFLD_MAGIC, BFLD_VERSION}; +use wifi_densepose_bfld::sink::{ + check_class, LocalKind, LocalSink, MatterKind, MatterSink, NetworkKind, NetworkSink, Sink, +}; +use wifi_densepose_bfld::{ + BfldError, BfldFrameHeader, CoherenceGate, EmbeddingRing, GateAction, IdentityEmbedding, + MatchOutcome, NullOracle, PrivacyClass, SignatureHasher, SoulMatchOracle, EMBEDDING_DIM, + RF_SIGNATURE_LEN, RING_CAPACITY, SITE_SALT_LEN, +}; + +#[test] +fn always_available_types_are_re_exported() { + // Type-existence witnesses. Each line will fail to compile if the + // corresponding `pub use` is removed from lib.rs. + let _: PrivacyClass = PrivacyClass::Anonymous; + let _: GateAction = GateAction::Accept; + let _: MatchOutcome = MatchOutcome::NotEnrolled; + let _: BfldFrameHeader = BfldFrameHeader::empty(); + let _: CoherenceGate = CoherenceGate::new(); + let _: NullOracle = NullOracle; + let _: EmbeddingRing = EmbeddingRing::new(); + let _: SignatureHasher = SignatureHasher::new([0u8; SITE_SALT_LEN]); + let _: IdentityEmbedding = IdentityEmbedding::from_raw([0.0; EMBEDDING_DIM]); + + // Compile-time const witnesses. + let _: u32 = BFLD_MAGIC; + let _: u16 = BFLD_VERSION; + let _: usize = BFLD_HEADER_SIZE; + let _: usize = EMBEDDING_DIM; + let _: usize = RING_CAPACITY; + let _: usize = RF_SIGNATURE_LEN; + let _: usize = SITE_SALT_LEN; + let _: u16 = flags::HAS_CSI_DELTA; + let _: u16 = flags::PRIVACY_MODE; + let _: u16 = flags::SELF_ONLY; + let _: u16 = flags::KNOWN_FLAGS_MASK; + let _: u16 = flags::RESERVED_FLAGS_MASK; +} + +#[test] +fn sink_trait_hierarchy_re_exported() { + fn assert_sink() {} + fn assert_local() {} + fn assert_network() {} + fn assert_matter() {} + assert_sink::(); + assert_local::(); + assert_sink::(); + assert_network::(); + assert_sink::(); + assert_network::(); + assert_matter::(); + + // check_class is reachable. + let _ = check_class::(PrivacyClass::Anonymous); +} + +#[test] +fn soul_match_oracle_trait_re_exported() { + fn assert_oracle() {} + assert_oracle::(); +} + +#[test] +fn bfld_error_re_exported_with_all_named_variants() { + let _ = BfldError::InvalidMagic(0); + let _ = BfldError::UnsupportedVersion(0); + let _ = BfldError::Crc { expected: 0, actual: 0 }; + let _ = BfldError::PrivacyViolation { reason: "X" }; + let _ = BfldError::InvalidPrivacyClass(0); + let _ = BfldError::TruncatedFrame { got: 0, need: 0 }; + let _ = BfldError::MalformedSection { offset: 0, reason: "X" }; + let _ = BfldError::InvalidDemote { from: 0, to: 0 }; +} + +// --- `std` feature exports -------------------------------------------- + +#[cfg(feature = "std")] +mod std_surface { + use wifi_densepose_bfld::{ + availability_topic, identity_risk_score, offline_message, online_message, publish_event, + publish_availability_offline, publish_availability_online, publish_discovery, + render_discovery_payloads, render_events, BfldConfig, BfldEmitter, BfldEvent, BfldFrame, + BfldPayload, BfldPipeline, BfldPipelineHandle, CapturePublisher, IdentityFeatures, + PipelineInput, PrivacyClass, PrivacyGate, Publish, SensingInputs, TopicMessage, + PAYLOAD_AVAILABLE, PAYLOAD_NOT_AVAILABLE, RISK_FACTOR_BYTES, + }; + + #[test] + fn std_only_types_are_re_exported() { + let _: BfldConfig = BfldConfig::new("seed-snap"); + let _: BfldPipeline = BfldPipeline::new(BfldConfig::new("seed-snap")); + let _: BfldEmitter = BfldEmitter::new("seed-snap"); + let _: PrivacyGate = PrivacyGate; + let _: CapturePublisher = CapturePublisher::default(); + + // Free-function exports + let _: u32 = wifi_densepose_bfld::BFLD_MAGIC; + let _ = identity_risk_score(0.0, 0.0, 0.0, 0.0); + let _: String = availability_topic("seed-snap"); + let _: TopicMessage = online_message("seed-snap"); + let _: TopicMessage = offline_message("seed-snap"); + let _: &'static str = PAYLOAD_AVAILABLE; + let _: &'static str = PAYLOAD_NOT_AVAILABLE; + let _: usize = RISK_FACTOR_BYTES; + + // Type-erased witnesses for the publish + render helpers. + let mut cap = CapturePublisher::default(); + let _ = publish_availability_online(&mut cap, "seed-snap"); + let _ = publish_availability_offline(&mut cap, "seed-snap"); + let _ = publish_discovery(&mut cap, "seed-snap", PrivacyClass::Anonymous); + let _: Vec = render_discovery_payloads("seed-snap", PrivacyClass::Anonymous); + + // Event + frame + payload constructible. + let event = BfldEvent::with_privacy_gating( + "seed-snap".into(), 0, false, 0.0, 0, 0.0, None, + PrivacyClass::Anonymous, None, None, + ); + let _ = render_events(&event); + let _ = publish_event(&mut cap, &event); + + let _: BfldFrame = BfldFrame::new( + wifi_densepose_bfld::BfldFrameHeader::empty(), + Vec::new(), + ); + let _: BfldPayload = BfldPayload::default(); + let _: IdentityFeatures<'_> = IdentityFeatures::from_risk_factors(0.0, 0.0, 0.0, 0.0); + + // Publish-trait usage path. + fn _accepts_publisher(_: &mut P) {} + + // Sensing-inputs surface. + let _: SensingInputs = SensingInputs { + timestamp_ns: 0, + presence: false, + motion: 0.0, + person_count: 0, + sensing_confidence: 0.0, + sep: 0.0, + stab: 0.0, + consist: 0.0, + risk_conf: 0.0, + rf_signature_hash: None, + }; + + // PipelineInput + Handle types reachable from lib.rs. + let _ = PipelineInput { + inputs: SensingInputs { + timestamp_ns: 0, + presence: false, + motion: 0.0, + person_count: 0, + sensing_confidence: 0.0, + sep: 0.0, + stab: 0.0, + consist: 0.0, + risk_conf: 0.0, + rf_signature_hash: None, + }, + embedding: None, + }; + // BfldPipelineHandle type witness (don't actually spawn — costs a thread). + fn _accepts_handle(_: BfldPipelineHandle) {} + } +} + +// --- `mqtt` feature exports ------------------------------------------- + +#[cfg(feature = "mqtt")] +mod mqtt_surface { + use wifi_densepose_bfld::{with_lwt, RumqttPublisher}; + + #[test] + fn mqtt_publisher_types_are_re_exported() { + fn _accepts_pub(_: RumqttPublisher) {} + fn _accepts_with_lwt_signature( + opts: rumqttc::MqttOptions, + node: &str, + ) -> rumqttc::MqttOptions { + with_lwt(opts, node) + } + let _ = _accepts_with_lwt_signature; + } +}