From 1634bb1970ebd07e857228c9dfaf7cf81bd12b59 Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Sat, 11 Jul 2026 07:56:41 -0700 Subject: [PATCH] Pete news code-review fixes: realm-first ordering, parked-row reap, death-emit gate - emitZoneClearNews: claim realm-first before the name guard so an unnamed straggler's first clear seeds news_realm_firsts (else the next named clearer is mis-announced as first-ever). Mirrors backfillZoneFirsts. - RunMaintenance: reap permanently-parked pete_emit_queue rows (unsent, >30d) so a durable Pete outage can't accrete rows forever. - emitDeathNews: gate on Enabled()/newsEmissionOn() before the char DB reads; markAdventureDead fires per-member on party wipes + in the sim. --- internal/db/db.go | 5 +++++ internal/plugin/dnd_combat.go | 5 +++++ internal/plugin/pete.go | 12 ++++++++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/internal/db/db.go b/internal/db/db.go index 8a4ac99..7e0a893 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -619,6 +619,11 @@ func RunMaintenance() { // Pete adventure-news queue — reap delivered rows (kept only for // idempotency); undelivered rows stay so the sender can retry/park them. {"pete_emit_queue", `DELETE FROM pete_emit_queue WHERE sent_at IS NOT NULL AND sent_at < ?`, []interface{}{cutoff7d}}, + // ...and reap permanently-parked rows: the sender exhausts its retries + // within a few hours, so anything still unsent after 30 days is dead + // weight the drain query already skips — drop it so a durable outage + // can't accrete rows forever. + {"pete_emit_queue_parked", `DELETE FROM pete_emit_queue WHERE sent_at IS NULL AND created_at < ?`, []interface{}{cutoff30d}}, // Rate limits — purge entries older than today {"rate_limits", `DELETE FROM rate_limits WHERE date < ?`, []interface{}{today}}, diff --git a/internal/plugin/dnd_combat.go b/internal/plugin/dnd_combat.go index dce4f11..2429236 100644 --- a/internal/plugin/dnd_combat.go +++ b/internal/plugin/dnd_combat.go @@ -583,6 +583,11 @@ func markAdventureDead(userID id.UserID, source, location string) { // unless the seam is enabled. Uses the character name (never the Matrix handle); // skips silently if the name is unknown. func emitDeathNews(userID id.UserID, location string) { + // Gate before the char lookups: markAdventureDead fires per-member on a + // party wipe and in the sim harness, so skip the DB reads when disabled. + if !peteclient.Enabled() || !newsEmissionOn() { + return + } name := charName(userID) if name == "" { return diff --git a/internal/plugin/pete.go b/internal/plugin/pete.go index 96fede6..bb22f1c 100644 --- a/internal/plugin/pete.go +++ b/internal/plugin/pete.go @@ -266,6 +266,14 @@ func emitZoneClearNews(userID id.UserID, exp *Expedition) { if !peteclient.Enabled() || !newsEmissionOn() { return } + // Claim the realm-first BEFORE the name guard, so an unnamed straggler's + // genuine first clear still seeds news_realm_firsts. Otherwise the next + // named clearer would claim it and be mis-announced as the first-ever. + // Mirrors backfillZoneFirsts, which claims before its own name check. + eventType, tier := "zone_clear", "bulletin" + if claimRealmFirst("zone", string(exp.ZoneID)) { + eventType, tier = "zone_first", "priority" + } name := charName(userID) if name == "" { return @@ -278,10 +286,6 @@ func emitZoneClearNews(userID id.UserID, exp *Expedition) { } } lvl := charLevel(userID) - eventType, tier := "zone_clear", "bulletin" - if claimRealmFirst("zone", string(exp.ZoneID)) { - eventType, tier = "zone_first", "priority" - } ts := nowUnix() disc := fmt.Sprintf("%s:%d", exp.ZoneID, ts) emitFact(peteclient.Fact{