vm: batch 2, rooms and calls on three servers #22

Merged
asxpi merged 133 commits from vm/batch-2 into main 2026-11-10 01:09:48 +00:00
Member

Batch 2 of vms/PLAN.md (rooms and calls) on ejabberd, prosody and
anotherim-server, plus the urn:tacky specs carried over from batch 1 on
prosody and anotherim-server: about 78 GUI runs, each with a filled
EVIDENCE.md, and the harness work the agents did on the way (new
xep0249.py, xep0402.py, xep0513.py; xep0045admin restart/C34 and #95
checks; web API actions for moderation, room avatar, group call; Gajim
and Dino UI fixes).

Coverage: tools/interop.py gaps prints 0 for XEP-0045, 0249, 0425, 0486,
0402, 0353, 0167 and the four urn:tacky specs on all servers. Still open,
by PLAN's scope: XEP-0272/0482 on prosody, XEP-0513 with anotherim-web on
prosody and anotherim-server.

Found or re-recorded (details in each run's EVIDENCE.md; issue #9 is
updated after merge):

  • new, libtacky: XEP-0482/0272 accept/reject/left withheld in an OMEMO
    room ("not sent in the clear") although the invite came in clear
  • new: aim → anotherim-web leg of a Muji group call is silent (opus
    arrives, audioLevel 0); 1:1 calls fine; side not traced
  • full-JID membership/ban (prosody refuses it, ejabberd accepts); aim
    sends only mediated invites (no XEP-0249); no room-avatar or moderation
    UI; moderated message shown without moderator; duplicate chat after a
    mediated invite
  • C34 fixed (libtacky 1.2.2); anotherim-js#95 destroyed-room view not
    shown (commented there); C30 fixed; C35 is Prosody's 5-minute lock;
    C36, P3 reproduced; omemo-trust:0 publishes only on Undecided ->
    Verified (C42)
  • XEP-0425 na on prosody (no mod_muc_moderation); lacks: Gajim 0513,
    Dino 0425/0513

🤖 Generated with Claude Code

Batch 2 of vms/PLAN.md (rooms and calls) on ejabberd, prosody and anotherim-server, plus the urn:tacky specs carried over from batch 1 on prosody and anotherim-server: about 78 GUI runs, each with a filled EVIDENCE.md, and the harness work the agents did on the way (new xep0249.py, xep0402.py, xep0513.py; xep0045admin restart/C34 and #95 checks; web API actions for moderation, room avatar, group call; Gajim and Dino UI fixes). Coverage: `tools/interop.py gaps` prints 0 for XEP-0045, 0249, 0425, 0486, 0402, 0353, 0167 and the four urn:tacky specs on all servers. Still open, by PLAN's scope: XEP-0272/0482 on prosody, XEP-0513 with anotherim-web on prosody and anotherim-server. Found or re-recorded (details in each run's EVIDENCE.md; issue #9 is updated after merge): - new, libtacky: XEP-0482/0272 accept/reject/left withheld in an OMEMO room ("not sent in the clear") although the invite came in clear - new: aim → anotherim-web leg of a Muji group call is silent (opus arrives, audioLevel 0); 1:1 calls fine; side not traced - full-JID membership/ban (prosody refuses it, ejabberd accepts); aim sends only mediated invites (no XEP-0249); no room-avatar or moderation UI; moderated message shown without moderator; duplicate chat after a mediated invite - C34 fixed (libtacky 1.2.2); anotherim-js#95 destroyed-room view not shown (commented there); C30 fixed; C35 is Prosody's 5-minute lock; C36, P3 reproduced; omemo-trust:0 publishes only on Undecided -> Verified (C42) - XEP-0425 na on prosody (no mod_muc_moderation); lacks: Gajim 0513, Dino 0425/0513 🤖 Generated with [Claude Code](https://claude.com/claude-code)
asxpi added 133 commits 2026-11-10 00:57:24 +00:00
PLAN.md batch 2 asks whether a destroyed room comes back from its
bookmark on the next sign-in (#1 C34): restart aim at the end and fail
the step if it sends a join presence to the room again.

Assisted-by: Claude:opus-5-5
Batch 2 needs XEP-0249 both ways: aim invites the peer, the peer
invites aim (accepted), and a third invitation aim declines, so #1 C30
(the decline addressed to the room instead of the inviter) can be
checked on the wire. aim's invitation card has Decline left of Accept
and a confirmation dialog with an optional reason.

Assisted-by: Claude:opus-5-5
ENC=omemo xep0234.py with "Send files directly" on: aim offers the picture
over a WebRTC data channel with a JET <security/> of type
urn:tacky:jingle:jet-omemo:2 holding an OMEMO 2 key transport for web's
and aim's devices (client.log:329); web accepts, confirms <received/> and
terminates with success (:373, :412, :417), no upload. Both screens show
the picture with the lock. Same as the ejabberd run 173948.

Assisted-by: Claude:opus-5-5
The XEP-0425 and XEP-0486 cells for anotherim-web and Dino had no
scenario: aim has no UI to moderate others' lines or set a room avatar,
and Dino 0.5.1 neither. The example now picks the room's owner among
the peers (Gajim, else web), invites aim and the other peer, and logs
what each watcher got (moderation, status 104, vCard fetch). web
moderates and sets the avatar through libtacky's API (message.moderate,
muc.set_avatar), as with its polls: not a GUI step.

Assisted-by: Claude:opus-5-5
PLAN.md batch 2 asks for bookmarks seen across clients (G=, W=) and for
a call answered on one device to stop the other ringing; neither example
did that. xep0402.py: aim joins a room (bookmark published), the second
device of vm-aim follows it; aim removes it (Room actions › Remove
bookmark…, new remove_bookmark()); the other device's room reaches aim.
xep0353.py: with peer2 web:aim the peer calls, aim answers, web:aim must
stop ringing. aim's cancel_call is its Hang up while ringing.

Assisted-by: Claude:opus-5-5
XEP-0513 had no example: aim and web @-mention each other in a room
through the composer's people picker, the <mention/> is checked on the
wire and web's record says mentions_me. Gajim 2.6 (nbxmpp 7.4.0) and
Dino 0.5.1 carry no urn:xmpp:mentions:0, and Dino none of
urn:xmpp:message-moderate either, so those cells need no run.

Assisted-by: Claude:opus-5-5
The XEP-0272/0482 cells for anotherim-web had no scenario: the app has
a Join on the invite card but no way to start a group call in its UI.
web now starts one through libtacky's groupcall.start (as its polls and
moderation), in a room it created with join_room; aim's side is the
same card as with Dino.

