DEV Community

Charlie Xu
Charlie Xu

Posted on

Green on Your Laptop Is Not a Grade: A Bootcamp Lab on Runtime Contracts

Stop grading the generated server. Grade whether a stranger can boot it on a pinned runtime without guessing Node, ports, or hidden env vars. That is the whole lab.

AI agents are getting fluent at writing Express handlers. They are still reckless about the machine those handlers will live on. You have seen the failure: green on a laptop, red in CI, mysterious in a classmate's demo. Why do we keep scoring the souvenir instead of the replay?

This is a proposed bootcamp lab, not a claim that I already ran it on a giant cohort. Steal the worksheet. Change the stack. Keep the rule: no runtime contract, no grade.

The problem the lab is actually about

Students now paste a ticket into a coding assistant and get a folder that looks like a service. Tests pass locally. Then the grader clones the repo on a different OS, a different Node, a missing .env, and a port already taken by Discord. Suddenly the agent looks brilliant and the student looks unlucky.

Luck is not a learning outcome.

The agent did not fail at JavaScript. It failed at environment honesty. It assumed node meant whatever was on PATH. It assumed PORT=3000 was free. It assumed fetch existed. It assumed the grader would silently install whatever npm i felt like that afternoon.

Sound familiar? Same class of bug as an agent inventing AWS resource names. Different surface.

Lab goal (one sentence)

Ship a tiny Node HTTP service and a machine-readable runtime contract so a grader can replay boot, test, and a single happy-path request on a pinned server — not on the author's laptop.

If the grader has to ask you a question, the contract is incomplete.

What you are allowed to use

You may use an AI coding assistant. You should. The point is not abstinence.

You may not hide the environment. If the model writes npm start and you never freeze Node, that is a lab failure, not a style issue.

For students who do not have a cloud bill and cannot share a home laptop with a grader, a shared free server plus free model access is the boring, useful setup. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is one option that currently offers free model access and a free server option; I am not inventing model names, quotas, or hardware here, and I am not asking you to treat any vendor as the subject of the lab. The subject is the contract file.

If you already have a Codespace, a campus VM, or a Raspberry Pi under the desk, use that. The checklist does not change.

Setup (30–45 minutes, before any feature code)

Work in a clean directory. Do not generate the service yet. Seriously. The agent will try to skip this. Do not let it.

mkdir runtime-contract-lab && cd runtime-contract-lab
git init
node -v
npm -v
uname -s
Enter fullscreen mode Exit fullscreen mode

Create three files by hand, even if a model offers to "just scaffold everything":

  1. runtime-contract.json — the source of truth
  2. contract.test.mjs — the grader's first command
  3. README.md — human translation of the JSON, not a substitute for it

Proposed contract shape (treat this as a lab spec, not a standard):

{
  "contractVersion": 1,
  "runtime": {
    "language": "javascript",
    "node": { "major": 22, "exact": null },
    "packageManager": "npm@10"
  },
  "boot": {
    "install": "npm ci",
    "start": "node server.mjs",
    "healthPath": "/health",
    "portEnv": "PORT",
    "defaultPort": 3000,
    "readyTimeoutMs": 5000
  },
  "env": {
    "required": ["PORT"],
    "forbidden": ["AWS_SECRET_ACCESS_KEY", "OPENAI_API_KEY"],
    "notes": "No paid APIs in the core path. Health must work offline."
  },
  "assumptions": []
}
Enter fullscreen mode Exit fullscreen mode

That empty assumptions array is a trap. The agent will leave it empty. You will not.

Checkpoint 0 — freeze the machine, not the vibes

On the same host the grader will use, record reality:

node -p "process.version"
node -p "process.platform"
printf 'PORT=3000\n' > .env.example
Enter fullscreen mode Exit fullscreen mode

If you cannot get a shared host, stop. Do not start coding. Pair with someone who can, or wait for lab staff to hand you a URL. Local-only work is practice, not a submission.

The tiny service (deliberately boring)

Requirements, not a product pitch:

  • GET /health returns 200 and {"ok":true}
  • GET /whoami returns Node version and whether fetch exists
  • No database. No auth. No "while we are here" features.
  • Must listen on process.env.PORT
  • Must exit non-zero if PORT is missing

A starter the student (or the model) can complete:

// server.mjs — complete this; do not add a framework unless the contract says so
const port = process.env.PORT;
if (!port) {
  console.error("PORT is required");
  process.exit(1);
}

const server = Bun; // trap: delete this line. You are on Node.
Enter fullscreen mode Exit fullscreen mode

Yes, I put a trap in the snippet. If your agent copies it blindly, that is data. Write the failure into assumptions or fix the file. Do not shrug.

A fair implementation looks closer to this:

// server.mjs
import http from "node:http";

const port = process.env.PORT;
if (!port) {
  console.error("PORT is required");
  process.exit(1);
}

const server = http.createServer((req, res) => {
  if (req.url === "/health" && req.method === "GET") {
    res.writeHead(200, { "content-type": "application/json" });
    res.end(JSON.stringify({ ok: true }));
    return;
  }
  if (req.url === "/whoami" && req.method === "GET") {
    res.writeHead(200, { "content-type": "application/json" });
    res.end(
      JSON.stringify({
        node: process.version,
        fetch: typeof fetch === "function",
        platform: process.platform,
      })
    );
    return;
  }
  res.writeHead(404);
  res.end();
});

