Skip to content

Play the game end to end, in a real browser - #213

Merged
0xward merged 1 commit into
mainfrom
claude/nullstate-onchain-text-q6g2a3
Jul 31, 2026
Merged

Play the game end to end, in a real browser#213
0xward merged 1 commit into
mainfrom
claude/nullstate-onchain-text-q6g2a3

Conversation

@0xward

@0xward 0xward commented Jul 31, 2026

Copy link
Copy Markdown
Owner

A test that plays, rather than one that checks a system. No production code changes — one new script.

Why

Every bug you reported was found by playing, not by reading: props sealing a corridor, the inventory stacking duplicate bars, burns not counting, floors regenerating on Continue. Each individual system was fine — the failures were between them, and no test looked there.

This is the test that would have caught them, and it is the last thing to run before a build goes out.

What one run does

Opens the map → loads the engine → walks a floor respecting walls → kills its enemies → smashes a prop → opens a lockable container → saves → continues → checks the dungeon came back exactly as it was left.

Covered
the map paints five bunkers, daily bar, bunker profile
a floor generates populated and the player can actually walk it
kills are credited and batched 1 request for 8 kills, not 8
the run reports contract progress with a well-formed body
Save & Exit writes floors + seed + position, under a kilobyte
Continue dead monsters dead, broken props broken, opened container open, player where they stood
the whole run nothing 404s, nothing 5xx's, nothing throws

The limit, named rather than glossed over

The map is checked on the real /game route; the run is driven on a harness that loads the same 14 engine scripts. Not because it is easier: with no wallet and no Celo RPC the shell sits on "loading player profile…" forever, so DungeonGame never mounts and never loads the engine.

So the one thing this does not cover is the single hand-off between shell and engine. That is written in the file, because a test that hides its own blind spot is worse than one that has none.

Four of its first five failures were the test's fault

Worth listing, because each is now a comment where someone would otherwise repeat it:

  1. It mounted with walletAddress: null, then asserted the engine reported to the server — which it correctly does not do without a wallet.
  2. It walked the player straight through rock, so the resume guard correctly refused to put them back there — which read as a save bug.
  3. It asserted XP only ever rises. XP is the bar within a level and drops on level-up, so the kill counter is the thing to assert on.
  4. It asserted a fragment POST from Decor.open(), but that credit fires from tryInteract (proximity + the OPEN button), which this does not drive.

The fifth was real environment noise (no route to Firebase, static server can't POST), filtered by a named list rather than a catch-all — a broad filter is how a test stops finding bugs.

And it fixes the same flake twice-learned

A floor averages 1.6 lockable containers, so one run in four had none and the loot step failed at random. It retries the mount, not a probe — a fresh mount draws a new RUN_SEED, so a probe's floor is not the next mount's floor. Five consecutive clean runs.

Verification

Suite
npm run test:play (new) 26/26, five consecutive runs
test:season 38/38 · test:streak 31/31 · test:seeded 15/15 · test:bunkers 34/34 · test:inventory 7/7 pass
test:raid · test:contracts · test:floors · test:streak-ui · test:bunkers-ui · test:season-routes pass
test:traversable 0 stuck · 0 unreachable lifts · 0 sealed rooms · 0 sealed containers
tsc --noEmit clean
lint 34 errors — unchanged baseline

Generated by Claude Code

Every other test here checks one system. This one plays: opens the map,
loads the engine, walks a floor respecting walls, kills its enemies,
smashes a prop, opens a lockable container, saves, and continues — then
checks the dungeon came back exactly as it was left.

WHY IT EXISTS. Every bug the owner reported was found by playing, not by
reading: props sealing a corridor, the inventory stacking duplicate bars,
burns not counting, floors regenerating on Continue. Each individual system
was fine; the failures were between them. This is the test that would have
caught them, and it is the last thing to run before a build goes out.

WHAT IT COVERS THAT NOTHING ELSE DID
  - the map paints, with five bunkers, the daily bar and a bunker profile
  - a floor generates populated, and the player can actually walk it
  - kills are credited, and BATCHED (1 request for 8 kills, not 8)
  - the run reports contract progress with a well-formed body
  - Save & Exit writes floors + seed + position, in under a kilobyte
  - Continue restores the same floor: dead monsters dead, broken props
    broken, opened container open, player where they stood
  - nothing 404s, nothing 5xx's, and nothing throws for the whole run

TWO PAGES, AND THE LIMIT NAMED. The map is checked on the real /game route;
the run is driven on a harness that loads the same 14 engine scripts. Not
because it is easier: with no wallet and no Celo RPC the shell sits on
"loading player profile…" forever, so DungeonGame never mounts and never
loads the engine. That is an environment limit, and the one thing this
therefore does not cover is the single hand-off between shell and engine.
Said in the file rather than glossed over.

FOUR OF ITS FIRST FIVE FAILURES WERE THE TEST'S FAULT, not the game's, and
each is now a comment where someone would otherwise repeat it:
  - it mounted with walletAddress: null, then asserted the engine reported
    to the server — which it correctly does not do without a wallet;
  - it walked the player straight through rock, so the resume guard
    correctly refused to put them back there, which read as a save bug;
  - it asserted xp only ever rises — xp is the bar WITHIN a level and drops
    on level-up, so the kill counter is the thing to assert on;
  - it asserted a fragment POST from Decor.open(), but that credit fires
    from tryInteract (proximity + the OPEN button), which this does not
    drive.
The fifth was real environment noise, filtered by a NAMED list rather than
a catch-all, because a broad filter is how a test stops finding bugs.

Also fixes the same flake test-seeded-floors had: a floor averages 1.6
lockable containers, so one run in four had none. It retries the MOUNT, not
a probe — a fresh mount draws a new RUN_SEED, so a probe's floor is not the
next mount's floor. Five consecutive clean runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017A764RdnwpyWnG7uCNhMiQ
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nullstate Ready Ready Preview Jul 31, 2026 7:44am

@0xward
0xward merged commit 3f71364 into main Jul 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant