peerMesh
Build a symmetric peer-mesh Seam — the contract-conforming N-peer fabric.
Identical to meshSeam/hubMesh except for the drain contract: when a peer-mesh that was non-empty loses its last link, it latches SeamState.Torn and completes Seam.incoming together — single-shot and ordered (Torn is latched inside the teardown lock; the inbound spool then closes) — honouring the "incoming completes once the seam reaches Torn, whether via local close or a remote disconnect" invariant on Seam.incoming. Use this for a genuine peer-to-peer session whose life ends when every peer has gone.
Because "was non-empty, now empty" cannot distinguish a drained peer-mesh from a hub that simply lost its one joiner, the role is explicit at construction — it is not inferred. A hub uses hubMesh instead.
A peer-mesh MAY start empty and grow. Pass an empty connections list for the deliberate start-empty → grow-via-Mesh.addLink → drain lifecycle (e.g. a voter mesh both ends of which dial each other in via addLink): the seam is born SeamState.Woven, and only latches SeamState.Torn once it has been non-empty and then drained back to empty. The drain latch keys on that runtime "was non-empty, now empty" transition (via Mesh.addLink then link loss) — never on the construction list.
Reject-all at construction is dead on arrival. A construction that requests connections but has them all rejected by admission is born SeamState.Torn (the "A2" rule) — it asked for peers, got none, and has nothing that could ever drain, so it must not sit Woven forever with an empty roster. This is a quiet born-Torn, consistent with "rejection never fails construction" — not a thrown constructor.
This makes for one honest asymmetry: peerMesh(listOf(rejectedConn)) is born-Torn, but a peerMesh(emptyList()) whose first Mesh.addLink is later rejected stays Woven. Construction is a closed roster claim (you named the peers you expect); addLink is open-ended (a hub-style accept-pump feeding an initially empty roster). Requesting peers and being denied all of them is failure; requesting none and then having a speculative joiner declined is not.
All other parameters behave exactly as documented on meshSeam.