fix(marmot-interop): diagnose B->Amethyst kind:445 drops in Test 02

When `wn_b messages send` fires but Amethyst never displays the reply,
the harness used to swallow the send output and only ask the operator a
blind yes/no — giving no signal about which side of the pipe failed.
Amethyst subscribes to kind:445 on the MLS GroupContext `relays`
extension (falling back to `Account.homeRelays`), so the two
actionable failure modes are:

  - wn published to a relay Amethyst isn't subscribed to (group-relay
    mismatch between the MLS extension and Amethyst's subscription)
  - Amethyst received the event but `GroupEventHandler.add` dropped it
    (no `h` tag, `isMember=false`, decrypt error)

Teach the harness to dump both sides automatically on the reply step:

  - Capture wn's `messages send` stdout/stderr into the run log.
  - `dump_outbound_diagnostics` prints wn's local view of the group
    messages (did the send persist to wn's own DB?), the MLS
    GroupContext relay list (what Amethyst should be subscribed to),
    and live relay connection status.
  - On operator-reported fail, prompt for an Amethyst-side logcat
    grep targeted at the exact log points — MarmotGroupEventsEoseManager
    filter updates, GroupEventHandler.add arrivals, and the membership
    drop branch — naming the current group id so the operator can grep
    for the right hex prefix.
This commit is contained in:
Claude
2026-04-22 18:21:22 +00:00
parent 13741be695
commit fda58f4413
2 changed files with 95 additions and 1 deletions
+64
View File
@@ -471,6 +471,70 @@ Then paste /tmp/amethyst-marmot.log contents here so the failure report has
both the Amethyst-side publish log and the wn-side subscription log ($note)."
}
# Dump everything we know about an outbound kind:445 from a wn daemon, so
# when the operator reports "Amethyst didn't see it" we can tell them which
# side of the wire to investigate:
#
# - `messages list` output (did the daemon persist the message locally,
# i.e. is this a relay-reach issue, a membership issue, or a pure
# Amethyst-receive issue?)
# - `groups show` metadata (which MLS-extension relays is wn using for
# this group — these are the SAME relays Amethyst subscribes to, so if
# the lists don't match that's the bug)
# - live relay connection status
#
# Output is tee'd to stderr (live) and $LOG_FILE (forward).
dump_outbound_diagnostics() {
local who="$1" gid="$2" needle="${3:-}" tag="${4:-post-send}"
local wnfn
if [[ "$who" == "B" ]]; then wnfn=wn_b
else wnfn=wn_c; fi
printf '\n%s%s---- [%s] %s outbound diagnostics (group %.8s…) ----%s\n' \
"$C_BOLD" "$C_CYAN" "$tag" "$who" "$gid" "$C_RESET" >&2
printf '\n==== [%s] %s outbound diagnostics (group %s) ====\n' "$tag" "$who" "$gid" >>"$LOG_FILE"
step "$who local view of latest messages in group"
local list_raw
list_raw=$("$wnfn" --json messages list "$gid" --limit 5 2>/dev/null || true)
printf ' %s\n' "${list_raw:-<no output>}" >>"$LOG_FILE"
printf '%s' "$list_raw" \
| jq -r '(.result // .) | .[]? | " \(.id // .event_id // "?") \((.content // .text // "") | tostring | .[0:80])"' 2>/dev/null \
| tee -a "$LOG_FILE" >&2 || true
if [[ -n "$needle" ]]; then
if printf '%s' "$list_raw" | jq -e --arg n "$needle" \
'(.result // .) | .[]? | select((.content // .text // "") | contains($n))' \
>/dev/null 2>&1; then
info "$who local store contains \"$needle\" — send reached wn's own DB (kind:445 went out)"
else
warn "$who local store does NOT contain \"$needle\" — `messages send` never persisted; check stderr.log"
fi
fi
step "$who MLS group metadata (relays here are what Amethyst subscribes on)"
local meta_raw
meta_raw=$("$wnfn" --json groups show "$gid" 2>/dev/null || true)
printf ' %s\n' "${meta_raw:-<no output>}" >>"$LOG_FILE"
local meta_relays
meta_relays=$(printf '%s' "$meta_raw" \
| jq -r '(.result // .) | (.relays // .group.relays // [])[]? | (. | tostring)' 2>/dev/null \
| paste -sd',' -)
info " group relays (MLS extension): ${meta_relays:-<none — Amethyst will fall back to homeRelays>}"
local meta_name meta_epoch
meta_name=$(printf '%s' "$meta_raw" | jq -r '(.result // .) | (.name // .group.name // "?")' 2>/dev/null)
meta_epoch=$(printf '%s' "$meta_raw" | jq -r '(.result // .) | (.epoch // .group.epoch // "?")' 2>/dev/null)
info " group name: $meta_name epoch: $meta_epoch"
step "$who relay connection status right now"
"$wnfn" --json relays list 2>/dev/null \
| jq -r '(.result // .)[]? | " \(.url // .relay.url // "?") [\(.type // "?")] [\(.status // .connection_status // "?")]"' 2>/dev/null \
| tee -a "$LOG_FILE" >&2 || true
printf '%s%s---- end outbound diagnostics ----%s\n\n' \
"$C_BOLD" "$C_CYAN" "$C_RESET" >&2
printf '==== end [%s] %s outbound diagnostics ====\n\n' "$tag" "$who" >>"$LOG_FILE"
}
# ------- group cleanup -------------------------------------------------------
cleanup_group() {
local gid="$1" who="${2:-B}"