Stage 1 of the dev-as-prod-mirror rework. The auto-provisioned "Dev
Sandbox" game and dummy users are removed so the dev contour starts
empty like prod; the separate legacy-report loader stays as the
test-data path.
- delete backend/internal/devsandbox (package + tests)
- drop the bootstrap call + DevSandboxConfig (struct, Config field,
BACKEND_DEV_SANDBOX_* env, defaults, loader, validation)
- strip BACKEND_DEV_SANDBOX_* from dev-deploy + local-dev compose and
.env.example; the generic engine-recycle / prune-broken-engines logic
stays (it serves real games)
- update tooling docs (dev-deploy README + KNOWN-ISSUES, local-dev
README + Makefile) and stale comments; DeleteGame and
InsertMembershipDirect remain (exercised by lobby integration tests)
No app behaviour change beyond not auto-creating the sandbox game.
Reshape the lobby UI from a single Overview into a two-level sidebar
(games · profile · DEV synthetic-reports) with four games sub-panels
(active-past · recruitment · invitations · private-games). Move the
`create new game` button into the private-games panel, merge the
applications section into recruitment cards as status chips, and add
DEV-only synthetic-report loader as a top-level screen.
Add a paid-tier gate at backend `lobby.game.create`: free callers get
`403 forbidden` before the lobby service is invoked. The UI hides the
private-games sub-panel + create button on free tier (DEV affordances
flag overrides). Update every integration test that creates a game to
use a new `testenv.PromoteToPaid` helper; add a new
`TestLobbyFlow_FreeUserCreateGameForbidden`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously a cancelled / finished / start_failed sandbox game would
hang in the dev user's lobby until manually cleaned up — `make up`
would create a new running game alongside it but the dead tiles
piled up. Now backend's `devsandbox.Bootstrap` deletes every
terminal sandbox game owned by the dev user before find-or-create
runs, so the lobby always shows exactly one running tile.
Schema: `runtime_records` and `player_mappings` gain
`ON DELETE CASCADE` on their `game_id` foreign keys so a single
`DELETE FROM games` cleans every referencing row in one write.
Pre-prod migration rule applies — change goes into
`00001_init.sql`, not a new migration.
API: `lobby.Service.DeleteGame` is the new destructive helper that
backs the bootstrap purge. It bypasses the cancel-cascade-notify
pipeline; production callers must stay on the regular lifecycle.
The dev-sandbox docs in `tools/local-dev/README.md` spell out the
new behaviour.
Tests:
- backend/internal/lobby/lobby_e2e_test.go gains
`TestDeleteGameCascadesEverything` proving CASCADE works
end-to-end against a real Postgres testcontainer.
- backend/internal/devsandbox keeps its existing terminal-status
contract test; the new `purgeTerminalSandboxGames` helper rides
on the same `terminalSandboxStatus` predicate.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>