Files
functional-programming-snake/docs/superpowers/notes/2026-10-29-build-log.md
T

52 lines
5.4 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.
# Build log — Pure Snake course (2026-10-28 → 2026-10-29)
Record of how the course was built, the decisions made on the owner's behalf, and what is still open. Spec: `docs/superpowers/specs/2026-10-28-pure-snake-course-design.md`. Plan: `docs/superpowers/plans/2026-10-28-pure-snake-course.md` (its "contract" section is the canonical API; changes below override it).
## Owner decisions (from the conversation)
- Audience: CS bachelor's/master's students working alone at home, solo or with an AI as Socratic mentor (MENTOR.md).
- Everything purely functional, including UI and input: hand-built `IO` type and event streams; no "before"/stateful starter version.
- Tooling: plain JS ES modules, zero deps, `node --test`, canvas. Plus full Scala 3 and F# tracks with their own runnable test suites (shared JSON fixtures).
- Size unconstrained; reference solutions shipped in `<track>/solutions/`.
- 2026-10-29: **no Scala bridging** in the text — bridges start from general programming knowledge (`> **From everyday programming:**`); FP basics (map/filter/reduce, Option, pattern matching) are taught in Modules 1–2. The Scala *track* remains as a language option.
- Remote: `origin` = https://gitea.lucaswinther.info/lucasw89/functional-programming-snake (main pushed 2026-10-29).
## Rulings made during execution (decision — why — cost if wrong)
1. `scripts/serve.mjs` owned by Task 1 — duplicate ownership in plan — none.
2. `scripts/smoke.mjs` created in Task 8 (headless Chrome via CDP, zero deps) — no task owned it — rework smoke approach.
3. Subagent-driven execution without asking — owner's CLAUDE.md: no review stops after design approval — higher token spend.
4/16/17/19. Some implementers ran in parallel on disjoint files (F# track vs course text; Task 17 vs F# review) — wall-clock — commit races only.
5. Scala fixture helper is `scala/test/Fixtures.test.scala` — scala-cli scoping — none.
6. Typed fixture decoders added with the Scala/F# ports — none.
7. `js/test.sh`; JS runs `node --test "test/*.test.js"` — Node 24 rejects dir arg — none.
8. Small adjacent tasks batched into one implementer + one review — overhead — larger review surface.
9. `flatMap(Pure(a), f)` evaluates `f(a)` eagerly at construction; explained in Module 7 — unobservable for pure f — a later laziness lesson would need a Suspend case.
10. `flatMap` not stack-safe for ~100k left-nested binds; Module 7 discusses trampolining — game uses right-nested `sequence` — students' long flatMap loops overflow.
11. With a fully stubbed tree, m09 tests fail at import — students reach m09 with io done — noisier output if skipping ahead.
12. Pre-port JS polish: `pongKey` accepts W/S/R too (contract tweak), explicit held-key mapping, shared `classicMain` test helper, stronger no-mutation test.
13. Added `scripts/check-progression.sh <track>`: for N=1..11, solutions for modules ≤N + stubs for the rest must pass M01..MN — caught a real "test helper imports src" bug.
14. Scala/F# stream input type is `Event` (Time/KeyDown/KeyUp), not `Input` — name clash with the Input module.
15. F# idioms: `bind` (flatMap), `choose` (filterMap), PascalCase record fields; `IO.randomSeed ()`, `App.main ()`, `pongMain ()` are unit functions so stubs fail per test.
18. Module 2 guiding questions may hint at the design (e.g. "why two directions?") — intended scaffolding — design exercise slightly easier.
20. F# mulberry32 routes uint32 `*`/`+` through uint64 helpers because Fable doesn't wrap uint32 arithmetic; Module 4 warns students — no automated Fable-vs-.NET drift check.
## Verification status (last full run 2026-10-29, before the docs-only Scala-bridge rewrite)
- `scripts/verify.sh js|scala|fsharp`: 383 / 361 / 362 tests pass against solutions (JS re-run after the rewrite: 383 pass).
- `scripts/check-stubs.sh <track>`: M01–M11 fail against stubs in all tracks.
- `scripts/check-progression.sh <track>`: all checkpoints pass in all tracks.
- `scripts/smoke.mjs`: all 6 pages (snake + pong × 3 tracks) paint with no console errors when built from solutions.
- `node scripts/check-links.mjs`: 103 external links + all relative links OK.
- Not done: a human playing the games end to end.
## Open follow-ups (judged OK to leave by the final review)
- cats-effect / ZIO / fs2 still mentioned as "In production" comparisons in Modules 7–9 (owner may want them cut after the no-Scala-bridging change).
- No automated guard that the Fable (F# browser) build reproduces the RNG fixtures.
- Module 11 hint 11.4 gives away the stream `merge` shape; some near-solution hint skeletons are thin.
- check-links.mjs ignores reference-style/HTML links and bare `(url)`; no 429 retry; soft-404s pass.
- Purity audits exclude the whole runtime file; JS comment stripper cuts at `//` inside strings; F# `printf` ban misses `eprintfn`/`Console`.
- Two Pong unknown-key fixture cases are vacuous in Scala/F# (unrepresentable by the types); m10 progression checkpoint adds no files for Scala/F#.
- `interval` with a backwards `Time` re-emits; `history` is O(n²); `deepFreeze(obj, ancestors=[])` breaks if passed directly to `.map`.
## Environment notes
- scala-cli installed at `~/.local/bin/scala-cli` (downloads its own JVM 17); dotnet at `~/.dotnet/dotnet`; Fable 5.24.0 via `fsharp/.config/dotnet-tools.json`; headless Chrome at `/usr/bin/google-chrome`.
- First `scala/build-web.sh` / `fsharp/build-web.sh` run needs network (Scala.js linker / Fable restore). `scripts/serve.mjs` binds 127.0.0.1:8000; run from the repo root.