Skip to content

feat: add the Glitch Invaders challenge - #23

Merged
theycallmeswift merged 8 commits into
mainfrom
feat/glitch-invaders
Oct 17, 2026
Merged

theycallmeswift merged 8 commits into
mainfrom
feat/glitch-invaders

Conversation

@mpsiebert

@mpsiebert mpsiebert commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Adds challenges/glitch-invaders, the repo's first browser challenge: a shooter where three bugs fight back and the participant fixes them from simulated Sentry evidence (stack trace, breadcrumbs, performance trace) in an Incident Console. It ships in demo mode only; live Sentry returns once boothstrap provides sponsor settings (MLH/boothstrap.sh#519).

Decide

  • PUT /api/progress accepts any stage for any bug, so one curl and a reload can mark all three fixes repaired. Recommend: keep it as is, because the spec's trust model accepts monitor reports as-is and DevTools can edit any browser challenge's DOM. If that changes, the page posts events and the server computes stages against the correct patch ids.
  • Live Sentry mode is removed rather than fed from env, .env, or live.config.json, none of which a station provides. Recommend: bring it back through station-provided sponsor settings, tracked in MLH/boothstrap.sh#519.

Ready gate

Step Evidence
Tests and lint npm run verify: 4 packages ok. npm test: 229 tests, 229 pass, 0 fail. npm run lint: 18 files, 0 issues. ✅
Red run On 51bd37a the review measured no CSP header, 500 on /%E0%A4%A, and a GET following a symlinked glitch-progress.json outside the workspace; the new tests assert the CSP, 400, and 422. The directory-at-progress-path, symlinked .glitch/runtime-config.json, and symlinked server.pid tests fail with the setup and cleanup fixes reverted (the last with the port still accepting connections). ✅
Sibling sweep A grep over the package and test/ for VITE_, live-config, live.config, inactivity, __GLITCH, and sentry.io finds nothing. No distDir, MemoryProgressStore, or createDemoRuntimeConfig remains. ✅
Docs and specs Package README.md, intro.md, challenge.json done_whens, monitor/SKILL.md (list_pages/select_page fallback) and skills/start-challenge/SKILL.md (standalone branch reading glitch-progress.json) updated. Root README index entry unchanged and still accurate. ✅
Standalone run Pi with a model can't run in this environment. Instead: ./setup in a temp workspace under env -i with only BOOTHSTRAP_* set, and with none set; curled the CSP header, traversal (404), malformed escape (400), oversized body (413), bad JSON (400), symlinked, hard-linked, and FIFO progress files (422, outside file untouched); cleanup twice, after a failed setup, with a missing workspace, and with a directory or symlinks planted under the workspace (exit 0, port refused afterwards, outside targets untouched). Loaded the page in Chromium: no CSP violations, the glitch-state v1 line is visible and reads launched=yes after Start, and a refused save shows "Progress can't be saved; reload the page." ⚠️ no Pi run
Editorial pass Reread against docs/style/ by a separate reviewer; cut live-mode text, the restated marker rules, and stale build wording. ✅
Skipped tests None. ✅
Secrets, permissions No secrets; the live Sentry DSN channel is removed. The page server sends default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'. Setup and cleanup never write or read through a symlink planted in the workspace. ✅

🤖 Generated with Claude Code

https://claude.ai/code/session_01UwzZoAziJnZnizZ4cDr5Xc

@theycallmeswift theycallmeswift left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this; it's a substantial challenge and the game, server, and tests are in good shape. Two independent reviews ran on 51bd37a (one against the challenge contract and tests, one on security and network), with verify, test (206 pass), lint (18 files, 0 issues), and the hooks exercised by hand. Five things need to change before merge, one is a call for MLH, and the rest are nits.

