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).
This commit is contained in:
prosolis
2026-07-14 21:12:37 -07:00
parent 6d402343e6
commit ba306f0b59
9 changed files with 646 additions and 12 deletions

View File

@@ -462,6 +462,9 @@ func runMigrations(d *sql.DB) error {
// same durability, backoff and parking, so it rides the same queue and the
// row says where it's going. Existing rows are all facts, hence the default.
`ALTER TABLE pete_emit_queue ADD COLUMN path TEXT NOT NULL DEFAULT '/api/ingest/adventure'`,
// Mischief M3 — the web order a contract was opened for, the storefront's
// end-to-end idempotency key. "" for a Matrix buy. See placeWebMischief.
`ALTER TABLE mischief_contracts ADD COLUMN order_guid TEXT`,
}
for _, stmt := range columnMigrations {
if _, err := d.Exec(stmt); err != nil {
@@ -1784,6 +1787,11 @@ CREATE TABLE IF NOT EXISTS mischief_contracts (
blessing_count INTEGER NOT NULL DEFAULT 0,
source TEXT NOT NULL DEFAULT 'matrix',
outcome TEXT,
-- The web order this contract was opened for, "" for a Matrix buy. It is the
-- storefront's idempotency key: a poll loop whose claim-ack was lost retries
-- the whole placement, and finding the contract already stamped with the order
-- is what stops a second one being opened (and a second euro debit chased).
order_guid TEXT,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
window_ends_at DATETIME NOT NULL,
resolved_at DATETIME