Commit Graph

4 Commits

Author SHA1 Message Date
prosolis
11bfce780c adventure: gogobee pushes each adventurer's sheet to Pete's detail page
The board on Pete shows flat rows; this hands it two more channels so a name
can become a page. Public stats + equipped gear ride the roster snapshot
(RosterDetail on each entry, keyed by the anonymous token, no handle). The
private self-view — inventory, vault, house, pets — rides its own push keyed
by localpart, so Pete only ever serves it back to the one signed-in owner it
belongs to; the board token rides along so the ownership check is a join, never
a reversal of the one-way token. The private set skips no one for opt-out (that
governs the public board only) and skips the dead (no live page to own).
2026-07-14 22:22:39 -07:00
prosolis
ba306f0b59 mischief: gogobee learns to fill orders placed on Pete's web board
The game-side half of Mischief Makers M3. gogobee polls Pete for storefront
orders and opens the contract itself — the money, the eligibility, the fight
are all its own, exactly as a Matrix !mischief buy.

- roster push now carries each buyer's advisory euro balance (keyed by localpart,
  a separate keyspace from the anonymous board token) and the live tier catalog,
  so the storefront renders gogobee's current prices and never hardcodes one
- placeWebMischief: the fulfilment path, debit-first-then-refund so the money
  state is a pure function of the order guid. DebitIdem + an order_guid stamp on
  the contract make a retried claim neither double-charge nor double-open
- resolveRosterToken recomputes the one-way board token per live player to name
  a mark; the buyer is @<username>:<homeserver> from Authentik's preferred_username
- a 30s poll loop drives it; a lost verdict just leaves the order pending to
  re-run, so the loop is its own retry and needs no durable queue
- euro.HasExternalTx lets the affordability gate tell a first attempt (no debt
  for a mischief buy, like Matrix) from a retry of one already paid

order_guid column added to mischief_contracts (schema + idempotent ALTER).
2026-07-14 21:12:37 -07:00
prosolis
2ab6e7843a roster: a quietly-succeeding ticker looks exactly like a dead one
The push logged nothing on success, so during the first deploy an empty board
and a silent log were indistinguishable from a ticker that never started — and
the debug went looking for a bug that wasn't there. (The snapshot was simply
2 minutes out; the first tick hadn't fired yet.)

Logging every push at INFO would be noise forever. Log the transitions instead:
the first time it works, the moment it breaks, and when it recovers.
2026-07-13 18:19:16 -07:00
prosolis
ced75786b9 news: tell Pete who's out there right now, not just who died
We only ever told Pete about outcomes. Nothing emitted when an expedition
*started*, which is why the two bored adventurers walked into dungeons and the
news feed said nothing at all — it wasn't broken, it had nothing to say.

Two halves:

A roster snapshot, pushed every 2 minutes. Deliberately NOT on the durable fact
queue: a fact is history and losing it loses it forever, so it retries. A
snapshot is a photograph of the present, and a retried one is a lie — by the
time it lands, she's moved. The next tick carries the truth. That's also what
lets Pete's staleness timer work: if we stay down, nothing arrives, and the
board stops claiming to be live instead of insisting forever that Josie is
still in holymachina.

And a "departure" bulletin when a bored adventurer lets itself out.

The snapshot omits opted-out players rather than anonymizing them, and carries a
board token distinct from every event token, so a standing row can't become the
key that links a player's dispatches back together.

The player_meta scan folds last_player_action_at/created_at in Go instead of
COALESCE()ing in SQL — modernc rebuilds time.Time from the declared column type
and COALESCE erases it. A failed scan here would publish an empty board and
every adventurer would vanish from the page.
2026-07-13 18:05:49 -07:00