The sim now observes TX from both nodes (current client fixed observation)
but nothing is ever delivered to the other node. Suspect: random placement
leaving nodes out of range, or the receiver decision. Provide a --from-file
scenario with nodes spaced 10 m apart (guaranteed adjacency) and print the
transmitter -> receivers decision per TX in the sim log.
Both e2e directions time out: nothing is delivered between nodes although
the adapter connects fine and bridging code is equivalent to meshtastic's own
firmware_harness. Before concluding, instrument the pinned meshtasticator:
- on_receive now prints every observed packet (topic keys) to sim.log,
- a [sim-text] listener prints any text packet any sim interface sees,
- cleanup sends 'exit' through the cmdloop (graceful, no EOF-spam flood that
was drowning the log tail),
- workflow failure step prints 400 sim-log lines + the node logs dump.
Run 10538 booted the full simulator and ran pytest — both failures were
test-side: the installed meshtastic client moved sendText from Node to the
Interface, and the outbound chunked message never arrived at the peer (cause
still unknown). Fix the sendText call and add in-run diagnostics so the next
failure is self-explaining:
- e2e: outbound-timeout failure now reports what arrived at the peer and the
simulator log tail; sim now boots with -v (meshtastic debug logging).
- run_e2e.sh: on pytest failure in docker mode, dump per-node meshtasticd
logs (meshtasticator-nodes.log) before cleanup removes the container.
Run 10537 reached node boot but the simulator crashed again at Graph
construction: lib/gui.py move_figure() unconditionally touches the Tk window
manager (canvas.manager.window), which does not exist under Agg. Patch it to a
no-op guard alongside the TkAgg->Agg backend swap. Also feed the simulator's
interactive cmdloop from a FIFO held open until teardown — CI stdin is EOF, so
the sim would otherwise exit immediately after booting the nodes.
Run 10536 reached pytest but every connect was reset: the simulator died at
import with 'Tkinter is needed' — lib/gui.py calls matplotlib.use("TkAgg")
unconditionally (an explicit use() overrides MPLBACKEND=Agg), and the port
probe passed instantly because it was accepting the localhost forwarders, not
the nodes.
- run_e2e.sh: patch meshtasticator lib/gui.py TkAgg->Agg before boot;
probe readiness on the upstream host (docker daemon) when forwards are
active; docker pull meshtastic/meshtasticd up front so the pull is off the
simulator's node-boot path.
- e2e tests: cancel leftover loop tasks (watchdog) at loop teardown.
First dispatch (run 10535) failed instantly with Permission denied on the
orchestrator: the file lost its executable bit in the branch move. Set mode
100755 and call through bash so a future mode regression cannot break the job.
Adds .gitea/workflows/meshtasticator-e2e.yaml (workflow_dispatch until
validated green, pull_request trigger commented out): builds the pinned
Hermes contract env like release.yaml, installs the plugin with deps,
checks out Meshtasticator at a pinned commit, installs the simulator's
own (conflicting-version) requirements in an isolated venv, then runs
scripts/meshtasticator_e2e/run_e2e.sh --mode docker.
Supporting changes:
- run_e2e.sh: SIM_VENV support; automatic localhost->daemon TCP forwards
(scripts/meshtasticator_e2e/tcp_forward.py) when DOCKER_HOST is a tcp://
daemon, because Meshtasticator hardcodes localhost for its node control
connections; node-boot settle wait; fixed docker cleanup (container
'Meshtastic', volume 'Meshtasticator').
- e2e tests: connect via a module-long event loop in a background thread
(adapter requires a running loop for watchdog + inbound dispatch, so
per-call asyncio.run was wrong) with retry while the node TCP API boots.
- README: sim-venv guidance, remote-daemon forwarding, CI section.
Docker access follows the deployment examples in
eric/actions-docker (docker is available to runner jobs).
Adds a skip-gated end-to-end suite (plugin/tests/e2e) that drives the
adapter against live Meshtasticator simulated nodes - real MeshtasticD
instances speaking the actual TCP/protobuf API - covering the layer no
contract test can reach: TCP handshake, inbound text over the simulated
mesh, and chunked outbound sends arriving at a peer node.
scripts/meshtasticator_e2e/run_e2e.sh boots N nodes (Docker or Linux
native MeshtasticD), discovers their TCP API ports, and runs the suite.
Radio-level reconnect and CI wiring are documented as open items pending
a Linux/Docker validation run.
- Resolve the Hermes gateway API lazily via get_adapter_class() so the adapter
always inherits the real gateway.platforms.base.BasePlatformAdapter instead
of the ImportError fallback stub (root cause of the AttributeError:
'set_message_handler' crash at gateway startup).
- Pass Platform('meshtastic') enum to super().__init__ and align
connect(*, is_reconnect=False) -> bool with the gateway contract.
- Split pure formatting utils into formatting.py; remove the stub fallback.
- Add tests/contract (L0-L3): real-base binding, ABC instantiation, gateway
run.py wiring replay, signature and event-shape conformance, run against
hermes-agent pinned to the deployed image source (v2026.8.31).
- Add scripts/contract-test.sh and a contract-test CI job; release publish
now requires contract tests to pass.
- Untrack stray __pycache__ artifacts.