Add replayable debug reports.

A report dumps everything needed to understand a game that went wrong:
the full state (decks card by card, shop row and tier deck order,
discards, pending choices, banked Mana/Trumpets/apples), the round's
battles, and the whole event log. Available as JSON, which replays, or
text, which reads — and the text ends with a paste-ready repro test.

Battles now record the randomness they consume alongside what they
started from, so a result can be replayed long after the round cleared
those banks: same lineups, same dice, same events, down to the log text.
Results predating the recording say so rather than quietly re-rolling.

Three ways in: the 🐛 panel's download/copy buttons, GET
/api/debug/report (DEBUG-only, since a report holds both players' hands
and the shop deck order), and `mise run report`, which reads the
database directly so a live game can be dumped without DEBUG.
This commit is contained in:
Greyson Parrelli
2026-08-10 10:06:06 -04:00
parent a4f5f6910d
commit 8ccde03023
16 changed files with 1480 additions and 19 deletions
+21
View File
@@ -32,6 +32,27 @@ export async function fetchCatalog(packs?: string[]): Promise<Card[]> {
return (await res.json()) as Card[]
}
// debugReportURL builds the link to a game's debug report — the full state,
// battles and event log, enough to replay the situation in a test. Served only
// when the server runs with DEBUG on. `text` renders it for reading; otherwise
// it downloads as JSON, which is the form a test can replay.
export function debugReportURL(session: Session, format: 'json' | 'text', note?: string): string {
const params = new URLSearchParams({
game: session.gameId,
player: session.playerId,
token: session.token,
format,
})
if (note) params.set('note', note)
return `/api/debug/report?${params}`
}
export async function fetchDebugReportText(session: Session, note?: string): Promise<string> {
const res = await fetch(debugReportURL(session, 'text', note))
if (!res.ok) throw new Error('failed to fetch the debug report')
return res.text()
}
export function loadSession(): Session | null {
try {
const raw = localStorage.getItem(SESSION_KEY)