From 96fa68e0cbdf2db1cdf3a3bdb94f05ef5e279b32 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 6 May 2026 23:26:06 +0000 Subject: [PATCH] =?UTF-8?q?chore(nests):=20mv=20cli/hang-interop=20?= =?UTF-8?q?=E2=86=92=20nestsClient/tests/hang-interop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per maintainer request: the Rust sidecar workspace lives under the module that owns it, parallel to the upcoming nestsClient-browser-interop/ harness. The cargo workspace, Gradle wiring, gitignore, CI YAML, plan docs, and harness kdoc are all updated. cargo build --release still compiles; HangInteropTest.amethyst_speaker_to_hang_listener_stereo_440_660 green at the new path. Note: the live Phase 4 browser-harness agent (worktree agent-a97a6be483ecee618) was branched from before this move and references `cli/hang-interop` in its plan-doc imports. It will need to rebase onto this commit before it pushes; no code-level conflicts since the agent works exclusively in nestsClient-browser-interop/ + a new BrowserInteropTest.kt. https://claude.ai/code/session_01ERJPUYfdLPwZ99pr5EcEcV --- .github/workflows/build.yml | 10 +++---- .gitignore | 4 +-- nestsClient/build.gradle.kts | 10 +++---- ...-05-06-cross-stack-interop-test-results.md | 10 +++---- .../2026-05-06-cross-stack-interop-test.md | 30 +++++++++---------- ...26-05-06-i4-stereo-cross-stack-scenario.md | 4 +-- .../2026-05-06-phase4-browser-harness.md | 2 +- .../interop/native/NativeMoqRelayHarness.kt | 4 +-- .../tests}/hang-interop/Cargo.lock | 0 .../tests}/hang-interop/Cargo.toml | 0 {cli => nestsClient/tests}/hang-interop/REV | 0 .../hang-interop/hang-listen/Cargo.toml | 0 .../hang-interop/hang-listen/src/main.rs | 0 .../hang-interop/hang-publish/Cargo.toml | 0 .../hang-interop/hang-publish/src/main.rs | 0 .../hang-interop/udp-loss-shim/Cargo.toml | 0 .../hang-interop/udp-loss-shim/src/main.rs | 0 17 files changed, 37 insertions(+), 37 deletions(-) rename {cli => nestsClient/tests}/hang-interop/Cargo.lock (100%) rename {cli => nestsClient/tests}/hang-interop/Cargo.toml (100%) rename {cli => nestsClient/tests}/hang-interop/REV (100%) rename {cli => nestsClient/tests}/hang-interop/hang-listen/Cargo.toml (100%) rename {cli => nestsClient/tests}/hang-interop/hang-listen/src/main.rs (100%) rename {cli => nestsClient/tests}/hang-interop/hang-publish/Cargo.toml (100%) rename {cli => nestsClient/tests}/hang-interop/hang-publish/src/main.rs (100%) rename {cli => nestsClient/tests}/hang-interop/udp-loss-shim/Cargo.toml (100%) rename {cli => nestsClient/tests}/hang-interop/udp-loss-shim/src/main.rs (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ff37dcdb5..38c3fef79 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -172,15 +172,15 @@ jobs: path: ${{ matrix.desktop-artifact-path }} # Cross-stack interop tests (T16). Builds the Rust hang-listen + - # hang-publish sidecars (cli/hang-interop/), `cargo install`s + # hang-publish sidecars (nestsClient/tests/hang-interop/), `cargo install`s # moq-relay + moq-token-cli at the version pinned in - # `cli/hang-interop/REV`, then runs `:nestsClient:jvmTest + # `nestsClient/tests/hang-interop/REV`, then runs `:nestsClient:jvmTest # -DnestsHangInterop=true`. See # `nestsClient/plans/2026-05-06-cross-stack-interop-test.md`. # # Linux-only: the cargo install of `moq-relay` 0.10.x has nontrivial # native deps (aws-lc-sys, ring) that take 5+ min cold; we cache - # both ~/.cargo and the cli/hang-interop/target tree so the warm + # both ~/.cargo and the nestsClient/tests/hang-interop/target tree so the warm # path is a few seconds. macOS / Windows runs would double the # matrix cost without catching anything Linux doesn't catch — the # protocol logic is platform-agnostic and the JNA libopus natives @@ -220,9 +220,9 @@ jobs: path: | ~/.cargo/registry ~/.cargo/git - cli/hang-interop/target + nestsClient/tests/hang-interop/target ~/.cache/amethyst-nests-interop/hang-interop-cargo - key: ${{ runner.os }}-cargo-${{ hashFiles('cli/hang-interop/Cargo.lock', 'cli/hang-interop/REV') }} + key: ${{ runner.os }}-cargo-${{ hashFiles('nestsClient/tests/hang-interop/Cargo.lock', 'nestsClient/tests/hang-interop/REV') }} restore-keys: | ${{ runner.os }}-cargo- diff --git a/.gitignore b/.gitignore index 45b8a6b30..a2162ccf5 100644 --- a/.gitignore +++ b/.gitignore @@ -178,5 +178,5 @@ benchmark/src/main/jniLibs/ /tools/marmot-interop/state # Cargo build artifacts for the cross-stack interop sidecars at -# cli/hang-interop/. Cargo.lock is committed (binary workspace). -/cli/hang-interop/target/ +# nestsClient/tests/hang-interop/. Cargo.lock is committed (binary workspace). +/nestsClient/tests/hang-interop/target/ diff --git a/nestsClient/build.gradle.kts b/nestsClient/build.gradle.kts index 2a346f46d..52496e557 100644 --- a/nestsClient/build.gradle.kts +++ b/nestsClient/build.gradle.kts @@ -128,7 +128,7 @@ tasks.withType().configureEach { // ---- Cross-stack interop: Rust sidecar build + binary path forwarding ------- // -// Phase 1 of the interop plan ships the workspace at `cli/hang-interop/` +// Phase 1 of the interop plan ships the workspace at `nestsClient/tests/hang-interop/` // with three stub binaries (hang-listen, hang-publish, udp-loss-shim). // `interopBuildHangSidecars` runs `cargo build --release` against it and // resolves the upstream `moq-relay` + `moq-token` binaries via @@ -140,15 +140,15 @@ tasks.withType().configureEach { // actual interop scenarios land in Phase 2 once `hang-listen` / // `hang-publish` have real subscribe/publish loops. See // `nestsClient/plans/2026-05-06-cross-stack-interop-test.md` for the -// full plan and the pinned upstream versions in `cli/hang-interop/REV`. +// full plan and the pinned upstream versions in `nestsClient/tests/hang-interop/REV`. -val hangInteropDir = rootProject.layout.projectDirectory.dir("cli/hang-interop") +val hangInteropDir = rootProject.layout.projectDirectory.dir("nestsClient/tests/hang-interop") val hangInteropCacheDir = layout.projectDirectory .dir(System.getProperty("user.home") ?: "/tmp") .dir(".cache/amethyst-nests-interop/hang-interop-cargo") -// Versions are duplicated from cli/hang-interop/REV so Gradle has them +// Versions are duplicated from nestsClient/tests/hang-interop/REV so Gradle has them // at configuration time; bumping requires touching both files. val moqRelayVersion = "0.10.25" val moqTokenCliVersion = "0.5.23" @@ -194,7 +194,7 @@ val interopInstallMoqTokenCli by tasks.registering(Exec::class) { } val interopBuildSidecars by tasks.registering(Exec::class) { - description = "cargo build --release for cli/hang-interop sidecars" + description = "cargo build --release for nestsClient/tests/hang-interop sidecars" group = "interop" workingDir = hangInteropDir.asFile commandLine("cargo", "build", "--release") diff --git a/nestsClient/plans/2026-05-06-cross-stack-interop-test-results.md b/nestsClient/plans/2026-05-06-cross-stack-interop-test-results.md index 743f02492..7d4670691 100644 --- a/nestsClient/plans/2026-05-06-cross-stack-interop-test-results.md +++ b/nestsClient/plans/2026-05-06-cross-stack-interop-test-results.md @@ -107,13 +107,13 @@ the spec, and the concrete pickup points for Phase 2. ## What landed -### Cargo workspace (`cli/hang-interop/`) +### Cargo workspace (`nestsClient/tests/hang-interop/`) - Workspace with three binary crates: `hang-listen`, `hang-publish`, `udp-loss-shim`. **All three are Phase-1 stubs** — they parse their CLI flags via `clap`, print a banner, and exit 0. Phase 2 fills the bodies. -- `cli/hang-interop/REV` documents the pinned upstream +- `nestsClient/tests/hang-interop/REV` documents the pinned upstream `kixelated/moq` rev (`9e2461ee...`) plus the published crate versions on crates.io that track that rev (`moq-relay 0.10.25`, `moq-token-cli 0.5.23`, `hang 0.15.8`, `moq-lite 0.15.15`, @@ -128,7 +128,7 @@ the spec, and the concrete pickup points for Phase 2. the binary already exists in the cache. - `interopInstallMoqTokenCli` — same shape for `moq-token-cli`. - `interopBuildSidecars` — `cargo build --release` over the local - `cli/hang-interop/` workspace. + `nestsClient/tests/hang-interop/` workspace. - `interopBuildHangSidecars` — umbrella task that depends on the three above. Runs as a test dependency only when `-DnestsHangInterop=true` is set. @@ -210,7 +210,7 @@ rendition with `container.kind == "legacy"` and `Bytes`-encoded VarInt timestamp + Opus packet, run the Opus packets through `audiopus::Decoder`, write Float32 PCM to `--output-pcm`. Dependencies to add to -`cli/hang-interop/hang-listen/Cargo.toml`: +`nestsClient/tests/hang-interop/hang-listen/Cargo.toml`: ```toml hang = "0.15" @@ -297,7 +297,7 @@ the smoke test. ## Files ``` -cli/hang-interop/ +nestsClient/tests/hang-interop/ ├── REV ├── Cargo.toml + Cargo.lock ├── hang-listen/{Cargo.toml,src/main.rs} # Phase 2: real subscribe + decode diff --git a/nestsClient/plans/2026-05-06-cross-stack-interop-test.md b/nestsClient/plans/2026-05-06-cross-stack-interop-test.md index 6176c757e..0b4a2e3c4 100644 --- a/nestsClient/plans/2026-05-06-cross-stack-interop-test.md +++ b/nestsClient/plans/2026-05-06-cross-stack-interop-test.md @@ -90,7 +90,7 @@ git rev. Cached. rev. Use the same rev as the production `nostrnests/nests` Docker image's `moq-relay` build, if discoverable; otherwise pin to the latest commit on `main` at implementation time and document it in a - `cli/hang-interop/REV` file. + `nestsClient/tests/hang-interop/REV` file. - Build: `cargo build --release -p moq-relay --manifest-path /Cargo.toml`. - Cache key: pinned rev sha. First run: ~30 s cold build. Warm: < 1 s. - Config (rendered to a temp file at test setup): @@ -140,10 +140,10 @@ self-signed cert without populating the system trust store. ### Rust hang sidecar binaries -New cargo workspace at `cli/hang-interop/` in this repo. +New cargo workspace at `nestsClient/tests/hang-interop/` in this repo. ```toml -# cli/hang-interop/Cargo.toml +# nestsClient/tests/hang-interop/Cargo.toml [workspace] members = ["hang-listen", "hang-publish", "udp-loss-shim"] @@ -157,7 +157,7 @@ anyhow = "1" Three binaries: -#### `hang-listen` — `cli/hang-interop/hang-listen/src/main.rs` +#### `hang-listen` — `nestsClient/tests/hang-interop/hang-listen/src/main.rs` Args: `--relay-url ` `--jwt ` `--broadcast ` `--duration ` `--output-pcm `. Behaviour: 1. Connect to relay via `web-transport-quinn` with WT-Available-Protocols @@ -176,7 +176,7 @@ Output format: little-endian Float32 PCM, no header. One channel for mono, interleaved L/R for stereo (controlled by catalog's `numberOfChannels`). The Gradle test reads this file or pipe. -#### `hang-publish` — `cli/hang-interop/hang-publish/src/main.rs` +#### `hang-publish` — `nestsClient/tests/hang-interop/hang-publish/src/main.rs` Args: `--relay-url <...>` `--jwt ` `--broadcast ` `--freq-hz ` `--duration ` `--channels <1|2>`. Behaviour: 1. Connect, open session, claim broadcast under given path. @@ -189,7 +189,7 @@ Behaviour: opus_packet`, push into groups of 5 frames, FIN per group. 4. Run for `duration` seconds, then send `Announce::Ended` and exit. -#### `udp-loss-shim` — `cli/hang-interop/udp-loss-shim/src/main.rs` +#### `udp-loss-shim` — `nestsClient/tests/hang-interop/udp-loss-shim/src/main.rs` Args: `--listen 127.0.0.1:` `--upstream 127.0.0.1:` `--loss-rate <0..1>`. Behaviour: standard tokio UDP relay. For each datagram received, `if rng.gen::() < loss_rate { drop }` else forward. Used for I9. @@ -503,15 +503,15 @@ Total: ~5 days. P0 deliverable (1+2+4) is **3 days**. ### Phase 1 — Native moq-relay + Rust sidecars (1 day) -1. Pick `kixelated/moq` rev. Clone to `cli/hang-interop/.cache/moq` or +1. Pick `kixelated/moq` rev. Clone to `nestsClient/tests/hang-interop/.cache/moq` or reference via `git` Cargo source. Document rev in - `cli/hang-interop/REV`. -2. Write `cli/hang-interop/Cargo.toml` workspace + the three binary + `nestsClient/tests/hang-interop/REV`. +2. Write `nestsClient/tests/hang-interop/Cargo.toml` workspace + the three binary crates (`hang-listen`, `hang-publish`, `udp-loss-shim`). Verify `cargo build --release` succeeds end-to-end. 3. Add Gradle task `interopBuildHangSidecars` (in `nestsClient/build.gradle.kts`) that: - - Runs `cargo build --release` in `cli/hang-interop/`. + - Runs `cargo build --release` in `nestsClient/tests/hang-interop/`. - Exposes binary paths to JVM tests via `tasks.test { systemProperty(...) }`. - Caches based on `Cargo.lock` hash. 4. Write `NativeMoqRelayHarness.kt` (subprocess management, JWT mint, @@ -603,8 +603,8 @@ jobs: path: | ~/.cargo/registry ~/.cargo/git - cli/hang-interop/target - key: ${{ runner.os }}-cargo-${{ hashFiles('cli/hang-interop/Cargo.lock') }} + nestsClient/tests/hang-interop/target + key: ${{ runner.os }}-cargo-${{ hashFiles('nestsClient/tests/hang-interop/Cargo.lock') }} - run: ./gradlew :nestsClient:jvmTest -DnestsHangInterop=true browser-interop: @@ -619,7 +619,7 @@ jobs: ~/.cargo/registry ~/.cache/ms-playwright nestsClient-browser-interop/node_modules - key: ${{ runner.os }}-browser-${{ hashFiles('nestsClient-browser-interop/bun.lockb', 'cli/hang-interop/Cargo.lock') }} + key: ${{ runner.os }}-browser-${{ hashFiles('nestsClient-browser-interop/bun.lockb', 'nestsClient/tests/hang-interop/Cargo.lock') }} - run: ./gradlew :nestsClient:jvmTest -DnestsBrowserInterop=true ``` @@ -640,7 +640,7 @@ Acceptable for PR-level CI. | Risk | Mitigation | |---|---| -| `kixelated/moq` HEAD breaks pin | Pin git rev in `cli/hang-interop/Cargo.toml` + REV file. Bump deliberately. | +| `kixelated/moq` HEAD breaks pin | Pin git rev in `nestsClient/tests/hang-interop/Cargo.toml` + REV file. Bump deliberately. | | moq-relay config schema changes between revs | Pin rev. Document `relay.toml` fields used. Smoke test: boot relay, assert known broadcast lookup works, in `@BeforeAll`. | | Self-signed cert + Chromium WebTransport rejection | Use `--ignore-certificate-errors-spki-list` (preferred) or `--ignore-certificate-errors` for test-only Chromium instance. | | JVM Opus encoder/decoder availability | If `:nestsClient` JVM target lacks Opus, vendor `audiopus` JNI or write a small wrapper. Reuse `MediaCodecOpusEncoder`/`Decoder` if a JVM `MediaCodec` polyfill exists; otherwise pure-Java `concentus` library is a fallback. Decide at Phase 1. | @@ -664,7 +664,7 @@ Acceptable for PR-level CI. committed at `nestsClient/plans/2026-05-06-cross-stack-interop-test-gap-matrix.md`. 6. Both `-DnestsHangInterop=true` and `-DnestsBrowserInterop=true` in the default PR-level GitHub Actions config. -7. `cli/hang-interop/REV` and `nestsClient-browser-interop/REV` +7. `nestsClient/tests/hang-interop/REV` and `nestsClient-browser-interop/REV` document the pinned upstream revs. 8. New plan filed at `nestsClient/plans/2026-05-06-cross-stack-interop-test-results.md` diff --git a/nestsClient/plans/2026-05-06-i4-stereo-cross-stack-scenario.md b/nestsClient/plans/2026-05-06-i4-stereo-cross-stack-scenario.md index 943286dbd..e62577e07 100644 --- a/nestsClient/plans/2026-05-06-i4-stereo-cross-stack-scenario.md +++ b/nestsClient/plans/2026-05-06-i4-stereo-cross-stack-scenario.md @@ -192,14 +192,14 @@ FFT assertion on each. ZCR can be done the same way. ### `hang-listen` already supports stereo Verify by reading the existing -`cli/hang-interop/hang-listen/src/main.rs`: it already passes +`nestsClient/tests/hang-interop/hang-listen/src/main.rs`: it already passes `audio_cfg.channel_count` into `opus::Decoder::new(...)` and allocates a stereo PCM buffer if the catalog says so. No Rust change needed. ### `hang-publish` already supports stereo -Verify by reading `cli/hang-interop/hang-publish/src/main.rs`: +Verify by reading `nestsClient/tests/hang-interop/hang-publish/src/main.rs`: the `--channels <1|2>` flag plumbs through to the catalog, the opus encoder, and the sine generator. No Rust change needed. *(Note: the current sine generator uses the same frequency on diff --git a/nestsClient/plans/2026-05-06-phase4-browser-harness.md b/nestsClient/plans/2026-05-06-phase4-browser-harness.md index f6a4765f2..fbd58c859 100644 --- a/nestsClient/plans/2026-05-06-phase4-browser-harness.md +++ b/nestsClient/plans/2026-05-06-phase4-browser-harness.md @@ -104,7 +104,7 @@ nestsClient-browser-interop/ Pin all `@moq/*` deps to the same versions `nostrnests/nests` ships in `NestsUI-v2/package.json`. Document the rev in `nestsClient-browser-interop/REV` (parallel to -`cli/hang-interop/REV`). +`nestsClient/tests/hang-interop/REV`). ### 2. `listen.ts` — browser listener diff --git a/nestsClient/src/jvmTest/kotlin/com/vitorpamplona/nestsclient/interop/native/NativeMoqRelayHarness.kt b/nestsClient/src/jvmTest/kotlin/com/vitorpamplona/nestsclient/interop/native/NativeMoqRelayHarness.kt index b0a0b38fb..4b2fd6798 100644 --- a/nestsClient/src/jvmTest/kotlin/com/vitorpamplona/nestsclient/interop/native/NativeMoqRelayHarness.kt +++ b/nestsClient/src/jvmTest/kotlin/com/vitorpamplona/nestsclient/interop/native/NativeMoqRelayHarness.kt @@ -36,7 +36,7 @@ import kotlin.concurrent.withLock * binary paths for the cross-stack interop harness. * * - `moq-relay` is `cargo install`ed at the version pinned in - * `cli/hang-interop/REV` and cached under + * `nestsClient/tests/hang-interop/REV` and cached under * `~/.cache/amethyst-nests-interop/hang-interop-cargo/bin/`. * - TLS: `--tls-generate localhost` so the relay self-signs at * startup. Kotlin clients use the existing @@ -95,7 +95,7 @@ class NativeMoqRelayHarness private constructor( /** * `interopBuildHangSidecars` writes this. Points to - * `cli/hang-interop/target/release` where the (Phase-1 + * `nestsClient/tests/hang-interop/target/release` where the (Phase-1 * stub) sidecar binaries live. */ const val SIDECARS_DIR_PROPERTY = "nestsHangInteropSidecarsDir" diff --git a/cli/hang-interop/Cargo.lock b/nestsClient/tests/hang-interop/Cargo.lock similarity index 100% rename from cli/hang-interop/Cargo.lock rename to nestsClient/tests/hang-interop/Cargo.lock diff --git a/cli/hang-interop/Cargo.toml b/nestsClient/tests/hang-interop/Cargo.toml similarity index 100% rename from cli/hang-interop/Cargo.toml rename to nestsClient/tests/hang-interop/Cargo.toml diff --git a/cli/hang-interop/REV b/nestsClient/tests/hang-interop/REV similarity index 100% rename from cli/hang-interop/REV rename to nestsClient/tests/hang-interop/REV diff --git a/cli/hang-interop/hang-listen/Cargo.toml b/nestsClient/tests/hang-interop/hang-listen/Cargo.toml similarity index 100% rename from cli/hang-interop/hang-listen/Cargo.toml rename to nestsClient/tests/hang-interop/hang-listen/Cargo.toml diff --git a/cli/hang-interop/hang-listen/src/main.rs b/nestsClient/tests/hang-interop/hang-listen/src/main.rs similarity index 100% rename from cli/hang-interop/hang-listen/src/main.rs rename to nestsClient/tests/hang-interop/hang-listen/src/main.rs diff --git a/cli/hang-interop/hang-publish/Cargo.toml b/nestsClient/tests/hang-interop/hang-publish/Cargo.toml similarity index 100% rename from cli/hang-interop/hang-publish/Cargo.toml rename to nestsClient/tests/hang-interop/hang-publish/Cargo.toml diff --git a/cli/hang-interop/hang-publish/src/main.rs b/nestsClient/tests/hang-interop/hang-publish/src/main.rs similarity index 100% rename from cli/hang-interop/hang-publish/src/main.rs rename to nestsClient/tests/hang-interop/hang-publish/src/main.rs diff --git a/cli/hang-interop/udp-loss-shim/Cargo.toml b/nestsClient/tests/hang-interop/udp-loss-shim/Cargo.toml similarity index 100% rename from cli/hang-interop/udp-loss-shim/Cargo.toml rename to nestsClient/tests/hang-interop/udp-loss-shim/Cargo.toml diff --git a/cli/hang-interop/udp-loss-shim/src/main.rs b/nestsClient/tests/hang-interop/udp-loss-shim/src/main.rs similarity index 100% rename from cli/hang-interop/udp-loss-shim/src/main.rs rename to nestsClient/tests/hang-interop/udp-loss-shim/src/main.rs