Files
amethyst/quartz/interop/verify-with-ts-mls.sh
T
Claude 09d356edfb refactor: fold contextvm and cordn into quartz
Both were Gradle modules on reasoning that does not survive examination.

`:cordn`'s stated reason was circular: "the coordinator client needs
`:contextvm`, and quartz cannot depend on it without a cycle" -- true, but only
because `:contextvm` had been put outside quartz first. A consequence of an
earlier choice was written down as if it were a constraint.

`:contextvm`'s reason was "a peer of `:quic`". That analogy does not hold.
`:quic` is a transport library with no Nostr in it at all -- which is exactly
why it is a module. ContextVM is nothing but Nostr: kind 25910 events, NIP-59
gift wraps, relay subscriptions. Its build file also claimed quartz's
serialization dependency was not transitive, which only mattered across a
boundary the same decision had created; quartz has had
kotlinx-serialization-json all along.

What actually governs is the precedent already in the tree. `marmot/` is 18,876
LOC of non-NIP protocol family living inside quartz with its own MIP spec
series -- the same shape as cordn and three times the size of both modules
combined. `concord/` is another, `buzz/` a third. CLAUDE.md's own rule is that
quartz holds implementations of Nostr specifications, and CEPs are to ContextVM
what NIPs are to Nostr core.

So: `quartz/…/contextvm/` and `quartz/…/cordn/`, as the plan said in the first
place. Two build files and two settings entries deleted.

Two things got better rather than just tidier:

- **Both now build for every quartz target**, iOS and linuxX64 included.
  Neither had a single platform-specific file, so as jvm+android modules they
  were narrower than the code required. Verified with
  `compileCommonMainKotlinMetadata`, which type-checks commonMain against the
  intersection of all declared targets.
- **The crypto-dependent tests moved from `jvmTest` to `jvmAndroidTest`**,
  where quartz keeps the rest of its secp256k1 tests, so they now run on the
  Android host too instead of the JVM alone.

`:quartz:jvmTest` 5086 -> 5330, exactly the 174 + 70 that moved, none lost. The
ts-mls interop gate still passes all ten checks after the move.

`:quartz:testAndroidHostTest` has four failures in `NostrServerTest` and
`LiveNegentropyIndexStoreTest`. They are pre-existing and unrelated -- relay
server and SQLite store -- confirmed by stashing this change and reproducing
them identically at the parent commit.

CLAUDE.md now records the placement rule, so the next protocol does not have to
rediscover it: a protocol over Nostr is a quartz package, not a module;
`quic`/`nestsClient`/`marmotQuic` are modules because they carry no Nostr.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012BfD4txdnsaPRXmNXbup9n
2026-09-18 20:58:23 +00:00

63 lines
2.6 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# The reverse interop direction: ts-mls checks OUR output.
#
# `quartz/cordn`'s own tests prove we can read what ts-mls writes. That is half of
# interoperating -- a client can parse everything correctly and still emit
# something nobody accepts, and that failure keeps our tests green while every
# peer silently drops us. This script closes the loop by handing our artifacts
# to the implementation cordn's client actually runs.
#
# It is opt-in because it needs two checkouts and a Node toolchain, which CI
# for this repo does not carry. `KotlinArtifactProducerTest` writes the
# artifacts unconditionally; only the verification below needs the extras.
#
# Usage:
# quartz/interop/verify-with-ts-mls.sh
#
# Environment:
# CORDN_DIR cordn checkout with `pnpm install` run (default ../cordn)
# STAIRCASE_DIR staircase checkout, for its verify.ts (default ../staircase)
#
# git clone https://github.com/Cordn-msg/cordn ../cordn && (cd ../cordn && pnpm install)
# git clone https://code.relay.tools/opensauce/staircase ../staircase
#
set -euo pipefail
REPO="$(cd "$(dirname "$0")/../.." && pwd)"
CORDN="${CORDN_DIR:-$REPO/../cordn}"
STAIRCASE="${STAIRCASE_DIR:-$REPO/../staircase}"
OUT="$REPO/quartz/build/interop"
FIXTURES="$REPO/quartz/src/commonTest/resources/tsmls"
VERIFY="$STAIRCASE/conformance/fixtures-gen/verify.ts"
die() { echo "error: $*" >&2; exit 2; }
[ -d "$CORDN/packages/cli" ] || die "no cordn checkout at $CORDN (set CORDN_DIR)"
[ -d "$CORDN/node_modules" ] || die "run 'pnpm install' in $CORDN first"
[ -f "$VERIFY" ] || die "no staircase checkout at $STAIRCASE (set STAIRCASE_DIR)"
# Node, not bun. staircase's own run.sh uses bun, and bun's WebCrypto has no
# X25519 DHKEM, so ts-mls there cannot open a Welcome at all -- not even one it
# produced itself. The failure surfaces as `DecapError: The algorithm is not
# supported` deep inside HPKE and looks exactly like a wire-format mismatch,
# which is worth knowing before spending an afternoon on it.
command -v node >/dev/null || die "node is required"
echo "==> producing artifacts from quartz"
"$REPO/gradlew" -p "$REPO" :quartz:jvmTest --tests '*KotlinArtifactProducerTest*' --rerun-tasks -q
# verify.ts reads <root>/gen for the ts-mls KeyPackage it joins with, and
# <root>/kotlin for everything we produced.
echo "==> assembling fixture root at $OUT"
mkdir -p "$OUT/gen"
cp "$FIXTURES/bob2-kp.bin" "$FIXTURES/bob2-privkp.bin" "$OUT/gen/"
STAGE="$CORDN/packages/cli/.staircase"
mkdir -p "$STAGE"
cp "$VERIFY" "$STAGE/"
echo "==> running ts-mls against them"
cd "$CORDN/packages/cli"
node --experimental-strip-types "$STAGE/verify.ts" "$OUT"