mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-08 23:54:39 +00:00
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:
@@ -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-
|
||||
|
||||
|
||||
+2
-2
@@ -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/
|
||||
|
||||
@@ -128,7 +128,7 @@ tasks.withType<Test>().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<Test>().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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+2
-2
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user