Assisted-by: Claude:opus-5-5
ENC=omemo xep0234.py over the WebRTC data channel: aim's session-initiate
and web's session-accept both carry their DTLS fingerprint sealed in an
OMEMO 2 <encrypted/> (client.log:358, :394); <received/> and a success
terminate follow (:442, :448), no fingerprint in clear. Both screens show
the picture with the lock. Same as ejabberd 174347.

Assisted-by: Claude:opus-5-5
On Prosody aim's new room stays locked (#9 C35) and the invitee's join
fails; the example then took no screenshot at all. Catch the refusal
per invitee, log the error condition, shoot the peer's join error and
aim's room, then fail the step, as xep0045.py does.

Assisted-by: Claude:opus-5-5
On Prosody aim's new room stays locked (#9 C35): the invitee's join of
R1 is refused and the step failed without a screenshot. Log the error
condition and shoot the peer's error and aim's room before failing.

Assisted-by: Claude:opus-5-5
After aim's kick Gajim's open room shows a narrow "You left this group
chat" box with a Join button; the Start Chat route then found neither
"left" nor "Back" and the kick step failed, the ban after it too.
Click that Join first when the kicked room is the open chat.

Assisted-by: Claude:opus-5-5
omemo_sync.py PART=chats with anotherim-web as a second device of vm-aim
(fresh disk, signed in through its UI). With the own devices verified
both ways, aim's OMEMO off for vm-gajim goes out as a sealed omemo-chats
item (client.log:549), web gets the event (peer log :1186) and shows the
chat unencrypted; back on, the same (:561, :1226). Same as ejabberd 181559.

