Architecture
The short version, for a contributor who wants the shape of the system without reading every source file. For full depth, the repo's own docs/ carries the detailed versions: design.md for pedagogical framing, system_design.md for mechanics, deep-dive.md for a step-by-step trace of what actually executes.
From a module to a real, importable symbol
Each module has two forms of the same content:
data/src/NN_name/NN_name.py: the source of truth. This is what gets edited, whether by a maintainer authoring the curriculum or (indirectly, via the generated notebook) a student solving it.data/modules/NN_name/: a stub notebook, generated from the source via Jupytext, with solution cells stripped. This is what a student actually opens and edits in Jupyter.data/solutions/NN_name/: the fully-solved reference notebook, gitignored, maintainer/CI-only. Exists so the curriculum's correctness can be verified independent of any student's in-progress work.
tren module complete runs a four-step pipeline against whichever notebook is relevant (a student's data/modules/ notebook, or, during maintainer curriculum verification, the data/solutions/ reference):
- Unit tests: runs the module's own tests against the source file directly, no export needed yet
- Notebook syntax check: catches a syntax error in the student's notebook before it can produce a broken export
- Export:
nbdevturns the notebook's#| export-tagged cells into real files undertrentorch/, following each module's#| default_exptarget - Integration tests: runs against the just-exported package, the first point that imports from
trentorchrather than the raw source
Only complete actually updates what a student can import; tren module test runs the same checks without exporting.
The CLI itself
platforms/cli/ is split by what it's for, not by file type:
cli_platform/: the CLI bootstrapping itself: setup, system maintenance, dev/CI toolingprocesses/: what a student does once inside the workflow: the module loop, milestones, benchmarks, conversion
The entry point is tren = platforms.cli.main:main (see pyproject.toml), wired up by bin/tren.
CI pipeline
.github/workflows/validate.yml runs the whole curriculum on every push, as seven stages:
| Stage | What it checks |
|---|---|
| 1 · Inline Build | Builds the package from data/src/ progressively, module by module: the maintainer's curriculum-correctness check |
| 2 · Unit Tests | Per-module unit tests |
| 3 · Integration | Cross-module integration tests |
| 4 · CLI Tests | The tren CLI's own test suite |
| 5 · E2E Tests | End-to-end user journey tests |
| 6 · Fresh Install | A Docker-based simulation of a brand-new student cloning and installing from scratch |
| 7 · User Journey | Every milestone, run against Stage 1's already-built package, then a real tren system reset verified to actually clear state |
Stages 2 through 5 and 7 only depend on Stage 1's build artifact, not on each other, so they run in parallel rather than in a chain.
Why the pipeline is fast
As of the 2026-08-27 optimization pass, the full pipeline runs in well under 4 minutes, down from over 8. The short version of how:
- Found and fixed a real bug, not just a slow spot: completing certain modules during automated curriculum verification was silently triggering a full, unintended milestone run, a correctness bug that happened to also be the single biggest time sink.
- Matched test scope to what's actually being verified. TrenTorch's Conv2d is deliberately naive (nested Python loops, that's the pedagogical point), so several tests were running it at full MNIST/CIFAR scale just to check output shapes, which don't depend on input size at all. Shrinking those inputs lost no real coverage.
- Made CLI startup lazy. The CLI was importing every command's dependencies (including NumPy and the framework itself) on every single invocation, regardless of which subcommand actually ran. Deferring those imports to where they're actually used cut CLI startup time by roughly 3x.
- Right-sized one real training demo under CI. A milestone's default demo trains for 50 epochs to show a convincing curve to a human; CI only needs to confirm it runs end-to-end, so it uses 5 epochs there while a real student still gets the full 50.
- Ran Stage 1's per-module loop in-process instead of spawning two fresh Python subprocesses per module.
Full detail, fix by fix, is in maintainer_use/CHANGELOG.md.
Where to read next
docs/design.md: the pedagogical framing and what this fork deliberately removed from upstreamdocs/system_design.md: dependencies, data flow, error handlingdocs/cli_file_organization.md: a map ofplatforms/cli/by file, with the gotchas