Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ links to the package's own README.
| [hello-world](challenges/hello-world/README.md) | Meet your AI guide and make your first file. |
| [vibe-coding-mad-libs](challenges/vibe-coding-mad-libs/README.md) | Fill four blanks, and Pi builds the web app you described. |
| [birdsbirdbirds](challenges/birdsbirdbirds/README.md) | Draw a bird and watch it fly across the sky. |
| [glitch-invaders](challenges/glitch-invaders/README.md) | Shoot the bugs, then debug the three that fight back using Sentry evidence. |

Challenges follow the contract in boothstrap.sh's challenge spec and
authoring guide; agents working here start from [AGENTS.md](AGENTS.md).
Expand Down
72 changes: 72 additions & 0 deletions challenges/glitch-invaders/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Glitch Invaders: On-Call

Fly a patch drone in Chrome, trigger three intentional bugs while defending the
cluster, and debug each one with Sentry evidence and Pi. Each failure opens an
Incident Console with a stack trace, breadcrumbs, or a performance trace, asks
for the root cause, and applies your chosen patch so you can watch the fix work
in play.

The game always runs in demo mode: the Incident Console shows simulated Sentry
evidence and no telemetry leaves the machine. Live Sentry mode returns once
boothstrap provides sponsor settings; see
[boothstrap.sh issue 519](https://github.com/MLH/boothstrap.sh/issues/519).

## Files

| File | Purpose |
| --- | --- |
| `challenge.json`, `plugin.json` | Manifest and Agent Plugin metadata |
| `intro.md` | Intro screen text |
| `skills/start-challenge/SKILL.md` | The guide Pi follows |
| `monitor/SKILL.md` | Headless browser monitor skill |
| `setup`, `cleanup` | Start and stop the server (`server/setup.mjs`, `server/cleanup.mjs`) |
| `server/` | Local page server on `127.0.0.1:4373` |
| `shared/` | Progress validators |
| `game/` | Browser arcade game and Incident Console |

There is no `install` hook: the package has no dependencies beyond Node and
browser ES modules.

## How steps advance

The game shows a `glitch-state v1` line, and the headless monitor reports
`launch-game`, `fix-weapon`, `fix-shield`, and `fix-boss` from it, as
[monitor/SKILL.md](monitor/SKILL.md) describes. The game also saves
`glitch-progress.json` in the workspace so Pi can see which bug and stage
(`unseen`, `encountered`, `investigated`, `repaired`) the participant is on and
emit non-PII milestone events. Once all three bugs are `repaired`, Pi asks
which clue helped most and reports `debrief`.

The server listens on port 4373. Set `GLITCH_PORT` to use another port, though
the challenge manifest expects 4373.

## Run without boothstrap

Needs Node 26 and a `GEMINI_API_KEY`. Follow the standalone steps in
boothstrap.sh's `docs/authoring-challenges.md` to install Pi and load the
package as an Agent Plugin, then:

```sh
challenge=/absolute/path/to/glitch-invaders
mkdir /tmp/my-glitch-run && cd /tmp/my-glitch-run
"$challenge"/setup
```

Open `http://127.0.0.1:4373/`, then start Pi in the same folder:

```sh
pi --model google/gemini-3.8-flash \
--skill "$challenge"/skills/start-challenge/SKILL.md \
'/skill:start-challenge'
```

When done:

```sh
"$challenge"/cleanup
```

`cleanup` stops the server and removes `.glitch/` and `glitch-progress.json`.
Without boothstrap there is no `report_step`, `session_info`, or `emit_event`,
so no monitor runs: the skill reads `glitch-progress.json` and acknowledges
finished steps in plain words.
59 changes: 59 additions & 0 deletions challenges/glitch-invaders/challenge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"id": "glitch-invaders",
"title": "Glitch Invaders: On-Call",
"tagline": "Shoot the bugs, then debug the three that fight back using Sentry evidence.",
"icon": "bug",
"type": "browser",
"agent": "pi",
"model": "google/gemini-3.8-flash",
"vault_services": [
"gemini"
],
"points": 150,
"estimated_minutes": 9,
"learning_objectives": [
"reading stack traces and error context",
"tracing a logic bug with breadcrumbs",
"spotting bottlenecks in a performance trace"
],
"featured_tech": [
"Sentry"
],
"intro": "intro.md",
"steps": [
{
"id": "launch-game",
"title": "Launch your patch drone",
"hint": "Press Start in the game, then shoot the first glitches.",
"done_when": "The page shows the line \"glitch-state v1 …\" containing \"launched=yes\"."
},
{
"id": "fix-weapon",
"title": "Fix the jammed weapon",
"hint": "Grab the gold upgrade and fire. Then trace the error in the Incident Console.",
"done_when": "The page shows the line \"glitch-state v1 …\" containing \"weapon=repaired\"."
},
{
"id": "fix-shield",
"title": "Fix the shield that hurts",
"hint": "Grab the blue shield and take a hit. Read the breadcrumbs, then patch it.",
"done_when": "The page shows the line \"glitch-state v1 …\" containing \"shield=repaired\"."
},
{
"id": "fix-boss",
"title": "Fix the slow boss spawn",
"hint": "Clear the wave so the boss spawns. Find the bottleneck in the trace.",
"done_when": "The page shows the line \"glitch-state v1 …\" containing \"boss=repaired\"."
},
{
"id": "debrief",
"title": "Tell your guide what you found",
"hint": "Say which clue helped most: the stack trace, the breadcrumbs, or the trace.",
"done_when": "All three fixes are repaired and the participant has told the guide, in their own words, which evidence helped them find a cause."
}
],
"browser": {
"start_url": "http://127.0.0.1:4373/",
"monitor_skill": "monitor/SKILL.md"
}
}
4 changes: 4 additions & 0 deletions challenges/glitch-invaders/cleanup
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
# Cleanup hook: stop the page server and remove this attempt's files. Safe to run twice.
set -eu
exec node "${BOOTHSTRAP_PACKAGE_DIR:-$(dirname "$0")}/server/cleanup.mjs"
72 changes: 72 additions & 0 deletions challenges/glitch-invaders/game/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="dark" />
<title>Glitch Invaders: On-Call</title>
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' fill='%2315171c'/%3E%3Cpath d='M3 5h10v6H3z' fill='%23e73427'/%3E%3C/svg%3E" />
<link rel="stylesheet" href="/src/styles.css" />
</head>
<body>
<div id="app" class="app">
<header class="topbar" id="topbar">
<h1 class="brand">
<span class="brand-mark" aria-hidden="true">◆</span>
Glitch Invaders <span class="brand-sub">On-Call</span>
</h1>
<ul class="stats" aria-label="Ship status">
<li>HULL <b id="stat-hull">3</b></li>
<li>SHIELD <b id="stat-shield">off</b></li>
<li>SQUASHED <b id="stat-kills">0</b></li>
<li>SCORE <b id="stat-score">0</b></li>
</ul>
<span id="mode-badge" class="badge" title="Demo mode: the diagnostics are realistic simulations.">DEMO · simulated evidence</span>
<button type="button" class="topbar-reset" id="reset-run" title="Reset and start a fresh run">Reset ↺</button>
</header>

