adventure: send Pete the run, room by room

The engine narrates every fight, trap and haul to one Matrix DM and then
discards the shape underneath it. This records that shape as it happens so
Pete can retell the run to somebody who wasn't in the room.

Beats ride the roster ticker (one extra request per two minutes, not one per
room) but on their own table, never pete_emit_queue: they are high-volume and
low-stakes, and a chatty run must not be able to spend the retry budget a
death dispatch depends on. Unlike the snapshots they ARE retried — a dropped
beat is a hole in a story, not a stale number the next tick corrects.

Recording is a leaf everywhere it is called. If a beat can't be written the
walk carries on exactly as it did before this existed.

Privacy is stricter here than on the board. The board omits an opted-out
player from a snapshot; a liveblog would be an account of where they are and
what is happening to them, so their beats never leave the box at all — and a
run whose owner can't be resolved is refused rather than published.

Claude-Session: https://claude.ai/code/session_012bxpQQJDjC1mTtLN3VVtBQ
This commit is contained in:
prosolis
2026-07-24 16:34:02 -07:00
parent 0570afc2e4
commit a5b1961486
13 changed files with 953 additions and 3 deletions
+8
View File
@@ -295,6 +295,7 @@ func startZoneRun(userID id.UserID, zoneID ZoneID, dndLevel int, rng *rand.Rand)
); err != nil {
return nil, fmt.Errorf("insert zone run: %w", err)
}
beatRunStart(userID, run, zone)
return run, nil
}
@@ -581,6 +582,7 @@ func abandonZoneRun(userID id.UserID) error {
if r == nil {
return ErrNoActiveRun
}
beatRunEnd(r, "abandoned")
_, err = db.Get().Exec(`
UPDATE dnd_zone_run
SET abandoned = 1,
@@ -599,6 +601,12 @@ func abandonZoneRunByID(runID string) error {
if runID == "" {
return nil
}
// The generic funnel: it fires for an idle reap, a region retirement, and a
// completed run being tidied up alike. beatRunEnd is first-writer-wins, so
// this only ever supplies the outcome nothing more specific already did.
if r, _ := getZoneRun(runID); r != nil {
beatRunEnd(r, "abandoned")
}
_, err := db.Get().Exec(`
UPDATE dnd_zone_run
SET abandoned = 1,