Monotonic time & prep for router graceful retirement#10718
Open
NomDeTom wants to merge 4 commits into
Open
Conversation
WS2 — Monotonic uptime abstraction:
- New src/Time.{h,cpp}: getMillis() (drop-in for millis()) and getMillis64()
(rollover-immune 64-bit, for >49.7-day durations). Test injection via
Time::setTestMillis()/advanceTestMillis(), default OFF so suites relying on
real time are unaffected. Hosted via configuration.h so it's available
codebase-wide without per-file includes.
- Swept all ~485 millis() call sites across src/ to Time::getMillis(), except
the providers (platform millis() definitions, USBHal override) and the seam
itself. HopScalingModule's production clock now routes through Time::.
- Added explicit Time.h includes to the ~19 files that didn't transitively
reach configuration.h.
WS3 — bin/run-tests.sh: runs the native coverage suite and emits one
RED/AMBER/GREEN verdict with a canonical suite-count cross-check (AMBER if
fewer suites ran than exist under test/), per .notes/test-passfail-filter.md.
New test/test_time/ covers injection + 64-bit rollover across 0xFFFFFFFF.
Verified: native `pio run -e native` links clean; `bin/run-tests.sh` => GREEN
20/20 suites, 342/342 cases (incl. test_time, hop_scaling, traffic_management).
Deferred (recorded): unsafe millis() deadline comparisons in Power.cpp /
GPS.cpp left as rename-only (uint32 sentinel hazard) — to convert with test
coverage in a follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The same outcome is spelled differently by each layer (Unity :PASS/:FAIL: per assertion, pio [PASSED]/[FAILED]/[ERRORED] per suite, "N succeeded"/"M failed" summary). Grepping one spelling misses the others and yields a false verdict (the trap that produced earlier false greens). FAIL_RE/PASS_RE now alternate over every spelling. The canonical suite cross-check now treats pio-SKIPPED suites as accounted-for so hardware-only skips don't false-AMBER. Deliberately not aligned to the CI invocation — this is the local/agent verdict tool; CI keeps its JUnit path. Validated against captured logs + a live run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Auto-demote an unattended infrastructure node one rung per ~3 months of
cumulative uptime with no admin session. Opt-in (default OFF).
- Proto (hand-stubbed in generated nanopb; .proto/regeneration deferred to the
upstream protobufs PR — see .notes/router-retirement-proto.md):
* DeviceState.router_retirement_credit_secs (persisted credit, tag 14)
* ModuleConfig.RouterRetirementConfig {enabled, step_threshold_secs} oneof
member (tag 17) + the LocalModuleConfig runtime field (tag 18)
- RouterRetirementModule (OSThread): hourly credit accrual; on threshold,
demote one rung via installRoleDefaults + saveToDisk + reboot. Policy split
into pure static helpers (isRetirableRole / nextRetirementRole /
effectiveThresholdSecs / shouldRetire) for unit testing without globals.
- AdminModule: any admin session (local OR remote) resets the credit.
- test/test_router_retirement/: 12 cases over the policy helpers.
Validated: pio run -e native links clean; bin/run-tests.sh => GREEN 21/21
suites, 354/354 cases (incl. new test_router_retirement + test_time).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
⚡ Try this PR in the Web FlasherWarning This is an automated, unreviewed CI test build. Back up your device configuration Supported boards built by this PR (24)
Build artifacts expire on 2026-07-15. Updated for |
Contributor
Firmware Size Report22 targets | vs
Show 17 more target(s)
Updated for a995220 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three related infrastructure workstreams on one branch (off
develop@8a0c7592c):millis()call site insrc/swept onto it.bin/run-tests.sh, a single RED/AMBER/GREEN verdict so abroken suite is caught locally, not in CI.
itself so abandoned infrastructure stops clogging dense meshes.
All three are either behaviour-neutral or default-off. ~129 files, mostly the mechanical clock sweep.
1 · Monotonic time (
src/Time.{h,cpp})A small root-level utility (mirrors
memGet), hosted viaconfiguration.h:getMillis()— drop-in formillis()(rollover-safe with the usual subtraction idiom).getMillis64()— 64-bit monotonic ms; immune to the 49.7-dayuint32_twrap.PIO_UNIT_TESTING, a settable virtual clock (Time::setTestMillis()/advanceTestMillis()) — default OFF, so suites using real time are unaffected. One seam for allsuites, replacing the ad-hoc per-module
s_testNowMsclocks.Every
millis()insrc/is swept togetMillis()except the providers themselves (platformdefs,
USBHal.hoverride) and the seam. The two existing module clocks (HopScaling,TrafficManagement) now delegate to
Time::.2 ·
bin/run-tests.shThe authoritative local test verdict. Runs the native suites, then:
[PASSED]/:PASS,[FAILED]/:FAIL:,[ERRORED],error:, crash signatures) — a positive summary is required, not just absence offailed;ls test/test_*/→ AMBER if one silentlygoes missing (SKIPPED-aware);
RESULT: GREEN N/N/AMBER ran X/N missing: …/RED …).Deliberately not wired to CI's exact invocation — it's the local pre-flight.
3 · Router retirement slope (default OFF)
A persisted "managed-uptime credit" accrues with uptime and resets on any authenticated admin
session (remote over-mesh or local USB/BLE — any management channel counts as "still
tended"). At the threshold (~3 months) the node demotes one rung ROUTER → ROUTER_LATE → CLIENT
and resets the credit; CLIENT is the floor (no-op). Demotion reuses the existing role-change path
(
installRoleDefaults+saveChanges+ reboot).Policy is pure static helpers (fully unit-tested); device wiring is an
OSThreadregistered behindthe config flag. Default OFF — no behaviour change unless explicitly enabled.
Testing
test/test_time/— provesgetMillis64()crosses0xFFFFFFFFcorrectly under injection.test/test_router_retirement/— credit accrual, remote/local admin reset, the full slope,threshold boundary, CLIENT no-op, disabled-by-default (uses the WS1 time seam to fast-forward
3-month windows with no real sleep).
./bin/run-tests.sh→ GREEN;pio run -e nativelinks.DeviceState(
router_retirement_credit_secs) and aRouterRetirementConfigtomodule_config, hand-editeddirectly in the generated
*.pb.{h,cpp}. This branch will not build for others until theupstream
meshtastic/protobufsPR lands and the submodule is bumped (same caveat pattern asPR1's
snr_q4). The.protosource change + regeneration is a follow-up.millis()rollover comparisons atPower.cpp:806/811andGPS.cpp:1532were leftrename-only — the
uint32-1sentinel makes the subtraction-idiom conversion risky withouttest coverage. Convert later with tests;
getMillis64()is available.runOnceaccrual + demote/reboot) is unit-tested at thepolicy-helper level only; exercise the full demote on bench/sim before relying on it.
Commits
0ec742c0f— monotonic time: centralTime::seam + fullmillis()sweep + test runner42c99c6e4— run-tests.sh: match all pass/fail spellings; SKIPPED-aware count check78f7cb3ad— router retirement slope (ROUTER → ROUTER_LATE → CLIENT)🤖 Generated with Claude Code
🤝 Attestations