TEACHER REFERENCE
Lab Guide — source index
The Markdown source for the student Lab Guide. Later it gets rendered to static HTML and served by the Hub at https://arena.cf2026iscte.nobledemos.com/lab.
Files (in order)#
| File | Section | Clock |
|---|---|---|
| 00-welcome.md | What you'll build | 14:30 |
| 01-setup.md | Create the Cloudflare account first (verify email, workers.dev subdomain), then GitHub, tools, CLI logins. No API keys | 14:35 |
| 02-deploy.md | Deploy, clone, run locally | 15:05 |
| 03-make-it-yours.md | Mission 1: file tour, NAME / TAGLINE / EMOJI / CHARACTER_ID, push, register | 15:15 |
| 04-your-character.md | Mission 2: getCharacter(id) (SWAPI person + homeworld, cache, fallback) + secrets sidebar | 15:27 |
| 05-checkpoint.md | Break + catch-up | 15:40 |
| 06-meet-your-droid.md | Phase 2 intro + Coding Agent setup (Skills) | 15:48 |
| 07-build-your-droid.md | Mission 3: tone by hand, agent builds /api/chat, then a page feature | 16:00 |
| 08-rounds.md | Rounds + debugging | 16:23 |
| 09-whats-next.md | Behind the scenes (Durable Object), bonus ideas | 16:45 |
| appendix-browser-path.md | No Node? Browser-only path | — |
| appendix-troubleshooting.md | Common problems | — |
Placeholders to replace before publishing#
| Placeholder | Meaning |
|---|---|
arena.cf2026iscte.nobledemos.com | Hub hostname on the teacher's zone |
<TEMPLATE_REPO_URL> | Filled: https://github.com/RoderickOxen/iscte-workers-template |
<DEPLOY_BUTTON_URL> | Filled: https://deploy.workers.cloudflare.com/?url=https://github.com/RoderickOxen/iscte-workers-template |
| Join Code | Not written in the guide; shown on the projector only |
The template today#
The Lab Guide matches the template repo (template/) exactly. The current files:
wrangler.jsonc name, entry point, assets ./public, ai binding · vars: NAME, TAGLINE, EMOJI, CHARACTER_ID, HUB_URL
package.json scripts: dev, deploy
.gitignore node_modules, .dev.vars, .wrangler
src/index.js router: / , /api/card , /api/chat (pre-built; card = { name, tagline, emoji, characterId })
src/character.js getCharacter(id) + FILM_TITLES + PLACEHOLDER (Mission 2: students write it)
src/droid.js TONE = "(your tone here)" + MODEL (@cf/meta/llama-3.2-3b-instruct) + handleChat stub (Mission 3: tone by hand, handleChat by the Coding Agent)
src/prompt.js buildSystemPrompt({ character, tone }) (pre-built)
src/page.js renderPage(env, character, colo, origin) → ID card HTML (pre-built; shows a hint while the character is the placeholder)
src/theme.js themeFor(character) → theme name from the homeworld's terrain / climate (pre-built)
src/avatar.js avatarSeed(url): the same seed function as the Hub's Pixel Droid (pre-built)
public/ style.css, tokens.css, theme.js, theme-toggle.js, avatars.js, favicon.svg, font + licence (pre-built)
solutions/ character.js, droid.js: the "Show solution" source. Not deployed, not imported
AGENTS.md short brief for Coding Agents (below)
README.md Deploy to Cloudflare button, how to run locallyThe "Show solution" blocks in sections 04 and 07 are copied byte for byte from template/solutions/character.js and template/solutions/droid.js. If you change those files, paste them into the guide again.
AGENTS.md (ships in the template)#
Short on purpose: it's what keeps the agent from exploring the repo and burning a free model's tokens.
# Student Worker
A Cloudflare Worker in plain JavaScript (no TypeScript, no frameworks). It serves a Droid ID card
page (`/`), a card (`GET /api/card`) and a Star Wars droid (`POST /api/chat`) that the class Hub calls.
## Files
- `src/droid.js`: the droid. `handleChat(request, env)` is where droid work happens.
- `src/character.js`: `getCharacter(id)`, the student's Mission 2. Once it works, use it; don't rewrite it.
- `src/prompt.js`: `buildSystemPrompt({ character, tone })`, a working helper. Use it; don't rewrite it.
- `src/index.js`: router. Don't change it unless asked.
- `src/page.js`, `src/theme.js`, `src/avatar.js`, `public/`: the pre-built ID card page (HTML, CSS, droid picture,
planet themes). Page features go here.
- `wrangler.jsonc`: config (`vars`). `solutions/` is the teacher's reference: ignore it.
## `getCharacter(id)` returns
`{ name, height, mass, birthYear, gender, homeworld: { name, climate, terrain, population } | null, films: [up to 3 titles] }`
## Contract (never break)
- `GET /api/card` → `{ name, tagline, emoji, characterId }`
- `POST /api/chat` with `{ round, question }` → `{ answer }` (max 280 chars) within 8 s.
## Rules
- Never change `TONE` in `src/droid.js`: the student writes it.
- Never change `"name"` in `wrangler.jsonc`.
- Never run `wrangler deploy`. Production deploys happen through `git push` (Workers Builds).
- Never commit secrets or `.dev.vars`.
- Only commit and push when the student asks.
- The page sends a strict CSP (`default-src 'self'`): no inline `<script>` or `style="..."`. Put browser JavaScript in a file in `public/` and load it with `<script src="/name.js" defer>`.
- Keep changes small and show the diff.
## Test
`npm run dev`, then:
curl -X POST http://localhost:8787/api/chat -H "content-type: application/json" -d '{"round":"intro","question":"Introduce yourself."}'Assumptions to check in the dry run#
- The Deploy button asks for nothing but names: no secret prompts, and the build succeeds on a fresh free account.
CHARACTER_IDthemes render. Try 1 (desert), 14 (forest), 20 (space), 71 (ice), 5 (grass), 3 (swamp), 51 (lava), 72 (ocean), 12 (city), 26 (gas). 17 shows the hint and the placeholder.- The Pixel Droid matches the Hub after deploy: the one on the live Student Page equals the one in the Directory. On localhost it differs, and the footer says so.
- SWAPI: both swapi.dev and swapi.info answer the day before. The students' solution gets person and homeworld, and
cacheTtlmakes the second load fast. With both down, the placeholder card and hint appear. MODEL = "@cf/meta/llama-3.2-3b-instruct"is still in the catalog and works on a fresh free account. Tested 2026-10-27: 10/10 in character, ~0.5 s, ~2.5 Neurons per answer (~4,000 answers/day free). Avoid reasoning models (glm-4.7-flash,gemma-4): withmax_tokens: 120they return no answer. Check how often the 3B model drifts off-character.wrangler devwith the AI binding on a login with several Cloudflare accounts needsCLOUDFLARE_ACCOUNT_ID(the troubleshooting appendix says so).- On the Deploy page, does the project name set both the Worker name and the GitHub repo name (the guide assumes
<your-droid-name>for both)? Is the workers.dev subdomain shown on a brand-new account, or only created when first asked (1.1.4 and 02 cover both cases; confirm which one students see)? gh auth loginis enough forgit pushover HTTPS on Windows, macOS and Linux.npm install -g opencode-aiworks on Windows, macOS and Linux, and OpenCode offers usable free models with no signup. Check how 40 people on one network affect its rate limits.npx skills add https://github.com/cloudflare/skillsdetects OpenCode, Claude Code, Cursor and Copilot. Check what it adds to the repo (commit it or.gitignoreit).- A free model builds
handleChatfrom the section 7 "Show prompt" in one go, and stays within its free limits for a whole Phase 2. - The agent respects
AGENTS.md: leavesTONEalone, doesn't commit secrets, doesn't runwrangler deploy. - The "Ask my Droid" page feature works with the page's Content-Security-Policy (the script has to live in
public/, not inline). The section 7.4 prompt says so.
Checks are saved automatically on this browser.