Iter 30 — defends the iter 29 REST endpoints + iter 23 WebSocket
broadcast with tests, AND deduplicates the four call sites that all
built the same NodeSyncSnapshot inline.
Refactor:
Add `NodeState::sync_snapshot() -> Option<NodeSyncSnapshot>` as the
single source of truth. All four call sites simplified:
1. node_sync_endpoint (REST /api/v1/nodes/:id/sync) — 12 → 5 lines
2. mesh_endpoint (REST /api/v1/mesh) — 11 → 3 lines
3. WebSocket vitals-only NodeInfo (line 4284) — 9 → 1 line
4. WebSocket CSI-frame NodeInfo (line 4617) — 9 → 1 line
Net: -35 lines, single point of contact for any future schema change.
Tests (3 new, all green; brings binary suite to 95+):
fresh_node_with_no_sync_returns_none
Mirrors REST 404 "no_sync" + WebSocket sync omission paths.
node_with_latest_sync_produces_correct_snapshot
Mirrors REST 200 OK + WebSocket sync field paths.
Asserts §A0.10's measured 1_163_565 µs offset survives the helper.
snapshot_reflects_leader_state
Leader-case shape: is_leader=true, offset≈0 (–7 µs call-stack).
These tests cover BOTH REST routes and BOTH WebSocket NodeInfo sites
through the shared helper — one test per behavioral path, no axum
state plumbing required. cargo check -p ...sensing-server → green.
Co-Authored-By: claude-flow <ruv@ruv.net>