feat: demo Guests and devtools on local and preview stacks #522

Merged
root merged 22 commits from spec/394-demo-guests into main 2026-10-14 11:15:24 +00:00
root commented 2026-10-14 10:13:34 +00:00 (Migrated from gitlab.core-project.space)

Summary

Local and preview stacks carry two Demo Guests with decided Games, and the TanStack devtools gain a Guest panel to switch to one. One image everywhere: the gate is the runtime flag DEVTOOLS_ENABLED, on only when exactly true (ADR 0039).

sequenceDiagram
    participant up.sh
    participant seed
    participant app as API (app)
    participant front as Front
    up.sh->>seed: after migrate, chess-pr-* only
    seed->>seed: refuse unless DEVTOOLS_ENABLED=true
    seed->>seed: upsert Demo Guests + 13 Games by fixed id
    front->>app: GET /rpc/devtools (once, at boot)
    alt flag on
        app-->>front: 200 { demoGuests }
        front->>front: import() devtools: Router, Query, Guest panels
    else flag off (production)
        app-->>front: 404, no devtools chunk downloaded
    end
 apps/api/src/
+├── seed.ts              # entry point beside migrate.ts: db:seed, db:seed:dev, pnpm dev
+├── devtools/            # Nest module, imported only when the flag is on
 ├── games/
+│   ├── demo.ts          # Demo Guests A and B, their Games, seedDemo()
+│   ├── ending-lines.ts  # board-decided lines, shared with the Room fixtures
+│   └── game-row.ts      # Game → row, shared with GamesService
 └── common/
+    └── guest-hash.ts
 apps/front/src/app/
+├── devtools.tsx         # probe, schema-checked; lazy mount on 200 only
+├── devtools-panels.tsx  # Router, Query, Guest
+└── guest-panel.tsx      # masked Guest, "Switch to…", "Paste a secret"
 deploy/
   compose.preview.yaml   # seed service; DEVTOOLS_ENABLED on app and seed
   up.sh                  # seed after migrate, fails the deploy on error
   verify.sh              # seed exited 0; /rpc/devtools 200 on previews, 404 in prod
   lib.sh                 # is_preview helper
  • Demo Guest A owns 12 Games across story 6's matrix; Demo Guest B owns one that A did not play.
  • A third fixed Guest, DEMO_OPPONENT, holds the other Seat in A's Games. It is not a Demo Guest offered by the probe; it exists because B may own only one Game.
  • Adds Demo Guest to GLOSSARY.md and ADR 0039 to the index.
  • Lint config, approved by the owner: apps/api/.oxlintrc.json gains a devtools layer after games.

Proposed rule changes

  • api CODING_STANDARDS.md rule 1, and the layer rule's message in apps/api/.oxlintrc.json, still list only room/, games/, common/ and config/; they should name devtools/.

Evidence

  • Before: "My games" on a fresh local or preview stack is empty; there are no devtools in production builds and no way to become another Guest.
    After: gate green (api 513, front 1104, engine 738 tests); apps/e2e 36/36 in its built target with devtools on.
  • Seed, through the RPC on in-process Postgres:
    seed → game.list as A   → 12 Games in endedAt order, expected results
    seed → game.get B's Game → readable; absent from A's list
    seed twice              → same rows
    flag unset or not "true" → refuses, naming DEVTOOLS_ENABLED
    unmigrated database     → fails, leaves nothing behind
    GET /rpc/devtools       → 200 { demoGuests } when on, 404 when off
    
  • Front view tests: probe 404, 500, network error, a 200 HTML page and 200 JSON without demoGuests mount no devtools and keep the board; a 200 probe mounts the Guest panel; "Switch to…" and "Paste a secret" change the next request's x-guest and My games refetches; an empty paste is refused.
  • Pipeline #84 caught a crash in language-switch.test: an unstubbed probe got Vite's index.html as a 200, and the panel crashed the root. The probe answer is now checked against DevtoolsProbeSchema.
  • Not yet run on a real stack: up.sh and verify.sh against a preview and production; the Guest panel inside the real TanStack devtools shell.

Merge Danger

Door: two-way

The seed only upserts fixed ids and refuses without the flag; no migration.

Blast Radius: deploys