server.listen(Number(port), () => {
  console.log(`listening on ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Keep it this small on purpose. If the model opens a React app, you failed the prompt, not the runtime.

The grader's first command

Do not make humans read a novel. Make Node fail loudly.

// contract.test.mjs
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { test } from "node:test";

const contract = JSON.parse(await readFile("./runtime-contract.json", "utf8"));

test("contract declares a Node major that matches this process", () => {
  const actualMajor = Number(process.versions.node.split(".")[0]);
  assert.equal(contract.runtime.node.major, actualMajor);
});

test("boot.install is npm ci, not a vibes command", () => {
  assert.equal(contract.boot.install, "npm ci");
});

test("core path forbids secret-shaped env vars", () => {
  for (const name of contract.env.forbidden) {
    assert.equal(process.env[name], undefined);
  }
});

test("assumptions is not an empty shrug", () => {
  assert.ok(Array.isArray(contract.assumptions));
  assert.ok(contract.assumptions.length >= 3, "name three things the agent might have invented");
});
Enter fullscreen mode Exit fullscreen mode

Run it on the pinned host:

npm ci
node --test contract.test.mjs
PORT=3000 node server.mjs &
curl -sS http://127.0.0.1:3000/health
curl -sS http://127.0.0.1:3000/whoami
Enter fullscreen mode Exit fullscreen mode

If /whoami reports a Node major that is not in the contract, the lab is not done. Do not "fix" it by editing the test. Fix the contract or the host. Which one is cheaper? Usually the JSON.

Checkpoints (staff can grade these in order)

Use these as gates. Later work does not unlock earlier work.

  1. Host exists. Student posts a replay URL or SSH alias for a shared runtime. Laptop screenshots do not count.
  2. Contract matches the host. node --test contract.test.mjs is green on that host.
  3. Boot is copy-pasteable. A grader who has never opened the chat log can npm ci and start the server from the contract alone.
  4. Health is offline. Unplug extra APIs. /health still returns 200.
  5. Assumptions are ugly and specific. "Might need fetch" is a fail. "Node 18 has no global fetch; we pinned 22" is a pass.
  6. Transcript exists. A short lab-log.md lists prompts, files touched, and commands run, in order. Not a novel. A receipt.

Miss gate 1 and I will not read your handlers. Harsh? Maybe. Faster than debugging three Node versions by emoji.

Stretch goals (optional, scored separately)

  • Add engines to package.json and make npm ci fail on the wrong major.
  • Add a scripts/replay.sh that reads the contract and refuses to start if node -p process.versions.node disagrees.
  • Break the server on purpose on a second host (different major) and paste the error. That failure is the lesson.
  • Replace the HTTP server with a CLI that prints the contract and exits. Same gates. Smaller surface.

Do not stretch into Kubernetes. If you need a mesh to prove you can listen on a port, the lab has already escaped.

Fair grading rubric (100 points)

Score the replay, not the chat charisma.

Points What I actually look at
15 Shared host is reachable by the grader without a meeting
20 runtime-contract.json matches the host; tests green there
15 PORT required; process exits non-zero when missing
15 /health and /whoami behave as specified, no extra network
15 assumptions has ≥3 concrete, falsifiable notes
10 lab-log.md is a command/prompt receipt, not a diary
10 Stretch: engines field or replay script that fails closed

Automatic zeros:

  • Secrets in the repo or in the contract
  • "Works on my machine" as the only evidence
  • Contract that says Node 22 while /whoami prints v18
  • Generated framework the ticket did not ask for
  • Empty assumptions array

I will not grade spelling in the README if the JSON is honest. I will grade a beautiful README that lies about Node.

How the assistant is allowed to participate

Give the model the contract before the ticket. Then give it the ticket. Then freeze the contract again if the model tries to add Redis "just in case."

A prompt that has worked in dry runs of this worksheet:

You are not allowed to write server.mjs until you propose three edits
to runtime-contract.json. Each edit must be a fact about Node, PORT,
or install, not a feature. Wait for me to accept the JSON. Then write
the smallest http server that satisfies /health and /whoami.
Enter fullscreen mode Exit fullscreen mode

If the model starts coding anyway, that is a student skill issue. Stop it. Paste the contract back. Ask: which line of JSON authorizes this file?

Free model access matters here because every student needs the same chance to iterate on that argument without a personal API key. A free server matters because the grader and the student must lose the argument against the same process.version. Paid clouds are fine. They are not required for the learning outcome.

Limitations (read these before you adopt the lab)

  • This does not teach product design. The service is a fixture.
  • This does not prove the model is "good." It proves the student can pin a machine.
  • JSON will not save you from a host that mutates under your feet. Pin an image or refuse the host.
  • npm ci needs a lockfile. If you never commit package-lock.json, you did not pin installs.
  • Health checks can lie. /health returning 200 while /whoami crashes is still a fail under this rubric.
  • I have not published vendor benchmarks, token ceilings, or uptime promises. If a free tier moves, the contract still works on whatever host you still control.

Who should not use this approach

Skip this lab if you are teaching algorithms on paper. Skip it if your students have no network and no shared VM — the replay is the point. Skip it if the course already grades production Kubernetes. Skip it if you want to evaluate prompt style; this rubric will under-score a gorgeous chat that ships an unpinned runtime.

Staff on a two-hour workshop: use only checkpoints 1–4. Cut stretch. Cut /whoami if time explodes.

What I want back from you

Not a screenshot of a passing test on a stickered MacBook. A contract, a lockfile, a replay command, and three ugly assumptions.

Can your agent describe the computer, or only the code? If you need a no-card-required box to run the same checklist with a classmate acting as grader, MonkeyCode's free server option is one way to pin that replay. Steal the JSON either way.

Top comments (0)