Files
Pete/adventure_expansion_progress.md
prosolis cd84f64a22 adventure: write the handoff down where the next session will find it
The spec says what the wire should look like. This says where the work
actually stands, which is the part that rots silently: two unpushed
commits, nothing deployed, and a trophy case that can only count
forward from a deploy that hasn't happened.

Also records the decisions already settled (room graph over
coordinates, server-side fog cut, mischief shape for equip, no faking
treasures from vault contents) so they don't get relitigated, and the
landmines that cost money to learn the first time.
2026-07-17 06:29:33 -07:00

8.0 KiB

Adventure expansion — progress & handoff

Last updated 2026-07-17. Companion to adventure_expansion_spec.md (the wire contract). This file is the state of the work: what's done, what's next, and what will bite whoever picks it up.


Read this first

  1. adventure_expansion_spec.md — payloads, endpoints, file:line refs into both repos. It supersedes any older sketch of these asks.
  2. This file — where the work actually stands.

The gogobee repo is at /home/reala-misaki/git/gogobee. It runs on millenia (reala@192.168.1.212). Pete runs on reala@www.parodia.dev (bare ./pete in a screen session, built on the server, with cgo).


Status of the five asks

# Ask State
Trophy case / timeline ("The record" / "The trail") Built, tests green, committed, NOT pushed, NOT deployed
Contract spec for the five Written (adventure_expansion_spec.md)
4 Richer item view Next up. Spec'd. Partly buildable — see below
3 Room graph → dungeon map + fog of war Spec'd. Graph already exists in gogobee
5 equip_orders queue Spec'd. Copies mischief
1 treasure_found → treasures Spec'd, but blocked on a gogobee feature — no loot fact exists
2 LLM-authored dispatches Spec'd. Do last — inverts a security guarantee

Build order and the reasoning behind it are in the spec's last section.


⚠️ Unpushed and undeployed

Pete has two unpushed commits on main:

  • cbe9e67 — adventure: keep the facts, not just the sentence we made of them (the trophy case / timeline feature — adventure_events table, internal/storage/adventure.go, the who-page sections)
  • 5fac163 — adventure: pin the gogobee contract before Pete assumes it (the spec)

Nothing is deployed. The record/trail sections are live nowhere.

This is not a neutral delay. Trophies are only countable going forward — they're built from adventure_events, which only starts filling once the feature is deployed. Every fact gogobee emits between now and the deploy is counted by nobody and cannot be counted back out later (the past is prose in the story feed). The longer it sits, the more of every adventurer's history is permanently uncountable. Deploy it soon, independent of where the expansion goes.

gogobee's tree has unrelated uncommitted postgame-zone work (combat engine, expedition sim, plan docs). It's mid-flight and not part of this project — leave it alone, and keep adventure-expansion edits in separate files and commits.


Next: ask 4, richer items

Chosen because it's additive-only, has real sources, carries no deploy-order hazard and no security surface, and is the smallest change that visibly improves the who page.

gogobee: itemViews (internal/plugin/pete_roster.go:210-225) currently sends five of AdvItem's fields and drops two that already exist:

  • Slot (EquipmentSlot, non-empty for MasterworkGear)
  • SkillSource (string, non-empty for MasterworkGear)

Also worth sending, from adjacent structs:

  • Desc — resolve at the push site from MagicItem.Desc (magic_items.go:35-46) or EquipmentDef.Description (adventure_character.go:172-177). AdvItem rows carry no description of their own, which is why this is a push-site join and not a field copy.
  • Attunement (does it need a bond) and Attuned (does it have one). Distinct, and both matter to a player deciding what to wear — the bond cap is 3.

Pete: extend ItemView (internal/storage/detail.go:23-29) to match peteclient/client.go:356-362, then surface it on the who page.

Rides the private /api/ingest/detail push, so either side can deploy first.

Not buildable — do not spec it in: stat modifiers and requirements are not modeled anywhere, not merely unsent. No attack/AC/ability deltas exist on AdvItem or MagicItem; effects derive from Tier/Slot/SkillSource in combat_stats.go:36 and combat_bridge.go:396,489. "+2 to hit" needs a gogobee engine change first. Deriving a display-only approximation from Tier/Slot at the push site is a lie the first time the engine and the display disagree.


Landmines

Things that cost money to learn, or that will cost money if forgotten.

Deploy order is a data-loss rule, not a preference. An unknown event_type is a 400 on Pete; gogobee retries with backoff to maxAttempts=8 and then parks the bulletin forever (peteclient/client.go:79-86, plugin/pete.go:279-281). Pete's handler ships before gogobee emits treasure_found, or the first treasures are gone permanently. Additive fields on existing event types are safe in either order.

A limit on the fetch truncates a tally, not a list. EventsBySubject(name, 0) means unlimited, and anything that counts must pass 0 and cap in the caller (storage/adventure.go:108-114). A capped read would freeze a veteran's kill count at 40 forever, reading as a fact rather than a missing page.

INSERT OR IGNORE on the guid is load-bearing, not defensive habit (storage/adventure.go:45-58). gogobee retries facts whose ack it lost. This is the only adventure store where a duplicate is permanently wrong — the roster forgives one by replacing itself; a double-counted boss kill is in the tally forever.

Only subject earns a trophy, never opponent. A duel Josie lost still names her (as the opponent in the winner's dispatch). It belongs on her trail, but crediting it would score a loss as a win. Pinned by TestTrophyCaseIgnoresOpponentCredit.

Events key on character name, not roster token — that's what a fact carries; gogobee puts no stable character id on the wire. A rename takes the history with it.

Snapshots are dropped on failure, never queued (peteclient/client.go:320-327). A retried snapshot is a lie about a moment that has passed, and the silence is what makes Pete's 12-minute staleness timer honest. Asks 3 and 4 ride snapshots: a dropped push means a stale map, not a wrong one.

The who page's tests render the real template, so a field slip 500s in the suite — but only on paths the test data reaches. HasHistory false skips the whole history block, which is why TestWhoHistoryPanels seeds facts through the real ingest handler.

/api/mischief/claim is misnamed. It's a verdict endpoint; mischief has no claimed state (storage/mischief.go:37-42). Don't repeat the name in equip_orders — the spec calls it /api/equip/verdict.

The LLM dispatch guard inversion (ask 2). factGuard only checks the structured Subject/Opponent fields, which is safe today only because Pete's templates can print nothing Pete didn't interpolate. The moment gogobee's LLM authors the prose, the guard is checking fields that are no longer the thing being rendered, and character names are player-chosen. A prose-level guard is required in the same change that accepts headline/lede, not as a follow-up. Templates stop being the renderer and become the safety net — do not delete them.


Decisions already made (don't relitigate)

  • Room graph, not coordinates or a bare trail. Rooms with exits, a real graph. It already exists in gogobee (zone_graph.go); the wire throws it away.
  • Fog of war is a server-side cut, not a CSS style. Send visited nodes plus a one-hop ring with kind: "unknown". The map is a public page; "view source to find the boss room" is not fog of war.
  • equip_orders copies mischief, not escrow. An equip is a desired end state, not a delta, so a replay converges — mischief's precondition exactly. No claimed state, no stale-reoffer window; the poll loop is its own retry.
  • Equip UI says "queued", never claims it landed. It lands on gogobee's next poll tick, up to 30s out. The order row's status is the truth.
  • "Treasures found" won't be faked from vault contents. A bought sword is not a trophy. Needs a real treasure_found fact.
  • Blank state over a wall of zeroes. Adventurers predating adventure_events render no record section at all. A clean absence, deliberate.