up.sh and verify.sh now fail a preview whose seed fails, and fail production if /rpc/devtools answers anything but 404. Existing worktrees need pnpm env:sync (run by pnpm install) before pnpm dev, or the seed step refuses.

Closes #394
Closes #395
Closes #396
Closes #397

## Summary Local and preview stacks carry two **Demo Guests** with decided Games, and the TanStack devtools gain a Guest panel to switch to one. One image everywhere: the gate is the runtime flag `DEVTOOLS_ENABLED`, on only when exactly `true` (ADR 0039). ```mermaid sequenceDiagram participant up.sh participant seed participant app as API (app) participant front as Front up.sh->>seed: after migrate, chess-pr-* only seed->>seed: refuse unless DEVTOOLS_ENABLED=true seed->>seed: upsert Demo Guests + 13 Games by fixed id front->>app: GET /rpc/devtools (once, at boot) alt flag on app-->>front: 200 { demoGuests } front->>front: import() devtools: Router, Query, Guest panels else flag off (production) app-->>front: 404, no devtools chunk downloaded end ``` ```diff apps/api/src/ +├── seed.ts # entry point beside migrate.ts: db:seed, db:seed:dev, pnpm dev +├── devtools/ # Nest module, imported only when the flag is on ├── games/ +│ ├── demo.ts # Demo Guests A and B, their Games, seedDemo() +│ ├── ending-lines.ts # board-decided lines, shared with the Room fixtures +│ └── game-row.ts # Game → row, shared with GamesService └── common/ + └── guest-hash.ts apps/front/src/app/ +├── devtools.tsx # probe, schema-checked; lazy mount on 200 only +├── devtools-panels.tsx # Router, Query, Guest +└── guest-panel.tsx # masked Guest, "Switch to…", "Paste a secret" deploy/ compose.preview.yaml # seed service; DEVTOOLS_ENABLED on app and seed up.sh # seed after migrate, fails the deploy on error verify.sh # seed exited 0; /rpc/devtools 200 on previews, 404 in prod lib.sh # is_preview helper ``` - Demo Guest A owns 12 Games across story 6's matrix; Demo Guest B owns one that A did not play. - A third fixed Guest, `DEMO_OPPONENT`, holds the other Seat in A's Games. It is not a Demo Guest offered by the probe; it exists because B may own only one Game. - Adds **Demo Guest** to `GLOSSARY.md` and ADR 0039 to the index. - **Lint config, approved by the owner:** `apps/api/.oxlintrc.json` gains a `devtools` layer after `games`. ### Proposed rule changes - api `CODING_STANDARDS.md` rule 1, and the layer rule's message in `apps/api/.oxlintrc.json`, still list only `room/`, `games/`, `common/` and `config/`; they should name `devtools/`. ## Evidence - **Before:** "My games" on a fresh local or preview stack is empty; there are no devtools in production builds and no way to become another Guest. **After:** gate green (api 513, front 1104, engine 738 tests); `apps/e2e` 36/36 in its `built` target with devtools on. - Seed, through the RPC on in-process Postgres: ```text seed → game.list as A → 12 Games in endedAt order, expected results seed → game.get B's Game → readable; absent from A's list seed twice → same rows flag unset or not "true" → refuses, naming DEVTOOLS_ENABLED unmigrated database → fails, leaves nothing behind GET /rpc/devtools → 200 { demoGuests } when on, 404 when off ``` - Front view tests: probe 404, 500, network error, a 200 HTML page and 200 JSON without `demoGuests` mount no devtools and keep the board; a 200 probe mounts the Guest panel; "Switch to…" and "Paste a secret" change the next request's `x-guest` and My games refetches; an empty paste is refused. - Pipeline #84 caught a crash in `language-switch.test`: an unstubbed probe got Vite's `index.html` as a 200, and the panel crashed the root. The probe answer is now checked against `DevtoolsProbeSchema`. - Not yet run on a real stack: `up.sh` and `verify.sh` against a preview and production; the Guest panel inside the real TanStack devtools shell. ## Merge Danger **Door:** two-way The seed only upserts fixed ids and refuses without the flag; no migration. **Blast Radius:** deploys `up.sh` and `verify.sh` now fail a preview whose seed fails, and fail production if `/rpc/devtools` answers anything but 404. Existing worktrees need `pnpm env:sync` (run by `pnpm install`) before `pnpm dev`, or the seed step refuses. Closes #394 Closes #395 Closes #396 Closes #397
project_1_bot_a8dd9262857d850eef8dca5206e61de5 commented 2026-10-14 10:17:56 +00:00 (Migrated from gitlab.core-project.space)

