Files
amethyst/commons
Claude 1741077c4d test(cordn): put our MLS and theirs in one group
Tier B runs amy against amy through the reference coordinator. Both MLS
endpoints are ours, so the ratchet tree, the Welcome and the Commit only ever
agree with themselves — it proves the transport and the coordinator client and
nothing about RFC 9420 interop. `interop-client.sh` closes that: `@cordn/cli`
(ts-mls) on one end, amy (quartz) on the other, one group, live wire. That
half is MIT and comes from npm; only the coordinator underneath it carries the
licensing problem, and `stack.sh` now holds that warning in one place for both
harnesses.

Three directions, and the third is why it was worth building.

1. **Their group, our joiner.** Our engine opens a ts-mls Welcome and reads
   their GroupContext extensions, metadata and credentials out of it.
2. **Our group, their joiner.** Their engine opens OUR Welcome — the direction
   no fixture can test, because a fixture we wrote accepts what we emit by
   construction.
3. **Our later Commit.** Until here their epoch came from a Welcome, which
   carries the group state ready-made. This is the first time they must apply
   one of our handshake messages, and ours are public-framed (wireformat 2)
   where theirs are private-framed. `CordnGroupManager.invite` has asserted in
   its KDoc since it was written that their `processMessageBase64` admits
   both — a claim read off their source and never executed. It holds.

All of it passes, and the harness bites: sealing `result.commitBytes` instead
of `result.framedCommitBytes` fails direction 3 and the third-member join
while **leaving direction 2 green**, because a peer that joined by Welcome
never parses that Commit and only stalls once it has to. That is exactly why
direction 3 is its own case rather than a variation of 2, and it is now
demonstrated instead of argued.

`amy cordn invite` gained a `kp_ref` field on the way: the harness needs to
tell their client which Welcome to accept, and reporting it is right anyway —
a KeyPackage is one-time, so the invite names something the invitee can no
longer be invited with by anyone else.

One asymmetry found and deliberately left open: the reference client sends
kind 25910 **in the clear** where we pin `EncryptionMode.REQUIRED` and always
gift-wrap (§8.6). Both work, so nothing is broken — but the two clients
exercise different halves of CEP-4 against the same server, and our encrypted
path is the one with no second implementation behind it. That is a Tier D
vector exchange, not something this harness can settle.

`tier-b.sh` is refactored onto `stack.sh` rather than keeping a second copy of
the boot; re-run after the refactor and still green.

Note on the suite: `Nip46ConsentInfoBuilderTest` failed once mid-session and
has not reproduced — not in isolation, not in two full `./gradlew test` runs,
not in a `--rerun-tasks` rebuild of that module. Its inputs are constants and
its collaborator is injected, so there is no nondeterminism in the test
itself; the likeliest cause is a stale incremental artifact, the same failure
mode that hit `:commons:jvmTest` earlier today. Recording it rather than
calling it a flake, because the report was overwritten before I could read it
and I cannot prove which it was.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012BfD4txdnsaPRXmNXbup9n
2026-09-22 13:53:56 +00:00
..