Blocking

  1. Live Sentry config comes from a channel a station never provides. server/live-config.mjs reads GLITCH_*/VITE_* environment variables, a package .env, or live.config.json next to challenge.json. The station passes hooks only the spec's Environment table, so on a station this is always demo mode, and a staffer who follows the header comment ends up with a real DSN in a file that is not git-ignored (only .env is), which is then served to the page at /runtime-config.json. Fix: ship demo-only for now, remove live-config.mjs with the env, .env, and VITE_* readers (setup.mjs:99), and bring live mode back once Sentry settings arrive through a station channel (a vault service or the event config). If you keep the file channel, add challenges/*/live.config.json to .gitignore and make the comment true.
  2. No Content-Security-Policy. curl -sI http://127.0.0.1:4373/ returns only cache-control and x-content-type-options, so inline script and any external origin are allowed. Fix: send the bird server's policy (default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self') from server/page-server.mjs, and replace the two style: attribute strings in game/src/ui/incident-console.mjs (around lines 642 and 646) with element.style assignments so style-src needs no 'unsafe-inline'.
  3. intro.md says each failure "sends telemetry to Sentry" and to "Open it in Sentry". In demo mode, which is every station today, nothing goes to Sentry. Fix: say the Incident Console shows the Sentry evidence (stack trace, breadcrumbs, trace) and drop "Open it in Sentry".
  4. The start-challenge skill has no standalone branch. The spec requires the skill to tell the participant which step they finished when report_step is missing; only debrief is covered. Fix: when report_step and session_info are absent, read glitch-progress.json and acknowledge each step whose launched or stage reached yes or repaired.
  5. The in-page inactivity reset wipes progress. After 120 s without page input the page warns, and 30 s later it DELETEs glitch-progress.json and reloads at launch state (game/src/app/app.mjs around line 520, defaults in shared/runtime-config.mjs). Time in the terminal talking to Pi counts as idle, so the debrief step trips it and the guide's "all three repaired" gate can no longer pass. The station already owns idle teardown and wipes everything. Fix: remove the in-page reset, or never clear progress once launched.

For MLH to decide

PUT /api/progress accepts any stage for any bug in one write, and the page republishes it on load, so a participant can mark all three fixes done without playing (one curl, then reload, and #boothstrap-state reads all repaired). The spec's trust model accepts monitor reports as-is, and DevTools can edit any browser challenge's DOM, so this may be acceptable. If not, let the page POST events (encounter, diagnosis option, patch id) and have the server compute stages against the correct patch ids.

Nits

  • Dead weight to delete: game/src/sentry/reporter.mjs and game/src/diagnostics/evidence.mjs are 0 bytes; the vite dev fallbacks (MemoryProgressStore, createDemoRuntimeConfig in main.mjs) serve a build tool the repo doesn't have; window.__GLITCH_STATE__/__GLITCH_WORLD__ are read by nothing; server/setup.mjs:23 mentions a nonexistent dist/.
  • page-server.mjs: the GET handler follows a symlinked glitch-progress.json (my probe read a file outside the workspace); lstat first and refuse anything that isn't a regular file with one link, and write the temp file with flag: 'wx', as birdsbirdbirds/workspace-file.mjs does. A malformed percent-escape (/%E0%A4%A) returns 500; return 400.
  • challenge.json done_whens name #boothstrap-state, which the monitor's accessibility snapshot doesn't expose; phrase them on the visible line, e.g. The page shows the line "glitch-state v1 …" containing "launched=yes".
  • monitor/SKILL.md: a target=_blank tab can leave the snapshot on the wrong page; add "if the snapshot is not http://127.0.0.1:4373/, list_pages, select_page, snapshot once."
  • test/glitch-invaders.test.mjs: the hook test is the happy path plus a double cleanup. Add the house cases from the Mad Libs test: server in setup's process group, busy port fails naming it, cleanup after failed setup, after a crash with a foreign listener, with a missing workspace, and a run with no BOOTHSTRAP_* variables (all pass by hand today). Add server refusals for bad JSON, a malformed escape, and a symlinked progress file.
  • README.md: rewrap to 80 columns and link to monitor/SKILL.md instead of restating the marker rules.
  • PR body: give measured counts (206 tests, 18 lint files), drop the process narrative, and tick the Ready gate rows once they hold.

Generated by Claude Code

@theycallmeswift theycallmeswift left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at ecc41f4: every item from the first review is resolved or recorded under Decide. Live mode is gone, the server sends a CSP with no inline allowances, the progress and state files are read and written only through lstat-guarded paths, the inactivity reset is removed, the skill has its standalone branch, and the hook edge cases are tested (229 tests, 18 lint files, verify ok, CI green, Chromium load clean). Merging as agreed with the repo owner.


Generated by Claude Code

@theycallmeswift
theycallmeswift merged commit de800c0 into main Oct 17, 2026
1 check 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.

3 participants