fuzzyworld

explodorama / commit

Plan: bug reports as animated replays (Q104), Explodorama's part

5588b4e744794b2db5b0816ea109b804c4915785 · Pavel Safronov · 2026-11-03 20:51:19 -0800 · parents 351348d116da

Plan: bug reports as animated replays (Q104), Explodorama's part

Section 20: what is built (dev replay page), what a published replay needs
(production page, record limits, rules-version sim files, compact records).

FW-Tokens-In: 454 177212 136342834
FW-Tokens-Out: 5fd260d6-fb44-4767-8699-cda9f628d999
FW-Tokens-Cached: 4485225 13541
FW-Session:
FW-Rtk-Saved:
FW-Rtk-Cmds:

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
 PLAN.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/PLAN.md b/PLAN.md
index 17d299d..f218421 100644
--- a/PLAN.md
+++ b/PLAN.md
@@ -1516,3 +1516,72 @@ report, so the lab can compare the game before and after on the same seeds.
   with the lab's bots, so 19.1 comes first there too.
 - **19.4 needs 19.3's** acid puddles and (for the alternative) cracked floor.
 - **19.5 uses all of them** for its ramp, but could start with what exists today.
+
+## 20. Bug reports as animated replays (Q104; planned 2026-11-03, nothing built beyond §20.1)
+
+*A planning section. The server side (what receives, stores and links reports) is in
+`~/code/fuzzyworld-tools/PLAN.md`, "Issues" → "Report replays". This section is Explodorama's part.*
+
+**Idea.** A bug report already holds the whole run (`Run record: {…}`, the seed and the actions; see
+`reportBug` in `main.ts`). The game can replay a record exactly, so an issue page on code.fuzzyworld.net
+could show *what the reporter saw* as a short looping animation, and (for autopilot or rules bugs) what
+today's game does instead. A report then answers itself: no need to reproduce by hand.
+
+### 20.1 Built (2026-11-03, dev only; committed 351348d)
+
+- `npm run report-replay -- <mail>` reads a report's record (quoted-printable mail, a pasted text or a
+  bare record) into `replays/<name>.json` and prints page addresses.
+- `?replay=<name>`: two players on one board, red = the recorded turns, blue = the same until the
+  autopilot took over and then today's autopilot (`sim/replayCompare.ts` `buildReplay`).
+- `&rules=<commit>`: the whole run under an older commit's rules (red) and today's (blue)
+  (`sim/ruleCompare.ts` `compareRules`; the old `src/sim` is taken out of git into `rules-versions/`).
+- The page takes no input and is removed from builds (`import.meta.env.DEV`).
+- The Q101 report (autopilot walking into its own bomb with Bomb Pass) is the first fixture
+  (`test/fixtures/q101-bomb-pass.json`).
+
+### 20.2 What a published replay needs from the game (to do, in order)
+
+1. **A production replay entry** (`replay.html` beside `index.html`, its own small bundle, same-origin
+   script only, so the site's CSP `script-src 'self'` holds). It takes `?report=<same-origin json path>`,
+   plays the record in a loop, takes no input, saves nothing, records no scores. The dev page's code
+   (`replayTick`, the `rival` second player, labels) moves out of `main.ts` into a module both use; the
+   dev page stays as a thin wrapper that adds `?rules=`.
+2. **Treat the record as hostile data.** It comes from a mail. Limits before anything runs: size at most
+   64 KB, at most 20,000 actions, `seed` and `simVersion` integers, each action's shape checked; every
+   `step` call in a try/catch (a refused action stops that side, as `compareRules` already does); every
+   text shown through `textContent`, never as HTML. No path or name from the record is ever used.
+3. **Which game plays it.** A report is only exact on the rules it was made under (`simVersion`).
+   Options, cheapest first:
+   - *Today's rules only*: right while `SIM_VERSION` is unchanged (the Q101 report replays exactly on
+     today's rules, because only the autopilot's decisions changed). A `simVersion` mismatch shows a
+     visible "the rules have changed since this report" banner (the dev page's label does this only in
+     the autopilot mode; make it show everywhere) and offers the next option.
+   - *The report's own sim*: `fw deploy` of a release also publishes just `state` + `turn` (+ what they
+     import, i.e. `src/sim` bundled as one ES module, a few tens of KB) as `/explodorama/sim/<simVersion>.js`.
+     The replay page loads the one matching the record, and today's for blue. One file per **rules
+     version**, not per commit: they change rarely. This replaces `rules-versions/` + `git archive`
+     for the published page; the dev page can keep using git for any commit.
+   - *Full old builds* per tag: heavier (each is the whole game with art); only if the sim-only way proves
+     not enough.
+4. **Compact records.** Today a long run's record (66 turns ≈ 8 KB) does not fit a `mailto:` link
+   (`MAILTO_MAX` 1900), so the report asks the player to paste from the clipboard, and some will not. A
+   compact encoding (actions as short codes, deflate, base64url) might fit a typical run in the link.
+   Measure on real reports first (the Q101 one is 8 KB raw); keep the plain `Run record:` form readable
+   for anything that does not fit.
+5. **Which fork to show.** The dev page's "autopilot took over" fork is one of several comparisons.
+   For a published page the default should be the plain recorded run (what the reporter saw) with the
+   last turns slowed, and the comparison (`&rules=` or autopilot) as an option, since an issue is often
+   about something no code change touched.
+
+### 20.3 Open points
+
+- Whether the replay page is part of the game's deploy (`/explodorama/replay.html`) or a separate small
+  app; fuzzyworld-tools decides how an app announces its viewer (see its PLAN.md).
+- Whether the animated page may show the reporter's device string or error list (default: no; only the
+  record and the version/commit).
+- The `sim/<simVersion>.js` files: who builds them (`fw deploy` with a per-app hook, or the CI runner on a
+  tag), and whether they are checked by a test that replays `test/fixtures/*.json` through each file.
+- A **regression-fixtures habit**: every fixed bug whose report has a record keeps it in `test/fixtures/`
+  with a test that replays it (as Q101 does), so a published replay and a unit test stay one artefact.
+
+Related queue items: Q101 (the first report), Q102 (admin panel), Q103 (the dev page), Q104 (this).