diff --git a/docs/superpowers/notes/2026-10-29-build-log.md b/docs/superpowers/notes/2026-10-29-build-log.md new file mode 100644 index 0000000..ae66226 --- /dev/null +++ b/docs/superpowers/notes/2026-10-29-build-log.md @@ -0,0 +1,51 @@ +# 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 `/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 `: 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 `: M01–M11 fail against stubs in all tracks. +- `scripts/check-progression.sh `: 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.