A dead mainframe, one language, and everything worth building small
The fiction is a basement. The machine is real: whichever computer you're sitting at — the whole course runs standalone, on one box, no second machine, no cloud. By the end the SERPENT plays games you wrote, runs scrapers and watchers while you sleep, answers your friends on a Discord switchboard, charts your actual year from your own exports, performs a demoscene when idle, and ships a tool of your own design as a versioned package. Every automated staff member is a daemon: an AI agent builds the candidate, you run its Tryout, and only a hire you can defend gets a badge.
Under the fiction, this is a ~150-hour Python apprenticeship built for how 2026 actually works: an AI agent codes with you from mission 1, but every mission assigns you a seat, and the seat is the lesson. (All names and places here are fiction; the skills, the code, and the tools are not.)
The four seats
Blank page, by hand, no agent. Reserved for the ~20% of concepts where the struggle is the learning: syntax, state, game loops, data munging, chains.
The agent writes; you must find what's wrong before it ships. There is always something. Merging unread code fails the mission.
You write the spec and direct the agent through it. The grade is whether your spec survived contact with implementation.
Something is broken and the logs are lying. Appears mostly inside Tryouts, where breakage is safe and encouraged.
The Tryout
No daemon runs unattended until it survives an interview: an agent builds the candidate, and you read its code cold, break it on purpose in a sandbox (a scratch folder, a throwaway copy, a test server), then rule hire, fix-then-hire, or reject — in writing. Rejections are wins. The basement has one rule from the old days: nothing untested runs at night.
The premises
┌─────────────────────────────────────────────┐ │ the SERPENT · your machine · one box │ │ │ │ cabinets/ games you build (Arc 1) │ │ nightshift/ watchers + digests (Arc 2) │ │ switchboard/ the Discord bot (Arc 3) │ │ punchcards/ your data, charted (Arc 4) │ │ demoscene/ generative toys (Arc 5) │ │ dist/ the shipped package (Arc 6) │ └─────────────────────────────────────────────┘
The Floor lives at the flagship. The five commissioned cabinets — the ~150-hour elective app-building track — belong to MARQUEE, the original building. This basement is arc-spine only: 27 missions, one language, no cabinets. Same skeleton, different beast.
ps -ef · seven processes between you and gold master
Processes go amber while you work them, green at 100%. Select one to jump there.
The night shift roster
Badges light when a Tryout ends in a hire. This wall is the course's real scoreboard.
candidate
candidate
candidate
candidate
candidate
candidate
Arc 0 · Power-On Self-Test
~20 hrsThe bench, the syntax, and a machine that cleans up after you.
-
Install Python 3.14 and make the bench yours: REPL fluency, venv-per-project discipline, pip, VS Code with the Python extension, Ruff wired in, git ready.
Deliverable — a clean Python bench: a venv you can create, destroy, and recreate without thinking, and a hello-tool that proves the chain.
python 3.14REPLvenvpipruffBriefing
The play
Install the current stable Python from python.org/downloads (3.14.x as of this writing; 3.15 lands around October 2026 — the course tracks stable). On Windows, take the python.org installer over the Store version and check "Add python.exe to PATH"; you get the
pylauncher for free, which is how you address a specific version when several exist.py -3.14 -m venv .venv # every project gets its own venv. every one. .venv\Scripts\activate # (macOS/Linux: source .venv/bin/activate) python -m pip install ruff # tools go IN the venv, not global python -m pip list # know what's on the benchThen the editor: VS Code + the Python extension (official walkthrough). One non-negotiable: the interpreter picker (status bar, bottom right) must point at this project's
.venv— nine-tenths of "it works in the terminal but not the editor" is that picker. Wire Ruff in as the linter and formatter and let it nag you from day one; its nagging is a free tutor.REPL fluency (the actual skill of this mission)
The REPL is where Python people think. Spend a real hour in it: arithmetic, strings,
help(str),dir(list),import this. The 3.14 REPL has colors, multiline editing, and block paste — it's a genuinely good scratchpad now. The habit to build: wondering how something behaves and checking in five seconds instead of searching for a blog post about it.Failure modes that matter
- The Store ghost: on Windows, typing
pythoncan open the Microsoft Store instead of Python (App execution aliases). Turn those aliases off, or always usepy. - Global pip sprawl: installing into the system Python works until two projects disagree. The venv ritual costs ten seconds and prevents the whole disease class.
- Two interpreters, one confusion: when anything acts
haunted,
python -c "import sys; print(sys.executable)"tells you who you're actually talking to.
Prove it
Write
hello.py: greets by name from a command-line argument (argparse, even this small — the habit starts now), and prints which Python it's running under. Run it from the venv, from VS Code, and from a fresh terminal. Three green runs, mission done.Reference shelf, all free (verified 2026-08-09)
- The official tutorial — chapters 1–3 tonight.
- Packaging User Guide — the venv/pip mental model.
- Automate the Boring Stuff, 3rd ed. — full book free online; your Arc 0–2 companion.
- The Store ghost: on Windows, typing
-
Core syntax through tiny playable toys: a dice roller, a number guesser, mad libs, rock-paper-scissors — types, control flow, functions, collections, and exceptions, each learned by making something you can immediately play.
Deliverable — a
toys/folder of working single-file games, each one playable by a friend.typescontrol flowfunctionscollectionsexceptionsBriefing
The play
One toy per concept cluster, built in order, each one playable the moment it runs:
- Dice roller — numbers,
random, loops, f-strings. Roll3d6, show each die and the total. - Number guesser —
while, comparisons,input(), and your first exception:int("banana")raises, and the game must shrug, not die. - Mad libs — strings and lists: collect words, slot them into a template, print the disaster.
- Rock-paper-scissors — dicts as rules
(
beats = {"rock": "scissors", ...}), functions with real signatures, a best-of-five score.
Craft bar (hold it even on toys)
- All code lives in functions; the file ends with the
if __name__ == "__main__":guard. It matters in Arc 6; the habit is free now. - Names say things:
secret_number, notx. - Every
input()is treated as hostile. Players type garbage. Good toys don't crash; they sass.
Failure modes that matter
- Tutorial hell: the trap is watching Python instead of writing it. The tutorial and the book are the reference shelf; the toys are the mission. Stuck twenty minutes? Look it up. Not stuck? Don't.
- Copy-paste learning: type every line yourself, even from references. The fingers are part of the memory.
Reference shelf, all free
- Official tutorial ch. 3–5 — types, control flow, data structures.
- Automate the Boring Stuff 3e — ch. 1–6 map to exactly this mission.
- Exercism's Python track — 140+ free drills; do a few between toys as reps.
- Dice roller — numbers,
-
Create the serpent repo — home of everything you build here — with modern layout (pyproject.toml, src/), push it to GitHub over SSH, and adopt a commit discipline.
Deliverable —
~/serpentrepo with README, pushed; first three commits tell a story.pyproject.tomlsrc layoutgit disciplineBriefing
The play
One repo for the whole course:
~/serpentwithREADME.md(what this place is),toys/(M0.2 moves in),docs/(specs live here, starting next mission), and — from Arc 1 on — proper packages undersrc/. Drop in a minimalpyproject.tomlnow (name, version,requires-python = ">=3.14"): it does almost nothing today and becomes the beating heart of Arc 6's packaging mission. The PyPA tutorial shows the modern shape; you're borrowing its skeleton early.Commit discipline (the whole lesson)
- One coherent change per commit; the message says why, not what.
- Commit before every experiment, so reverting is casual, not surgery.
- Branches are free. Anything exploratory gets one.
Failure mode that matters
The junk-drawer repo: write the
.gitignorebefore the first commit —.venv/,__pycache__/,*.pyc, and.envbefore any.envexists (Arc 3's bot token will thank you). A venv committed in week one haunts you in month six. -
First architect seat: audit your real Downloads-folder chaos, write the sorting rules spec by hand, then build the organizer — pathlib and shutil do the moving, a dry-run mode prints its plan first, and an undo journal makes every run reversible.
Deliverable — your actual Downloads folder cleaned by code you trust: dry-run diff shown, undo proven.
pathlibshutildry-run designloggingundo journalBriefing
The architect seat, defined
You don't write the script first. You write the spec first, in
docs/janitor-spec.md: WHAT folder it works on, the RULES table (extension → destination, plus an "older than 90 days → archive" rule if you dare), the COLLISION policy (same filename already at the destination — rename with a suffix, never overwrite), DRY-RUN as the default (moving is opt-in via--do-it), and the UNDO journal (every move appended as a JSON line: source, destination, timestamp). Then you direct the agent to implement the spec and review the result against it, line by line. The spec surviving contact is the grade.The Python in it
from pathlib import Path downloads = Path.home() / "Downloads" for item in downloads.iterdir(): # pathlib is the whole API you need if item.is_file(): dest = rules.get(item.suffix.lower()) # shutil.move for the act; json lines for the journal; # logging (not print) for the record — the night shift reads logs.Failure modes that matter
- No dry run: a mover that acts on first run against a real folder is a coin flip with your files. Plan first, print the plan, act only on the flag.
- The silent clobber: moving
report.pdfonto an existingreport.pdfeats one of them. Your collision policy exists because the agent's first draft usually won't have one — catching that in review is the mission inside the mission. - Testing on the real thing: first runs happen on a copy of Downloads in a scratch folder. This is the sandbox habit, and every Tryout in the course stands on it.
Prove it
Dry-run on the copy; read the plan; run for real on the copy; spot-check; run
undo; verify everything came back. Only then point it at the real Downloads. The building's first real job, and it's reversible.
Arc 0 notes
Arc 1 · The Snake Pit
~24 hrsYour first game loop, your first review, your first PR, your first hire.
-
Read real idiomatic Python, decision by decision: a small respected codebase against PEP 8 and Python idiom, and file your objections.
Deliverable — written attacks on at least two of the codebase's decisions, in your Arc 1 notes.
PEP 8idiomscode readingBriefing
The reviewer seat, defined
Reviewing is not reading; it's interrogation. For every choice the code makes, ask the three reviewer questions: What does this actually do? What input or future change breaks it? What would I have done, and can I defend the difference? "It's fine" is a failed review; nothing is fine.
The codebase (it ships inside your install)
The standard library is on your disk, written by the people who wrote the language. Find it:
python -c "import textwrap; print(textwrap.__file__)" python -c "import json.tool; print(json.tool.__file__)"Read
textwrap.pyend to end (~500 lines of very idiomatic Python: a class doing real work, docstrings, edge-case handling with comments explaining why), thenjson/tool.py(~80 lines: a complete command-line program — argparse, files, a main()). Keep PEP 8 open and note every place the stdlib itself bends its own style guide — those are the most interesting finds.Why this is mission one of the arc
From here on you'll read far more code than you write — every agent session is a code review. Training the reflex on code this good calibrates your ear: when the agent's output sounds different from
textwrap, you'll hear it.File objections like
- "This method is 60 lines and does three jobs — where would I split it, and would my split survive its edge cases?"
- "This variable name lies a little. What would tell the truth?"
-
Build Snake on the green screen, no engine: the game loop, tick rate, non-blocking keyboard input, collision, score. This is where the SERPENT earns its name.
Deliverable — playable Snake in any terminal, written entirely by you.
game looptick ratekeyboard inputcollisionterminal renderingBriefing
Mental model
Every game ever shipped is this loop: read input → update state → render → wait for the next tick. Snake is the smallest game that makes you build all four honestly. State is a deque of body cells, a direction, and a food position; everything on screen is a projection of it.
The two hard parts, named up front
- Non-blocking input.
input()waits; games can't. Installreadchar(small, cross-platform, alive — v4.2.x) and run it in a reader thread that pushes keys into aqueue.Queue; the game loop drains the queue each tick and never blocks. That's threads, queues, and input handling in one honest lesson. - Flicker-free rendering. Print cell-by-cell and the
screen tears. Build each frame as ONE string (rows joined with
newlines), move the cursor home with the ANSI escape
"\x1b[H", print once per tick. Hide the cursor during play ("\x1b[?25l"), and restore it in afinally:— a crashed game must not leave the terminal broken.
The tick
import time TICK = 0.12 # seconds per frame — the difficulty dial next_tick = time.monotonic() # monotonic, never time.time, for game clocks while alive: next_tick += TICK handle(keys.drain()); step(); draw() time.sleep(max(0, next_tick - time.monotonic()))Failure modes that matter
- The 180° insta-death: pressing left while moving right must be ignored, not fatal. Every first Snake has this bug; finding yours is a rite.
- Sleeping a fixed TICK instead of sleeping until the next tick — the game slows down as frames get expensive.
- Windows terminals: use Windows Terminal (ANSI on by default), not the legacy console.
Prove it
A friend plays it without instructions from you, dies, says "one more," and the terminal is intact afterward. That's the ship bar.
- Non-blocking input.
-
State and persistence, by hand: JSON high-score table, a settings file, a difficulty curve, pause and resume. The cabinet starts remembering its players.
Deliverable — the arcade remembers you between sessions: scores, settings, and all.
jsonfile I/Ostate designsettingsBriefing
Mental model
Persistence is a round trip: in-memory state → serialize → disk → deserialize → the same state. Design the state first — a
@dataclassfor settings (tick speed, board size), a list of score entries (name, score, date ISO-stamped) — then the JSON is just those shapes written down. Add a"version": 1field today; future-you gets to migrate instead of crash.The one professional trick this mission exists to teach
Atomic writes. If the game crashes mid-write, a naive
open(..., "w")leaves a half-file and the scores are gone. Write to a temp file in the same directory, thenos.replace(tmp, final)— the OS swaps whole files, so readers see old-or-new, never half. Three lines, and it's the same move every database you'll ever meet makes underneath.And the mirror of it
Loading trusts nothing: file missing → defaults; JSON malformed → defaults plus a warning line (the file was probably you, mid-experiment); unknown version → say so. A corrupt save must never kill the game.
Failure modes that matter
- Pause that isn't: 'p' must stop the update, not the loop — input still drains (or unpausing can't work), time accounting resumes cleanly.
- Schema drift: you'll add a field next week. The version key and defaults-on-missing make that a non-event.
-
Claude Code's first feature on your codebase: a ghost replay of your best Snake run, on a branch, reviewed like money depends on it.
Deliverable — a merged (or rejected) PR with your written review; at least one demanded change.
PR reviewbranchesdiffsBriefing
Setup
Point Claude Code at
~/serpentand ask for the feature as a spec, not a vibe: record each run as its starting RNG seed plus the sequence of (tick, direction) inputs; on a new game, replay the best run's inputs through the same game rules to draw a dim "ghost" snake in the background. Demand: a branch, a small diff, no rewrite of your loop — the ghost is a layer, and if the agent claims it must restructure your code to add it, that claim is the first thing you interrogate.The review discipline (use it forever)
- Read the diff cold before running anything. Line by line. Out loud helps.
- For each hunk: what does it do, what breaks it, does it belong in this PR?
- Run it. Then feed it garbage: a recording from a different board size, a truncated recording file, a seed that isn't an int.
- Demand at least one change, even on good code. Finding it is the exercise.
- Merge only what you can defend line by line. You are the reviewer of record.
Why a ghost, specifically
Deterministic replay only works if the game is honestly deterministic — same seed, same inputs, same ticks, same outcome. If the ghost drifts from the wall it died on, your game loop has a hidden dependency on wall-clock time or input timing, and the ghost just found it. The feature is a test in a costume.
-
Your first daemon, deliberately low-stakes: an agent builds a typing-speed cabinet (WPM, accuracy, high scores). You run the full Tryout format for the first time and make the call.
Deliverable — a second cabinet on the floor, and one page of tryout notes.
tryout formatreading agent codeacceptance criteriaBriefing
The candidate spec (you write this first — architect muscle)
A 60-second typing test in the terminal: shows a passage from a local
texts/folder, measures gross WPM (chars÷5 per minute) and accuracy, writes results to a scores file in the same shape as Snake's (one convention, two cabinets), and exits clean on Ctrl-C. Small enough to try out in an evening, real enough to matter.The Tryout format (learn it here, reuse at every hire)
- Paper round: read the candidate's code cold. Note every question before running anything.
- Floor round: run it in a sandbox — a scratch copy of the repo, not the real one. Probe: a tiny 60×15 terminal, a passage with unicode quotes, mashed paste-input, walking away for the full 60 seconds without typing.
- Stress question: ask the agent "what happens if X?" for your three nastiest X's, and check its answers against what the floor round actually showed. Where the answer and the behavior disagree, you've learned the most important thing this course teaches.
- Ruling: hire / fix-then-hire / reject, with reasons, in writing. Rejections are wins.
Failure mode that matters
The polite interview. If the Tryout finds nothing, the Tryout was too soft. There is always something: a WPM formula that counts backspaces as progress, a crash on a passage shorter than the window, division by zero on zero typed chars. Find it.
Arc 1 notes
Arc 2 · Night Shift
~26 hrsScrapers and watchers that work while you sleep.
-
Scraping by hand: requests and BeautifulSoup pull a real site you actually care about into JSON/CSV — with robots.txt respected, delays between hits, and retries that don't hammer anyone.
Deliverable — fresh structured data on disk, fetched politely by code you wrote.
requestsbeautifulsouprobots.txtpolitenessretriesBriefing
Authored when you approach Arc 2 — with that month's library versions, not today's. Say "author Arc 2 briefings."
-
Architect a watcher framework: a base class, per-source plugins (a price drop, a new release, tomorrow's weather), one config file. You design the shape by hand; the agent writes plugins to your spec; you review.
Deliverable — three watchers running through one framework whose design you can defend.
OOPclassesplugin designconfig filesreviewBriefing
Authored when you approach Arc 2.
-
Scheduling for real: the schedule library for in-process loops, the system scheduler (Task Scheduler or cron) for jobs that survive reboots. The watchers run nightly and deliver a morning digest — email or webhook — with logging and retries.
Deliverable — a digest waiting for you at 7am that you didn't touch.
scheduletask scheduler / cronsmtplib / webhooksloggingretriesBriefing
Authored when you approach Arc 2.
-
An agent builds a new watcher plugin with pytest tests. The Tryout runs it in a sandbox against saved fixture pages — including changed ones: does it fail politely when the site moves, or does it lie?
Deliverable — the Clerk's badge lit; a new plugin on the night shift with tests you trust.
pytestfixturesfailure modestryout formatBriefing
Authored when you approach Arc 2.
Arc 2 notes
Arc 3 · Switchboard
~19 hrsA bot your friends actually talk to.
-
A Discord bot from nothing: developer portal, token kept out of git, the gateway connection, and your first commands — dice, 8-ball, quotes. Async Python arrives here, gently.
Deliverable — your bot online in your own server, answering your friends.
discord.pyasync basicscommandssecrets handlingBriefing
Authored when you approach Arc 3.
-
SQLite from the standard library: you design the schema by hand (reminders, per-user scores, a quote archive); the agent writes the queries; you review. No server, no install — the database is a file.
Deliverable — the bot remembers everything across restarts.
sqlite3SQL basicsschema designreviewBriefing
Authored when you approach Arc 3.
-
Wire the arcs together: Snake and Typist leaderboards post to a channel, the night-shift digest lands in another. The bot becomes the building's voice.
Deliverable — your friends watch high scores fall without you posting anything.
integrationscheduled postsembedsBriefing
Authored when you approach Arc 3.
-
An agent builds a trivia cog. The Tryout runs it in a sandbox server and plays dirty: spam, ties, cheaters, mid-game leavers. Hire only what keeps score honestly under fire.
Deliverable — the Quizmaster's badge lit; trivia night runs itself.
cogsedge casesstate under concurrencytryout formatBriefing
Authored when you approach Arc 3.
Arc 3 notes
Arc 4 · Punch Cards
~21 hrsYour own life as a dataset, charted in green.
-
Architect the data pipeline: pick your real exports (Spotify, YouTube, bank CSV — chosen at briefing time), design the cleaning by hand with csv, dataclasses, and type hints; the agent writes the loaders; you review.
Deliverable — messy real-life exports flowing into clean typed records.
csvdataclassestype hintsdata cleaningBriefing
Authored when you approach Arc 4.
-
The deep build, all by hand: group, aggregate, and rank your own year — then chart it with matplotlib styled phosphor-green-on-black and compose the pages into one generated report.
Deliverable — a report about your actual life, every number traceable to a line of code you wrote.
aggregationmatplotlibchart designreport generationBriefing
Authored when you approach Arc 4.
-
An agent builds the same style of report for a second dataset. The Tryout is a correctness eval: you seed the data with impossible dates, duplicate rows, and absurd values — does it notice, or does it lie with a straight face?
Deliverable — the Analyst's badge lit; a pytest eval harness you reuse every time the data changes.
evalspytestdata validationseeded corruptionBriefing
Authored when you approach Arc 4.
Arc 4 notes
Arc 5 · Demoscene
~19 hrsGenerative toys — text, images, mazes, an attract mode.
-
A Markov-chain text generator by hand from a Project Gutenberg corpus: build the chain with dicts, walk it with weighted choice, tune the order until the output gets eerie.
Deliverable — the SERPENT writes almost-sentences in a dead author's voice.
dictsdefaultdictprobabilitytext processingBriefing
Authored when you approach Arc 5.
-
Image to ASCII with Pillow: brightness mapping, character ramps, glitch modes — architected as a proper CLI with subcommands, output to terminal, text file, or PNG.
Deliverable — any photo rendered as green-screen phosphor art.
pillowbrightness mappingCLI designargparse subcommandsBriefing
Authored when you approach Arc 5.
-
A procedural maze, generated and solved live in the terminal: depth-first carving, breadth-first solving, animated so you watch it think.
Deliverable — a maze that carves itself, then solves itself, before your eyes.
recursionstacks and queuesgraph searchterminal animationBriefing
Authored when you approach Arc 5.
-
An agent builds the attract mode: an idle screensaver cycling the arc's toys — ASCII art, maze runs, ghostwriter lines. The Tryout runs it unattended for an hour: no crash, no leak, graceful in a tiny terminal.
Deliverable — the Projectionist's badge lit; the SERPENT performs when nobody's touching it.
long-running processesresource disciplinegraceful degradationtryout formatBriefing
Authored when you approach Arc 5.
Arc 5 notes
Arc 6 · Gold Master
~21 hrsTests, CI, a shippable package, and a build of your own design.
-
A pytest suite on your best tools, then GitHub Actions running Ruff and pytest on every push. Robots judge your code before it lands, the way every Python team works.
Deliverable — a red X you caused, understood, and turned green.
pytestCI/CDgithub actionssetup-pythonBriefing
Authored when you approach Arc 6.
-
Real packaging: pyproject metadata, a built wheel, pipx-installing your best tool on a clean machine. The optional TestPyPI upload is a public act and gates on your explicit go.
Deliverable —
pipx installof your own tool on a machine that has never seen the repo.packagingwheelspipxversioningBriefing
Authored when you approach Arc 6.
-
Design a build this course never imagined: spec it (architect), hand-write its riskiest core (writer), direct the agent through the rest, run your own candidate's Tryout ruthlessly (reviewer/debugger), and stamp it gold.
Deliverable — a working build of your own design, tagged v1.0, its spec and tryout notes in the repo. That's the diploma, and it runs.
everythingBriefing
Authored when you approach the capstone; the spec you can start daydreaming now.