Lab GuideLab Guide — source index
14:30 17:00

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)#

FileSectionClock
00-welcome.mdWhat you'll build14:30
01-setup.mdCreate the Cloudflare account first (verify email, workers.dev subdomain), then GitHub, tools, CLI logins. No API keys14:35
02-deploy.mdDeploy, clone, run locally15:05
03-make-it-yours.mdMission 1: file tour, NAME / TAGLINE / EMOJI / CHARACTER_ID, push, register15:15
04-your-character.mdMission 2: getCharacter(id) (SWAPI person + homeworld, cache, fallback) + secrets sidebar15:27
05-checkpoint.mdBreak + catch-up15:40
06-meet-your-droid.mdPhase 2 intro + Coding Agent setup (Skills)15:48
07-build-your-droid.mdMission 3: tone by hand, agent builds /api/chat, then a page feature16:00
08-rounds.mdRounds + debugging16:23
09-whats-next.mdBehind the scenes (Durable Object), bonus ideas16:45
appendix-browser-path.mdNo Node? Browser-only path—
appendix-troubleshooting.mdCommon problems—

Placeholders to replace before publishing#

PlaceholderMeaning
arena.cf2026iscte.nobledemos.comHub 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 CodeNot written in the guide; shown on the projector only

The template today#

The Lab Guide matches the template repo (template/) exactly. The current files:

text
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 locally

The "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.

md
# 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_ID themes 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 cacheTtl makes 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): with max_tokens: 120 they return no answer. Check how often the 3B model drifts off-character.
  • wrangler dev with the AI binding on a login with several Cloudflare accounts needs CLOUDFLARE_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 login is enough for git push over HTTPS on Windows, macOS and Linux.
  • npm install -g opencode-ai works 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/skills detects OpenCode, Claude Code, Cursor and Copilot. Check what it adds to the repo (commit it or .gitignore it).
  • A free model builds handleChat from the section 7 "Show prompt" in one go, and stays within its free limits for a whole Phase 2.
  • The agent respects AGENTS.md: leaves TONE alone, doesn't commit secrets, doesn't run wrangler 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.

Your project

Pick your Droid Name first. Add the rest when you know it; every field is optional. These details stay in this browser and are never sent anywhere.

A nickname, not your full name. Used for your Student Worker and repo. Lowercase letters, numbers and hyphens; up to 40 characters.

The workers.dev subdomain you choose at signup. Same naming rules as your Droid Name.

Add this later, or paste a GitHub repo URL to fill your username and Droid Name.

My URL is different

Optional. Use your public https://…workers.dev URL. Paths and trailing slashes are removed. Leave blank to use your Droid Name and subdomain.

LIVE PREVIEW

Your droid will live at https://<your-droid-name>.<your-subdomain>.workers.dev

Your repo: github.com/<your-github-user>/<your-droid-name>