Phase 4 of docs/frost-batch-signing.md: the app-facing surface for what Phase 3
built, plus the two rules a caller has to know before reaching for it.
FrostSigningRepository.proposeSigningBatch takes a List<EventTemplate<*>> and
returns the one session that signs all of them. proposeSigning stays exactly as
it was -- AddDialectViewModel, AddArtifactViewModel and GroupKeyStateManager
need no edit, and none is made here.
Both forms now share one `proposing` helper for the throw-to-null conversion.
The reason it exists is unchanged and now covers two more cases: proposing
throws when the group has no key, when this device was not in the ceremony, and
now when a batch is empty or over MAX_BATCH_SIZE. All four are states the UI is
supposed to have checked for, so they become a null the caller reports.
## The two rules, written where a caller will read them
A batch is only as available as its worst item. It is all-or-nothing, so if any
event cannot be aggregated the session fails and none of them are applied --
which means events that do not belong together should not travel together.
A retry is a new batch, never the same one again. A failed batch looks like it
has perfectly good nonces going spare; it does not. Every item's seed has
already been published against an aggregate, and reusing one would produce two
partial signatures over a single secret nonce. proposeSigningBatch mints fresh
seeds, so proposing afresh is safe by construction and re-proposing is the only
way to get it wrong.
GroupKeyStateManager.propose records that it must never be batched: it is the
statement every other session in the room is opened against, so bundling it
with a dialect would make the room's ability to sign at all depend on that
dialect's aggregation succeeding.
Per-item partial success stays out of scope -- it would need mixed-state UI, a
transcript that can say "3 of 5", and a complete() that applies a subset, for an
outcome that indicates a bug or a dishonest coordinator rather than a normal
ending.
356 jvmTest and 224 testDebugUnitTest pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>