Files
amethyst/nestsClient
Vitor Pamplona 54f667f5bd fix(nests): unstick reconnecting speaker + listener interop on real relays
Two production-path bugs surfaced during a full interop sweep
(`./gradlew :nestsClient:jvmTest -DnestsInterop=true` +
`-DnestsHangInterop=true`) plus the toolchain / harness friction that
was hiding them.

Production fixes:

- ReconnectingNestsSpeaker's hot-swap path opens publishers via
  `openPublisherForHotSwap` instead of `startBroadcasting`, so the
  underlying `MoqLiteNestsSpeaker`'s state machine never made the
  Connected -> Broadcasting transition. The reconnect wrapper mirrors
  that state, so callers waiting on Broadcasting (the VM,
  `connectReconnectingNestsSpeaker` interop tests) hung on Connected
  forever. Adds `HotSwappablePublisherSource.reportBroadcasting(isMuted)`,
  implemented in `MoqLiteNestsSpeaker`, and the hot-swap pump now
  calls it each iteration so a JWT-refreshed session re-enters
  Broadcasting too.

- The stale-group filter on listener reconnect assumes monotonic
  group lineage across publisher session-restarts (true for
  ReconnectingNestsSpeaker, which seeds each new publisher with the
  prior `nextSequence`; false for external publishers like
  kixelated's hang-publish reference, which mints a fresh state on
  every reconnect and restarts at 0). Without compensation, the
  watermark from a session-1 max ~18 dropped the entire post-restart
  stream from session 2. The collect now resets the watermark on the
  first object of a new subscription when its group id arrives well
  below the current watermark — a publisher-restart signal that a
  relay-cache replay (which carries exactly the prior max) wouldn't
  produce.

Test harness fixes — these are what blocked the suites from running
at all on a clean Apple-Silicon box:

- NostrNestsHarness: the cloned `docker-compose-moq.yml` declares no
  `depends_on`, so on a cold `up -d` moq-relay raced moq-auth's Node
  boot, got `Connection refused` on its JWKS GET, exited with no
  retry, and every later QUIC handshake failed with "read loop exited
  (socket closed or peer closed)". Gate on moq-auth's /health first,
  then idempotent `up -d moq-relay` so the relay always boots against
  a live auth sidecar.

- nestsClient/build.gradle.kts: cargo builds inside the hang-interop
  task panic under CMake 4.x because `audiopus_sys` / rustls' aws-lc-sys
  ship a `CMakeLists.txt` that predates CMake 4's minimum-version
  floor. Set `CMAKE_POLICY_VERSION_MINIMUM=3.5` on each cargo Exec.

- JvmOpusEncoder + the hang-interop Test task: opus-java 1.1.1's
  bundled `natives/darwin/libopus.dylib` is x86_64-only, so its
  in-jar loader reports unsupported on Apple Silicon. Probe a small
  set of canonical system libopus locations (`brew install opus` on
  macOS, distro paths on linux) and `System.load` by absolute path so
  the symbols are in the process when JNA's lazy `Opus.INSTANCE`
  init falls back to RTLD_DEFAULT. Gradle also threads
  `/opt/homebrew/opt/opus/lib` onto `jna.library.path` for
  completeness; both paths are no-ops where the in-jar binary
  already loads.

After these:
- :nestsClient:jvmTest -DnestsInterop=true             — 312/312, 0 fail
- :nestsClient:jvmTest -DnestsHangInterop=true         — 312/312, 0 fail
- quic/interop/run-matrix.sh -s aioquic                — 19 passed,
  0 failed, 3 unsupported (E/V2/CM are documented amethyst gaps).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:01:55 -04:00
..