Screenshots

Before is production, after is this merge request's review app at 960aace.

Desktop

Page Before After
Home
Local game
Room

Mobile

Page Before After
Home
Local game
Room
<!-- chess-screenshots --> ### Screenshots Before is production, after is this merge request's review app at 960aace. #### Desktop | Page | Before | After | | --- | --- | --- | | Home | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/before/home-desktop.png" width="400"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/after/home-desktop.png" width="400"> | | Local game | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/before/local-desktop.png" width="400"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/after/local-desktop.png" width="400"> | | Room | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/before/room-desktop.png" width="400"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/after/room-desktop.png" width="400"> | #### Mobile | Page | Before | After | | --- | --- | --- | | Home | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/before/home-mobile.png" width="200"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/after/home-mobile.png" width="200"> | | Local game | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/before/local-mobile.png" width="200"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/after/local-mobile.png" width="200"> | | Room | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/before/room-mobile.png" width="200"> | <img src="https://gitlab.core-project.space/amaurycoudr/chess-project/-/jobs/783/artifacts/raw/review-screenshots/after/room-mobile.png" width="200"> |
root commented 2026-10-14 10:23:41 +00:00 (Migrated from gitlab.core-project.space)

added 4 commits

  • d66cfef5 - feat(contracts): add the devtools probe, GET /devtools, answering the Demo Guests
  • ac98523c - feat(api): answer the devtools probe with the Demo Guests, only with devtools on
  • 43204a29 - feat(ci): turn devtools on in every preview's app, and verify the probe by stack
  • 6ff5425a - feat(front): mount the devtools only after a 200 devtools probe, from a lazy chunk

Compare with previous version

added 4 commits <ul><li>d66cfef5 - feat(contracts): add the devtools probe, GET /devtools, answering the Demo Guests</li><li>ac98523c - feat(api): answer the devtools probe with the Demo Guests, only with devtools on</li><li>43204a29 - feat(ci): turn devtools on in every preview&#39;s app, and verify the probe by stack</li><li>6ff5425a - feat(front): mount the devtools only after a 200 devtools probe, from a lazy chunk</li></ul> [Compare with previous version](/amaurycoudr/chess-project/-/merge_requests/22/diffs?diff_id=134&start_sha=ff03e64083c7db87e58f80d31c0c1aa062c519e8)
root commented 2026-10-14 10:30:12 +00:00 (Migrated from gitlab.core-project.space)

added 1 commit

  • e968f642 - feat(front): switch the browser's Guest from a Guest panel in the devtools

Compare with previous version

added 1 commit <ul><li>e968f642 - feat(front): switch the browser&#39;s Guest from a Guest panel in the devtools</li></ul> [Compare with previous version](/amaurycoudr/chess-project/-/merge_requests/22/diffs?diff_id=135&start_sha=6ff5425a71a26c5a9cc238308bb9f0ca93080c9c)
root commented 2026-10-14 10:59:50 +00:00 (Migrated from gitlab.core-project.space)

added 10 commits

  • 2b822000 - chore(api): give devtools a layer of their own after games in the API lint config
  • 92cd1433 - refactor(api): move the devtools module and controller to a devtools/ feature folder
  • 1c4761b4 - refactor(api): map a decided Game to its games row in one function
  • 72f8ac4b - test(front): build the devtools test's Games from the games harness' stored Game
  • 9200faf0 - fix(front): mount the devtools only for a probe answer that lists Demo Guests
  • f92e57cf - test(api): play a Room's line as the string the Game stores, under one Fool's Mate name
  • 6216fffe - docs(repo): drop two ADR 0039 citations for claims the ADR does not make
  • 927e31a2 - refactor(api): hand the demo seed the devtools flag its entry point parsed
  • c82246b8 - refactor(front): spare the probe's key from a Guest switch through the probe's own query options
  • ac6152bf - refactor(ci): ask is_preview whether a stack is a preview, in every deploy script

