Files
functional-programming-snake/docs/HANDOFF.md
T
2026-11-11 12:13:14 +01:00

101 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Handoff — Pure Snake course
Written 2026-11-11 for whoever works on this repository next. It assumes you know nothing about the earlier sessions. Read this page, then `docs/contract.md`.
## State in one paragraph
The course is complete and green. It teaches functional programming by building a purely functional Snake (then Pong, then Conway's Life) in three language tracks: JavaScript (the reference), Scala 3 and F#. Modules 0–13 plus an optional Module 7b exist as text, student stubs, reference solutions, tests and shared fixtures. The work was merged into `main` as `c67d81b` (feature commit `edd57f0`), followed by small commits that added this page and the fixture generators (`git log` is authoritative); `main` is pushed to `origin` (`https://gitea.lucaswinther.info/lucasw89/functional-programming-snake`). The feature branch `audit-and-extensions` was merged and deleted. Nothing is in progress.
**The one thing nobody has done: play the games by hand.** Every check is automated.
## Where things are
| Path | What |
|---|---|
| `README.md` | For students: what the course is, module map, checkpoints. "For maintainers" lists every check script. |
| `course/` | The text: `00-setup.md` … `13-capstone-life.md`, `07b-stack-safe-io.md`, `method.md`, `reference.md` (all names in three languages). |
| `<track>/src`, `<track>/solutions`, tests | Stubs, finished code, tests, for `js`, `scala`, `fsharp`. Same file names in `src` and `solutions`. |
| `fixtures/` | Shared JSON test cases, read by all three tracks. |
| `e2e/`, `scripts/e2e.sh` | Playwright end-to-end tests (maintainers only; the tracks have no dependencies). |
| `scripts/fixtures/` | The small scripts that generated the 2026-11-11 fixtures, the bot behind the replay sample, and a README saying how to use them safely. |
| `scripts/` | `verify.sh`, `check-stubs.sh`, `check-progression.sh`, `typecheck-js.sh`, `mutate.mjs` (+ `mutate-allow.txt`), `smoke.mjs`, `check-links.mjs`, `serve.mjs`. |
| `docs/contract.md` | **The canonical contract**: data shapes, every module's rules, constants, pinned values, fixture inventory, required checks. |
| `docs/superpowers/specs/` | `2026-10-28-…-design.md` (original design), `2026-11-11-audit-pass.md`, `2026-11-11-course-extensions.md` (twelve items, with the rules every builder followed). |
| `docs/superpowers/notes/2026-10-29-build-log.md` | Every decision made on the owner's behalf, verification results and open items, in three dated sections. |
| `docs/superpowers/notes/2026-11-11-session-log.md` | What happened on 2026-11-11, in order, and what was learned about working this way. |
| `docs/superpowers/plans/2026-10-28-pure-snake-course.md` | The original build plan. Its "contract" section is **superseded** by `docs/contract.md`. |
| `MENTOR.md`, `AGENTS.md`, `CLAUDE.md` | Instructions for an AI helping a *student* (Socratic mentor) and the pointer for maintainers. |
## Before you change anything
Run these from the repository root and expect them to pass. If one fails on a clean checkout, that is your first task.
```sh
scripts/verify.sh js && scripts/verify.sh scala && scripts/verify.sh fsharp # 728 / 689 / 691 tests
for t in js scala fsharp; do scripts/check-stubs.sh $t && scripts/check-progression.sh $t; done
scripts/typecheck-js.sh # needs network (npx typescript); exits 2 without it
node scripts/mutate.mjs # ~30 s; exit 0 = no surviving mutant outside the allow-list
scripts/e2e.sh # ~25 s after the first build; 176 tests, Chromium + WebKit
node scripts/check-links.mjs # online; two okmij.org PDFs sometimes time out and pass on retry
```
Environment on the machine this was built on: Node 24; `scala-cli` at `~/.local/bin/scala-cli` (fetches its own JDK and Scala 3.5.2); .NET SDK 10 at `~/.dotnet/dotnet` (Fable 5.24.0 as a local tool); Google Chrome at `/usr/bin/google-chrome`; Playwright 1.65.0 with Chromium and WebKit builds in `~/.cache/ms-playwright` (no Firefox; the e2e config skips missing browsers and never downloads). The first Scala and F# builds need the network and are slow. `scripts/serve.mjs` binds `127.0.0.1:8000` by default and takes a port argument.
## How a change is made here
1. **Decide the behaviour** and write it into `docs/contract.md` (and a spec under `docs/superpowers/specs/` if it is more than a fix).
2. **JavaScript first**: stub with its doc comment, solution, tests, fixtures (generated from the JS solution with a small script — see `scripts/fixtures/README.md` — then read by hand), course text.
3. **Port to Scala and F#**, touching only that track's directory. Use the same seeds and run counts so pinned values match; if a pinned value differs, the draw order differs — find out why before changing a test.
4. **Run every check above** for every track you touched, and the e2e suite for anything a page shows.
5. **Record** what you decided in the build log.
Rules that are easy to break:
- **Course text never explains an idea by reference to Scala** and assumes no FP background. Bridges come from everyday programming, in blockquotes starting `> **From everyday programming:**`. (Owner's rule, 2026-10-29.)
- Every exercise has signatures in all three languages, three folded hints (nudge, stronger hint, near-solution skeleton with holes — never the solution; capstones have two), a "Tests"/"Proved by" list, and "Check your understanding" questions.
- A stub must compile and fail per test, not at import or module load.
- `fsharp/src/Snake.fsproj` and `fsharp/solutions/Snake.fsproj` stay identical.
- Existing fixture cases are contract: do not change one to make something pass.
- Features arrive in the module that teaches them. A field or platform method may exist earlier but dormant (`frame`, `queued`, `onClick`), and the tests of earlier modules must pass for a student who has not built the later feature. `check-progression.sh` cannot prove that (it copies finished solution files), so check it by hand in a temp copy when you add something cross-cutting.
- F# on Fable does not wrap `uint32` arithmetic: all seed arithmetic goes through `Random.fs`.
- Expecto silently drops plain `testCase` items in a list that also has `for … do yield`; use `yield! [ for … ]` and compare test counts with the JS suite.
- In Scala and F#, a module tag must select only its own module (`./test.sh M07` must not run M07b). Both `test.sh` files handle this; keep it working when adding tags.
## How the owner likes to work
- Once a design is approved, carry it through spec, plan and execution **without extra review stops**, and report the decisions you made at the end.
- "Skip the questions" means: decide, and say what you decided.
- For large builds: the main session writes the specs and verifies; **Opus-class subagents do the coding** (one JS reference builder per wave, then one Scala and one F# porter, each confined to its directory, never two builders on the same files at once). This is how the twelve extensions were built.
- Commit, merge and push only when asked. Commit messages end with the co-author line used in the existing history.
- Reports lead with what was found and what is not verified.
## What is not verified
- **Play by a person**, in a real browser: whether speed and controls feel right, a real hidden tab, browser zoom with Life clicks, key auto-repeat, touch, Safari and Firefox proper.
- **The JS type errors as an editor shows them.** Only the `tsc` command line was run (TypeScript 7.0.2 and 5.9.3); the version is not pinned (`TS_PACKAGE` can pin it).
- **Rendered pixels and fonts.** The e2e suite compares draw calls with the model, not images.
- **Firefox** in the e2e suite (no build installed).
- After the last change (the stored-best rule in the three browser glue files) the Scala and F# *unit* suites were not re-run, because they do not compile the glue; the e2e suite exercised it in both.
## Known small gaps
- Scala lacks the JS test "play without a best answers LoadBest with 0".
- Skipping optional Exercise 4.7 (`State`) leaves red M04 tests unless the student copies the solution file; the text says so. Giving it its own tag, like 7b, would be cleaner.
- `check-progression.sh` and `check-stubs.sh` still carry a workaround for the old M07/M07b tag collision; harmless, could be removed.
- `web/Web.fsproj` does not list `State.fs`, `SafeIO.fs`, `Check.fs` (the browser does not need them).
- The Module 9 and 10 tests on `main` accept the program with or without the stored-best load, so a student between the two modules stays green; the exact sequence is pinned by an Exercise 10.7 test.
- Earlier open items still stand in the first section of the build log (e.g. `check-links.mjs` ignores reference-style links; the purity audits exclude the whole runtime file).
## Ideas not yet built
- A third capstone from `course/method.md`'s list (Tetris or Breakout).
- An auto-pause policy for long gaps instead of dropping time (Module 10, question 9 sketches it).
- Drag-to-draw in Life (needs pointer-move events).
- Exporting a recording from a played game, so the replay viewer can show the student's own game (needs a new `Op`).
- Visual regression snapshots in the e2e suite.
- Pinning the TypeScript version and running the type check in the editor matrix.
## If something looks wrong
Trust, in this order: the fixtures, the stub doc comments, `docs/contract.md`, the course text. The build log explains why things are the way they are; search it for the function or fixture name before "fixing" a surprising rule.