The original hardening got the structural selectors right, but the
labs/kits/book sidebar still had two divergences from the instructors
reference: (a) section headers and leaf links rendered with the same
muted color (no hierarchy) because the section-header rule only matched
the chevron `<a class="sidebar-item-toggle">` and missed the section
text `<a class="sidebar-link text-start">`, and (b) the active-state
rule colored the text in the accent color rather than white on a subtle
accent tint, so active links read as loud accent fills instead of the
clean instructors look.
Changes per file:
* labs/, kits/: extend section-header selector list to include
`a.sidebar-link.text-start`; drop `font-weight: 400 !important` from
the leaf rule so the section-header weight (600) wins on elements
matching both; move active-state color/background/weight into the
high-specificity `#quarto-sidebar` guard block to beat Bootstrap's
default `.sidebar-link.active { color: var(--bs-primary) }`.
* book/quarto/: same treatment, plus include `.part-divider` in the
section-header selector list to keep the part-divider hierarchy.
* tinytorch/quarto/: same active-state hardening, plus rename
`$accent` → `$accent-dark` throughout (the theme partial
shared/styles/themes/_theme-tinytorch.scss declares both variables;
since Quarto auto-`!default`s every scss:defaults declaration, the
reassignment of `$accent` here was silently ignored and rgba calls
resolved with the light #D4740C value). Drop the unused
`$link-color-dark` definition that referenced `$accent-dark` from
scss:defaults — Quarto hoists user defaults above shared partial
defaults, so `$accent-dark` isn't yet defined when that line runs.
Rules in scss:rules resolve `$accent-dark` correctly because rules
are processed after all defaults are merged.
Verified with Playwright probes on /vol1.html (slides), /index.html
(labs, kits), /big-picture.html (tinytorch), /api-stability.html
(mlsysim), /foundations-syllabus.html (instructors): all six now show
white active text on rgba(accent, 0.15) tint, weight 500, with bright
section headers (#e6e6e6) at weight 600 and muted leaf links (#888888
or #cbd5e1).
Extends PR #1633's instructors fix to the rest of the Quarto ecosystem.
Quarto renders floating sidebars with `<a class="sidebar-link">` and
`<a class="sidebar-item-toggle">` directly (not nested inside an
`.sidebar-item a` wrapper), so the legacy selector missed all real DOM
nodes.
Sites updated:
* slides, interviews, tinytorch/quarto, mlsysim/docs: full hardening
(toggle, leaf-link, active-state, search input, TOC rail).
* labs, kits, book/quarto: added missing search-input theming
(core sidebar and TOC fix already present from earlier work).
Each site's accent color is preserved: indigo for instructors and
interviews, pink for slides, amber for tinytorch, cyan for mlsysim,
teal for kits and labs, crimson for book.
The h3 and pre code rules set near-white colors unconditionally, which
made these elements unreadable in light mode. Gate them on
[data-bs-theme="dark"] so light mode falls back to its own contrast.
Adopt the shared release-versioning pattern. Pure additions: no
existing build steps, configs, or sources are touched. The 6-place
manual version sprawl in the publish workflow stays as-is —
pyproject.toml remains the canonical source the manifest READS from.
quarto/_quarto.yml:
- <meta name="release-manifest" content="/tinytorch/release-manifest.json">
added to include-in-header so the pill's runtime fetch resolves
correctly under /tinytorch/.
- shared/release/release-pill.html added to include-after-body so
every rendered page carries the pill in the DOM.
.github/workflows/tinytorch-publish-live.yml:
- New "📦 Emit release manifest" step inserted between the slide-deck
download and the gh-pages deploy. Runs scripts/version/release.py
compute-hash over modules/, quarto/ (excluding _build), pyproject.toml,
MANIFEST.in. Writes release-manifest.json into the site build dir
so it deploys at /tinytorch/release-manifest.json.
The pill is best-effort chrome — silent if the manifest is missing,
which keeps preview-dev and local renders from breaking. Production
publishes always emit it before deploy, so the live site shows
"TinyTorch v0.1.10 · Apr 28, 2026" in the footer once this lands.
Three small but layered fixes that together make every page under the
tito CLI module fully readable in light mode, dark mode, and the hybrid
state where data-bs-theme="dark" stays sticky on <html> while the light
stylesheet is the active bundle.
- Light-fill panel text-color safety net (style.scss). Pairs with the
bg-flip safety net upstream introduced in 2707c0546. The bg-flip only
fires in dark mode; this rule fires in both modes and pins text
inside inline-styled light-fill cards (tito/modules.qmd, tito/data.qmd,
etc.) to #1e293b regardless of what --bs-body-color resolves to.
Without it, cards stayed light-fill in the hybrid state but text
inherited the dark-palette light body color = invisible. The matching
!important `color: #e6e6e6` line in dark-mode.scss flips back when
the card flips to #2d2d2d.
- Code blocks inside cards. The previously-merged
`pre code { color: #e6e6e6 }` rule was painting plain (no-language)
fenced blocks light grey on the light card surface — visible on the
box-drawing console output blocks under "Understanding the Export
Process" on tito/modules.qmd. Nest a `pre code, code` override inside
the safety-net selector list so card code is dark in light/hybrid and
light in true dark mode.
- Scope existing global h3 + pre code overrides to [data-bs-theme="dark"].
Both rules were left unscoped previously to compensate for the hybrid
state. The card safety net above now handles hybrid for cards, so the
global rules can be properly scoped — fixes the "### Milestone
Achievements: `tito milestone status`" h3 on tito/data.qmd which was
rendering #d0d0d0 on white in true light mode.
- Table zebra-stripe override (style.scss). shared/styles/partials/_tables.scss
sets the even-row bg to #fafbfc unconditionally; on the dark body the
light text inheriting from --bs-body-color was invisible against the
light row stripe. Add a [data-bs-theme="dark"]-scoped rule using a
subtle white-overlay rgba so striping survives without hard-coding
a specific dark hue.
Light mode unchanged on every page that doesn't use inline-styled cards.
Map inline 700/600 Material accent text to 400-shade variants for WCAG on
#2d2d2d surfaces; add linear-gradient(e3f2fd...) panel rule missed by
solid background hex selectors.
Iterates on the post-merge 404 redesign across all 8 sub-sites:
- SVG roofline-plot fonts bumped for readability without changing
layout: axis labels 9.5pt to 14pt, region labels 9.5pt to 14pt,
title and "your page (404)" annotation 11pt to 16pt.
- Random-joke font shrunk from 1.6rem to 1.3rem (1.1rem on mobile)
so the joke no longer dominates the SVG above it.
- Removed the static "Looks like this page slipped past our load
balancer" subline from 6 sub-sites — it read as a competing static
joke alongside the random rotation. Slides and instructors keep
their informational sublines.
- Joke pool tightened 97 to 79 via a strict ML-systems-centric test:
if you could swap "page" for any generic web/ops resource and the
joke still works, cut. Cuts removed the H&P canonical material
(Amdahl's Law, TLB miss, Dennard scaling, false sharing, fat-tree
topology) that is general computer architecture rather than ML
systems specifically. 19 borderline jokes were rewritten to anchor
punchlines in concepts only ML practitioners decode (KV cache,
gradient AllReduce, prefill, ZeRO, speculative-decode acceptance,
1F1B schedule, ridge point, BPE merges).
Three small follow-ups on top of the recent dark-mode UX work
(#1529 / #1532-style fixes already on dev):
- _quarto.yml: split `highlight-style: github` into
`light: github` / `dark: github-dark` so code blocks pick up
proper dark theme tokens instead of the light theme over a
dark surface.
- style.scss: pin .who-card paragraphs to #475569 (slate-500)
so they stay readable on the #fafafa card surface even when
the page is in the hybrid state where data-bs-theme="dark"
is set on <html> but the light stylesheet is active — body
color flips to #dee2e6 in that case and inheriting it
invisibly bleached the cards. Also add the global
`#quarto-content h3 { color: #d0d0d0 !important }` and
`pre code { color: #e6e6e6 }` overrides so subheadings and
code text remain readable in dark mode (h3 here is needed
because Quarto re-loads the light bundle as
quarto-color-scheme-extra after the dark bundle, so equal-
specificity rules from _headers.scss otherwise win).
- dark-mode.scss: pair the .who-card paragraph fix above —
when the card flips to #2d2d2d in dark mode, paragraphs
go to #e6e6e6 to match.
Replace the inline-game 404 across all 8 sub-sites with a unified
design: roofline-plot illustration, random pick from a 97-joke
editorial-curated pool, dual CTA (home + playground), and a
GitHub-issue-backed contribute link for community submissions.
The joke pool was curated through a multi-agent editorial process:
4 generators (each with a different ML systems lens) produced 240
candidates; 5 reviewer personas (architect, NLP researcher, production
engineer, undergrad, copy editor) scored each; the synthesizer kept 92
surviving jokes plus 5 new canonical-architecture additions (Amdahl,
TLB, Dennard, false sharing, fat-tree topology) the architect flagged
as missing from the H&P-tradition canon.
Self-contained CSS with prefers-color-scheme: dark adapts to host
themes without coupling to each sub-site's bespoke styling. SVG
follows the book's semantic palette (blue memory-bound slope, green
compute roof, MIT red error annotation). Per-site navigation
preserved across all sub-sites.
Adds .github/ISSUE_TEMPLATE/404_joke.yml so contributions arrive
structured (joke text, theme dropdown, license checkbox) and
auto-tagged (site, 404-joke, good-first-issue) for triage.
Quarto's default styles set
div.sourceCode { background: rgba(233, 236, 239, 0.65) }
on every code block — a LIGHT fill at 65% opacity. Over the dark body
(#1a1a1a) that blends to a washed-out medium grey (~#a0a0a0) which
fights the syntax-highlighted foreground and makes code hard to read.
Override in dark-mode.scss with an explicit dark surface slightly
lighter than the body so blocks read as distinct rectangles, plus a
1px border using $border-color-dark for clean delineation.
Inline code (`tinytorch/`-style spans in prose) had the same washed-grey
problem from the same rgba-light pattern. Switched to a translucent
slate (rgba(110, 118, 129, 0.35)) with light text (#e6edf3) — keeps it
visually distinct from surrounding prose without competing with full
code blocks.
Verified locally on getting-started.html (the page with the most
visible code blocks). Computed bg now rgb(34, 38, 44) = #22262c with
border #454d55, body bg unchanged at #1a1a1a.
Align public README and site messaging around the curriculum components, adoption paths, and current early-release status so newcomers can move from reading to building, deployment, practice, and teaching.
Adds a small amber pill below the existing "A Build-It-Yourself
Companion to..." subtitle showing "🔥 TinyTorch v0.1.10 →", linked to
the GitHub Releases page filtered for tinytorch tags.
Why: the previous sidebar had no persistent version indicator, and
the announcement-bar approach was rejected (see config/announcement.yml
header note: "Release news belongs in a changelog, not in the always-
visible nav bar"). The sidebar subtitle area is the natural home for
this kind of meta-info — sticky on desktop, not competing with content,
and follows the existing JS injection pattern.
Design choices:
- "🔥 TinyTorch" prefix + visual divider above the pill prevent the
reader from parsing "v0.1.10" as the textbook's version (which
would be wrong — the textbook is versioned by Volume).
- Amber border + amber text + faint amber background match the
TinyTorch brand color used throughout the announcement bar and
accent system.
- target="_blank" + rel="noopener" — the chip leaves the docs site,
so external-link semantics apply.
- !important on color rules because Quarto's link styles otherwise
win at higher specificity (matches the existing `.sidebar-subtitle
a:hover` pattern in this file).
Version source: hardcoded as "v0.1.10" with a TINYTORCH_VERSION_CHIP
sed marker. The tinytorch-publish-live workflow's UPDATE_VERSION step
already syncs 6 files (pyproject.toml, settings.ini, install.sh,
announcement, README badge, init); adding this file to that step is
a follow-up. For now, manual updates on each release.
Verified in Playwright on index + tito/milestones, light + dark mode.
Computed color rgb(212, 116, 12) light / rgb(245, 158, 11) dark; href
points to releases?q=tinytorch as intended.
#1529 fixed dark-mode contrast on preface.qmd by refactoring inline
style="background: #..." panels to .who-card / .callout-note. The
remaining 200+ inline-styled panels across other pages (big-picture,
index, tito/*, datasets, etc.) were still failing in dark mode — the
fix worked for one page but the user-visible experience was patchwork.
Two structural gaps closed:
1. Sidebar had zero dark-mode coverage. dark-mode.scss declared
$sidebar-bg-dark = #212529 but never applied it to a selector,
and shared/styles/partials/_sidebar.scss hardcodes #495057 link
text against an implicit white surface. Result: white sidebar
visible on every page in dark mode. Fixed by adding rules for
#quarto-sidebar, .sidebar-navigation, sidebar items, section
headers, and dividers — uses the existing $sidebar-bg-dark and
$border-color-dark variables instead of leaving them dead.
2. Inline-style panels site-wide. Cataloged the 14 light-fill hex
values that appear with `background: #X` across the .qmd corpus
(counts in the SCSS comments) and added a [style*=...] attribute
selector layer that flips them to #2d2d2d in dark mode, plus the
8 mid/dark-grey text colors used inline that fail when their
surface flips. !important is required because inline style= beats
class specificity. Decorative dark/saturated fills (#263238,
#0f172a, button blues/oranges, gradient banners) are deliberately
excluded — they render correctly in both modes.
Plus: thin 1px border on .who-card in dark mode for delineation
against the body bg ($body-bg-dark = #1a1a1a vs card #2d2d2d).
Verified across index, big-picture, and tito/milestones in Playwright
with colorScheme: 'dark' (uses Quarto's prefers-color-scheme initial
toggle path, not just data-bs-theme attribute setting). All measured
contrast ratios >= 11.0 WCAG AA. The defensive layer is a transitional
bridge — long-term, individual pages should be refactored to .who-card
/ .callout-note like preface and tito/milestones already are.
Same failure mode flagged in #1529 preface refactor: inline
style="background: #f8f9fa..." beats CSS class specificity, so
dark-mode rules never apply. Five light-fill panels affected:
- Quick Start workflow (#f8f9fa) -> .callout-note
- Discover Milestones card (#e3f2fd) -> .who-card + #2196f3 border
- Learn About card (#fff3e0) -> .who-card + #ff9800 border
- Run Milestones card (#f3e5f5) -> .who-card + #9c27b0 border
- Track Progress card (#f0fdf4) -> .who-card + #22c55e border
- Three Tracking Systems (#f8f9fa) -> .callout-note
- Next Steps CTA panel (#f8f9fa) -> .who-card
Hero gradient banner (lines 5-7), accent border-lefts on the four
command cards, and the two CTA buttons retain inline style — they are
mode-invariant decorative accents (gradient + white text + colored
buttons render correctly in both modes), matching the preface fix.
TinyTorch quarto site is HTML-only (no PDF format in _quarto.yml),
so no when-format gating needed.
Welcome page and several adjacent surfaces had dark-on-dark text in dark
mode after the v0.1.10 release. Two root causes:
1. preface.qmd used inline `style="background: #..."` divs for the
"Why does this matter?" callout, the MLSysBook/TinyTorch comparison,
and the "Who This Is For" persona grid. Inline styles beat CSS
class specificity, so dark-mode rules were ignored.
2. dark-mode.scss had no overrides for several components defined in
style.scss with hardcoded light colors: .comparison-title, the
.tier-{foundation,architecture,optimization,olympics} cards, the
.milestone-card family, .ml-timeline-tech, .preview-badge, and the
callout-body / callout-content text-color cascade.
Changes:
- Refactor the three preface.qmd blocks to use existing .callout-note
and .who-card / .who-grid classes (kept colored left-borders inline
since they are mode-invariant accents).
- Append dark-mode rules covering every selector identified above.
Mirrors the .callout-body !important cascade pattern from
shared/styles/_ecosystem-base-dark.scss:577-584.
PDF branches (when-format="pdf") untouched. Light mode unchanged.
Two related fixes that landed together during a UX walkthrough.
1. H2–H6 decoration override was dropped in a merge conflict.
PR #1524 added an override in tinytorch/quarto/assets/styles/style.scss
that stripped the shared /partials/_headers.scss L-shape decoration
(thick accent left-border + thin accent bottom line) from H2–H6 on
TinyTorch only. The override disappeared at some point between that
merge and today — every H3 on a TinyTorch page is currently rendering
with `border-left: 4px solid #d4740c` + `border-bottom: 1px solid
rgba(212, 116, 12, .25)`. Restoring the override with `!important`
so it survives future shared-partial additions. Scoped to TinyTorch;
book/kits/labs/mlsysim keep their decoration.
2. Comparison-grid "Traditional ML Education" / "TinyTorch: Build →
Use → Reflect" titles converted from H3 to a styled <span>.
Problem: H3 at 24.65px with the accent L-shape reads as a page
heading jammed into a card — awkward scale, awkward hierarchy
(the whole card IS the content; the title isn't a separate
sub-section). Solution: use a `.comparison-title` span class
at 1.15rem (~18.4px), 600 weight, no border — properly card-
scaled, and no heading semantics pollute the doc outline / TOC.
Markup changed from `### Title {.unnumbered}` to
`[Title]{.comparison-title}`.
Verified via Playwright on `http://localhost:4123/`:
- No H3 exists inside `.comparison-bad` / `.comparison-good` now.
- New `.comparison-title` span renders at `font-size: 19.55px`,
`border-left: 0`, `border-bottom: 0`.
- Applied screenshot: /tmp/banner-work/compare-fixed.png shows the
card titles as clean bold text, no underline, no accent bar.
Font audit (same walk): body Inter 17px/25.5px, H1 42.5px/700,
H2 28.05px/600, H3 24.65px/600, code JetBrains-Mono 14.9px, sidebar
15.7px, TOC 15.3px. All consistent with the ecosystem font stack and
reasonable in isolation — no additional font changes needed.
Round-2 UX polish pass after a full Playwright walkthrough surfaced a few
cheap, high-impact wins. Scoped to three files.
HOME (index.qmd)
- Hero hierarchy swap. The prior markup rendered a 2.5rem <p> "Build
Your Own ML Framework" followed by an <h2> "Don't import it.
Build it." — which flipped the semantic hierarchy: the tagline
subtitle read as the most important element, and the memorable
slogan (which matches the pagetitle) read as secondary.
Fix: "Don't import it. Build it." is now the page's h1; "Build your
own ML framework — from tensors to systems." is the subtitle below
it. Screen readers, search engines, and the eye all agree now.
- Preview badge: "Classroom ready 2026" -> "Classroom ready Fall 2026".
The date is now unambiguous (we're in 2026; old copy read as "not
yet shipped in 2026" or "already shipped in 2026" depending on
reader).
- Star-count CTA copy: was "⭐ 23,763 learners · every ⭐ helps
support free ML education". Problem: the dynamic number is the
GitHub stars count, not a learner count — conflating the two is
misleading. Rewrite: "⭐ 23,763 stars on GitHub — add yours and
support free ML education". Concrete number, concrete verb, honest.
MILESTONES HUB (milestones/index.qmd)
- Add a six-card roster at the top showing 1958 -> 2018 at a glance
(Perceptron, XOR Crisis, MLP Revival, CNN Revolution, Transformers,
MLPerf). Previously the hub opened with two prose paragraphs before
the journey table; a first-time reader had to scroll past them to
see the milestone list. The 6-card grid makes the arc instantly
legible; the detailed per-milestone journey table stays in place
further down for the reader who wants module-by-module dependencies.
- Cards are links, each to the milestone's own page.
- Color coding mirrors the tier that produces the milestone's modules:
Foundation (blue), Architecture (purple), Optimization (orange).
CSS (tinytorch/quarto/assets/styles/style.scss)
- New .milestone-grid + .milestone-card classes, styled to match the
preface audience-card aesthetic shipped in the previous polish PR
(neutral #fafafa fill, colored left-border carrying the tier
identity). Responsive: 3-col desktop, 2-col tablet, 1-col mobile.
- data-era="{foundation|architecture|optimization}" attribute on each
card drives the border-left color via the attribute selector, so
the era -> color mapping stays declarative and doesn't need per-card
inline style.
Verified (Playwright against local quarto preview):
- Home h1 is now "Don't import it. Build it." at 42.5px; badge reads
"Preview · Classroom ready Fall 2026"; star link reads "⭐ {N}
stars on GitHub — add yours and support free ML education"
- Milestones hub renders the 6-card grid with correct data-era colors
(foundation = rgb(25, 118, 210), architecture = rgb(123, 31, 162),
optimization = rgb(245, 124, 0)).
Deferred — acknowledged as valid observations in the UX walkthrough but
out of scope for this PR:
- Swap module-page action cards above Module Info (touches ~20 module
files; wants a template-level change, not ad-hoc QMD rewrites).
- Sticky per-module progress indicator (wants JS + scroll listener).
- Announcement-bar collapse on narrow viewport (JS + cross-site).
- Olympics "Coming Soon" -> dated promise (intentionally left alone:
the real Olympics launch content is landing in a separate PR in
the next few days).
cards + header decoration + PDF milestone transitions
Polishes several rough edges in the TinyTorch site surfaced by a visual
walkthrough. Six independent fixes in one PR because they all landed on
the same mental pass:
HTML SIDEBAR
- Rename two sections so their titles don't wrap on the 250px sidebar:
"Capstone Competition" -> "Capstone"
"TITO CLI Reference" -> "TITO CLI"
(navbar link "TITO CLI Reference" -> "TITO CLI" too, for
consistency)
- Remove all three milestone sections ("Foundation Milestones",
"Architecture Milestones", "Optimization Milestones") from the HTML
sidebar. They interleaved between tiers and broke the
Foundation -> Architecture -> Optimization -> Capstone flow the
sidebar is meant to communicate. Milestones stay fully accessible
via the navbar's "Historical Milestones" entry, and the PDF build
(which has its own _quarto.yml) is untouched -- interleaved
milestones are the correct reading experience in print.
- YAML comment added where the milestone sections used to live so a
future contributor knows the removal was intentional.
SIDEBAR SCROLL
- shared/styles/partials/_sidebar.scss: add
`overscroll-behavior: contain` to #quarto-sidebar and
.sidebar-navigation. Before: hovering-and-scrolling over the sidebar
chained the scroll to the body the moment the sidebar hit a
boundary -- so scrolling the sidebar felt like it was never actually
engaging. After: the sidebar's scroll stays in the sidebar.
- Single-source-of-truth win: this improves every Quarto site in the
ecosystem (book/kits/labs/mlsysim/tinytorch/site), not just tinytorch.
H2 L-SHAPE DECORATION (TINYTORCH ONLY)
- shared/styles/partials/_headers.scss decorates H2-H6 with a thick
accent left-border + thin accent bottom line -- an "L-shape" that
reads well in long-form textbook prose. For tinytorch (framework
docs with code blocks, comparison grids, card-based layouts) the
decoration felt heavy and competed with the content itself.
- tinytorch/quarto/assets/styles/style.scss: add a local override
that strips border-left/border-bottom/padding-left/padding-bottom
from H2-H6. Scoped to tinytorch -- book/kits/labs/mlsysim keep the
decoration.
TIER CARDS
- index.qmd's .tier-foundation / .tier-architecture /
.tier-optimization / .tier-olympics cards used four different
gradient fills (blue/purple/orange/pink). Visually loud; fought
the comparison grid and hero on the same page; tier-optimization
read as specifically orange-heavy.
- Flatten to the same neutral #fafafa fill the .audience-card and
preface.qmd cards use, with just a 4px colored left-border carrying
the tier's identity. Each tier still has its distinct color cue;
the page calms down.
PDF NARRATIVE TRANSITIONS
Milestones appeared suddenly in the PDF when a tier ended. Readers
lost context for why they were there and which just-built modules
they were about to exercise. Six short transition paragraphs added
via the narrative-flow-analyzer subagent:
modules/08_training.qmd forward-hook into Foundation Milestones
modules/13_transformers.qmd forward-hook into Architecture Milestones
modules/19_benchmarking.qmd forward-hook into Optimization Milestone
milestones/01_perceptron.qmd tier-components open
(Tensor/Linear/BCELoss/SGD/Trainer)
milestones/04_cnn.qmd first-Architecture-Milestone framing +
three-tier arc explanation
milestones/06_mlperf.qmd sole-Optimization-Milestone +
"third act" framing
Files left as-is because the transitions were already good:
milestones/index.qmd, milestones/02_xor.qmd, milestones/03_mlp.qmd,
milestones/05_transformer.qmd.
DRIVE-BY FIX
modules/13_transformers.qmd had four lines of pre-existing corrupted
trailing content after the last callout (duplicate sentence,
orphan "44B parameters |" table row, orphan Capstone row). Removed
since this PR was already editing the file.
Verification (Playwright against local preview):
- Sidebar section labels: ["Getting Started", "Foundation Tier",
"Architecture Tier", "Optimization Tier", "Capstone",
"Conclusion", "TITO CLI", "Reference", "Community"] -- no
wrapping, no milestones.
- #quarto-sidebar overscroll-behavior: "contain"
- H2 "Don't import it. Build it." computed border-left-width: 0px,
border-bottom-width: 0px, padding-left: 0px
- .tier-card backgrounds: all rgb(250, 250, 250);
border-lefts: 4px solid {tier-color} each.
The TinyTorch announcement bar was reverted during the v0.1.10 publish
merge on 2026-04-22 (commit 0009f55a9) from the 4-line ecosystem template
shipped in PR #1505 back to a single-line release notice:
🎉 v0.1.10 released — v0.1.10 (retry after TINYTORCH_SITE path fix) · See release →
Three problems with the release-notice form:
1. Single-line + type: info breaks visual consistency with the other
eight Quarto announcement bars (all type: primary, 4 lines).
2. Copy reads like a commit message ("retry after TINYTORCH_SITE path
fix") — internal engineering language in always-visible nav copy.
3. Release news persists long after the release — "v0.1.10" is
already stale-sounding and will look abandoned when v0.1.11 ships.
Restore to the unified template used by vol1, vol2, kits, labs, mlsysim,
slides, instructors, site, and staffml: identity line + book link +
"Alongside the book:" sibling row (Hardware Kits / MLSys·im / Labs) +
newsletter. TinyTorch is the "build" lens of the curriculum; line 3 omits
TinyTorch itself and lists the three other learner-tool siblings.
Also documents in the file header comments that this reversion happened
so a future release publish doesn't silently overwrite again. If a future
release warrants a top-of-bar mention, the right pattern is an optional
release_note field on the shared schema that auto-expires — not
overwriting the ecosystem-navigation copy.
Every site's announcement bar now follows one template:
Line 1 — identity + primary CTA (what is THIS site)
Line 2 — the book (or, on book sites, the other volume)
Line 3 — "Alongside the book:" sibling row (3 most-relevant verbs)
Line 4 — newsletter
The book is the anchor of the curriculum; every other site is a verb
applied to it — TinyTorch (build), Hardware Kits (deploy), MLSys·im
(simulate), Labs (explore), Slides + Instructors (teach). Making that
shape visible in every bar turns nine independent sites into chapters
of one curriculum.
Removed stale copy: "Happy New Year!" on kits + labs, "coming in 2026"
on labs (we are in 2026; replaced with "Coming Summer 2026"), TinyTorch
v0.1.10 single-line release notice (belonged in a release changelog,
not the always-visible nav bar).
Normalized outbound links: /book -> /vol1/ and /vol2/, consistent
trailing-slash hygiene on every URL. Newsletter link points to
https://mlsysbook.ai/newsletter/ (the actual Quarto page) instead of
#subscribe (which only resolves on the landing page).
The landing site (mlsysbook.ai) uses a 5-line variant that covers all
four learner verbs + the teacher-tools row; every other site uses the
4-line form.
StaffML is a Next.js app, not a Quarto site, so its banner is out of
scope for this PR and will ship as a separate React component change.
CI workflows repeated a ~20-line tlmgr install block inline. Consolidate
to two requirements files that work locally and in CI:
pdf/apt-requirements.txt - system packages (librsvg2-bin)
pdf/tex-requirements.txt - 24 TeX packages
New Makefile target 'make install-deps' reads both files, calling
apt-get + tlmgr. Skips sections when the tool is not on PATH (no-op
on macOS, effective on Ubuntu CI).
CI workflows simplified to 'make install-deps && make pdf', matching
local dev exactly. Adding a dependency now means editing one file,
not two.
Cross-reference audit subagent: scanned all 30 scoped .qmd files for
orphan table / figure / listing labels (caption with {#tbl-/fig-/lst-...}
but no corresponding @label reference in prose). Added natural
references for orphans so every labeled artifact is now introduced in
the surrounding text.
Final counts: 247 labels defined, 216 refs used (87% coverage). The
remaining ~30 orphans were either self-describing (milestone-result
tables whose placement is obvious from context) or inside scope I left
untouched to preserve existing voice.
Also included: tiers-optimization-dependencies.svg updates from the
earlier Gemini consistency audit that had been left uncommitted.
Audit report at .claude/_reviews/crossref-audit-report.md.
Wave 4 editorial content across 20 modules + new glossary back matter:
1. Module opener hooks (20 new 2-3 sentence paragraphs between the
chapter heading and Module Info callout). Every hook LEADS with
the systems angle (memory, bandwidth, arithmetic intensity,
cache, HBM, roofline, KV cache, hardware utilization, etc.) and
connects back to the ML story. Reinforces that this is a lab
guide for ML systems, not an ML-theory textbook.
2. Code-listing captions on substantive code blocks (roughly >10
lines, defines a class/function/algorithm). Populates Quarto's
List of Listings front matter. Combined across F1/F2/L/O
subagent waves: roughly 60 listings now carry
'**Listing N.M — Brief description**' captions.
3. Figure alt-text audit across 20 module diagrams. Most already
carried objective specific alt-text; a handful were rewritten
for precision.
4. Glossary at back matter (tinytorch/quarto/glossary.qmd + registered
in pdf/_quarto.yml). 90 alphabetical entries spanning tensor /
memory / autograd / training / architecture / optimization
terms. One-sentence definitions. Module cross-references where
the term is central. Lab-guide voice, not dictionary.
5. Style discipline: no em-dashes in prose (caption templates
'— Description' are the only exception, required by parser).
All agent outputs and the hand-revised hooks audited for em-dash
use.
6. SVG trailing-newline hygiene: 8 SVGs touched by the Gemini style
audit had lost their trailing newline. Restored per the SVG
file-hygiene rule.
Bring the six lab-guide-style SVGs introduced in 8928562e6 into visual
parity with the 57f9abed5 diagram polish:
- soften primary strokes from #555 to #bbb on all box borders and
arrow paths
- soften arrow marker fills from #555 to #bbb
- remove the opaque white background rect so the figures inherit the
page background (fixes dark-mode rendering)
- consolidate the arrow / arrow-muted markers in tito-module-workflow
(the dashed stroke alone carries the "secondary" signal)
Orange accent (#fdebd0 / #c87b2a) on the focal element and the MIT red
pills (#a31f34) for year / achievement badges are preserved. Text
colors and geometry unchanged.
Apply a consistent polish pass across all 22 module diag SVGs and the
two site-wide overview SVGs (big-picture-module-flow,
modules-transformer-gpt-architecture):
- soften primary strokes from #555 to #bbb so diagram linework reads
as structural guides rather than bold borders
- soften arrow marker fills from #555 to #bbb to match the lighter
stroke weight
- remove the opaque <rect fill="#ffffff"/> background so SVGs render
correctly in both light and dark page contexts
- narrow the canvas (viewBox 680 -> 640 where applicable) and adjust
the group transform to remove excess whitespace around content
Net effect: every figure in the lab guide and the website reads lighter
and breathier, matching the minimalist neutral + single-accent
convention without losing any semantic detail.
Quarto's post-render cleanup leaves *_files/ auxiliary dirs around
when chapter sources live outside the project root (our case:
pdf/_quarto.yml references ../modules/*.qmd). These accumulated
at both pdf/modules/*_files and pdf/getting-started_files etc.
Add a rm -rf *_files to the Quarto project's root so all stale
auxiliary dirs get cleaned with one command.
The user was confused about which PDF was 'official' when Quarto's
post-render cleanup error occasionally left a stray copy at the
top-level pdf/ dir. This tightens the Makefile so every build ends
with exactly ONE TinyTorch-Guide.pdf:
- pre-build: rm -f pdf/TinyTorch-Guide.pdf (stray from past runs)
- post-build: if top-level copy exists after _build/ produced one,
rm it — _build/ is authoritative
- end banner: prominently prints
FINAL PDF (the only shippable file):
pdf/_build/TinyTorch-Guide.pdf (X.XM)
in a box — impossible to miss
make clean also upgraded to remove pdf/.quarto/ cache and any
index.tex/log/aux intermediates, so a full reset leaves zero PDFs
anywhere in the repo. .gitignore already covers all these; the
Makefile changes only affect local filesystem state.
Header rule simplified: double line (0.6pt + 0.3pt flameorange) →
single 0.4pt flameorange!70 rule. Cleaner, quieter, still visible.
Link validation ran across 168 unique URLs in .qmd sources.
3 URLs actually broken (vs 10 that 403 due to HEAD blocks but work
in a browser):
- GPT-2 paper: cloudfront URL deprecated → cdn.openai.com
- PyTorch extending doc: .md → .html
- mlu-explain.github.io/relu/ doesn't exist → neural-networks
CONTRIBUTING.md at blob/main 404s because the file lives on dev;
will resolve on next release merge to main — leaving as-is.
Convert the six remaining mermaid code fences across the tier overviews
(foundation, architecture, optimization) and tito developer pages
(modules, data, testing) into hand-authored SVGs that match the
minimalist convention used by the PDF lab-guide module diagrams:
- neutral #f7f7f7 fill + #555 stroke for all boxes
- one #fdebd0 / #c87b2a orange accent per figure marking the focal
element (essential command, pivot step, destructive action, or
deployable artifact)
- #a31f34 MIT red pills reserved for year and achievement badges
- panel header top-left at 12pt; fig-cap carries the long-form title
- stroke-width 1.2 throughout; typography 12 / 10 / 9 pt
Figures introduced:
- tito-module-workflow.svg (5-step build/export/test cycle)
- tito-progress-tracking.svg (6-step progress flow)
- tito-test-hierarchy.svg (7-level test stack)
- tiers-foundation-milestones.svg (1958 / 1969 / 1986)
- tiers-architecture-milestones.svg (1998 / 2017)
- tiers-optimization-milestones.svg (baseline -> MLPerf)
Why: the deployed site rendered mermaid timelines and flowcharts as
"syntax error in text" bombs on several pages (foundation, big-picture,
etc.). Switching to hand-authored SVGs embedded via the standard Quarto
figure-div pattern (::: {#fig-... fig-env=figure ...}) centers the
figures correctly, supplies proper fig-cap / fig-alt, and routes
through the existing svg-to-pdf.lua filter for HTML + PDF parity.
The minimalist two-tone palette (neutral + single accent) mirrors the
22 module diagrams in assets/images/diagrams/, giving the site and the
lab guide a single visual family.
A signed callout-tip at the very end: warm congratulations, direct
reference to the 'build book' ethos, explicit acknowledgement of the
hard path the reader took to finish. Signed VJR/Harvard so students
know whose voice closes the book.
Subagent A: add Quarto caption + {#tbl-...} label to every pipe table
so they index into the List of Tables (previously empty). 151 tables
across 28 files: 20 modules, 7 milestones, big-picture,
getting-started, conclusion. Caption style: single bold sentence
under 15 words; labels namespaced by module number.
Subagent B: hyperlink well-known external references in Further
Reading / Additional Resources sections. 20 links added spanning
arXiv papers (Attention Is All You Need, Scaling Laws, Deep
Compression, AlexNet, LeNet, Rosenblatt 1958, Rumelhart/Hinton/
Williams 1986), Jay Alammar's Illustrated Transformer / Word2Vec,
Karpathy's Recipe for Training Neural Nets, Ruder's gradient descent
overview, Jurafsky & Martin SLP3, Cybenko's UAT, Drepper cache paper,
HuggingFace Transformers.
6 references left unlinked (physical textbooks and ambiguous blog
posts where canonical URL couldn't be verified).
Dense prose pages (module chapters with 400+ word counts) read tight
at Quarto's default linestretch 1.0. Palatino's natural leading is
~1.2× font size, which is crisp but unforgiving for long-session
lab-book reading. A 1.1 bump adds ~10% more line whitespace, landing
at the Stripe Press / Swift Book / Pragmatic Bookshelf sweet spot for
technical body text.
Side-by-side inspection (module 02_activations 'Why ReLU Dominates'
page at 150 DPI): noticeably more breath between lines, paragraph
transitions clearer, eye travels more comfortably. Code blocks and
callouts unaffected (Verbatim + tcolorbox have independent spacing).
Trade-off: page count grows by roughly 8-12 pages across the 350-page
guide. Acceptable at this scale — readability over pagination.
One-line YAML change in format.pdf block.
Audit found two inconsistencies that this fixes:
1. Systems Implication callouts split 9 note / 12 warning — the
12 warning instances were pre-existing 'legacy' classification.
Per the preamble convention (Systems Implication = insight =
note, not warning), convert all 12 to callout-note. Result:
21/21 Systems Implication callouts now use callout-note (blue
bar), consistent semantic signal across all modules.
2. Answer callouts (101 instances) were callout-note. Since
Check-Your-Understanding wrappers are callout-tip (green), and
answers are the 'reveal' to questions (productive/reward
semantic, not neutral info), switch all 101 to callout-tip
collapse=true. Green callout feels like reveal, matches CYU's
visual language, and distinguishes answers from the 149 generic
notes used for Coming-Up/Module-Info/Further-Reading boxes.
Final callout inventory across 20 modules:
callout-note ~140 (Systems Implication, Coming-Up, Further-
Reading, Module-Info, Historical Context)
callout-tip ~180 (Check Your Understanding wrappers,
Answers, Key Takeaways titles)
callout-warning ~22 (Save-Your-Progress, Performance-Note,
other warnings — not Systems Implication)
No content change — only class swaps. Titles untouched.
The mid-recipe '# ...' lines were being consumed as part of the joined
shell command (via backslash-newline continuation) and masked the
self-heal mv. Move the explanatory comment above the recipe so Make
recipe body is pure shell.
Four parallel subagents completed the content work derived from Wave 2
audits. Each of the 20 module files now has:
- A Check Your Understanding callout (callout-tip) at chapter end,
with 3-5 technical-specific checkboxes keyed to that chapter's
unique content. Checkbox content targets specific concepts, NOT
generic 'did you understand this' wording. Callout titles are
text-only ('Check Your Understanding — <Module>') — no emojis,
survives the strip filter, renders identically cross-platform.
- A Key Takeaways section (3-4 bullet recap + Coming-next hook
to the next module) inserted before Further Reading / What's
Next. Serves as the section students flip back to when reviewing.
Per-module audit gap fills:
- 01_tensor: normalized to canonical 12-section order; added
Get Started section; removed duplicate Further Reading fragment.
- 04_losses: added explicit O(B × C) complexity line in Core Concepts.
- 07_optimizers: added SGD O(P) / Adam O(P) + O(2P) memory line.
- 11_embeddings: added Systems Implication callout (sparse gradients,
HBM layout, distributed sharding).
- 13_transformers: added O(N² · d) attention complexity + Systems
Implication callout (KV-cache memory growth under autoregressive
decoding).
- 14_profiling: added FLOP/bandwidth complexity framing + Systems
Implication callout (reading the roofline as a decision tree).
- 15_quantization: added constant-factor speedup complexity framing.
- 16_compression: added pruning O(N log N) / compression ratio math.
- 17_acceleration: added fused-kernel memory-complexity reduction.
- 19_benchmarking: added Little's Law L = λW block equation.
- 20_capstone: added end-to-end inference complexity decomposition
with each optimization module mapped to its attacking term.
20 modules × avg ~24 lines added each (~480 lines of new pedagogy).
All callout titles are text-only. No ::: fence regressions.
Audit trail:
.claude/_reviews/section-consistency-report.md
.claude/_reviews/systems-emphasis-report.md
.claude/_reviews/wave-plan.md
Audit findings from .claude/_reviews/section-consistency-report.md and
systems-emphasis-report.md:
- Strip leading emoji prefixes from Systems Implication callout titles
across 17 modules. strip-emojis.lua removes them from PDF render
anyway (XeLaTeX + Latin Modern fonts don't cover emoji ranges
cross-platform), so source now matches what ships.
- Remove duplicate trailing '## Get Started' section from modules
10, 11, 13, 14 — copy-paste artifacts.
- Update pdf/_quarto.yml preamble comment: callout conventions
are class + title-word, no emoji prefixes.
- Running headers: verso shows 'Chapter N · Title' on outer-left, recto
shows 'N.M · Section' on outer-right, small caps navy. Wordmark
centered. Page numbers on outer-edge in footer. Follows H&P / CLRS /
Swift Book convention.
- Book-style paragraph typography: parskip=0pt plus 0.5pt, parindent=1.2em.
First-line indent instead of web-style paragraph gaps.
- List of Figures + List of Tables: lof: true / lot: true in YAML front
matter. One page each, makes 350-page guide's figures navigable.
- Callout title convention documented in preamble comment. Six types
keyed off emoji prefix (Check Your Understanding ✅, Systems
Implication ⚡, Historical Context 📚, Exercise 🎯, Pitfall ⚠️,
Checkpoint 🚦). Reuses Quarto's 5 shipped classes; no new LaTeX.
- Makefile self-heal: when Quarto post-render cleanup errors and
leaves the PDF at top-level pdf/ instead of pdf/_build/, detect
and move it to the canonical location.
Two fixes:
1. Critical rendering bug in modules/10_tokenization.qmd: a Python code
block used for inline {python} variable refs was missing its
opening ```{python} fence. Pandoc interpreted the two `#` comments
inside ("# Character tokenizer: vocab 100, dim 512, float32" and
"# BPE tokenizer: vocab 50,000, dim 512, float32") as top-level
markdown headings, which the book class rendered as Chapters 19
and 20 with the prose lines below dumped as plain text.
Restoring the opening fence (with `#| echo: false`) puts the
variable defs back inside a proper code chunk. The inline
{python} tradeoff_char_embed etc. refs in the next paragraph
now resolve correctly.
2. Revert the TOC spacing change from the previous commit. User
confirmed the default book-class TOC leading is already fine;
tocloft bumps made it too airy. arraystretch 1.2 for tables
and enumitem itemsep 0.3em for lists remain — those targeted
visible defects.
PDF typography improvements:
- arraystretch 1.2: every tabular gets ~20% more vertical breathing
room. Previous default of 1.0 packed rows tight enough that
multi-line cells (like the "You'll Have Built" milestone table)
felt cramped.
- enumitem itemsep 0.3em + topsep 0.3em on numbered and bulleted
lists: small air between list items so code-listing-style items
like "19 Character tokenizer..." / "20 BPE tokenizer..." don't
touch each other.
- tocloft cftbeforesecskip 0.3em / cftbeforesubsecskip 0.2em: TOC
section-level entries (32.1, 32.2, ...) no longer stack on tight
leading. Scanning a 30-chapter TOC is easier.
getting-started.qmd:
- Remove "Early Explorer Territory" callout from the top. The
content warned readers about rough edges and pointed to
Summer/Fall 2026 classroom readiness — appropriate during early
drafts but out of place now that the Lab Guide is shipping.
The Prerequisites Check callout remains (different purpose —
tells readers what knowledge they need).
No change to chapter title styling ("Chapter N" eyebrow + huge title)
or header/footer setup — both already match textbook editorial
conventions (see pdf/_quarto.yml lines 321-395).
Replace the 3-column dependency map with a layered stack figure, after
comparing three candidate layouts (horizontal journey, layered stack,
question-driven typography). The stack was chosen because:
- The "ML stack" mental model is already in the book's prose.
- Uniform pill widths within each tier (Foundation 54, Architecture
74, Optimization 70) read cleanly vs the prior layout's 34-50px
variance.
- Capstone gets a deliberate visual "summit" — entire top band is
orange, with a prominent MIT-red pill.
- Upward arrows between layers convey the dependency direction
without the prior cross-tier routing clutter.
The stack was generated as .claude/_reviews/big-picture-option2-stack.svg
and iteratively polished by gemini-3.1-pro-preview (3 rounds), which
normalised pill widths and pitches and tightened arrow marker
alignment. One small post-gemini fix: "08 Tr" → "08 Train" (the prior
abbreviation was a pre-polish artifact).
Caption and fig-alt in big-picture.qmd updated to describe the new
layout. "Three tiers" language replaced with "Four layers, bottom-up."
The Makefile was copying pdf/_build/TinyTorch-Guide.pdf up to
pdf/TinyTorch-Guide.pdf as "local-dev convenience" so `open
pdf/TinyTorch-Guide.pdf` would work. This created two copies of the
same artifact at different paths — confusing when the user asked which
was authoritative.
Simplify to a single source of truth at pdf/_build/TinyTorch-Guide.pdf:
- Makefile: remove the `cp "$(PDF_BUILD)" "$(PDF_OUT)"` step. PDF_OUT
now IS the _build path. Help text and distclean target updated
accordingly.
- tools/measure-pdf-images.py: default PDF path updated from
pdf/TinyTorch-Guide.pdf to pdf/_build/TinyTorch-Guide.pdf.
CI workflows (tinytorch-build-pdfs.yml and tinytorch-update-pdfs.yml)
already point at pdf/_build/TinyTorch-Guide.pdf from the earlier
consistency pass (commit 7d9eab7ed era), so they're unaffected.
Local dev uses `open pdf/_build/TinyTorch-Guide.pdf` or an `open`
wrapper — one extra path segment, zero ambiguity about which file is
the real artifact.
Ran gemini-3.1-pro-preview as a multimodal reviewer against the
rendered PNG. Gemini identified two subtle clearance issues and
applied minimal fixes:
- F/A gutter routing for DataLoader -> CNNs/Tokenization was at
x=250 (the dashed divider line itself). Moved to x=265 so the
red arrow ascender has 15px clearance from the divider stroke
and doesn't visually merge with it.
- Cross-tier bus for CNNs -> Profiling was at y=68. The tier
headers sit at y=66 (font-size 12, baseline). Raised... sorry,
LOWERED the bus to y=72 so it clears the descenders of the
tier-header text (~4px clearance below baseline).
Iteration-0 and iteration-1 PNG renders captured at
.claude/_reviews/big-picture-iter-{0,1}.png for the audit trail.
Two visible line-geometry defects in the big-picture Module Flow PDF
render that were making the arrows look "not clean" at print size:
1. DataLoader -> Tokenization arrow double-drew the bus segment
x=250..335 at y=78. The DataLoader -> CNNs path (arrow 1) draws
that segment once, then the DataLoader -> Tokenization path (arrow
2) started back at x=250 and drew the same segment again. At
stroke-width 1.5, the double-draw rendered visibly thicker than
the rest of the bus. Fix: start arrow 2 at x=335 (the CNNs branch
point) so the two paths share that point but do not overlap.
2. CNNs -> Profiling arrow exited CNNs by going straight UP at x=390
(the right edge of CNNs). From y=106 (exit point) to y=90 (CNNs
top), that 16px vertical runs ON TOP of the CNNs right-edge
stroke, making the box border look double-thick. Fix: exit right
10px to (400, 106) before turning up, so the path never overlaps
the box stroke.
No semantic change — same arrow heads, same targets, same ink ratio.
Just cleaner routing.