feat(lxst): T:LXSTDEST + T:ANNLXST hooks for callee interop tests

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: <hash> ... 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) <noreply@anthropic.com>
This commit is contained in:
torlando-tech
2026-05-08 21:29:05 -04:00
co-authored by Claude Opus 4.7
parent 24bfd68a78
commit f5d9e6a480
3 changed files with 35 additions and 0 deletions
+21
View File
@@ -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),