Two properties here decide whether a ceremony can finish, and the compiler sees
neither.
A participant gets one message per round, and that is enforced by the composite
key (sessionId, participantPublicKey, kind) rather than by any code that writes
to the table. Rounds advance on countMessagesByKind reaching the participant
count, so a redelivered message that added a row instead of replacing one would
let the count reach the threshold while a member had still never been heard
from -- and the ritual would proceed on a participant set it never assembled.
Covered by resending a participant's message with a different payload and
asserting the count stays at one and the payload is the newer of the two, and
separately by writing the same participant into two different rounds and
asserting neither overwrites the other.
A key-holding session needs both halves. thresholdPublicKey without secretShare
is a ceremony that produced a group key this device cannot sign against;
secretShare without thresholdPublicKey is a share with no key to sign for.
Either alone is a failed ceremony, and offering it up as a signing key means
attempting to sign with half a result. Covered with all four combinations
present in the table at once, asserting only the complete one comes back.
Also covered: the live ritual for a room is the newest, because a group may have
abandoned earlier attempts and a resume that picked up an abandoned one would
wait forever on participants who have moved to the newer; rituals belonging to
another room are not offered as this room's; key-holding sessions come back
newest first; and messages are counted per session and per round rather than
across either.
And the ordering, which is the one with a reason beyond tidiness: a round's
messages come back ordered by participant public key, not by arrival. Every
device has to assemble a round in the same order to compute the same thing, and
arrival order is per-device. The test writes three participants in an order
deliberately unlike the sorted one.
Real secp256k1 keys throughout rather than hex filler, since these are the
values a canonical ordering is defined over.
Verified by mutation: relaxing the key-holding predicate to `OR` returns all
three of the incomplete sessions and fails that test; reordering the round query
by createdAt fails the canonical-order test. Both mutations were reverted; no
production source is touched by this commit.
8 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>