Compare with previous version

added 10 commits <ul><li>2b822000 - chore(api): give devtools a layer of their own after games in the API lint config</li><li>92cd1433 - refactor(api): move the devtools module and controller to a devtools/ feature folder</li><li>1c4761b4 - refactor(api): map a decided Game to its games row in one function</li><li>72f8ac4b - test(front): build the devtools test&#39;s Games from the games harness&#39; stored Game</li><li>9200faf0 - fix(front): mount the devtools only for a probe answer that lists Demo Guests</li><li>f92e57cf - test(api): play a Room&#39;s line as the string the Game stores, under one Fool&#39;s Mate name</li><li>6216fffe - docs(repo): drop two ADR 0039 citations for claims the ADR does not make</li><li>927e31a2 - refactor(api): hand the demo seed the devtools flag its entry point parsed</li><li>c82246b8 - refactor(front): spare the probe&#39;s key from a Guest switch through the probe&#39;s own query options</li><li>ac6152bf - refactor(ci): ask is_preview whether a stack is a preview, in every deploy script</li></ul> [Compare with previous version](/amaurycoudr/chess-project/-/merge_requests/22/diffs?diff_id=140&start_sha=e968f642949ed0eb2f93c40a60944f2c4d8b3331)
root commented 2026-10-14 11:00:19 +00:00 (Migrated from gitlab.core-project.space)

marked this merge request as ready

marked this merge request as **ready**
root commented 2026-10-14 11:00:19 +00:00 (Migrated from gitlab.core-project.space)

changed the description

changed the description
root commented 2026-10-14 11:00:42 +00:00 (Migrated from gitlab.core-project.space)

added 23 commits

  • d59ded02 - 1 commit from branch main
  • d59ded02...a4456a77 - 12 earlier commits
  • ca6d6960 - chore(api): give devtools a layer of their own after games in the API lint config
  • e335454e - refactor(api): move the devtools module and controller to a devtools/ feature folder
  • 98f907eb - refactor(api): map a decided Game to its games row in one function
  • de23bdb1 - test(front): build the devtools test's Games from the games harness' stored Game
  • e19edd09 - fix(front): mount the devtools only for a probe answer that lists Demo Guests
  • 3d1e3e5b - test(api): play a Room's line as the string the Game stores, under one Fool's Mate name
  • 75f423a3 - docs(repo): drop two ADR 0039 citations for claims the ADR does not make
  • 5109e874 - refactor(api): hand the demo seed the devtools flag its entry point parsed
  • 63faee27 - refactor(front): spare the probe's key from a Guest switch through the probe's own query options
  • 960aace0 - refactor(ci): ask is_preview whether a stack is a preview, in every deploy script

Compare with previous version

added 23 commits <ul><li>d59ded02 - 1 commit from branch <code>main</code></li><li>d59ded02...a4456a77 - 12 earlier commits</li><li>ca6d6960 - chore(api): give devtools a layer of their own after games in the API lint config</li><li>e335454e - refactor(api): move the devtools module and controller to a devtools/ feature folder</li><li>98f907eb - refactor(api): map a decided Game to its games row in one function</li><li>de23bdb1 - test(front): build the devtools test&#39;s Games from the games harness&#39; stored Game</li><li>e19edd09 - fix(front): mount the devtools only for a probe answer that lists Demo Guests</li><li>3d1e3e5b - test(api): play a Room&#39;s line as the string the Game stores, under one Fool&#39;s Mate name</li><li>75f423a3 - docs(repo): drop two ADR 0039 citations for claims the ADR does not make</li><li>5109e874 - refactor(api): hand the demo seed the devtools flag its entry point parsed</li><li>63faee27 - refactor(front): spare the probe&#39;s key from a Guest switch through the probe&#39;s own query options</li><li>960aace0 - refactor(ci): ask is_preview whether a stack is a preview, in every deploy script</li></ul> [Compare with previous version](/amaurycoudr/chess-project/-/merge_requests/22/diffs?diff_id=142&start_sha=ac6152bfcd0aa81ad9ae237af56653eb01c83849)
root (Migrated from gitlab.core-project.space) merged commit into main 2026-10-14 11:15:24 +00:00
Sign in to join this conversation.
No description provided.