chore(nests): mv cli/hang-interop → nestsClient/tests/hang-interop

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
This commit is contained in:
Claude
2026-05-06 23:26:06 +00:00
parent 79a4019438
commit 96fa68e0cb
17 changed files with 37 additions and 37 deletions
@@ -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
@@ -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 <pinned/checkout>/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 <https://...>` `--jwt <token>` `--broadcast <path>` `--duration <secs>` `--output-pcm <path-or-->`.
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 <token>` `--broadcast <path>` `--freq-hz <int>` `--duration <secs>` `--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:<port>` `--upstream 127.0.0.1:<port>` `--loss-rate <0..1>`.
Behaviour: standard tokio UDP relay. For each datagram received,
`if rng.gen::<f32>() < 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`
@@ -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
@@ -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