The credits page's two <style> blocks and the Learning Resources panels' inline
colors now live in style.scss as classes. The dark theme overrides them
directly, which replaces the attribute-selector patches and the body-prefixed
specificity workarounds.
Shared modules replace per-site copies:
- _announcement.scss / _dark-announcement.scss: the portal banner, light and
dark, in place of seven duplicated blocks. _announcement-lines.scss splits
the banner into one line per row and drops the companion line under 600px
(196px down to 93px on a phone; the textbooks go from 238px to 147px).
- _dark-secondary-nav.scss: the mobile breadcrumb bar no longer stays white.
- _dark-footer.scss, _dark-callouts.scss (kits and labs now import it),
_dark-tabs.scss.
Sites that had never been audited are brought up to the same bar: slides,
site (about, community, newsletter, games), MLSys-im and TinyTorch now use the
shared code and caption modules and fix their own light-only components (deck
cards, crimson links, filled buttons, game plates, credits, tabs, headings).
Audit at 1400px and 390px: all four volumes, instructors, kits, labs, slides,
site, MLSys-im and TinyTorch report no issues.
The page said five demos and claimed 44/44 unit tests, a count the video never
shows. Demo 02 predated the test-name fix and showed AutogradCore and
Module 06Completion.
Release-hardening and transformer-era release of TinyTorch, audited as one
tree. This commit brings tinytorch/ and the TinyTorch CI workflows to the
state of the audit/tinytorch-release branch, where the individual changes
are recorded commit by commit.
- Milestones: transformer-era parts (TinyGPT, sequence routing, TinyCopilot,
conversational Q&A), Milestone 06 "MLPerf to Generative Serving (2018)",
Milestone 07 kernels; per-part recording; NumPy-referenced losses and
causality, attention-content and position probes; try-it prompts after a
pass.
- Framework: dropout eval mode, target gradients, float32 stays float32,
layer validation, LoRA adapters.
- tito: loopback login callback with a state nonce, consented progress sync,
TLS-verified update check, Ctrl-C after a pass keeps the pass.
- Grading: graded tests compare values against NumPy references; tests that
could not fail now check something.
- CI: the publish preflight runs its tests and ships the commit it tested;
workflow inputs pass through env; third-party actions pinned.
- Guide and book: read line by line against the code; every demo is a real
recording.
See tinytorch/CHANGELOG.md, section 0.2.0.
On Milestone 01's separable points a random line usually lands near 0% or
100%. A 0% run was labeled 'Random Guessing!' and the header promised
~50%; 0% now reads 'Boundary points the wrong way!' with a diagnosis that
explains the swapped sides, and the header expectations say 0% to 100%.
The Quick Start capture is re-taken from a real run with the new label.
The dataset prompt treated a missing terminal as 'cancel' with no hint;
it now says why nothing downloaded, and TINYTORCH_AUTO_DOWNLOAD=1 answers
yes for scripted runs.
The four learning-path cards on About the Course compared paths across
boxes, and their label/value rows sat on different baselines. They are
now one table (path, modules, time, who it suits) that stacks each row
on phones. The note under it no longer says you can "switch paths
anytime": tito completes modules in numeric order, so it says a focused
path still passes through the modules in between.
The Glossary returns to the Reference section of the sidebar; Quick
Start still links to it for newcomers.
- Code blocks go back to wrapping: with scroll, trailing `# comments`
were cut off at the right edge with no cue, worst on phones. The
module command blocks already put comments on their own lines.
- Home page on phones: comparison cards stack instead of widening the
page to 531px, and the timeline is one left-aligned column.
- Dark mode: the home lead paragraph was hard-coded dark gray; the
shared navbar stays light, so its title is kept dark; the sidebar logo
gets a light plate so "TORCH" stays visible.
- Tables use smaller type on phones; definition-list entries
(Troubleshooting) get space between them.
- Credits printed a Markdown link as raw text inside an HTML block;
"1998." and "2018." paragraphs on Milestones 04 and 06 rendered as
ordered lists; "** Foundation Tier**" showed literal asterisks; the
Optimization page's nested bullets were flattened and it named its
cycle three ways; Datasets field lines ran together.
- Names: Quick Start is titled Quick Start; "TinyTorch Olympics" is
"Torch Olympics"; Community's educators section points to For
Instructors; milestone pages carry their title in front matter so
their contents list is not nested under it; the Glossary's A-Z
headings appear in its contents list.
- tools/diagrams/STYLE.md, an internal style guide, is no longer
published into the site and its search index.
- Module 01 links Module 02 from What's next.
- Glossary: opens with a "Tools and course terms" section (terminal,
virtual environment, activate, tito, Jupyter Lab, notebook, cell, the
two meanings of kernel, module, export, unit and integration tests,
milestone, Binder), adds weight, bias, and parameter, gives
broadcasting a two-line example, drops the Lab Guide wording, moves
into Getting Started in the sidebar, and is linked from Quick Start.
The newcomer reviewer found none of the tool words it stumbled on.
- Module 01's lead ("saturate the ALUs or stall on cache misses") told
the first page a beginner reads that they were underqualified; it now
says what a tensor is and what they will write.
- Figures on 02, 05, 06, 07, 08 carried a one-line caption inside the
image above the real caption (two of them overclaiming: "correctly
accumulating", "guarantees mathematical equivalence"); removed, canvas
trimmed, renders checked. 05's "drop_last drops remainder" labeled an
option TinyTorch's DataLoader does not have.
- Top of page: the tall audio and Binder cards become one compact row
(the Overview heading moves up about 150px); the Binder button says
the session is discarded when you leave.
- Commands: each command's comment sits on its own line so the block
survives a phone screen; the four steps `complete` runs move into
"How you know it works".
- How you know it works: unit tests and integration files are lists
(two columns on wide screens), and test names are escaped, since two
`__init__` names on Module 19 were rendering as bold "init".
- What you write: a stacked definition list instead of a two-column
table that clipped on phones.
- Headings in sentence case on all 20 pages; one "What you're not
building yet" label; H3/H4 a clearer step down from H2.
- Code blocks scroll instead of soft-wrapping, and the copy button no
longer covers the first line.
- Slides viewer: on a load failure the spinner stops and a plain
download link remains.
- Module 20: one name (Torch Olympics), Capstone in its info line, and
the Olympics section moved ahead of the troubleshooting sections.
- 13 and 20 say "module" for site pages; 15's lead sentence reworded.
About the Course opened with a screenshot of an older tito whose
welcome panel listed `tito system doctor` and `tito milestones status`,
neither of which exists now; a learner reviewer copied the first and
would have failed on step one. It is replaced by a capture of the
current `tito` (guide/tools/capture-terminal.py).
Capturing it exposed a CLI bug: the welcome panel padded commands to a
fixed width including their Rich markup tags, which differ in length
per section, so the Quick Start rows lost the description column.
Padding now uses the visible width.
The Milestone 01 figure did not match what `tito milestone run
perceptron` prints (a six-point "Synthetic Data & Decision Boundary"
step that no longer exists), and nothing in the repo could regenerate
it. guide/tools/capture-terminal.py runs a command with color forced
on and renders its exact output with Rich's SVG exporter, headed by the
command; --lines keeps unedited ranges and marks what was left out.
The new figure is an excerpt of a real run in a scratch checkout where
Modules 01-03 were completed with `tito module complete`. The run is
unseeded; the caption says so and that a run occasionally guesses right
by luck. The PNG is replaced by the SVG.
Every module page now has the same shape: Overview, Start and finish,
Learning Objectives, What You'll Build, What you write, How you know
it works, When it fails, Finished? Read why, What's Next. Pages drop
from 530-760 source lines to about 110.
Four sections are generated from the repository at render time:
- module-checks: the unit tests the notebook runs (by their headings
in src/), the integration files `tito module complete` selects
(same rule as tito), and the milestones this module unlocks (parsed
from tito/commands/milestone.py).
- book-chapter: the companion chapter by its real title, with the PDF
link and a note that the book prints reference solutions.
- stub-failure: what a bare NotImplementedError means.
- (from earlier) module-workflow and what-you-write.
"When it fails" lists at most three messages per module, each one
reproduced by injecting the named bug into src/ and running that
module's unit tests; none is paraphrased.
Removed from every page: Core Concepts, API Reference, Production
Context with its PyTorch tabs, Check Your Understanding / Self-Check,
Key Takeaways, Further Reading (now in the book), Get Started (it
repeated the Binder link and linked the full solution), and the
implementation-roadmap tables, which listed pre-solved scaffold as
student work. The three optimization-tier narrative sections
(14, 17, 18) are kept aside for the tier hub.
- Drop the "View Source" card. It linked straight to the module's
src/*.py, which holds the full reference solution.
- Fold the slide deck into a collapsed "Lecture slides" section; the
PDF is fetched only when a reader opens it instead of on every load.
- Button labels were invisible: the theme's link color overrode the
white text, leaving orange on orange.
- Dark mode: the shared heading partial colors #quarto-content h4 navy
in both themes, so card titles and body h4s were unreadable.
"28,106 stars on GitHub" is injected by inject_stats.py from
site/config/stats-cache.json at render time, so it is right when the site
is built and never again.
The main site handles this: site/index.qmd re-fetches on every page load,
both from stats.json (refreshed to gh-pages every six hours) and directly
from the GitHub API, with the comment that the star count "should track
the repo to the second rather than to the last scheduled refresh".
This site got the first half only. It emits the same
data-stat="stars" hook on two elements of the landing page but shipped
nothing to hydrate them, and it never reads stats.json at all, so a
CDN-cached page served the build-time number indefinitely. The committed
cache was generated 2026-09-14 and read 28,106; the API returned 28,258
today, 152 behind after three days.
The star count is public, so this fetches it directly rather than waiting
on a refresh cycle this site does not consume. On rate limit, offline or
a blocked request the rendered value stays, so the page always shows a
real number.
Verified in a browser: both elements read 28,106 at first paint and
28,258 after hydration.
assignments/release/ is the directory students actually receive, and
nothing ever looked at it. NBGRADER_RELEASE_TIERS.md specified this
check ("Pass 4: Add Release Validation") and it was never built.
Three separate paths have shipped the reference solution by accident:
Binder's postBuild, `--tier challenge`, and `nbgrader generate` writing
the student notebook path. Each was fixed at its source, but each was
also invisible until someone opened a notebook. `tito nbgrader release`
now reads back every notebook it just produced and fails if any still
contains a BEGIN SOLUTION region, naming the files.
Verified both directions against a planted release tree: a notebook with
a solution region exits 1 and is named; a notebook carrying only
nbgrader stubs exits 0.
Also normalizes the last diagram, 09_convolution-backward-route.svg. It
escaped the earlier pass because it strokes at 1.5 rather than 1.2, so
the grep missed it. All 145 site SVGs now carry the house border colour
and rx=2; re-rendered and inspected.
Three of the 109 current-generation diagrams carry a caption baked into
the image; fourteen of the twenty-five older ones do. Quarto already
prints a caption underneath, so the reader got the same voice twice in
two typefaces, and the canvas carried dead space to hold it.
Eight of those footers were safe to identify: a single #D1D5DB rule in
the trailing stretch of the file, with only text after it. Their text is
appended to the page's fig-cap before removal, so nothing is lost --
several carried qualifications the caption did not have, such as the
stride/padding/bias of the convolution example and that the
cross-entropy decimals are rounded.
The other six are not touched. In those, #D1D5DB is an internal grid
line rather than a footer rule: in 16_neuron-removal it appears on line
5 of 102. Treating it as a separator would have deleted the diagram's
own labels. They are also book-only files with no guide page to hold a
caption.
Canvases are re-sized to the content. The first attempt measured rect y
without adding rect height and clipped the bottom row of
01_tensor-diag-1; caught by rendering, which is why every one of the
eight was re-rendered and inspected after the fix.
Two generations of art shipped side by side. 109 diagrams follow the
current look; 25 predate it and read noticeably heavier on the page.
The tell is the box border. Across the 109 compliant diagrams, #9ca3af
is the dominant stroke (348 uses) and #111827 never appears as a stroke
at all. The older 25 outline every box in #111827, which competes with
the #ff8246 accent instead of letting it lead.
Repointed their strokes at #9ca3af and set rx=2, which STYLE.md
specifies and the compliant set agrees with (382 uses of rx=2). Content,
layout, text ink, and arrowheads are untouched: #111827 text and
arrowheads appear throughout the compliant set too, so neither is a
generation marker.
Deliberately not changed, because the house does not actually follow
these two lines of STYLE.md:
- "at most one accent per diagram" -- 46 of the 109 compliant diagrams
use more than one. Orange is a semantic highlight here, not a single
focal point, so enforcing the rule would make these 25 less consistent
with the rest, not more.
- arrowhead and text colours, per above.
All 25 re-rendered and parse clean.
Three separate overclaims, each checked against the code that would have
to produce the number.
1. --tier challenge emits the full reference solution.
solutions.py::solution_role_action strips a region under the challenge
tier only when it carries role="challenge". src/ contains zero such
annotations (236 regions: 189 scaffold, 6 core, 41 unannotated), so the
tier is a no-op. apply_release_tier output for challenge is byte-identical
to instructor, verified directly.
The code matches its design in NBGRADER_RELEASE_TIERS.md and the
assertions in tests/cli/test_release_tiers.py, so this is unfinished
annotation work, not a code defect, and the tier semantics are left
alone. But an instructor running the command we document gets a fully
solved "challenge" assignment, so `tito nbgrader generate --tier
challenge` now refuses when no region in the requested modules is marked
role="challenge", and says what to annotate.
Note for the author: INSTRUCTOR.md described challenge as "All 236 Blocks
(full implementation from scratch)", which is a different design from
NBGRADER_RELEASE_TIERS.md's "keep for student baseline, strip for
challenge". Those two need reconciling before the tier is annotated.
2. The student tier strips 47 regions, not 41.
Unannotated regions default to core (solutions.py::solution_role), so the
student tier strips 41 unannotated plus 6 explicit role="core" = 47, and
leaves the 189 role="scaffold" regions solved. The documented 41/195 split
is 236 minus 41, which silently reclassifies those 6 core regions as
scaffold. Corrected in getting-started.qmd, tito/overview.qmd, and
INSTRUCTOR.md.
3. The home page advertised optimization gains the milestone disclaims.
index.qmd promised "8-16x smaller, 12-40x faster". milestones/06_2018_mlperf
says compression and speedup "are outcomes to measure, not benefits the
script assumes", that it "does not certify a deployment target or
guarantee a fixed compression/speed" ratio, and that a small workload "may
become slower". The repo's own quantization ceiling is 4x. Reworded, along
with the same claims in ml-timeline.js, whose module range 14-18 was also
wrong (14-19).
Also: the CIFAR-10 milestone target was published as "75%+" in seven
places. milestones/04_1998_cnn states 65-75%, its README expects 70%+, and
02_lecun_cifar10.py passes at >= 65. Corrected to the stated range.
And `tito system jupyter` is documented as starting JupyterLab; it starts
the classic notebook unless given --lab.
Found while driving the site with Playwright to check the TITO CLI page.
ml-timeline.js appended #ml-timeline-popup to document.body on every page
load, whether or not the page had a timeline. Its closed state is
`display: flex; opacity: 0` with no pointer-events rule, so it sat as a
full-viewport, z-index 1000, hit-testable box over the content:
elementFromPoint at the middle of the page returned the overlay rather
than the article. Every click on 52 of the 62 pages went into it,
including the home page. The script set display:none only when *closing*
a popup, so a page became usable after opening and dismissing one.
Fixed in two layers, because either alone would have prevented it:
- style.scss gives the closed overlay pointer-events: none, restored to
auto on .active. Invisible now means non-interactive.
- ml-timeline.js returns early when a page has no .ml-timeline-content,
so the overlay is built only on the one page with a timeline.
Verified the feature still works on index.html: six cards, click opens
the popup, Escape closes it, page is clickable afterwards.
Also reworks "Commands by User Role". Three code blocks in a 3-up card
grid left each command roughly 220px, which wrapped
`tito nbgrader generate 01 --tier student` between the flag and its
value. A panel-tabset gives each role the full column width; every line
in all three tabs now fits without wrapping, verified by measuring
rendered height against line count.
And `tito module complete 03 --verbose` in troubleshooting.qmd exits 2,
because --verbose is global and belongs before the subcommand.
guide/tools/check-documented-commands.py now walks tito's real argparse
tree and checks every `tito ...` invocation in the .qmd sources for
unknown subcommands, unknown flags, and invalid values for flags that
declare choices. It re-execs under the project venv when the calling
interpreter lacks tito's dependencies, and turns a skip into a failure
under CI so the gate cannot quietly stop running. Wired into pre-commit.
The website diagrams in guide/assets/images/svg/ predate the current
visual standard in guide/tools/diagrams/STYLE.md. They used flat
#111827 boxes at rx=3, L-shaped connectors the spec forbids, captions
baked into the image that duplicate the Quarto fig-cap, and canvases
sized well past their content.
Rebuilt all 11 to the spec: 680 viewBox sized to content, greyscale
palette with at most one #ff8246 accent, rx=2, uniform 1pt strokes,
straight H/V connectors only. Three carried real defects beyond style:
- tito-progress-tracking had a box no arrow reached, and otherwise
duplicated tito-module-workflow. Redrawn around .tito/progress.json
and the two commands that read it, which is what its page is about.
- tito-test-hierarchy claimed increasing scope and drew six identical
boxes. Now a widening stack.
- The optimization fan-ins bent four arrows into a bus. The target is
widened to span its inputs so every arrow drops straight down.
Removing the in-image captions exposed that fig-gpt-architecture never
had a Quarto caption, so it rendered a bare "Figure 1"; its prose also
described the stack bottom-up while the figure reads top-down. Both
fixed. The module-flow caption named blue/green/orange tiers the figure
no longer uses.
Every SVG paints an opaque white canvas, which on the dark theme landed
as a hard-edged white slab. Referenced through <img src> the SVG is an
isolated document, so site CSS cannot reach inside it and an internal
prefers-color-scheme block would track the OS rather than the site
toggle. dark-mode.scss now frames them as deliberate plates instead,
which covers all 145 diagrams rather than only these 11.
Also repoints the .gitignore downloads whitelist at guide/, left stale
by the quarto -> guide rename.
Verified by building the site and screenshotting every figure at 1440px
light, 1440px dark, and 430px; audited all 62 pages for overflow and
broken images.