Build your own Claude Code
The Bash tool & your first real fix·30 min

Implement to spec

0 / 1
# grade this stage (from your course directory)
npx theinference test --stage implement-to-spec --watch

Fixing a one-line bug is the smallest possible win. The next-smallest real job is building something to a spec: a test states what the code should do, the implementation is a stub, and the agent has to fill it in until the test agrees. Same loop, same tools — but now the agent reads a contract and writes code to satisfy it, rather than reverting a typo.

The planted repo here has mul.js with a // TODO body and test.js asserting mul(3, 4) === 12. The agent reads the test to learn the contract, reads the stub, edits in return a * b;, re-runs the test, and stops. The point of the step is that your loop carries a real implementation task through start to finish — the same machinery, pointed at construction instead of repair.

Read the contract, satisfy it
Read  test.js          → the spec: mul(3,4) must equal 12
Read  mul.js           → the stub: (a, b) => { // TODO }
Edit  mul.js  "  // TODO" → "  return a * b;"
Bash  node test.js     → "ok", exit 0
(text) end_turn        → "Implemented mul."

What to build

Your task

Run an implement-to-spec task through the loop

There's no new program.ts branch to write — this reuses the run-task path you wired in the previous step. Your job is to confirm it carries a spec implementation end to end, not just a one-line revert: the cassette reads the test, reads the stub, edits the // TODO into a real body, runs the test, and stops. Make sure your Edit preserves the surrounding bytes exactly (the indentation and the lines around the TODO) so the final mul.js matches the spec result byte-for-byte and the planted node test.js exits 0.

src/program.ts
// reuses the run-task path:
//   mkSandbox(C.files) -> Tools -> makeExecute -> runLoop(C.cassette)
//   oracleExit = tools.bash(C.oracle).exit_code
//   print { trajectory, stopReason, files, oracleExit }
// the spec test (mul(3,4) === 12) must pass after the edits land
The whole file is graded, not just the line you changed

str_replace swaps an exact substring and leaves everything else untouched — that's why the result must be module.exports = (a, b) => {\n return a * b;\n};\n, two-space indent and trailing newline included. If your edit drifts the surrounding bytes (a stripped newline, a re-indent), the file-exact check fails even when node test.js happens to pass. Right diff and right bytes.

Larger observations flow back through the same loop

A spec task feeds bigger observations into the model than a one-line fix — the full test file, the stub, the test output — and they all ride back through the loop as tool_result blocks exactly as before. Nothing about the loop changes; it just carries more context. This is the seam that Chapter 13's compaction eventually manages when sessions grow long.

How it's graded

program.ts run-task against the spec fixture, checked the same four ways: the trajectory equals the recorded calls (Read test.js → Read mul.js → Edit → Bash); the final mul.js matches byte-for-byte; stopReason is end_turn; and oracleExit — the re-run node test.js — is 0. The agent built code that satisfies a spec and the spec's own test confirms it.