A build pipeline that turns the Reading Python exercise collection (9 stages, 214 items) into a static, gamified tutorial site, with memory diagrams generated by the University of Toronto's MemoryViz.
This repository is backend only. It produces a versioned JSON+SVG bundle; the front-end consumes it and is built separately.
content/source/*.md → dist/ (JSON + SVG, publishable to GitHub Pages)
| Parses | 9 stage files, glossary, answer-key conventions → 214 structured items |
| Executes | every code snippet under a per-line tracer → 2,956 memory states |
| Renders | 864 deduplicated MemoryViz SVGs |
| Derives | grading ground truth for 151 of 219 grading contracts, with no human authoring |
| Verifies | every answer key's stated output against real CPython |
| Models | XP, concept mastery, stage gating, spaced review, badges |
Full build: ~3.5 seconds, and byte-reproducible — two builds of unchanged input produce identical diagrams, traces and stage files.
Requires Node 21+ (memory-viz references the global navigator) and
CPython 3.11 specifically — not merely 3.11-or-newer. Execution order is
derived ground truth, and 3.12 changed it by inlining comprehensions (PEP 709),
so the build must match the version the collection's answers were verified on.
See docs/architecture.md §10.
npm ci # pins memory-viz 0.11.0 pip install -r backend/requirements.txt # pins python-ta 2.13.1 export PYTHONPATH=backend python -m pyviz.validate --root . # content checks; fails CI on error python -m pyviz.build --root . --out dist python -m pytest backend/tests -q # 74 tests
Useful flags: --no-render skips SVG generation (build drops to ~1s while
iterating on content); --stage 4 builds one stage.
The collection's own thesis, from its README:
Correct output alone is not evidence of understanding.
Two consequences shape the whole backend.
1. The interpreter is the answer key. Rather than hand-authoring expected answers for 214 items, the build runs each snippet and derives the ground truth. Predicted output, execution order, object graphs, block membership, snippet divergence, rewrite equivalence and bug repair are all computed from a real trace. 69% of grading contracts need no human input, and every one of them is checked against the answer it was derived from before it ships.
2. Mastery is not a score. A learner who answers "predict the output" correctly ten times has shown one skill, not a mental model. Mastery of a concept requires being right about it in two different exercise forms, and the stage gate reads error types — the collection's own four-way taxonomy — rather than a percentage. A learner passing on output-matching while consistently making object-model errors is held back, and told exactly that.
backend/pyviz/
models.py content + trace dataclasses; the whole wire format
normalize.py controlled vocabularies (forms, error types, form→grader map)
parser.py markdown → Collection; raises rather than partially extracting
concepts.py links items to a *shared* concept vocabulary (mastery keys)
tracer.py sys.settrace → MemoryViz entity arrays, per line
render.py MemoryViz SVG generation, content-addressed and batched
graders.py grader synthesis (build time) + grading semantics (reference)
mastery.py XP, mastery, gating, review scheduling, badges
store.py append-only progress log; ProgressStore protocol
authoring.py the human authoring surface for the remaining 68 items
build.py orchestrator → dist/
validate.py every check that can fail the build
tools/render_batch.mjs one Node process for all diagrams
backend/schemas/ JSON Schema for item, trace, manifest
backend/tests/ 74 tests
content/source/ the collection, treated as read-only
content/errata.json reviewed disagreements between the source and CPython
authoring/ supplements for prose and write-a-program items
docs/architecture.md design decisions and the front-end contract
dist/
manifest.json counts, coverage, gamification params, storage contract
collection.json stage index, concept catalogue, misconceptions, forms
stages/stage-01.json items: prompts, snippets, answer keys, grader specs
traces/<id>.json per-line memory states (schemas/trace.schema.json)
diagrams/<hash>.svg content-addressed; identical states share one file
authoring-gaps.json every item still needing a human supplement
manifest.json is the single source of truth for tuning constants — the
front-end reads XP weights, mastery thresholds, gate thresholds and badge rules
from it rather than keeping a second copy that drifts.
Running every snippet turned up two disagreements between the answer keys and
CPython, both recorded with evidence in content/errata.json:
- 4.3 — a real error. The key states
[[1, 99], [3, 4]]; appending to[1, 2]gives[[1, 2, 99], [3, 4]]. The surrounding prose about shallow copies is correct and unaffected. - C8.2 — not an error. The function-object repr is deliberately unpinned; recorded so the build knows the mismatch was reviewed.
The source markdown is never edited. An unlisted mismatch is a warning on every build, so the collection and the interpreter cannot silently drift apart.
design-kit/ is a self-contained brief: audience and tone, the screens to
design, the constraints that come from the teaching material, real interface
copy generated by running the graders, and real content to design against. It is
committed, so pointing a design tool at this repository is enough — there is
nothing to assemble and nothing to download.
Every file sits at the top level of that directory, so the folder also survives being uploaded to a tool that does not traverse subfolders.
Six of its files are derived from dist/; tools/make-design-kit.py refreshes
them and CI fails if they drift. docs/frontend-brief.md covers running the
handoff itself.
64 items need a human supplement — prose rubrics, token roles, and assertions for write-a-program exercises. They are enumerated and prioritised:
python -m pyviz.authoring status --root . # what's left, highest priority first python -m pyviz.authoring seed --root . # refresh stubs; authored work is preserved
Five worked exemplars are committed (authoring/rubrics.json → 1.16, C1.5;
token_roles.json → 1.1; code_properties.json → 1.13) to show the format.
Priority 1 is checkpoint items, because an unauthored checkpoint blocks a
learner's progress; a Stage 9 rubric only softens the last exercise of the
course.
Pipeline code: MIT. The exercise collection under content/source/ retains its
own authorship; MemoryViz is MIT, © David Liu and contributors.