View this page on MeshCore Docs.
Unit-test workflow follow-up - 2026-10-22
Baseline: ab6412e73c1c36a53e2ecf08c79e3c223200cb3d on
keymindCascade. The starting failure was
workflow run 39886822765.
Failure and build scope
The run exposed three independent failures:
- RAK4631 Full Companion exceeded its existing application flash region by 312 bytes.
- The Wio E5 Mini Repeater exceeded its existing application flash region by 1,284 bytes.
- The Wio Tracker L2 GPS host fixture lacked the new battery-calibration capability in its mock base class, preventing six harness builds.
The earlier calibration report's direct PlatformIO builds did not use the release wrapper. In particular, the wrapper links the Mini's runtime USB packet logger and embeds release metadata and Cascade defaults. A direct recipe passing its size check does not qualify that release artifact.
The relevant release builds must use these exact workflow commands, one PlatformIO process at a time:
bash build.sh build-firmware RAK_4631_companion_radio_full \
--firmware-version v1.18.0.1-halo-keymind-cascade-dev \
--radio-preset usa-cascade-fixed --profile cascade --require-ota
DISABLE_DEBUG=0 bash build.sh build-firmware wio-e5-mini_repeater \
--firmware-version v1.18.0.1-halo-keymind-cascade-dev \
--radio-preset usa-cascade-fixed --profile cascade --allow-no-ota
The exact Mini release-wrapper build passed with 245,376 of 245,760 application bytes used, leaving 384 bytes. Its runtime-memory gate also passed: 43,920 bytes available against 32,288 bytes required. The application boundary and existing 16 KiB filesystem are unchanged. The complete board matrix is tracked in the unit-test workflow.
RAK4631 Full Companion changes
The exact Full Companion release-wrapper build passed with 707,504 application bytes before its EndF trailer. The stamped UF2 application occupies 707,584 of 712,704 bytes, leaving 5,120 bytes within the existing boundary. The runtime memory gate passed with 81,228 bytes available against 74,576 bytes required; the DFU handoff and 13 capability checks also passed. Reader, contacts, Meshtext, sensors, Ethernet, SD support and LoRa OTA remain enabled. No flash boundary or memory gate was relaxed.
The RAK4631 recipe explicitly selects its existing voltage-policy calibration
store. RAK4631Board::begin() loads that policy before application settings.
This lets the compiler omit an unreachable second /battery_cal backend while
retaining the existing 20-byte NVP2 record and its CRC. The store fails closed
if called before policy initialization. A missing or corrupt policy initializes
safe defaults; unsuccessful writes still return an error. Other board recipes
retain their generic calibration backend and fallback behavior. A typed setter
also avoids formatting a CLI command only to parse it again.
The linked ARM crypto checks passed both RFC8032 vectors. Key generation used 1,200 bytes of function-frame stack and signing used 1,904 bytes, within the 2,048-byte gate. These figures exclude caller, ISR and task stacks. Linked OTA workspace checks passed four acquisition/release lifetimes without touching the protected SD authentication range. Neither check is hardware qualification. The linked Reader check passed all 879 references (885 calls), comparing 82,624 canonical bytes. Its maximum function-frame stack was 6,016 bytes; the check also reserves 1,536 caller bytes within the 8,192-byte loop stack. This excludes ISR/task stack and physical high-water measurements.
Beacon frequency and bandwidth parsing now shares the radio CLI's plain
decimal parser. This removes the general-purpose strtof/strtod conversion
code from the Full image and recovered 5,732 bytes in the final comparison.
Normal decimal settings, optional signs and decimal fractions remain available;
radio limits still reject invalid frequencies and bandwidths. Scientific and
hexadecimal numeric syntax is rejected, matching the ordinary radio CLI grammar.
NaN, infinity, whitespace and trailing junk are rejected as well.
Constant CLI error replies share their existing bounded formatter and prefix. The existing command replies retain their text, capacities and termination; new calibration replies use shorter wording while retaining the reading, effective multiplier and persistence result.
The first rerun exposed an RS-232 packaging check that requires one complete error-message literal as evidence that the merged command handler survived linking. That handler retains its original literal. The capability check is unchanged. Both Full and Reduced T114 Repeater release-wrapper builds then passed all eight capability checks and their flash, RAM and DFU gates.
The next run passed all twelve other jobs and reached a stale release-inventory
assertion in the main test job. The two SenseCAP Indicator LoRa repeater recipes
added by 3236164d2 legitimately preserve their N8/N16 partition layouts. The
fixture now expects 151 targets and explicitly checks both additions; all
partition-migration exclusions remain. The corrected selection suite, nRF52 OTA
profile suite and XIAO combined-layout suite pass locally. No firmware source
changed for this inventory correction.
Calibration coverage includes three command/scaling tests, three store tests
(including four platform backends), 19 battery-forwarding tests and four power
policy compatibility tests. The actual nRF52 policy/store integration checks
pre-initialization rejection with present or absent storage, failed mounts,
corrupt policy CRC, saved calibration, unchanged NVP2 bytes, reset, bounds and
write/rename failures. A sentinel /battery_cal remains untouched throughout
the RAK policy test. These are host tests, not physical ADC qualification.
Repeater diagnostic changes
Compact RX and TX packet records share one formatter. It emits the same
uppercase hexadecimal bytes, R or T prefix, and LF terminator. It retains
the existing bounded serial writer and DROP recovery marker.
When neither debug output nor packet-file logging is compiled, retry callbacks avoid formatting discarded diagnostic strings. Flood-retry reachability still updates for eligible successful echoes. Eligibility preserves the pending bridge state, source mask, progress marker, target mask and route-hash checks. The existing logged callback path remains intact.
The Repeater supplies its timestamp formatter whenever debug output, packet-file logging or noncompact USB packet records need it. Builds with no timestamp consumer inherit the base formatter without linking the unused conversion.
These changes retain the Mini's release USB packet logger, calibration, routing, host-service bridge, 50 neighbours and existing 16 KiB filesystem. They do not add feature cuts or change flash and RAM qualification gates.
Host regression results
Passed checks:
The complete native PlatformIO suite also passed all 1,897 test cases. The remaining 502 host scripts passed after fixture repairs, along with the required firmware-memory/ARM checks, both required Room browser suites, voice and G726 sanitizer variants, notification JavaScript checks and paired nRF52 logging checks. These results cover the steps beyond the stale inventory assertion without repeating unchanged firmware builds locally.
| Check | Coverage |
|---|---|
test/test_serial_packet_log/test_serial_packet_log.cpp - 8 tests |
Exact RX/TX bytes and LF; small FIFO; initially absent host; stalled host; 20 ms mid-record stall budget; truncation; drop counting and recovery markers. Run directly with GoogleTest, AddressSanitizer and UndefinedBehaviorSanitizer. |
test/test_mini_repeater_logging.py - 3 tests |
Actual Repeater RX, Dispatcher TX, packet codec and diagnostic callbacks across all eight carrier/management/file-logging combinations; maximum packet sizes; runtime logging gate; stalled writes; file-logging independence; host CLI and authorization contracts. |
| Flood-retry eligibility within that harness | Actual eligibility, bridge-heard mask and textual formatter agree for 576 source/target/progress/hash-size combinations in each feature variant. Missing state, empty/invalid paths, source-only echoes and progress-marker-only echoes do not refresh reachability. |
| Timestamp consumers within that harness | Production declaration and definition compile and return the expected formatter across all 16 debug/file/packet/compact combinations. |
test/test_cli_command_utils/test_cli_command_utils.cpp - 62 tests |
Includes three shared error-formatter regressions: exact output with literal percent directives; every capacity from zero through a complete reply; null destination at zero capacity; termination and untouched guard bytes; truncation at the 160-byte CLI boundary. Run directly with GoogleTest, AddressSanitizer and UndefinedBehaviorSanitizer. |
| CommonCLI dependency audit - 34 scripts, 166 tests | The 32 CommonCLI fixture suites and the Morse CLI and Beacon Manager suites pass after source-extraction fixtures include the real shared formatter. Includes RAK SD context handoff and its negative control, USB/ESP-NOW/RS232/UDP diagnostics, persistence, GPS, OTA, and the Morse FEM guard. |
test/test_beacon_manager.py - 20 tests, included in the audit above |
Actual Beacon Manager and extracted decimal parser accept ordinary signed/unsigned decimal tuples; reject exponent/hex notation, NaN/infinity, whitespace, trailing junk and out-of-range radio tuples. Existing scheduling, transactional storage and negative controls also pass. |
The Mini harness uses AddressSanitizer and UndefinedBehaviorSanitizer on Linux. Radio and serial hardware are stand-ins. These results do not qualify physical USB, RF, ADC readings, charging, bootloaders or installed devices.