73becef038
The existing testReaddAfterRemove_RejoinerCanEncryptAndDecrypt in MlsGroupLifecycleTest exercises the MLS layer (two raw MlsGroup instances, admin kicks peer, fresh KP, rejoin). Nothing covered the same flow one level up at the MlsGroupManager layer, where the persistent state store, retained-epoch wipe, and Welcome routing for the same nostrGroupId actually live. Adds testLeaveAndRejoin_SameGroupIdEndToEnd: two MlsGroupManager instances (Alice + Bob) with separate InMemoryGroupStateStores go through the full cycle — Alice creates with a MarmotGroupData extension, Bob joins via Welcome, bidirectional message round-trip, Bob calls manager.leaveGroup (asserts store + retained epochs both wiped), Alice removes Bob's leaf, Bob's second KeyPackage is admin- added, Bob processes the second Welcome under the same nostrGroupId, and bidirectional round-trip works again at the new epoch. Closes the "MarmotManager.leaveGroup + rejoin" and "persistent-store leave + fresh Welcome for same nostrGroupId" gaps from the review. Documents inline why Alice drives the eviction commit directly: the standalone-proposal ingestion pipeline (MarmotInboundProcessor) is a separate, still-unimplemented concern. https://claude.ai/code/session_01Unm6uLHGLj9UcBY7hWfJVW