fix(frontend): relay-aware staleness for infra nodes + dim-not-delete (#1598, PR A) - #1815
Conversation
…Kpa-clawbot#1598) Implements PR A from the Kpa-clawbot#1598 triage fix path (r6, decisions locked): - getNodeStatus() now accepts a full node object (legacy (role, lastSeenMs) signature still works). For infra roles (repeater/room), freshness is max(advert-based timestamp, last_relayed), so actively-relaying backbone repeaters with long/disabled advert intervals are no longer marked stale. - pruneStaleNodes() in the Live view dims repeater/room markers instead of deleting them, regardless of _fromAPI origin. - All seven getNodeStatus call sites (nodes.js, map.js, live.js) pass the node object; nodes.js status explanation surfaces 'Last relayed' when relay participation is the fresher signal. - Unit tests incl. the backbone-repeater fixture from triage (last_seen=25h, last_relayed=5min -> active). Empirical motivation: four operator deployments confirm 88% of infra nodes with last_relayed > last_seen; on live.saarmesh.de 160 infra nodes exceed even infraSilentMs=72h while actively relaying. Co-Authored-By: Claude <noreply@anthropic.com>
…t-delete + relay-aware fixture Co-Authored-By: Claude <noreply@anthropic.com>
Bot polish reviewVerdict: MERGE-READY Findings
Persona votes
Merge gate
Automated polish review. Human maintainer makes the final call. This is a clean, well-tested change; the MINORs are follow-up material, not merge blockers. |
|
Cross-referencing today's triage rounds on #1611, since they land on this PR. The #1611 re-triage this morning concluded that the cadence-adaptive threshold should be split out as a separate follow-up, and that the #1598 r6 fix is the critical path to ship first — assessed at effort S, described as a That implementation is this PR. Open since 2026-07-01, bot review MERGE-READY since 2026-07-02, implementing all three locked r6 decisions:
No rush intended; flagging it only because the design discussion has now converged on this change three times today across #1611, #1845 and #1598, and it may not be obvious that the code is already sitting here. Happy to rebase if it has drifted against master. |
Delta re-review —
|
…pa-clawbot#1815 design) Locally redesigned port of upstream Kpa-clawbot#1815, not a direct patch apply -- ported design diverges from upstream on two points found during review: last_relayed is never a status/display candidate (repeater_liveness.go's collectRelayEntriesLocked can source it from a 1-byte pubkey-prefix collision bucket), and status-freshness is kept separate from display-freshness (_liveSeen is a valid status signal but excluded from "Last activity" text, since live.js stamps it to Date.now() for every API-loaded node regardless of true recency). roles.js: new getNodeFreshness(node, nowMs, fields?) returns the newest of {_liveSeen, _lastHeard, last_heard, last_seen} that parses to a finite timestamp (number or Date-parseable string only; no implicit Date-coercion of booleans/objects/arrays), max-of-valid rather than first-truthy. A candidate within 30s of nowMs (reusing observers.js's existing clock-skew tolerance magnitude, not its code) is clamped to nowMs; further out is dropped. Ties resolve to the earlier field in priority order. getNodeStatus gains a node-object form (uses getNodeFreshness) alongside the untouched legacy (role, lastSeenMs) signature -- same threshold table and boundary comparison shared via small internal helpers so the two can't drift. live.js: pruneStaleNodes uses getNodeFreshness directly so "no safe timestamp" (skip, leave untouched) is distinguishable from "known but stale" (evaluate against threshold) -- a plain active/stale status can't make that distinction. Infra (repeater/room) is now always dimmed on staleness, never removed, even when WS-only; companion/ sensor/observer keep the prior removal behavior. Documented, not mitigated: this removes the memory bound that used to apply uniformly to all WS-only nodes -- a long-lived tab can accumulate one dimmed marker per distinct repeater/room ever observed, no LRU/cap added. map.js / nodes.js: the 7 existing getNodeStatus(role, ms) callsites now pass the full node object. nodes.js's getStatusInfo separates the two concerns explicitly: status via getNodeStatus(n) (full candidate set), but its "Last activity" explanation text is built from a narrower getNodeFreshness(n, now, ['_lastHeard','last_heard', 'last_seen']) call that excludes both _liveSeen and last_relayed -- falls back to "Last activity unknown" when nothing safe is available even if status is active via _liveSeen alone. Renamed from "Last heard" since last_seen can now be fresh purely from a safely-resolved relay hop (Kpa-clawbot#1855's touchRelayNodesLocked), not just an ADVERT. Tests: 31 new assertions in test-frontend-helpers.js (getNodeFreshness parsing/max-selection/tie-break/future-tolerance, getNodeStatus legacy + node-object forms, dim-not-delete matrix incl. WS-only infra and a last_relayed-only node being left untouched, getStatusInfo's Last activity/_liveSeen-exclusion/last_relayed-exclusion). Updated 3 existing assertions whose expected behavior intentionally changed (infra dim-not-delete, "Last heard" -> "Last activity", removed lastHeardMs field -> statusAge). Updated test-aging.js's structural BUG CHECK regex to match the new getNodeStatus(n) callsite pattern. node test-aging.js: 18/18 pass (legacy signature fully unchanged). node test-frontend-helpers.js: 660/2 (2 pre-existing unrelated favStar failures, unchanged baseline) -- stable across 5 repeated runs. 38 additional map/live/nodes-related test files run directly (since test-all.sh's set -e halts at the known favStar failures before reaching them): 6 pre-existing failures (clustering pill counts, tile-provider dark/voyager URLs, animation z-order/null-guards, 2x missing Playwright) verified byte-identical against a stashed pre-change baseline -- none touch getNodeStatus/getNodeFreshness. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Closing and immediately reopening to get this a CI run against current master. Nothing is wrong with your PR and no action is needed from you. A plain re-run would reuse the merge commit from the original run and so would not include the #1923 fix, which was failing PRs that never touched the affected page. Context and the full list in #1922. Apologies for the notification noise. |
|
Review from the queue triage. Written 2026-08-30 against the tree at that time; posting now that the maintenance window in #1922 has opened. Verdict: approve the staleness half. One question on the pruning half. The premise checks out. The dual signature ( The question is the pruning change, which is a bigger behaviour change than the title suggests. Dimming rather than deleting is the right call for the user-visible behaviour, so ask for a bound |
Implements PR A from the #1598 triage fix path (r6 — all three operator decisions locked: confidence ≥0.75 [PR B], dim-not-delete, in-place
getNodeStatussignature extension).Changes
public/roles.js—getNodeStatus()(role, lastSeenMs)signature keeps working unchanged.max(advert-based timestamp, last_relayed). Freshness precedence mirrors existing call sites:_liveSeen>_lastHeard>last_heard>last_seen.last_relayedis only consulted for infra — companions keep pure advert/heard-based staleness (per @liquidraver's collision caveat; the ≥0.75-confidence_liveSeenrefresh is PR B).public/live.js—pruneStaleNodes()_fromAPIorigin. WS-only non-infra nodes are still removed to prevent unbounded memory growth.Call sites — all seven (
nodes.js×3,map.js×3,live.js×1) now pass the node object. The Nodes-page status explanation shows "Last relayed …" when relay participation is the fresher signal, so an Active badge next to an old "last heard" isn't confusing.Tests — 16 new
getNodeStatusunit tests incl. the triage's backbone-repeater fixture (last_seen=25h,last_relayed=5min →active);pruneStaleNodestests updated for dim-not-delete plus a new relay-aware prune test.Test results
node test-frontend-helpers.js: 641 passed, 2 failed — the 2 failures (favStar ★ assertions) are pre-existing on current master (verified on a clean upstream clone).Validation offer
live.saarmesh.de currently has 160 infra nodes past
infraSilentMs=72h while actively relaying (incl. KatS disaster-relief repeaters) — a ready-made test population. Happy to run this branch there and report before/after node visibility.Refs #1598 (PR A of two; PR B =
_liveSeenrefresh onresolved_path≥0.75 confidence).