Commit Graph
5 Commits
Author SHA1 Message Date
prosolis 509df7fadf adventure: stop the log claiming a win it didn't see
Four things the review found, all of them the code telling a player
something that isn't true.

A run that walks into a dead end filed its ending as "cleared" whatever
the caller said, so both the liveblog and the summary reported a clear
for a party that merely ran out of map — and the end beat is
first-writer-wins, so nothing later could take it back. It says
"cleared" only for a boss now.

The re-offer branches of babysit and resume returned a zero cost, so a
player who was in fact charged read "0 coins" in the verdict. Both
re-quote the price they actually took.

And the realm-firsts reseed retired its one-shot job even when the read
under it had failed, which on a transient fault at Init would have left
the ledger mis-dated permanently. The read now says whether it worked,
and the job stays open when it didn't.

Claude-Session: https://claude.ai/code/session_012bxpQQJDjC1mTtLN3VVtBQ
2026-07-24 22:07:38 -07:00
prosolis a27fb298af adventure: repair the realm-firsts ledger the backfill got wrong
The one-shot that seeded news_realm_firsts filtered on `abandoned = 0`, and
`abandoned` does not mean anybody gave up: abandonZoneRunByID exists to retire a
run whose boss is already dead when the expedition travels onward, which is how
30 of prod's 32 boss kills are stored. So the ledger holds 6 zones where the run
history knows 9, and it dates every row to the minute the job ran rather than to
the clear.

The ledger is what claimRealmFirst tiers live dispatches against, so each missed
zone is a spurious PRIORITY "realm first" waiting to fire the next time somebody
clears it, months late.

Fix the shared query, and add a re-seed (not a re-run) under its own job name: it
writes the ledger, backdated to the real earliest clear, and emits nothing at
all. It runs regardless of the news switches, because a ledger that is right only
while emission is on mis-tiers the first dispatch after somebody flips it. Kept
in place afterwards as an ordinary bootstrap for fresh deploys.

Claude-Session: https://claude.ai/code/session_012bxpQQJDjC1mTtLN3VVtBQ
2026-07-24 20:35:47 -07:00
prosolis 4e0b8a298c news: the backfill files bulletins too
The live emitters stopped filing priority facts in 1cbd68a, but the launch
backfill kept doing it for historical zone-firsts and deaths. It is inert today
— Pete's handler short-circuits on no_push before the Matrix post, and headlines
key off event_type rather than tier — so nothing on the site or in the room
moves. It was a landmine: the echo rule was being enforced by a guard that
happens to sit in front of it, not by the tier itself, and anyone touching that
guard resurrects the echo at back-catalogue scale.

Bulletin is the rule. Nothing in the back catalogue is news anyway.
2026-07-13 21:04:39 -07:00
prosolis ae7ff38996 Pete news: salted per-event GUID token + zone_clear taxonomy
Closes the two deferred code-review follow-ups on the adventure-news
seam, plus folds in two pre-committed WIP fixes.

A. Privacy — the public GUID no longer leaks a stable per-player id.
   Replaced userHash(userID)=sha256(userID)[:6] with
   eventToken(userID, discriminator)=HMAC-SHA256(salt, userID‖disc).
   The salt is 32 random bytes, auto-generated once and persisted in the
   durable news_config table (cached via sync.Once). Because each event
   uses a distinct HMAC message, tokens are a PRF output and are BOTH
   uncomputable from a Matrix handle (no enumeration of a player's
   events, incl. ones anonymized after !news optout) AND mutually
   unlinkable (a named event can't be walked back to a player's other,
   anonymized events). Updated all emit sites: pete.go zone, dnd_combat
   death, adventure_duel rival, dnd_setup arrival, achievements
   milestone, bootstrap x3.

B. Taxonomy — repeat zone clears were mislabeled zone_first. Now emit
   zone_clear (bulletin) vs zone_first (realm-first, priority). Adopted
   the invariant GUID-prefix == event_type, which also fixes latent
   permalink mislabels (achv->milestone, rival->rival_result rendered a
   neutral "Dispatch" on their permalink pages).

Folded-in WIP fixes: create the news_config table b42beec's
newsEmissionOn reads but never created; reap sent pete_emit_queue rows
in RunMaintenance; don't burn a retry attempt when shutdown cancels an
in-flight send.

Tests: TestEventToken (salted/stable/per-event/persisted),
TestEmitZoneClearTaxonomy (first->zone_first, repeat->zone_clear),
updated pete_test.go prefixes. Full internal suite + vet green.

Unshipped. Deploy Pete first (it must know zone_clear), then gogobee.
2026-07-11 07:43:57 -07:00
prosolis b42beec348 Pete Adventure News: emit seam, !news command, cold-start backfill
gogobee's game-side of Pete's push-based Adventure news feed. Emits
structured event *facts* (not prose) to Pete's ingest endpoint over a
durable queue; Pete owns voice/authoring/publishing.

- internal/peteclient: durable pete_emit_queue + retry sender; Fact
  payload; FEATURE_PETE_NEWS master switch.
- pete.go: emitFact enforces runtime kill-switch + per-player opt-out
  (anonymize, never drop); zone-clear + realm-first tiering; !news
  command (player optout/optin, admin on/off).
- bootstrap_pete_news.go: cold-start backfill replays deaths +
  single-holder achievements + zone realm-firsts, backdated + NoPush,
  idempotent, fires only once the seam is live; seeds news_realm_firsts.
- Emit sites: death, zone_first, rival_result, milestone, arrival.
- db.go: pete_emit_queue, news_optout (+opted_out_at), news_realm_firsts.

Unshipped. Deploy Pete first, then set FEATURE_PETE_NEWS + ingest env.

Claude-Session: https://claude.ai/code/session_017mEwUmmS7aQTP2NQXj6rUa
2026-07-11 00:53:25 -07:00