Skip to content

feat(stability): per-source resync staleness override for Passive Mode (#3122)#3129

Merged
Yeraze merged 1 commit into
mainfrom
fix/issue-3122-configurable-staleness
May 21, 2026
Merged

feat(stability): per-source resync staleness override for Passive Mode (#3122)#3129
Yeraze merged 1 commit into
mainfrom
fix/issue-3122-configurable-staleness

Conversation

@Yeraze

@Yeraze Yeraze commented May 21, 2026

Copy link
Copy Markdown
Owner

Follow-up to #3125 / #3126 / #3128. Closes item 2 of the reporter's feedback on #3122 — exposing the resync staleness window as an advanced per-source setting.

Why

The reporter wrote: "4 hours is a reasonable default, but exposing it as an advanced per-source setting from the start would be ideal." Different fleets have different needs — a router-class node whose config rarely changes might prefer 24 h; a node with frequent remote channel rekeys might prefer 1 h. Hard-coding 4 h served the original bug fix; letting operators tune it without code changes is the next step.

What

  • `SourceConfig.passiveResyncStaleMs` — optional number (ms). Absent or invalid → 4 h default. Plumbed through:
    • `MeshtasticManager` constructor + `configureSource` (`this.passiveResyncStaleMs: number | null`).
    • All 5 construction sites in `sourceRoutes.ts`.
    • The update branch now restarts the transport when the value changes, alongside the existing passive-mode toggle.
  • `effectivePassiveResyncStaleMs()` — resolves the active threshold. Clamps overrides to [1 minute, 7 days]; anything outside that band falls back to the default. This prevents foot-guns where someone enters `0` (would resync on every flap) or an astronomical value (would never resync).
  • UI: a "Resync staleness window (hours)" numeric input appears under the Passive Mode toggle when it's checked. Blank = default. Range 0.0167 (1 min) to 168 (7 days), step 0.5. Server validates again — the UI bounds are just a hint.

What's not in this PR

All three reporter items are now landed when this merges. Future work that would still be welcome but isn't in scope:

  • Surfacing the active staleness window in the status response so the UI can show "refreshes every X hours".
  • Polling `GET /api/sources/:id/resync` for live cooldown countdown on the Resync button.

Test plan

  • `npx tsc --noEmit` clean
  • 9 new `passiveMode.test.ts` cases: default returned when no override; constructor override applied; `configureSource` apply + clear; below-floor + above-ceiling fall back to default; exact 1 min and 7 day boundaries accepted; NaN ignored
  • Full suite: 5293 pass / 3 pre-existing fails (`mqttBrokerManager` zero-hop encode failures; reproduce on clean main, unrelated)
  • Manual smoke: set a 1 h override, force a disconnect, verify reconnect after 1 h triggers a full sync; reconnect within the hour does not

🤖 Generated with Claude Code

#3122)

Follow-up to #3125 / #3126 / #3128. Closes item 2 of the reporter's
feedback on #3122 (advanced per-source setting for the staleness
window).

* SourceConfig gains optional passiveResyncStaleMs (ms). Falls back to
  the 4h class default when absent.
* MeshtasticManager.effectivePassiveResyncStaleMs() resolves the active
  threshold, clamping out-of-range overrides ([1 min, 7 days]) so a 0
  or astronomical value can't disable the safeguard.
* Plumbed through all 5 MeshtasticManager construction sites in
  sourceRoutes; a change in the value triggers a transport restart
  alongside the existing passiveMode toggle.
* UI: numeric "Resync staleness window (hours)" input appears under
  the Passive Mode toggle when enabled. Blank = use 4h default.

Tests: 9 new tests cover default, constructor override, configureSource
override + clear, below-floor / above-ceiling rejection, exact
boundary acceptance, and NaN rejection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented May 21, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


Code Review for PR #3129

Comprehensive review of the per-source resync staleness override implementation.

Todo List

  • Read repository guidelines (CLAUDE.md)
  • Analyze meshtasticManager.ts changes
  • Review DashboardPage.tsx UI implementation
  • Examine sourceRoutes.ts routing changes
  • Review test coverage in passiveMode.test.ts
  • Check for potential bugs and security issues
  • Evaluate performance considerations
  • Provide final recommendations

✅ Overall Assessment

This is a well-implemented, production-ready feature with excellent attention to detail. The code demonstrates strong defensive programming practices and thorough testing.

🏆 Strengths

Security & Validation

  • Excellent bounds checking: The 1-minute to 7-day range prevents foot-guns (instant resyncs from 0 or never-resyncs from astronomical values)
  • Type safety: Proper Number.isFinite() checks and null handling throughout
  • Input validation: Both client-side (UI bounds) and server-side validation

Code Quality

  • Clean architecture: Proper separation of concerns with validation in effectivePassiveResyncStaleMs() method
  • Consistent patterns: Follows established multi-source architecture conventions
  • Error handling: Graceful fallbacks when invalid values are provided
  • Documentation: Clear JSDoc comments explaining the validation logic

Testing

  • Comprehensive test coverage: 9 new test cases covering all edge cases
  • Boundary testing: Tests both min/max boundaries and invalid ranges
  • State management: Tests constructor, configureSource(), and clearing overrides
  • Defensive cases: Tests NaN, out-of-range, and non-numeric inputs

User Experience

  • Intuitive UI: Hours-based input (more user-friendly than milliseconds)
  • Clear guidance: Helpful placeholder text and range documentation
  • Progressive disclosure: Only shows when passive mode is enabled
  • Sensible defaults: Empty field = 4 hour default (no configuration burden)

🔍 Technical Analysis

MeshtasticManager Changes (src/server/meshtasticManager.ts)

// Lines 421-426: Well-defined constants
private static readonly PASSIVE_RESYNC_STALE_MS = 4 * 60 * 60 * 1000; // 4 hours  
private static readonly PASSIVE_RESYNC_STALE_MIN_MS = 60_000; // 1 minute
private static readonly PASSIVE_RESYNC_STALE_MAX_MS = 7 * 24 * 60 * 60 * 1000; // 7 days

The validation logic in effectivePassiveResyncStaleMs() is robust:

  • Uses Number.isFinite() to reject NaN/Infinity
  • Applies sensible bounds checking
  • Falls back gracefully to defaults

UI Implementation (src/pages/DashboardPage.tsx)

  • Smart conversion: Stores ms internally, displays hours to users (lines 358-363)
  • Proper form handling: Includes both UI bounds and server validation
  • Accessibility: Good labels and help text

Routing Integration (src/server/routes/sourceRoutes.ts)

  • Restart logic: Correctly restarts transport when staleness value changes (lines 435-436)
  • Consistent pattern: Used in all 5 construction sites as documented

🚀 Performance Considerations

  • Minimal overhead: Only affects passive mode sources
  • Efficient validation: Simple numeric comparisons
  • No runtime impact: Configuration is read once during connection setup

🔒 Security Assessment

  • No injection risks: Numeric input with proper validation
  • Resource protection: Bounds prevent resource exhaustion attacks
  • Data isolation: Properly scoped per-source (follows multi-source architecture)

🧪 Test Quality

The test suite is exemplary:

  • Covers all edge cases and boundary conditions
  • Uses proper mocking techniques
  • Tests both positive and negative scenarios
  • Follows established patterns in the codebase

📋 Minor Recommendations

  1. Consider future enhancement: The PR notes mention surfacing active staleness in status responses - this would be valuable for operators
  2. Internationalization: The UI strings use the t() function correctly for i18n
  3. Documentation: Consider updating any deployment docs if this setting affects operational procedures

🎯 Final Verdict

✅ APPROVED - This PR demonstrates exceptional code quality with:

  • Robust security and validation
  • Comprehensive testing
  • Clear user experience
  • Proper architectural integration
  • Excellent defensive programming

The implementation is production-ready and follows all repository best practices outlined in CLAUDE.md.


Job Run

@Yeraze Yeraze merged commit 66967c5 into main May 21, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant