ADR 008. Golden program tracking: what runs on nanopython¶
- Status: In Progress. Tooling (
tools/golden_bootstrap.py) and directory skeleton landed; populated buckets tracked ingolden-programs/. - Decider: project maintainers
- Supersedes: nothing
- Consequences: gives users + contributors a filesystem-level answer to "what programs run on nanopython"; turns "should we add
heapq/bisect/random?" from a vibes question into a numbers question (each feature unlocks N specific programs); coexists withcorpus/wasm-known-fails.txtandmake check(different disciplines, different questions); adds ~3 MB of.pyfiles to the repo (acceptable).
1. Context¶
ADR-001 made WASM the primary distribution target. ADR-003 committed to MicroPython compatibility on .mpy bytecode, language semantics, and the extension protocol. ADR-004 set up benchmarking. ADR-005 mapped the optimization landscape; ADR-007 picked up the size branch of that map.
The cross-cutting question none of those address:
"As a web developer evaluating nanopython, what Python programs will actually run? And if my program doesn't, what feature is missing to make it run?"
Today the project has three artifacts that touch this question without answering it:
corpus/nano/basics/*.out: 541 byte-exact goldens. Measures strangler discipline (does our strangled code match vendor MicroPython exactly?). The right gate for its purpose; the user's question requires a different gate.corpus/wasm-known-fails.txt: wasm-specific regression gate, 31 known-failing tests today. Documents what fails; the inverse view (what works) needs a separate artifact.- The 4 program collections under
sandbox/(prescrypt-internal, prescrypt-tryalgo, prescrypt-micropython, p2w-internal): 829 real Python programs. Nobody can tell at a glance which of them run on nanopython.
This ADR builds the missing answer.
1.1 What "running" means¶
A program "works on nanopython" when its output matches what vendor MicroPython (the project's reference implementation, sandbox/micropython/ports/unix/build-standard/micropython) produces. The strangler's contract is "match MicroPython behavior"; that's the natural oracle.
CPython is recorded as a secondary check. When MicroPython and CPython disagree, the program lands in disagreement/ for explicit human triage: a deliberate conversation surface that defers the bucket decision until the project commits to one side.
1.2 The choice of oracle: MicroPython over CPython¶
Two reasons. First, the strangler's contract is MicroPython-compatibility (ADR-003); using CPython as the primary oracle would conflate "works on nanopython" with "matches CPython," and the latter is a strictly bigger ask. Second, MicroPython is the realistic surface: a program that requires CPython-only features (full dataclasses, numpy, asyncio) falls under §1.4 below as out of scope; tracking it would noise the report.
1.3 What "in scope" means¶
In-scope: programs that nanopython could reasonably aim to run: pure Python, stdlib stdlibs, simple algorithms, basic types, classes, decorators, common idioms.
Out of scope (filtered at bootstrap): programs requiring third-party libraries we have no path to (numpy, scipy, pandas, flask, django, fastapi, jinja2, requests, the full dataclasses feature set, pytest, unittest, asyncio). Adding these is a multi-month port each; if a use case appears we can revisit, but tracking them today produces noise.
1.4 What changed since ADR-003¶
ADR-003 placed the frozen-tiny boundary based on what each demo imports. This ADR's needs-stdlib/ bucket measures the SAME thing from a different angle: what would a typical algorithm program need to run. The two intersect:
- ADR-003 frozen modules (
_thread,js,json) ship with every demo. golden-programs/needs-stdlib/lists every program that would graduate toworks/if we shipped one more module.
The intersection is the natural prioritisation signal for "what's the next module to add."
2. Decision¶
Create golden-programs/: a curated directory of real Python programs that we explicitly want nanopython to run, organised by profile (the minimal feature bundle that makes each program work). Run them against vendor MicroPython AND nanopython native AND nanopython wasm. Generate (eventually) docs/COMPATIBILITY.md from the result.
2.1 Layout¶
golden-programs/
README.md — what this directory is, how to add programs
profiles.md — feature bundles & their semantics
works/ — runs correctly on current nanopython (native AND wasm)
works-native-only/ — runs on native but not wasm (e.g. wasm-specific gaps)
needs-stdlib/ — would run with pure-Python stdlib stubs (heapq, bisect, …)
needs-grammar/ — would run with parser features (walrus, match)
needs-async/ — would run with async/await
needs-fs/ — would run with file-system access
needs-mpz/ — would run with arbitrary-precision integers (already exists empty on main as a placeholder)
disagreement/ — MicroPython and CPython disagree on the program's behavior
Each program is in exactly one bucket: the profile it minimally needs to work. A program that needs both heapq AND async lands in needs-async/ (the strictly larger feature requirement). The principle: bucket names answer "what profile do I need to ship?"
2.2 Annotation¶
Inline header comment in each .py file:
# golden: source=tryalgo; needs=heapq,recursion-depth-tolerant
# golden: works-on=micropython,nanopython-native,nanopython-wasm
"""Dijkstra's shortest paths algorithm."""
Format: # golden: KEY=VALUE; KEY=VALUE; … on contiguous lines. Keys:
source: where the program came from (tryalgo,prescrypt-internal, etc.)needs: features it depends on (auto-detected from imports + grep; can be manually adjusted)works-on: verified by running; auto-maintained by the bootstrap / run toolsmicropython-cpython-divergence: explicit note when MP and CPython differ
Annotation is auto-bootstrapped from imports and known feature patterns (async def → needs=async; import heapq → needs=stdlib.heapq; etc.), then committed. The tool detects drift and proposes updates.
2.3 Deduplication¶
The 4 source corpora have filename overlap (e.g. sort_advanced.py appears in both prescrypt-internal and p2w-internal). Programs are deduplicated by SHA-256 content hash at bootstrap; the surviving copy keeps the lexicographically-first source name in its annotation. On slim-wasm this reduced 825 raw files to 823 unique programs.
2.4 Relationship to existing infrastructure¶
| Artifact | Question | Decision |
|---|---|---|
corpus/nano/basics/*.out + make check |
Does the strangler match vendor byte-exactly? | KEEP: different discipline (regression detector) |
corpus/wasm-known-fails.txt + make wasm-check |
Does main's wasm pass the corpus subset that wasm32 is expected to pass? | KEEP: wasm-specific regression gate |
golden-programs/ (this ADR) |
What programs do users actually run on nanopython? | NEW: strategic source-of-truth |
These three layers answer three different questions. The layers are complementary.
3. Implementation plan¶
Phase 1. Skeleton + bootstrap¶
- Create
golden-programs/withREADME.md,profiles.md, and the empty bucket dirs. - Write
tools/golden_bootstrap.pyper the design. - Run the bootstrap once; commit the resulting bucket assignments.
Phase 2. Run + matrix¶
- Write
tools/golden_run.py: re-runs every program against three oracles, updatesworks-on=annotations, detects drift (a program now in the wrong bucket). - Wire
make golden(rebootstrap) +make compat(rerun + report) to the pipeline. - Generate
docs/COMPATIBILITY.md: the user-facing matrix + "what feature would unlock most programs" ranking.
Phase 3. Iteration¶
- As features land, programs migrate from
needs-X/toworks/. The tooling proposes; humans review. - The "what unlocks most" report drives prioritisation conversations: "Adding
heapqwould unlock 12 programs. Is the algorithm/data-science use case in scope?"
4. Consequences¶
Wins:
- Users (and prospective users) get a single, browseable answer to "what runs on nanopython."
- Feature decisions get a quantitative signal: "
heapqunlocks N programs across tryalgo + M internal; yes/no decision is now grounded." - Contributors can pick a
needs-X/bucket and see exactly what landing X does for users. - "Do we really need this for web apps?" conversations are grounded in
golden-programs/content rather than abstractions.
Costs:
- ~3 MB of
.pyfiles committed. Acceptable. make goldenruntime grows with corpus size; expect ~30s for ~800 programs.- Annotation discipline needs maintenance: when a feature lands, programs migrate. Tooling helps; manual review remains part of the loop.
- Some programs sit in
disagreement/for a while as the project collectively decides on MP-vs-CPython semantics. That state is preferable to silent bucketing.
What this preserves:
- The strangler discipline.
corpus/nano/basics/*.outbyte-exact gate remains the invariant. - The WASM regression gate.
corpus/wasm-known-fails.txtremains the invariant. - ADR-003's compatibility commitment.
golden-programs/measures from the user's perspective; the strangler's contract is unchanged. - ADR-001's WASM-primary positioning. The directory just makes the position more legible.
5. Open questions¶
works-native-only/retention. When a program fails on wasm because of a wasm-specific limitation (longjmp, smallint width), should it sit inworks-native-only/indefinitely, or migrate to aneeds-X/bucket where X names the specific blocker? Default position: keepworks-native-only/simple (one bucket for "everything that works native but not wasm"); add more granular split only if the bucket grows past ~50 programs.- Disagreement resolution policy. When MicroPython and CPython diverge on a program, the default is to follow MicroPython (per ADR-003's compatibility commitment). Cases where MP is plainly wrong remain open; the
disagreement/bucket surfaces them for explicit per-case decisions. - Maintenance cadence. Run
make golden(rebootstrap) on every commit, weekly, or only when a feature lands? Default: on demand, plus automated drift-check in CI.
6. See also¶
golden-programs/README.md: user-facing entry pointgolden-programs/profiles.md: feature bundle definitionstools/golden_bootstrap.py: the bootstrap tool- ADR 001: WASM-primary positioning that this ADR's
works-on=nanopython-wasmannotation tracks - ADR 003: frozen-tiny stdlib boundary, intersected by
needs-stdlib/ - ADR 007: wasm fat loss;
golden-programs/shows which user programs benefit from each fat-loss technique - The
corpus/wasm-known-fails.txtdiscipline that informs the wasm gate; together they answer "what works" from two angles.