fix(calibration): bind server receipts to room identity - #1903
Conversation
|
2026-09-11 readiness update: synchronized this branch with current RuView main and fixed the prior semantic-conventions failure by separating the serialized calibration receipt schema from telemetry literals. New exact head: Local verification: the formerly failing exact test passes; 22 Current CI has one known failure: S3 8 MB firmware is evaluated against the old absolute size gate inherited from main. The partition-relative S3 8 MB gate is already fixed and green in sibling PR #1904. Do not treat #1903 as merge-ready yet: merge #1904 first, resynchronize #1903, rerun its full gates, freeze/review the new exact head, then proceed to real three-C6 calibration. |
ruvnet
left a comment
There was a problem hiding this comment.
Dream cycle exact-head review at 2c0abcc8023a45c6bf21dcbcfd78e9139b7bf172.
Frozen hypothesis: the new boot/session/binding/model identities remain correct across start, finish, promotion, reset, and long-running expiry, with the required exact-head envelope green.
The identity additions and focused 22-test receipt are relevant, and four focused policy/security/benchmark workflows passed. The required Continuous Integration run 34633581574 did not: the Rust workspace job failed the candidate's own calibration_expiry_tests::long_running_expiry_is_not_reported_active_for_runtime_or_bootstrap assertion (active remained true when false was required). Docker, API documentation, and performance jobs were then skipped.
Because the failure is on the exact lifecycle invariant this PR changes, other green jobs cannot substitute for it. Repair the elapsed-time/unit path, retain the explicit identity mismatch cases, and rerun the full exact-head CI plus a real multi-node start→finish→promotion/restart receipt.
Verdict: REJECT.
…g stream The Mac app gates its held-out empty-room check on `calibrated_presence_evidence` carrying `ruview.calibration.calibrated-presence-evidence.v2`. No shipped server has ever emitted that key, so the check fails on its first iteration with "No fresh calibrated presence result matched this room's current model receipt", `bootstrap/promote` is never called, and `bootstrap_baseline::store` -- the only path that persists a calibration -- never runs. A completed 62-minute field model is therefore lost on the next restart. Emit the evidence bound to the active model receipt. `None` unless an explicit calibration is fresh and the field model scores the observation without a heuristic fallback, so a present value is always calibrated. Extract the field-model scoring history into `scoring_history()` and reuse it for both `person_count_at` and the evidence, so the published evidence cannot disagree with the count derived from the same model. Co-Authored-By: Ruflo & AQE Claude-Session: https://claude.ai/code/session_01Qh7eir6C2U5CSwD7GR43Zv
…fusals Assert the exact wire schema and every identity field the Mac app matches against its model receipt, plus that evidence agrees with the server's own person count. Pair the refusals (no receipt, bootstrap-only authority, expired model) with the positive case so an absent value can never be read as proof when the path is simply broken. Co-Authored-By: Ruflo & AQE Claude-Session: https://claude.ai/code/session_01Qh7eir6C2U5CSwD7GR43Zv
… baseline
A room binding may name several radios, and every one of them was admitted
into the field model. That model is single-link: one baseline, one set of
amplitude offsets. Averaging two radios' offsets into it flattens the
eigenstructure and leaves only a scalar energy threshold behind.
Measured on three ESP32-C6 nodes:
4-node binding baseline_eigenvalue_count 0, residual threshold 18.90,
empty room reported an occupant in 1162/1162 frames
1-node binding baseline_eigenvalue_count 5, residual threshold 3.65,
empty room absent 604/604, occupied present 616/616
`person_count_at` already refused to score a non-bound radio, calling it
"deterministic false occupancy from hardware-specific amplitude offsets" --
but only when exactly one node was bound. With more it fell through to the
shared mixed history, so the guard never fired in the case it described.
Restrict the baseline write, not the admission. Every bound radio is still
admitted and recorded as a contributor: `calibration_source_nodes_missing`
refuses to finalize until each one proves it is live on the frozen grid, so
gating them out of the feed path entirely deadlocks the capture -- measured
on hardware as 43,120 frames over 51 minutes stuck in `collecting` with
missing_source_node_ids=[12,13,14]. Only `maybe_feed_calibration` is
restricted to the grid-bound radio, and scoring follows that same radio
whatever the size of the room's node set.
This is the narrowing `bootstrap_baseline::store` already applies when it
persists `vec![binding.source_node_id]` as the frozen model source.
Co-Authored-By: Ruflo & AQE
Claude-Session: https://claude.ai/code/session_01Qh7eir6C2U5CSwD7GR43Zv
…rse minority `accept_grid` locks each node onto the densest grid it has seen and rejects sparser frames from the feature path -- on an ESP32-C6 the ~16% HT 64-bin minority that arrives alongside HE-SU 256-bin. `select_calibration_grid` ordered candidates by `max_gap_s` first, so it preferred whichever grid looked temporally smoothest. A sparse trickle at a metronomic cadence has a smaller worst-case gap than a busy stream with one scheduling hiccup, so selection kept choosing the grid admission is designed to discard. Measured on four ESP32-C6 nodes: a capture bound 64sc on node 11, every node then locked onto 256sc, the bound grid went stale with no frame for five minutes, frame_count froze at 10,997 and `calibration_grid_is_fresh` failed. The capture could never finalize -- `collecting` for 858 s with both minimums long since met, no error surfaced, and a flickering UI as the client retried. The same rig bound 256sc on a different capture and finalized with five baseline eigenvalues. Order by subcarrier count first so selection agrees with admission, then fall back to the previous gap/rate/ppdu ordering to choose among equally dense candidates. Co-Authored-By: Ruflo & AQE Claude-Session: https://claude.ai/code/session_01Qh7eir6C2U5CSwD7GR43Zv
calibration_status unconditionally overwrote status with "none" whenever active was false, which also erased the legitimate "expired" status for a calibration that ran long ago and has since expired -- losing the distinction between "never calibrated" and "was calibrated, now expired". Fixes the pre-existing failure in calibration_expiry_tests::long_running_expiry_is_not_reported_active_for_runtime_or_bootstrap, called out as a known, unrelated gap in PR #1936. Co-Authored-By: claude-flow <ruv@ruv.net>
…eld_bridge The new schema constant was defined and asserted as a raw string literal directly in main.rs, which the semconv registry test scans and requires every "ruview.*" literal there to be a registered attribute/event. This isn't a tracing event, though -- it's a wire-schema tag structurally identical to CALIBRATION_MODEL_RECEIPT_SCHEMA, which already lives in field_bridge.rs specifically to stay out of that scan. Move the new constant there and reference it by identifier from main.rs, matching the existing precedent instead of hand-editing the generated semconv registry. Co-Authored-By: claude-flow <ruv@ruv.net>
| /// Minimum time a candidate room-level classification must be observed | ||
| /// *consistently* before the debounced room state actually flips to it. | ||
| /// | ||
| /// `fuse_room` recomputes its plurality vote fresh every cycle with no |
There was a problem hiding this comment.
🌹🙏
| /// `fuse_room` recomputes its plurality vote fresh every cycle with no |
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
…bration-process-identity fix(calibration): bind server receipts to room identity dd02efe
Summary
Verification
cargo check -p wifi-densepose-sensing-servercargo test -p wifi-densepose-sensing-server --bin sensing-server bootstrap_vital_publication_tests --no-fail-fast— 22 passedcargo build --locked --release -p wifi-densepose-sensing-server --bin sensing-serverGET /api/v1/calibration/statusreturned a validcal-boot-<32 hex>identity,binding_mode: none, explicit legacy flag, null session/model identities, and empty contribution setsEvidence boundary
The isolated runtime probe verifies the idle contract. Real multi-node contribution, ten-minute empty-room collection, model finalization, and held-out promotion remain for the physical Mac/C6 test session.