Assisted-by: Claude:opus-5-5
Batch 2 recheck with xep0045admin.py. aim creates claude-test-234145,
gets 201 + 110 as owner and sends no muc#owner instant-room submit
(#9 C35), so Prosody keeps the room locked ("Room is locked, denying
entry", server.log:1709). aim's mediated invite reaches Gajim, whose
join is refused with item-not-found (peer-gajim.log:1518, shot-2); the
subject, role, kick and ban steps fail only as a consequence. Destroy
works, and aim does not rejoin the destroyed room after a restart
(#1 C34 not seen).

Assisted-by: Claude:opus-5-5
omemo_sync.py PART=trust with anotherim-web as a second device of vm-aim,
own devices verified both ways. aim sets vm-gajim's key from Undecided
to Verified and publishes a sealed omemo-trust item (client.log:424);
web gets the event the same second (peer log :406) and lists the key
Verified. On ejabberd (182653, #9 C42) the same key went Accepted ->
Verified and nothing was published: C42 may hinge on the key's previous
trust.

Assisted-by: Claude:opus-5-5
Room administration from aim (xep0045admin.py): create, mediated
invite, subject, voice and moderator by nick, membership, kick, ban,
destroy, restart. All reach Gajim, but aim's membership and ban items
carry Gajim's full JID (client.log:270, :294) where XEP-0045 §9.1
requires the bare JID for a ban; ejabberd accepts them. This records
#9's candidate "MUC ban with full JID".

The destroyed room did not come back after a restart: aim republished
its bookmark with autojoin=false (client.log:311), so #1 C34 is not
reproduced on ejabberd.

Assisted-by: Claude:opus-5-5
Recheck with the camera feed: aim calls Dino, audio then video. Tones
heard both ways in both calls (Dino's audio present, #9 P3 not seen);
Dino's NIC carries 1457 VP8 packets in and 138 out per 8 s and both
video windows show the other's live test card. aim's own NIC dump
counted no VP8 in the video call, a harness oddity the screens and
Dino's counts contradict.

Assisted-by: Claude:opus-5-5
Dino logs a message's subject text on its own line and joins under its
full JID as nick, which aim's Members pane cuts off with an ellipsis:
the subject wait and every occupant action failed against Dino. Allow
whitespace after <subject> and match a long nick by its start.

Assisted-by: Claude:opus-5-5
Batch 2 recheck with xep0045admin.py. aim sends no muc#owner submit
for its new room (#9 C35): Dino's first joins are refused, until
Prosody's own 5-minute lock timeout opens the room (server.log:1517);
then subject, roles and kick reach Dino. aim grants membership and bans
with Dino's full JID (client.log:285, :310), which Prosody rejects as
jid-malformed "Bare JID expected, got full JID": the ban silently does
nothing (#9 candidate, now recorded). Destroy works, no rejoin after a
restart (#1 C34 not seen).

Assisted-by: Claude:opus-5-5
Prosody unlocks a never-configured room by itself after
muc_room_lock_timeout (5 minutes): Dino's automatic retry got into aim's
C35-locked room at 5 min 34 s (run 20261109-195222) and the admin steps
ran. xep0045admin.py and xep0249.py now log the refusal, wait out the
lock, join again and go on, so the steps after it test something on
Prosody; the invite step stays failed for #9 C35.

Assisted-by: Claude:opus-5-5
Recheck with the camera feed: aim calls web, audio then video. Tones
heard both ways in both calls; ~400 opus packets each way per 8 s, and
in the video call ~1500 RED-wrapped VP8 packets each way on aim's NIC.
Both windows show the other's live test card.

Assisted-by: Claude:opus-5-5
First Kaidan call run on this server: #9 C36 in both calls. Kaidan's
session-accept carries no <rtcp-mux/>, aim's libwebrtc rejects the
answer ("RTCP-MUX is not enabled when it is required",
client-tacky.log:244, :603), no RTP on either NIC, Kaidan ends with
failed-transport. web and Dino pass with the same example here.

Assisted-by: Claude:opus-5-5
Room administration from aim with Dino in the room: subject, roles by
nick, kick and destroy all reach Dino (307, 301 in its log), but aim's
membership and ban items name Dino's full JID (client.log:347, :375),
as with Gajim; XEP-0045 §9.1 requires the bare JID. Dino 0.5.1 shows
no line for the kick or the ban. The destroyed room did not come back
after aim's restart (#1 C34 not reproduced).

Assisted-by: Claude:opus-5-5
OCR missed Dino's red-on-pink Reject (anotherim run 201739) and
find_text raised before the existing fallback by the widget's title
could run. xep0353's other-device step now shoots aim while it rings:
in that run aim sent <ringing/> but no Answer was found for 60 s.

Assisted-by: Claude:opus-5-5
With aim's incoming sheet up (shot of run 202532 on anotherim), the
whole-screen find_color for the green Answer boxed the strip's button,
the sheet's and the avatar together and never matched, and OCR missed
the labels for 60 s: the call went missed. The top strip's Answer is
alone in its band.

Assisted-by: Claude:opus-5-5
_open_room_aim switches between the room and the duplicate chat a
mediated invite leaves with Alt+1/Alt+2, which Chromium keeps for its
tabs: web never got into the room after accepting an invitation, and
every later room step failed. Fall back to clicking each search result
until the header says "members".

Assisted-by: Claude:opus-5-5
Batch 2 recheck with xep0045admin.py. aim's new room stays locked
(#9 C35) until Prosody's 5-minute lock timeout; web gets in at 352 s.
Subject, roles by nick, kick (307) and destroy then work. aim's
membership grant and ban carry web's full JID and Prosody refuses both
as jid-malformed "Bare JID expected, got full JID" (client.log:327,
:353), with nothing on aim's screen. web also keeps the room's bare JID
as a 1:1 chat request after the mediated invite (shot-3).

Assisted-by: Claude:opus-5-5
Correct the evidence of 20261109-201824: web's first presence to the
room is the harness's own join after the lock window; its click on the
invitation card sent nothing, so the run does not show the lock refusing
web. C35 itself (no muc#owner) and the full-JID ban stand.

Assisted-by: Claude:opus-5-5
xep0353.py with Dino and web as a second device of vm-aim: Dino's
reject reaches aim, aim's retract makes Dino show a missed call, aim
declines Dino's call, and aim answering Dino's call sends <proceed/>
whose carbon stops web ringing. The step failed only after that: Dino
got no session-terminate within 60 s of aim's Hang up, with aim's logs
ending before the click (left open, XEP-0167).

Assisted-by: Claude:opus-5-5
Kaidan rings and declines aim's call with <reject/>, aim shows it
declined; aim retracts a ringing call. Kaidan 0.16 keeps no call rows,
and the harness cannot start a call from Kaidan, so aim's own decline
and the other-device leg are not covered.

Assisted-by: Claude:opus-5-5
After a refused join (aim's locked room on Prosody, #9 C35) Gajim keeps
its "Failed to Join Group Chat" page with Cancel / Try Again, and
join_room's Ctrl+N path found no Back button there (run pr045g2). Click
Try Again (OCR, else Cancel +110) when that page is up.

Assisted-by: Claude:opus-5-5
find_text with a timeout raises when nothing shows, so the first
chat without "members" in its header ended _open_room_aim instead of
trying the next one; web's row-click fallback was never reached.

Assisted-by: Claude:opus-5-5
xep0353.py with web and web as vm-aim: web's reject, aim's retract, aim's
reject of web's call, and aim's proceed whose carbon stops the second
device ringing. Left open: web as caller sent no session-initiate after
aim's proceed in 37 s (no fake media in this example).

Assisted-by: Claude:opus-5-5
Three notches per OCR pass scrolled past "Members only" on
anotherim-server (run 205127): the form was left public and Dino then
offered no group call. One notch per pass, up to 30.

Assisted-by: Claude:opus-5-5
Room administration from aim with web in the room: invite, subject,
roles, kick, destroy reach web, but aim's membership and ban items name
web's full JID (client.log:270, :295), as with Gajim and Dino; XEP-0045
§9.1 requires the bare JID. web also keeps a second, 1:1-looking chat
of the room's JID with the invite text after accepting the mediated
invite (#9 candidate). C34 not reproduced.

Assisted-by: Claude:opus-5-5
aim's UI invites only through the room (mediated XEP-0045, client.log:241),
never with an XEP-0249 direct invitation; its room is also locked
(#9 C35), so Gajim's join is refused until Prosody's 5-minute lock
lapses. The receiving side works: Gajim's direct invitations show aim's
card, Accept joins, Decline keeps aim out and sends a mediated decline
to Gajim's JID through the room (C30 fixed).

Assisted-by: Claude:opus-5-5
After Gajim accepts an invitation from its Activity Feed the window
keeps the wide feed list and the room header's buttons sit 20 px
further right: 1119 hit the share button and the invite never opened.
Detect its "invite only" popover and take person+ at 1139.

Assisted-by: Claude:opus-5-5
Dino starts an audio Muji call in a private room, aim joins from its
XEP-0482 card: presences in the call room, aim's session-initiate and
Dino's accept, tones and 400 opus packets each way per 8 s. Also seen:
no XEP-0482 accept/left from aim, an OMEMO "Not sent" notice in the
room, and the duplicate chat a mediated invite leaves in aim's list.

Assisted-by: Claude:opus-5-5
aim's UI invites Dino only through the room (mediated XEP-0045,
client.log:260), never with an XEP-0249 direct invitation; Prosody adds
an x:conference element when forwarding, and Dino shows the invitation.
aim's room is locked (#9 C35): Dino's joins are refused (server.log:1443)
until Prosody's lock lapses at 346 s. Dino only receives here (PLAN.md).

Assisted-by: Claude:opus-5-5
With web as the owner (join_room) the room is aim's own client's and
stays locked on Prosody (#9 C35): aim's accept failed and nothing after
it ran. Shoot the refusal, wait out Prosody's 5-minute lock, join by
address and fail the step for C35, so moderation and the avatar are
still tested.

Assisted-by: Claude:opus-5-5
aim never sends a direct invitation: its Invite… is a mediated
XEP-0045 invite through the room (client.log:224); libtacky's
direct_invite is not reachable from the API or the UI. The receiving
side works: Gajim's two jabber❌conference invitations showed as
aim's invitation card, Accept joined the room, Decline went out as a
mediated decline to Gajim through the room (client.log:294, relayed
at peer-gajim.log:4513), naming the inviter (#1 C30 not reproduced).

Assisted-by: Claude:opus-5-5
Dino's clear call invite reaches aim's room card; aim declines and the
card says Declined, but libtacky withholds the <reject/>: "no member
… can receive an encrypted call invite, and it is not sent in the
clear" (client-tacky.log:21), the room showing "Not sent: it could not
be encrypted for vm-dino". The XEP-0272 run's accept and left were
withheld the same way.

Assisted-by: Claude:opus-5-5
aim's invitation to Dino is a mediated XEP-0045 invite through the
room (client.log:268), not a direct one; ejabberd's relay adds a
jabber❌conference element and Dino notifies the invitation
(notify-dino.log:9). Dino only receives in this scenario.

Assisted-by: Claude:opus-5-5
web sent its call invite (peer log) but the host read web's log after
the 30 s wait and the step failed before aim's side was tried
(anotherim run 211313).

Assisted-by: Claude:opus-5-5
Both aim and web invite only through the room (mediated XEP-0045),
never with XEP-0249. web shows no invitation card for aim's relayed
invite, only the room's JID as a chat request, and joins by address
after Prosody's lock window (aim's room never configured, #9 C35). aim's
Accept of web's mediated invite sent no join either. The decline leg
failed in the harness.

Assisted-by: Claude:opus-5-5
Both apps only send mediated XEP-0045 invitations (client.log:331,
:338), so no XEP-0249 direct invitation goes either way. The mediated
ones work: accepted from the card on both sides, and aim's decline
names the inviter (client.log:457, #1 C30 not reproduced). Every
mediated invitation also leaves a duplicate 1:1-looking chat of the
room's JID, kept after Decline (#9 candidate).

Assisted-by: Claude:opus-5-5
In a fresh Gajim room on prosody the quick click on the header's
person+ opened no picker and the typed JID went nowhere (XEP-0425 run
212122, parked). Retry with GTK's hover-and-slow-press at 1119 and
1139 (the Activity Feed layout) until the JID's row shows.

Assisted-by: Claude:opus-5-5
The check for the share button's "joined by invite only" popover read
x 940-1280, where a fresh room's narrow history column says "You
(vm-gajim) joined the group chat": it matched, the click moved to 1139
(between buttons) and no picker opened (prosody XEP-0425 runs). Read
from x 1030, right of the history column.

Assisted-by: Claude:opus-5-5
With Gajim's chat list wide (My Workspace or the Activity Feed, about
820 px) the room's message column is narrow: the hover bar's "…" sat
at 1001, not at 1218 - 252, and "contact info" at 1072, not 1037, so
moderation, the room avatar and destroy all failed. Find the bar's
icons as dark pixel runs on its row, and try the known header
positions for Group Chat Details.

Assisted-by: Claude:opus-5-5
OCR never read the card's "Decline" for web's invite (anotherim
212042), so aim neither joined nor declined. _group_card_aim falls
back to the blue Join, Decline 81 px left of it; xep0272 declines
through decline_group_call(). Drops a second _cancel_call_aim.

Assisted-by: Claude:opus-5-5
The room header's buttons move with the chat list's width (invite at
1119, 1139, 1160 seen), and the fixed or retried positions kept
missing it. Read the header row's dark icon runs (after the avatar:
"…", search, contact info, share, invite) and click the one wanted;
the fixed positions stay as the fallback.

Assisted-by: Claude:opus-5-5
The "Invite Contacts" picker's own line "Click on contacts you would
like to invite to this group chat" matched the check for the share
button's invite-only popover: the harness then closed the open picker
and clicked between buttons (prosody XEP-0425 runs, REPL). Take the
picker's "Contacts" title as proof it opened before that check.

Assisted-by: Claude:opus-5-5
One screen row missed Gajim's thin header icons and its "…" (whose
dots sit below row 81), so the invite button was taken at 1231. Count
dark columns over the strip's rows instead (dark_runs, generic), with
the pointer moved off: avatar, "…", search, contact info, share,
invite came out right on four saved header shots of three layouts.

Assisted-by: Claude:opus-5-5
The icon has the Join button's blue and widened find_color's box
(anotherim 213000); checked on that run's screenshot: 58x36 at 940,257.

Assisted-by: Claude:opus-5-5
The chat pane's left border made a 1 px dark run at x 880 and shifted
the button count by one (invite taken at share's 1140).

Assisted-by: Claude:opus-5-5
The icon reader put the invite button at 1085 (the share button) on
prosody, where the chat list is wider: no picker, and the typed JID went
into the room. Click the read x, then the places seen so far, each until
the picker's own text shows, and fail with a screenshot otherwise.

Assisted-by: Claude:opus-5-5
The pane's edge moves right with every new chat (880, 883: an 8 px
shadow run counted as an icon). Count the buttons after the avatar's
wide run when there is one.

Assisted-by: Claude:opus-5-5
web starts the Muji call through its API, aim joins from the card and
sends XEP-0482 accept and left (the room is unencrypted, so nothing is
withheld). aim hears web, but web hears nothing of aim: 403 opus
packets reach web per 8 s and its getStats reports audioLevel 0. In a
1:1 call web hears aim and Dino heard aim in a group call; which side of
this leg is at fault is not traced.

Assisted-by: Claude:opus-5-5
A room picture changes the avatar's dark run, so counting from the
avatar took "…" for contact info after setting the room avatar and
destroy could not open Group Chat Details. Share and invite are the
one linked pair ~33 px apart: count from there.

Assisted-by: Claude:opus-5-5
Gajim moderates aim's line in its room (peer-gajim.log:5487); aim gets
the tombstone with moderator and reason (client.log:337) and hides the
text, but shows the row as a plain "Message retracted", like its own
retraction, without who moderated it or why. Records #9's candidate
"moderated message without moderator". The room avatar part passes.
The destroy step failed in the harness (fixed since).

Assisted-by: Claude:opus-5-5
conference.prosody.asxp.io advertises no urn:xmpp:message-moderate
(mod_muc_moderation is not loaded): nothing to moderate with, for any
client. Found by slixmpp 20261107-065344 and again by aim vs Gajim
20261109-214403.

Assisted-by: Claude:opus-5-5
Prosody's test MUC advertises no urn:xmpp:message-moderate (its
mod_muc_moderation is not loaded, as the slixmpp run of 2026-11-07
found): Gajim's message menu offers no Moderate on aim's line. Mark
prosody na for XEP-0425 in its feature file, which also covers the
anotherim-web cell. The room's invite, aim's line and the avatar leg
work.

Assisted-by: Claude:opus-5-5
web starts a group call (API) in an unencrypted room; aim's card offers
Decline/Join, aim declines and sends <reject/> naming the invite's id
to the room, which web receives; the card reads Declined. In Dino's
OMEMO room the same reject was withheld (210351).

Assisted-by: Claude:opus-5-5
Gajim sets its room's avatar; aim gets status 104 and the new hash,
fetches the room's vCard (client.log:371, :378) and shows the picture
in the header and list. aim cannot do the same: anotherim-js offers no
room avatar setting (libtacky's muc set_avatar unused), so the leg
PLAN.md asks for (aim sets, peer shows) fails.

Assisted-by: Claude:opus-5-5
Assisted-by: Claude:opus-5-5
An XEP-0486 run of xep0425.py judged the room avatar but also failed
its moderate step, which says nothing about avatars (and moderation is
na on Prosody, which lacks mod_muc_moderation). Drop that step when the
run is XEP-0486.

Assisted-by: Claude:opus-5-5
C34 was fixed in libtacky v1.2.2 (1aed459c); with v1.2.3 the
restart after destroying the room is a fix check. Cite where aim
republishes the destroyed room's bookmark with autojoin='false'
(client.log:289, :325, :366) next to the missing rejoin.

Assisted-by: Claude:opus-5-5
On prosody the avatar pencil opened the "Update Avatar" page with its
Load Image button instead of a file chooser; the typed path went
nowhere and no vCard was sent (XEP-0486 run 215225, parked). Press Load
Image first when it is there.

Assisted-by: Claude:opus-5-5
With the chat list wide the Invite Contacts page sits at x 957-1275 and
its rows' JIDs end past 1130, so the picker's search for the invitee
found nothing.

Assisted-by: Claude:opus-5-5
Gajim's Join answers "Conference room does not exist" for a new
address (anotherim run 215904); web has no create action and joins.

Assisted-by: Claude:opus-5-5
Gajim sets the avatar of its room; aim gets status 104 and the photo
hash, fetches the room's vCard (client.log:290, :293, :296) and shows
the picture in the header and list. aim has no way to set a room's
avatar itself, so the PLAN.md sending case cannot run: fail, as on
ejabberd.

Assisted-by: Claude:opus-5-5
Inviting a second contact (aim, then Dino) found the first invite's
Invite Contacts page still up: the picker check took it as opened and
the JID was typed past its search field. Cancel that page first.

Assisted-by: Claude:opus-5-5
With the chat column narrow the picker is a page whose search field had
no focus for the second invitee: the typed JID went nowhere and OCR
missed the row's grey JID line (XEP-0486 dino+gajim run 220649, parked).
Click the search field when shown, and take the row by its name when
the JID line is not read.

Assisted-by: Claude:opus-5-5
New cross-device scenario, Gajim signed in as vm-aim: aim's bookmark
item makes Gajim join the room, aim's retraction reaches Gajim (which
stays in the room, not required to leave), and Gajim's item for a room
it created makes aim join it by itself.

Assisted-by: Claude:opus-5-5
xep0402.py with web as a second device of vm-aim: aim's bookmark makes
web join the room, aim's retraction makes it leave, web's bookmark for
a new room makes aim join it.

Assisted-by: Claude:opus-5-5
The JID pattern matched the picker's own search field (which holds the
typed JID) and a "New Contact" row before the contact: nothing was
invited (XEP-0486 run 221319, parked). Match the name first, below the
field, and take the last JID line otherwise.

Assisted-by: Claude:opus-5-5
In a Gajim-owned room Gajim sets the avatar; aim and Dino both get
status 104, fetch the room's vCard (client.log:357, peer-dino.log:2768)
and show the picture. aim cannot set a room avatar itself, so the
PLAN.md case aim -> Dino cannot run: fail, as with Gajim.

Assisted-by: Claude:opus-5-5
xep0045admin.py recheck: room, mediated invite, subject, roles and kick
by nick and destroy all reach Gajim, but aim's membership and ban name
Gajim's full JID (client.log:578, :606) where XEP-0045 §9.1 wants the
bare JID. C34 is fixed: after the destroy the bookmark goes
autojoin='false' (:622) and the restart does not recreate the room.

Assisted-by: Claude:opus-5-5
Gajim, as a third VM, owns the room and sets its avatar: Dino and aim
both fetch the room's vCard after status 104 and show the picture
(peer-dino.log:4155, client.log:545). The leg PLAN.md asks for, aim
setting it for Dino, cannot run: anotherim-js has no room avatar
setting.

Assisted-by: Claude:opus-5-5
A 320x240 gradient PNG is 169 kB in base64, over the 128 KiB a single
argv string may hold: interop-web --eval exited 126 and no avatar was
set (XEP-0486 web run 223000, parked). Send a 96x96 one (25 kB) when
the picture is too big.

Assisted-by: Claude:opus-5-5
message.history without a stamp gave no row with the line (now also
asked with before = now), and a 320x240 picture's Base64 was too long
for one interop-web --eval argument (exit 126): set a 64x64 one.

Assisted-by: Claude:opus-5-5
xep0045admin.py recheck: room, invite, subject, roles and kick by nick
and destroy reach Dino, but membership and ban name Dino's full JID
(client.log:592, :689), not the bare JID XEP-0045 §9.1 requires. C34
fixed: bookmark autojoin='false' after the destroy, no recreate after
the restart.

Assisted-by: Claude:opus-5-5
message.history answered no rows for the room web was in (0 rows,
twice), so moderation never ran. Try goto around now and a local
search for the word too, and log what each answered.

Assisted-by: Claude:opus-5-5
anotherim-js#95 (in rc8+) shows a destroyed room's chat with Remove and
"Go to the new room" instead of Join. The restart step now opens the
room's chat, OCRs those words and takes shot destroyed-room, beside the
C34 check.

Assisted-by: Claude:opus-5-5
The three prosody runs predate the example's look at a destroyed room's
chat after the restart. Their shots after the destroy show aim's Room
details "Not joined" with no "Remove" / "Go to the new room" (OCR):
record that it was not seen there.

Assisted-by: Claude:opus-5-5
web sets its room's avatar through the API (its UI has none); aim gets
status 104, the room's vCard (client.log:328, :335) and shows the
picture. aim cannot set one itself: fail. On the way aim showed web's
mediated invite only as a chat request from the room's JID, no
invitation card, and joined after Prosody's lock window (#9 C35).

Assisted-by: Claude:opus-5-5
Bookmarks sync between aim and Gajim signed in as vm-aim: aim's publish
reaches Gajim, which autojoins (refused only by Prosody's lock on aim's
unconfigured room, #9 C35); aim's retraction reaches Gajim; Gajim's
bookmark for its own room reaches aim, which joins it by itself
(client.log:385, :399).

Assisted-by: Claude:opus-5-5
xep0045admin.py recheck: room, invite, subject, roles and kick by nick
and destroy reach web, but membership and ban name web's full JID
(client.log:603, :629). C34 fixed (autojoin='false' after destroy, no
recreate after restart). web keeps a duplicate chat of the room's bare
JID after the mediated invite.

Assisted-by: Claude:opus-5-5
libtacky stores a room's messages under the chat key "<room>?join";
message.history, goto and search with the bare room JID answer no rows,
so web's API moderation found nothing to moderate. Found in a
tools/repl.py session; moderation by that key reached aim at once.
xep0513's mentions_me check reads the same key.

Assisted-by: Claude:opus-5-5
Bookmarks sync between aim and web signed in as vm-aim: publish,
retraction and the other device's item each reach the other side, which
autojoins (client.log:426, :440). Both autojoins are then refused by
Prosody's lock on rooms anotherim-js never configures (#9 C35), which
aim shows as "This room does not exist".

Assisted-by: Claude:opus-5-5
web, owning the room, moderates aim's line through libtacky's API
(peer-anotherim-web.log:260; the UI has no moderation); aim gets the
tombstone with moderator and reason (client.log:483) and hides the
text, but both apps show only "Message retracted", as for a sender's
own retraction (#9 candidate, as with Gajim). web's room avatar via
the API shows on aim.

Assisted-by: Claude:opus-5-5
Dino's reject, aim's retract while ringing, aim's reject of Dino's call,
and aim answering while its second device (web as vm-aim) rings, which
then stops with "Answered on another device", all work. Afterwards
aim's Hang up sent no session-terminate: its stream was silent from
23:09:02 to shutdown (server.log:3969, :4073); untraced, outside 0353.

Assisted-by: Claude:opus-5-5
Re-run with the anotherim-js#95 read: the same full-JID membership and
ban (client.log:587, :615), C34 fixed. The destroyed room stays listed
as an ordinary "Not joined" room until the restart, without the Remove
/ "Go to the new room" view, and is gone after the restart.

Assisted-by: Claude:opus-5-5
Kaidan rings and rejects aim's call; aim shows "Call declined by
contact" (client.log:303). aim retracts a ringing call (client.log:314)
and Kaidan's incoming notification goes; Kaidan 0.16 keeps no missed
row. Kaidan calling aim is not scripted.

Assisted-by: Claude:opus-5-5
web sets its room's avatar through libtacky's API (muc.set_avatar,
peer-anotherim-web.log:237); aim gets status 104, fetches the room's
vCard (client.log:506) and shows the picture. libtacky does the job;
anotherim-js has no UI to set a room avatar, so the user-facing leg
fails as on the Gajim and Dino runs.

Assisted-by: Claude:opus-5-5
web rejects aim's call, aim retracts a ringing one (web: Missed call),
and aim rejects web's call: each <reject/>/<retract/> on the wire
(client.log:335, :346, :363) and the outcome on both screens.

Assisted-by: Claude:opus-5-5
It returned the first groupchat stanza sent after the click, which was
a <composing/> chat state, so xep0513 reported no <mention/> though the
message carried one.

Assisted-by: Claude:opus-5-5
aim sends only XEP-0045 mediated invitations (the server adds a
jabber❌conference element when relaying), no XEP-0249 direct one.
Gajim's direct invitations reach aim's card; Accept joins, Decline
sends a mediated <decline/> through the room (client.log:604). Same as
ejabberd 210455.

Assisted-by: Claude:opus-5-5
The audio call carries both tones over prosody's TURN. In the video
call both test cards show, but Dino sends only RTCP on its audio SSRC
and no opus (rtp.log:11, :161), so aim hears nothing of Dino: #9 P3 on
Dino's send side, now also on prosody.

Assisted-by: Claude:opus-5-5
aim sends only a XEP-0045 mediated invitation; Dino receives it (with
the server's added conference element), notifies, and joins. No
XEP-0249 direct invitation from aim. Same as ejabberd 211132.

Assisted-by: Claude:opus-5-5
aim and web @-mention each other in a room from the composer's
picker: each line carries a XEP-0372 reference and a mentions:0
<mention/> with the occupant id over the "@nick" span
(client.log:498, peer-anotherim-web.log:310); web's record says
mentions_me and both rooms highlight the mention.

Assisted-by: Claude:opus-5-5
#9 C36 on prosody too: Kaidan's session-accept has no rtcp-mux, aim's
libwebrtc rejects the answer ("RTCP-MUX is not enabled when it is
required", client-tacky.log:284) and Kaidan ends both calls with
failed-transport. TURN is there since #13, so ICE is not the cause.

Assisted-by: Claude:opus-5-5
Bookmarks across one account's devices: aim's R1 item makes Gajim
(second device of vm-aim) join, aim's retraction reaches Gajim, and
Gajim's R2 item makes aim join R2 by itself (client.log:496, :510).
Gajim's first publish conflicted with the node's options aim had set
(precondition-not-met); Gajim reconfigured and published.

Assisted-by: Claude:opus-5-5
Neither side sends a XEP-0249 direct invitation, only XEP-0045 mediated
ones; those work both ways, Decline names the inviter (C30 fixed), and
each leaves a duplicate chat of the room's bare JID. Same as ejabberd
211510.

Assisted-by: Claude:opus-5-5
Bookmarks between two devices of vm-aim: aim's R1 item makes web join
and its retraction makes web leave (peer-anotherim-web.log:428, :453);
web's R2 item makes aim join R2 by itself (client.log:477, :491).

Assisted-by: Claude:opus-5-5
With prosody's TURN (#13) both calls connect over relay candidates:
tones heard both ways in the audio and the video call (406 opus each
way per 8 s), video both ways and the test cards read on both screens.
Supersedes the pre-TURN fail of 2026-11-08.

Assisted-by: Claude:opus-5-5
Gajim moderates aim's line with a reason; the room's notice carries the
moderator and reason in both the :0 and :1 forms (client.log:669). aim
drops the text but shows a plain "Message retracted" row without them,
while Gajim names both. Same as ejabberd 214417.

Assisted-by: Claude:opus-5-5
aim offers the file with JET security type jet-omemo:2 (client.log:356),
web accepts, receives it over the data channel and ends the session with
success (:455); no upload fallback.

Assisted-by: Claude:opus-5-5
Reject, retract and proceed each way with Dino (client.log:448, :455,
:469, :483); with web:aim as a second device both ring and web stops
with "Answered on another device" once aim answers. After that, aim's
hang-up left Dino in the call with no session-terminate seen within
60 s; aim's copied logs end before it, so it is noted, not judged.

Assisted-by: Claude:opus-5-5
Receiving passes: after Gajim sets the room's picture, aim gets status
104 and the x:update hash, fetches the room's vCard and shows the
picture. aim cannot set one: anotherim-js has no room avatar setting.
Same as ejabberd 215138.

Assisted-by: Claude:opus-5-5
Assisted-by: Claude:opus-5-5
Both ends seal their DTLS fingerprints in OMEMO 2 (client.log:355,
:378), the data channel comes up and the file arrives; session-terminate
with success (:427), no upload.

Assisted-by: Claude:opus-5-5
aim calls Kaidan: Kaidan rings and rejects from its notification
(peer-kaidan.log:565, :570), aim shows the call declined; aim cancels
a second call while it rings and Kaidan gets the retract
(peer-kaidan.log:596). Kaidan calling aim is not scripted.

Assisted-by: Claude:opus-5-5
Ringing, reject and retract both ways between aim and web: aim's
call declined by web and cancelled while ringing (web shows it
missed), web's call declined by aim (client.log:458, :477;
peer-anotherim-web.log:215, :244).

Assisted-by: Claude:opus-5-5
vm-aim on prosody lists seven own devices; the keys column shows four
before scrolling, and _trust_aim gave up on the fifth (omemo-chats run
234816, parked). Scroll down until the row is read, and match ids with
one misread digit too (OCR gave 469373856 for 469373858).

Assisted-by: Claude:opus-5-5
The invitation's notification (dunst, top right) lay over the Join
button, the click hit it and Dino never joined Gajim's room (anotherim
run 234641).

Assisted-by: Claude:opus-5-5
Recheck with the camera feed. The video call passes in full: tones
both ways, opus and VP8 each way, both test cards live. In the audio
call Dino sent no opus (RTCP only on its NIC, steps.log:57) though its
pipeline used FakeMic, so aim heard nothing: #9 P3 again (peer).

Assisted-by: Claude:opus-5-5
Gajim (third VM) owns the room and sets its picture; aim and Dino both
get status 104, fetch the room's vCard and show it. The aim -> Dino leg
cannot run: anotherim-js has no room avatar setting. Same as ejabberd
222656.

Assisted-by: Claude:opus-5-5
Recheck with the camera feed: Kaidan's session-accept has no
<rtcp-mux/> (client.log:541) and aim's libwebrtc rejects the answer
(client-tacky.log:217, :546), so neither call carries media; Kaidan
ends the video call with failed-transport. #9 C36 again.

Assisted-by: Claude:opus-5-5
With the account's devices verified both ways, aim's encryption off and
on for vm-gajim go out as sealed omemo-chats items (client.log:509,
:530) and web, signed in as vm-aim, follows both.

Assisted-by: Claude:opus-5-5
web sets its room's avatar through the API; aim gets status 104,
fetches the room's vCard and shows the picture (also on the duplicate
chat the mediated invite left). aim cannot set one: no room avatar
setting in anotherim-js. Same as prosody 224019.

Assisted-by: Claude:opus-5-5
Recheck with the camera feed: audio and video calls with tones both
ways, opus and video RTP each way on both NICs (rtp.log:17, :23), and
both test cards live on the other screen.

Assisted-by: Claude:opus-5-5
web (owner) moderates aim's line through the API; the room's notice
carries moderator and reason and aim drops the text, but shows a plain
"Message retracted" row without them, as with Gajim (233507).

Assisted-by: Claude:opus-5-5
What the anotherim-server runs of batch 2 found, and the harness lessons
behind its fixes (Dino's Reject and Join, aim's Answer strip and group
call card, Gajim's Join of new rooms, web's late log), so the next agent
does not rediscover them. README lists xep0402.py and xep0353's
other-device leg.

Assisted-by: Claude:opus-5-5
aim's OCR read Gajim's key as #1367340078, web's as #1367340978; the
prefix match then found no key on web, before and after, though web's
column shows it Verified (omemo-trust run 20261110-001032). Use the
aim client's id match, which allows one differing digit.

Assisted-by: Claude:opus-5-5
Dino starts a Muji group call in its private room; aim joins from the
XEP-0482 card (client.log:589, :617, :645), tones both ways and opus
each way on both NICs; Leave ends the call on both sides.

Assisted-by: Claude:opus-5-5
aim's new room is never configured (#9 C35): Dino's join is refused and
Prosody deletes the room when aim restarts (server.log:2103). aim then
fetches its autojoin bookmark, finds the room gone (item-not-found,
client.log:505) and does not rejoin, so the bookmark rejoin cannot be
shown on prosody.

Assisted-by: Claude:opus-5-5
aim declines Dino's group call from its card, which then reads
"Declined", but no <reject/> leaves aim: libtacky withholds call
signalling it cannot OMEMO-encrypt for every room member
(client-tacky.log:20-21, vm-dino: no usable device) and will not send
it in the clear, though Dino's invite came in the clear. The XEP-0272
run shows aim's <accept/> and <left/> withheld the same way.

Assisted-by: Claude:opus-5-5
aim sets Gajim's key Accepted -> Verified and later back to Accepted;
neither change is published to its other devices (no omemo-trust
publish in server.log), so web keeps a stale Verified from an earlier
list. An unrecorded attempt before saw aim publish only on Undecided ->
Verified. Same verdict as ejabberd 182653.

Assisted-by: Claude:opus-5-5
Results of the prosody runs and what the harness learned there: the
room lock lapsing after five minutes, Gajim's moving header and invite
picker, the web avatar size limit, the long keys column.

Assisted-by: Claude:opus-5-5
web starts a group call through the API (no UI for it), aim joins from
the XEP-0482 card: aim hears web, but web hears nothing although
aim's opus reaches it at full size (rtp.log:15) and web's getStats
counts the packets with audioLevel and energy 0 (rtp.log:24-25). The
same pair's 1:1 call plays both ways; autoplay is not restricted.

Assisted-by: Claude:opus-5-5
Assisted-by: Claude:opus-5-5
web starts a group call through the API; aim declines from its card
and the <reject/> reaches web through the room (client.log:569,
peer-anotherim-web.log:334). In this room OMEMO is off for aim, so
nothing is withheld, unlike the Dino run.

Assisted-by: Claude:opus-5-5
vms: batch 2 on ejabberd in AGENTS.md, new examples in README
All checks were successful
/ check (pull_request) Successful in 12m17s
ebf572edcd
Records the ejabberd results of batch 2 and what the harness learned
there (Gajim's moving header and hover bar read with dark_runs, the
kicked-room Join bar, Dino's full-JID nick, web's Alt+N tabs, the
<room>?join chat key of libtacky's API, mention()'s chat state), and
lists xep0249.py, xep0513.py, decline_invite(), mention(), dark_runs()
and the new peers of xep0425/xep0272 in the README.

Assisted-by: Claude:opus-5-5
asxpi merged commit fcc2190bc7 into main 2026-11-10 01:09:48 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
another-im/interop!22
No description provided.