test(meshcore): Phase 2 — companion channel + repeater DM auto-ack messaging system-test#4241
Conversation
Phase 2 of the MeshCore system-tests epic. Adds a small Node/ESM listener that connects to the app's Socket.IO endpoint with a bearer API token and resolves when a meshcore:send-confirmed event correlated by sourceId arrives, printing READY on connect so the calling bash test knows it is safe to send. The DM send itself stays in bash (session+CSRF) since the send route is session-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0158hFzsVbodeJK246qAE1dY
Appends Tests 8-12 to the Phase 1 MeshCore hardware harness:
- Test 8: acquire an API bearer token for the Socket.IO helper.
- Test 9: DM A->B, proven via B's /meshcore/messages?since= inbox poll,
with send-HTTP-status capture and a distinct diagnostic when every
send is rejected HTTP 400 (bad/unroutable pubkey) instead of RF loss.
- Test 10: create a dedicated #mm-systest hashtag channel on both
companions at a shared free slot, with the PSK derived the same way
as deriveHashtagSecretHex (SHA-256("#mm-systest")[0:16], base64).
- Test 11: channel message A->B on #mm-systest, verified via B's
per-channel backlog (falling back to the combined inbox filtered by
the channel's synthetic fromPublicKey).
- Test 12: resolve the "Yeraze Repeater" contact (tolerant name/advType
match + MESHCORE_REPEATER_PUBKEY override, hard-fail if absent),
start the ack-listener helper, wait for READY, then DM the repeater
with retries and assert the helper caught the firmware auto-ack.
All new traffic stays off Primary/gauntlet, using only DMs and the
dedicated #mm-systest channel. cleanup() now kills the helper process
and removes its temp files plus the send-status scratch file. No
change to tests/system-tests.sh -- the existing MeshCore phase already
invokes this script.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0158hFzsVbodeJK246qAE1dY
…ON_DM opt-in (near-field RF limitation) Live-hardware validation showed direct companion<->companion DM fails consistently on the reference rig (the two nodes sit ~3in apart -- near-field RF receiver overload), while channel messaging between the same two nodes and the Yeraze Repeater DM auto-ack both work reliably. This is a physical-layout limitation, not a test or product bug. Test 9 is now env-gated by MESHCORE_COMPANION_DM (default unset = SKIP with a clear diagnostic; the suite continues to Test 10). When opted in, it runs an advert-exchange + contacts/refresh pre-step (proven to establish mutual contacts/paths), then attempts the DM in both directions (A->B and B->A), passing if either direction delivers per either signal (receiver inbox poll or a meshcore:send-confirmed ack on the sender, via the existing meshcore-await-ack.mjs helper). Hard-fails only if both directions fail to deliver, or if every send is rejected HTTP 400 in both directions. Test 10 (channel creation), Test 11 (channel A->B receive), and Test 12 (repeater DM auto-ack) are unchanged -- both verified working on hardware. Final banner updated to reflect the new gating. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0158hFzsVbodeJK246qAE1dY
Companion<->companion DM gated behind MESHCORE_COMPANION_DM (near-field RF limitation, extensively diagnosed on live hardware); channel + repeater DM auto-ack are the hard assertions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0158hFzsVbodeJK246qAE1dY
|
Claude finished @Yeraze's task —— View job PR Review: Phase 2 MeshCore System TestsTasks:
Overall AssessmentThis is a well-structured test-infrastructure-only PR. The approach is pragmatic (round-robin port probing, opt-in DM gate, Socket.IO ack listener for firmware events that aren't REST-visible), and the live-hardware validation rationale is clearly documented. A few issues worth addressing before merge: Issues1.
|
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0158hFzsVbodeJK246qAE1dY
|
Claude finished @Yeraze's task —— View job PR Review: Phase 2 MeshCore System TestsTasks:
Overall AssessmentSolid test-infrastructure-only PR. The approach (opt-in companion DM gate, Socket.IO ack listener for firmware events not exposed over REST, live-hardware near-field rationale) is well-reasoned and clearly documented. A couple of real bugs worth fixing before merge. Bugs1. Duplicate
|
System Test ResultsMeshMonitor System Test ResultsTest Run: 2026-07-20 20:52:41 EDT ❌ Overall Result: FAILEDAborted on first failing test (fail-fast mode). Remaining tests skipped. Failed Test
|
…k + free-slot)
GET /api/channels returns a bare array (res.json(projected)); the '(.data // .)'
idiom throws in jq on an array ('.data' on an array errors, and '//' does not
catch a thrown error), so the Test 10 persistence check always saw an empty name
and the free-slot scan always saw an empty USED set (picking slot 1 blindly).
Select the array defensively by type instead. The channel itself persists and
delivers fine (verified live) — this was a verification-parsing bug.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0158hFzsVbodeJK246qAE1dY
|
Claude finished @Yeraze's task —— View job PR Review: Phase 2 MeshCore System TestsTasks:
Overall AssessmentSolid test-infrastructure-only PR. The architecture (Socket.IO ack listener for firmware events not exposed over REST, opt-in companion DM gate with clear near-field rationale, round-robin port probing) is well-designed. The two bugs flagged in earlier reviews remain unfixed — they're worth addressing before merge. Bugs (still present)1. Duplicate
|
System Test ResultsMeshMonitor System Test ResultsTest Run: 2026-07-20 21:13:06 EDT ❌ Overall Result: FAILEDAborted on first failing test (fail-fast mode). Remaining tests skipped. Failed Test
|
… non-delivery The channel PUT persists through to the physical companion (CMD_SET_CHANNEL), so a leftover #mm-systest from a prior run caused the free-slot picker to create a SECOND channel with the same derived secret — and MeshCore does not deliver a message sent on the duplicate slot (verified on hardware: same secret at slots 1+2 -> slot-2 send never received; each slot alone -> delivered). Delete every existing #mm-systest on both companions before creating exactly one, and delete it again after Test 11 to leave the device channel table as we found it. Validated end-to-end on the live rig: channel A->B + repeater auto-ack pass, companion DM skips by default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0158hFzsVbodeJK246qAE1dY
|
Claude finished @Yeraze's task —— View job PR Review: Phase 2 MeshCore System TestsTasks:
Overall AssessmentWell-structured test-infrastructure-only PR. The Socket.IO ack-listener design, the opt-in companion DM gate, and the pre/post-clean for the Bugs1. Duplicate
|
System Test ResultsMeshMonitor System Test ResultsTest Run: 2026-07-20 21:39:22 EDT Test Summary
✅ Overall Result: PASSEDAll deployment configurations are working correctly! Test DetailsConfiguration Import:
Quick Start Test:
Security Test:
V1 API Test:
Reverse Proxy Test:
Reverse Proxy + OIDC Test:
Virtual Node CLI Test:
Backup & Restore Test:
Database Migration Test:
DB Backing Consistency Test:
|
…urceId, slot comment, token status Review nits (PR #4241): (1)+(2) merge the two connect listeners behind a once-guard so READY prints once and the ack-wait budget starts from the first connect (stable TIMEOUT_MS, not TIMEOUT_MS+12s from process start); (3) String()- coerce data.sourceId before comparing; (4) comment the 1-7 channel-slot scan + note the MESHCORE_TEST_CHANNEL_SLOT override; capture the Test 8 token HTTP status. Behavior-preserving on the happy path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0158hFzsVbodeJK246qAE1dY
|
Thanks for the thorough review. Addressed in fcd2ce3:
Items #5-#10 and the Note: this is a behavior-preserving refactor of test-only code; the hardware system-test re-runs on this commit to re-validate. |
|
Claude finished @Yeraze's task —— View job PR Review: Phase 2 MeshCore System TestsTasks:
Overall AssessmentAll bugs from the previous review rounds are fixed in Previously Flagged Bugs: Resolved ✓1. Duplicate
|
System Test ResultsMeshMonitor System Test ResultsTest Run: 2026-07-20 22:03:48 EDT Test Summary
✅ Overall Result: PASSEDAll deployment configurations are working correctly! Test DetailsConfiguration Import:
Quick Start Test:
Security Test:
V1 API Test:
Reverse Proxy Test:
Reverse Proxy + OIDC Test:
Virtual Node CLI Test:
Backup & Restore Test:
Database Migration Test:
DB Backing Consistency Test:
|
Summary
Phase 2 of the MeshCore system-tests epic — adds messaging assertions to
tests/test-meshcore.sh(the harness from Phase 1, PR #4232). No product codechanges; test infrastructure only. No
system-tests.shchange needed — theexisting "MeshCore Hardware Test" phase already runs this script.
What it verifies
#mm-systesthashtag channel on both companions (PSK auto-derived:base64(SHA-256("#mm-systest")[:16])), sends a channel message A→B, andverifies B receives it.
name /
advType==2, DMs it, and catches the firmware auto-ack(
meshcore:send-confirmed) via a small Socket.IO listener helper(
tests/helpers/meshcore-await-ack.mjs, using the repo's ownsocket.io-client). Delivery ACK is Socket.IO-only — never exposed over REST.MESHCORE_COMPANION_DM=1, default SKIP).Why companion↔companion DM is opt-in (near-field RF)
Extensive live-hardware diagnosis (documented in
docs/internal/dev-notes/MESHCORE_SYSTEM_TESTS_EPIC.md): the two referencecompanions sit ~3 inches apart, and a direct DM+ACK round-trip between them
fails every configuration — before and after establishing contacts/paths via a
flood-advert exchange, over both a repeater-relayed (
pathLen=1) and a direct(
pathLen=0) path. Meanwhile channel broadcasts between the same two nodeswork fine (~3s, both directions) and the repeater DM auto-acks (~492ms).
Broadcasts getting through while the DM+ACK round-trip doesn't is the signature
of near-field receiver overload — a physical layout limitation, not a
software/test bug. So the companion↔companion DM is gated behind
MESHCORE_COMPANION_DM(default skip, with a clear SKIP message); when enabled itfirst runs an advert-exchange + contact-refresh and tries both directions
(inbox OR ack), for use once the nodes are physically separated.
Validation
Every shipping assertion was validated independently on the live rig this
session: channel A→B delivered in ~3s (both directions), repeater DM auto-ack in
~492ms, connect/handshake repeatedly green, companion-DM skip is deterministic.
Full Vitest suite green (3157 suites, 0 failures — test-only changes, outside
Vitest's collection glob);
npm run typecheckclean;bash -n+node --checkpass.
Operational note
The MeshCore system-test needs exclusive access to the companion serial ports
(ttyUSB) — locally and on the CI hw runner. Nothing else may hold a MeshCore
serial connection to those ports while the
system-test-labeled job runs.🤖 Generated with Claude Code
https://claude.ai/code/session_0158hFzsVbodeJK246qAE1dY