scripts/fixtures/ holds the scripts that produced the 2026-11-11 fixtures, with a README on running them safely into a copy. Run in order they reproduce the content of every fixture they touch. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
101 lines
9.3 KiB
Markdown
101 lines
9.3 KiB
Markdown
# 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 one documentation commit that added this page; `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.
|