<main class="stage-wrap" id="stage-wrap">
<div
id="stage"
class="stage"
tabindex="-1"
role="application"
aria-label="Game arena. Left and right arrows move. Space fires. P pauses."
>
<canvas id="arena" class="arena" aria-hidden="true"></canvas>
<div id="boss-loading" class="boss-loading" hidden></div>
<div id="banner" class="banner" role="status" hidden></div>
</div>
</main>

<footer class="dock" id="dock">
<section class="card objective" aria-labelledby="objective-title">
<h2 class="card-title" id="objective-title">Objective</h2>
<p class="objective-text" id="objective-text"></p>
<p class="objective-detail" id="objective-detail"></p>
<ol class="incidents" id="incidents" aria-label="Incidents"></ol>
</section>
<section class="card controls" aria-labelledby="controls-title">
<h2 class="card-title" id="controls-title">Controls</h2>
<div class="pads">
<button type="button" class="pad" id="pad-left" aria-label="Move left">◀</button>
<button type="button" class="pad pad-fire" id="pad-fire" aria-label="Fire">FIRE</button>
<button type="button" class="pad" id="pad-right" aria-label="Move right">▶</button>
</div>
<div class="control-links">
<button type="button" class="link-button" id="skip-ahead">Skip ahead ▸▸</button>
<button type="button" class="link-button link-button-muted" id="reset-controls">Reset ↺</button>
</div>
</section>
</footer>

<div id="overlay-root" class="overlay-root" hidden></div>
</div>

<p id="boothstrap-state" class="state-line">glitch-state v1 launched=no weapon=unseen shield=unseen boss=unseen completed=no mode=demo</p>
<p id="save-error" class="save-error" role="alert" hidden>Progress can't be saved; reload the page.</p>
<div id="announcer" class="visually-hidden" role="status" aria-live="polite"></div>
<noscript>Glitch Invaders needs JavaScript. Open this page in Chrome.</noscript>
<script type="module" src="/src/main.mjs"></script>
</body>
</html>
Loading
Loading