Every ownership lookup in the adventure module keys on a user id, and a
party member owns no row: not the expedition, not the zone run. P4 gave
them activeExpeditionFor; this gives them activeZoneRunFor, and gives the
DM seams the audience they never had.
- activeZoneRunFor(user) -> (run, isLeader, err). An owner's lookup is
exactly getActiveZoneRun, side effects and all -- in particular the
§4.3 idle reap, which force-extracts the wrapping expedition. A member
must never re-enter it, or glancing at the map would end the leader's
run. Pinned.
- expeditionAudience / fanOutExpeditionDM. Briefing, recap and digest all
DM'd id.UserID(e.UserID) alone. They now loop the roster, which
partyMemberIDs collapses to exactly the owner when there is none -- so
a solo expedition sends the same bytes to the same user it always has.
The briefing's body is expedition-scoped but its pet prefix is not:
each member has their own pet and their own sheet, so the roll rides a
per-reader decorator (the shape P5 settled on for combat narration).
The digest's A6 event anchor rolls per member for the same reason.
- releaseParty on every terminal transition. A seated member is barred
from adventuring elsewhere, so a roster outliving its expedition
strands the party. Deliberately NOT on 'extracting': that is a 7-day
resumable limbo and !resume must bring everyone back. The roster clears
when the window lapses to 'failed', which routes through
completeExpedition like the rest.
Rosters are still empty in production -- nothing seats a member yet -- so
every loop here has exactly one element and the whole change is a no-op
until P6b. Golden byte-identical, go test ./... green.