The workshop script opens on a familiar mess. Twelve laptops share one repository, and a patch drafted during the break already wears a confident commit message. The tests are green on the machine that asked for the draft. That green is a rumor until a clean checkout repeats it.
A spare bench is the room's answer to rumors. It is a second machine with a known tree, a fresh environment, a clock, and no leftover packages from last month's experiment. The bench can be a classmate's laptop. On days when a free server option is actually listed, the bench can be that server, and the brand still matters less than the rule: the machine that watched the draft does not get the final vote.
MonkeyCode shows up in this script only as a possible spare bench and a possible drafter. The operator describes it as an open-source coding assistant with free model access and a free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach. Those two availability claims are classroom conveniences, not a capacity contract.
This write-up names no model, no token total, no hardware shape, and no rental duration, because those facts live on the current product page and they move. The morning of the session, the instructor opens that page. A missing option means the class switches the bench to any other clean machine. It does not mean the class skips the ledger.
The script is a proposal for an eighty-minute sitting, not a report of a timed cohort or a measured pass rate. Twenty minutes pin a fixture. Twenty-five minutes draft a candidate behind a fence. Twenty-five minutes rerun that candidate on the spare bench, and the last ten minutes write a refusal note when the ledger disagrees.
A short break sits between the draft and the rerun. Nobody gets to "fix" the bench by copying a virtual environment across the room. The hour is about a command another person can type, not about a laptop that remembers too much.
The fixture is dull on purpose. Dull code survives a clean machine, and a workshop that opens with a framework spends its hour on installation theater. Students create a tiny package that answers a warehouse question: how many boxes hold a given item count when every box has the same size. The contract is integer arithmetic. Floats are a trap the later exercise will spring.
# spare-bench/binpack/count.py
def boxes_needed(item_count, box_size):
if box_size <= 0:
raise ValueError("box_size must be positive")
if item_count < 0:
raise ValueError("item_count must be non-negative")
return (item_count + box_size - 1) // box_size
An empty spare-bench/binpack/__init__.py keeps the import from depending on a path accident. The tests are frozen after the first successful run. Students may not edit them to make a candidate look clever.
# spare-bench/tests/test_count.py
import unittest
from binpack.count import boxes_needed
class CountTests(unittest.TestCase):
def test_exact_fit(self):
self.assertEqual(boxes_needed(8, 4), 2)
def test_remainder(self):
self.assertEqual(boxes_needed(9, 4), 3)
def test_zero_items(self):
self.assertEqual(boxes_needed(0, 4), 0)
def test_bad_box(self):
with self.assertRaises(ValueError):
boxes_needed(3, 0)
if __name__ == "__main__":
unittest.main()
The first block ends with two local runs, not one. The second run is there to prove the fixture itself is stable before anyone invites a model in. Commands below assume a POSIX shell. They are part of the exercise, and they are not presented as a benchmark.
mkdir -p spare-bench/binpack spare-bench/tests
python3 -m venv spare-bench/.venv
. spare-bench/.venv/bin/activate
cd spare-bench
PYTHONPATH=. python -m unittest discover -s tests -q
PYTHONPATH=. python -m unittest discover -s tests -q
Both runs should exit 0. A failure here is a path or packaging problem, and the class fixes that before any assistant is allowed to speak. Path confusion loves to dress up as a logic bug.
The second block permits a model. Free model access, including the option named above when the page still lists it, may draft boxes_needed. The prompt is fixed. Students paste the test file and ask for an implementation, then add one fence in the same breath: do not modify tests, do not add dependencies, do not rename the function, and do not include secrets. This fixture has none, and it should stay that way.
A fence is a constraint the ledger can see. A hunch is not. The worked candidate below is reconstructed for the exercise. It is not a transcript from a live model, and it should not be treated as a score against any product.
def boxes_needed(item_count, box_size):
if box_size <= 0 or item_count < 0:
raise ValueError("bad input")
return round(item_count / box_size)
round(9 / 4) is round(2.25). Python's round uses banker's rounding, and that call returns 2. The fixture expects 3, because nine items need a third box. The combined ValueError also erases the two distinct messages in the reference, which will matter if a later test matches text.
The class does not need a leaderboard. A tidy function can be wrong in a way a glance rewards. Students save that candidate over count.py, leave the tests untouched, and run the suite on the laptop. A local failure is useful. A local pass is still not acceptance, because the laptop that requested the draft is a witness and the spare bench is the judge.
The third block copies the tree, not the virtual environment. If the free server option is listed that morning, students use it as the spare bench and install nothing beyond a current Python 3. If the option is absent, a second laptop with a new virtual environment plays the same role. The script the class keeps runs the suite, hashes combined output, and appends one JSON line. It does not retry. A hidden retry would launder the flake this hour is trying to see.
# smoke_ledger.py
import hashlib
import json
import os
import subprocess
import sys
import time
from pathlib import Path
def run_suite(cwd: Path) -> dict:
start = time.perf_counter()
proc = subprocess.run(
[sys.executable, "-m", "unittest", "discover", "-s", "tests", "-q"],
cwd=cwd,
text=True,
capture_output=True,
env={**os.environ, "PYTHONPATH": str(cwd)},
)
blob = (proc.stdout + "\n" + proc.stderr).encode()
return {
"cmd": [sys.executable, "-m", "unittest", "discover", "-s", "tests", "-q"],
"exit_code": proc.returncode,
"seconds": round(time.perf_counter() - start, 3),
"output_sha256": hashlib.sha256(blob).hexdigest(),
"stdout_tail": proc.stdout[-400:],
"stderr_tail": proc.stderr[-400:],
}
def main() -> int:
cwd = Path(sys.argv[1]).resolve()
row = run_suite(cwd)
with (cwd / "smoke-ledger.jsonl").open("a", encoding="utf-8") as handle:
handle.write(json.dumps(row) + "\n")
print(json.dumps(row, indent=2))
return row["exit_code"]
if __name__ == "__main__":
raise SystemExit(main())
The same invocation runs on every bench. Two lines are the comparison, not a dashboard.
python3 smoke_ledger.py spare-bench
python3 smoke_ledger.py spare-bench
Students then read the last pair without adopting a framework to test their runner. The helper is small enough to retype on the board.
import json
from pathlib import Path
def last_pair(path):
rows = [json.loads(line) for line in Path(path).read_text().splitlines() if line]
left, right = rows[-2], rows[-1]
return {
"exit_codes": (left["exit_code"], right["exit_code"]),
"same_hash": left["output_sha256"] == right["output_sha256"],
"seconds": (left["seconds"], right["seconds"]),
}
Acceptance is narrow, and it is decided in prose so the rule cannot hide inside a config file. Both exit codes are 0, the hashes match, and the test file is byte-for-byte the file frozen in the first block. A matching hash with a failing exit code is a refusal. A passing exit code with a drifting hash is a note, not a celebration, because a timestamp or a reordered warning changes the digest even when the logic holds.
This fixture prints no logs, so the raw digest is enough. A later lab that adds logs should strip volatile lines before hashing, and should write that stripper down next to the fence. No hash from this article should be copied into a student's ledger. The script fills that field on their machine.
The reconstructed candidate fails the gate. test_remainder wants 3 and the function returns 2. The ledger's exit code is non-zero, and the stderr tail names the assertion. The repair is ceiling division, the same expression the reference used. After the repair, the student reruns the ledger on the spare bench and only then opens a pull request. The request links the ledger line. It does not link a screenshot of a chat.
The last ten minutes are a writing exercise with a mean streak. When the spare bench disagrees with the laptop, the student writes three sentences: what differed, which claim they withdraw, and which command a reviewer should rerun. They do not write that a model was wrong in general. They write that this candidate failed this fixture on this bench. The narrower sentence is the one a later class can trust.
A refusal note for the rounding candidate is short enough to read aloud. The first honest run failed test_remainder because round is not ceiling division. The withdrawn claim is that a readable implementation is a finished one. The rerun command is python3 smoke_ledger.py spare-bench from a clean tree, with the script setting PYTHONPATH.
This spare bench will not carry production work. It should not see secrets, customer records, or credentials. Free model access can change, throttle, or disappear between a Thursday plan and a Friday lab. A free server can be slow, asleep, or packaged differently from a student's laptop.
Nothing here measures that gap, and nothing here promises a quota. People who need a named model, a contractual uptime, a hardware shape, or graded deterministic generation should not use this setup as a system of record. They can still keep the ledger. The ledger does not care which assistant drafted the patch.
Courses that grade screenshots, prose style, or usage theater should skip the session. The artifact cannot see intent. It can see an exit code. Teams that want automatic merges should skip it too, until a human has accepted the refusal rule, because a green ledger on a toy fixture is not a release.
Integer box counts will not catch races, network calls, or migration hazards. The example is a scale model of a habit, and it should stay small enough to finish before the room gets restless. Windows students should translate the activation line rather than treat a missing source as a failed proof.
An instructor who wants this shape of bench can open the MonkeyCode project page, confirm that free model access and the free server option are still listed, read the license in the official repository, and only then point a class at it. If either option is gone, the eighty minutes still stand on a second laptop and the same script.
Top comments (0)