feat(#690): expose observer skew + per-hash evidence in clock UI#906
feat(#690): expose observer skew + per-hash evidence in clock UI#906
Conversation
…ck-skew API
Extend GET /api/nodes/{pubkey}/clock-skew to return recentHashEvidence
(most recent 10 hashes with per-observer raw/corrected skew breakdown)
and calibrationSummary (total/calibrated/uncalibrated sample counts).
Evidence is cached during ClockSkewEngine.Recompute() so the route
handler remains cheap. Fleet endpoint omits evidence to keep payload small.
Test: TestNodeClockSkew_EvidencePayload — 3-observer scenario verifying
per-hash array shape, corrected = raw + offset math, and median.
Fetch /api/observers/clock-skew on page load and join by observer ID. Shows signed offset with severity badge and sample count tooltip. Observers without calibration data (singletons) show '—' not '0'.
Shows offset value, sample count, severity badge, and inline explainer describing how the offset is computed from multi-observer packets. Mirrors the node clock card style. No chart in v1.
Renders recentHashEvidence from the API in a collapsible details section. Each hash shows observer count, median corrected skew, and per-observer breakdown (raw, corrected, observer offset). Includes calibration summary line and plain-English severity reason at top.
532935c to
ad07b51
Compare
Replace inline severity IIFEs in observers.js and observer-detail.js with a shared window.observerSkewSeverity() function in roles.js. Identical threshold logic (300s=warning, 3600s=critical) — just deduped.
PR Polish — self-review + rebase (commit
|
🧓 Greybeard Review — PR #906Head: Findings: 0 BLOCKER · 0 MAJOR · 0 MINOR · 2 NITAPI Shape Consistency ✅All new fields follow established camelCase convention ( Backend Correctness ✅
Frontend Defensive Coding ✅All three pages (
Test Coverage ✅
Performance ✅
Independent Live Re-verificationBuilt from branch HEAD, ran synthetic 3-observer scenario via Go test:
NITs
Greybeard audit complete. Ship it. |
…r observers (v3 rebase) (#969) Rebased version of #968 (which was itself a rebase of #905) — resolves merge conflict with #906 (clock-skew UI) that landed on master. ## Conflict resolution **`public/observers.js`** — master (#906) added "Clock Offset" column to observer table; #968 split "Last Seen" into "Last Status" + "Last Packet" columns. Combined both: the table now has Status | Name | Region | Last Status | Last Packet | Packets | Packets/Hour | Clock Offset | Uptime. ## What this PR adds (unchanged from #968/#905) - `last_packet_at` column in observers DB table - Separate "Last Status Update" and "Last Packet Observation" display in observers list and detail page - Server-side migration to add the column automatically - Backfill heuristic for existing data - Tests for ingestor and server ## Verification - All Go tests pass (`cmd/server`, `cmd/ingestor`) - Frontend tests pass (`test-packets.js`, `test-hash-color.js`) - Built server, hit `/api/observers` — `last_packet_at` field present in JSON - Observer table header has all 9 columns including both Last Packet and Clock Offset ## Prior PRs - #905 — original (conflicts with master) - #968 — first rebase (conflicts after #906 landed) - This PR — second rebase, resolves #906 conflict Supersedes #968. Closes #905. --------- Co-authored-by: you <you@example.com>
Summary
UI completion of #690 — surfaces observer clock skew and per-hash evidence that the backend already computes but wasn't exposed in the frontend.
Not related to #845/PR #894 (bimodal detection) — this is the UI surface for the original #690 scope.
Changes
Backend: per-hash evidence in node clock-skew API (commit 1)
GET /api/nodes/{pubkey}/clock-skewto returnrecentHashEvidence(most recent 10 hashes with per-observer raw/corrected skew and observer offset) andcalibrationSummary(total/calibrated/uncalibrated counts).ClockSkewEngine.Recompute()— route handler is cheap.Frontend: observer list page — clock offset column (commit 2)
/api/observers/clock-skewonce on page load, joins by ObserverID.Frontend: observer-detail clock card (commit 3)
Frontend: node clock card evidence panel (commit 4)
Test Results
New test:
TestNodeClockSkew_EvidencePayload— 3-observer scenario verifying per-hash array shape, corrected = raw + offset math, and median.No frontend JS smoke test added — no existing test harness for clock/observer rendering. Noted for future.
Screenshots
Screenshots TBD
Perf justification
Evidence is computed inside the existing
Recompute()cycle (already O(n) on samples). ThehashEvidencemap adds ~32 bytes per sample of memory. Evidence is stripped from fleet responses. Per-node endpoint returns at most 10 evidence entries — bounded payload.