From f5d9e6a48088fd8537f13468eb82d6be24fd9faa Mon Sep 17 00:00:00 2001 From: torlando-tech Date: Fri, 8 May 2026 21:29:05 -0400 Subject: [PATCH] feat(lxst): T:LXSTDEST + T:ANNLXST hooks for callee interop tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two harness hooks for testing pyxis as the LXST callee: - T:LXSTDEST: returns pyxis's lxst.telephony destination hash. The caller-side bot (real LXST.Telephony.Telephone in this case) needs this to dial pyxis. Backed by a new test_lxst_dest_hex() accessor on UIManager that reads _lxst_destination and returns hex (or empty if the destination isn't registered yet). - T:ANNLXST: forces a fresh announce of the lxst.telephony destination. The TCP-reconnect handler in main.cpp:963 only announces LXMF, so on a fresh boot the lxst.telephony destination is missing from rnsd's cache and link requests addressed to it get dropped with "no known path to final destination" (rnsd debug log). The harness pings this before each callee test to ensure rnsd has a fresh path. Also adds INFO logging to announce_lxst() mirroring the LXMF announce log (Announcing destination: ... announce sent), so it's visible in tdeck-side traces when an announce actually went out vs. silently no-oped. Validated: bot dials, pyxis transitions IDLE -> INCOMING_RINGING (UI shows incoming-call screen), harness sends T:CALL_ANSWER, state becomes ACTIVE. (Audio path crashes shortly after via the same Ed25519 announce-validation bug — fix landed in microReticulum f4bad06, but PIO's libdeps cache had been holding a stale copy; a manual rm -rf .pio/libdeps/tdeck/microReticulum was needed to pick it up. Tracked in #73.) Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/tdeck_ui/UI/LXMF/UIManager.cpp | 10 ++++++++++ lib/tdeck_ui/UI/LXMF/UIManager.h | 4 ++++ src/main.cpp | 21 +++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/lib/tdeck_ui/UI/LXMF/UIManager.cpp b/lib/tdeck_ui/UI/LXMF/UIManager.cpp index c26791d1..7d96a942 100644 --- a/lib/tdeck_ui/UI/LXMF/UIManager.cpp +++ b/lib/tdeck_ui/UI/LXMF/UIManager.cpp @@ -981,6 +981,11 @@ bool UIManager::test_call_answer() { return true; } +std::string UIManager::test_lxst_dest_hex() const { + if (!_lxst_destination) return std::string(); + return _lxst_destination.hash().toHex(); +} + const char* UIManager::test_call_state_name() const { switch (_call_state) { case CallState::IDLE: return "IDLE"; @@ -1792,7 +1797,12 @@ void UIManager::call_answer() { void UIManager::announce_lxst() { if (_lxst_destination) { + std::string h = _lxst_destination.hash().toHex(); + INFO(("Announcing LXST telephony destination: " + h).c_str()); _lxst_destination.announce(); + INFO("LXST announce sent"); + } else { + WARNING("announce_lxst skipped: _lxst_destination not constructed"); } } diff --git a/lib/tdeck_ui/UI/LXMF/UIManager.h b/lib/tdeck_ui/UI/LXMF/UIManager.h index d31121dc..ece0b6fb 100644 --- a/lib/tdeck_ui/UI/LXMF/UIManager.h +++ b/lib/tdeck_ui/UI/LXMF/UIManager.h @@ -203,6 +203,10 @@ public: */ bool test_call_answer(); + /** Pyxis's lxst.telephony destination hash, hex. Empty if the + * LXST destination has not been registered yet (early boot). */ + std::string test_lxst_dest_hex() const; + /** * String name of the current call state, eg "IDLE", "ACTIVE", * "INCOMING_RINGING". Stable for harness assertions. diff --git a/src/main.cpp b/src/main.cpp index 4ae78d1c..3adb6fcc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1655,6 +1655,17 @@ static void handle_test_hook_command(const String& line) { router->announce(); Serial.println("T:OK announced"); } + else if (cmd == "T:ANNLXST") { + // T:ANNLXST — force a fresh announce of the lxst.telephony + // destination. Required before pyxis-as-callee tests because + // the TCP-reconnect path at main.cpp:963 only announces LXMF; + // a brand-new boot ends up with the LXST destination absent + // from rnsd's cache, so the bot can resolve a path but the + // path doesn't actually route to pyxis. + if (!ui_manager) { Serial.println("T:ERR no ui_manager"); return; } + ui_manager->announce_lxst(); + Serial.println("T:OK announced"); + } else if (cmd == "T:PATHS") { const auto& path_table = RNS::Transport::get_path_table(); Serial.print("T:OK count="); @@ -1841,6 +1852,16 @@ static void handle_test_hook_command(const String& line) { } Serial.println("T:OK answered"); } + else if (cmd == "T:LXSTDEST") { + // T:LXSTDEST — pyxis's lxst.telephony destination hash. Used + // by the harness to set up pyxis-as-callee tests (the bot + // dials this hash). Returns "T:ERR not_ready" if the + // destination hasn't been registered yet (early boot). + if (!ui_manager) { Serial.println("T:ERR no ui_manager"); return; } + std::string h = ui_manager->test_lxst_dest_hex(); + if (h.empty()) { Serial.println("T:ERR not_ready"); return; } + Serial.println(String("T:OK ") + h.c_str()); + } else if (cmd == "T:CALL_STATS") { // T:CALL_STATS — return audio frame counters for the most recent // call. tx = frames sent over the wire (encoded by capture path),