Removes content-authoring scripts, figure and data generators, and their working notes, which are not needed to build, check, or release the books and sites. The vault CLI drops the matching subcommands and doctor check; docs, comments, ignore rules, and the codegen hash are updated to match what remains.
10 KiB
LEGO Cell Contract
Inline {python} cells in MLSysBook chapters follow a LEGO pattern: a small
class computes scenario values once, produces formatted *_str fields, and prose
references them with `{python} Class.field_str`.
This document defines how to author and review LEGO cells. It complements the
fmt/notation audit lane (binder/tools/audit/fmt/README.md) and the pre-commit checks
(lego-dead-code, ./binder/binder check math --scope canonical).
Core rule
One cell ≈ one narrative anchor — a callout, table, or tight paragraph
cluster. Place the cell immediately above the first {python} reference that
uses its output values.
Chapter-anchor exception: When one scenario's numbers must stay consistent
across a problem callout, a later walkthrough, and a summary bullet, mark the
header with # │ Scope: chapter-anchor and a one-line rationale. Focal verify
then allows multi-section reference span; render and cell-exec gates still apply.
This is not the unrelated mega-class anti-pattern (different output values in
disconnected narratives hundreds of lines apart).
Cell contract
Each LEGO cell should include:
- Header comment — Context (section/callout ID), Goal, Show, and How.
- Class name — Scenario-specific (
Gpt4ClusterMtbf), not generic (Calc). - Four blocks (when applicable): LOAD → EXECUTE → GUARD → OUTPUT.
- Output values — Formatted strings (
*_str) via typed helpers such asfmt_qty(),fmt_usd(),fmt_percent(), andfmt_multiple(). Units and fixed glyphs live in OUTPUT only; prose must not repeat units, percent signs, currency symbols, or multiplier glyphs after`{python} *_str`. Multiplier outputs use a semanticmulttoken, for examplespeedup_mult_strorspeedup_range_mult_str.
Example shape:
```{python}
#| echo: false
# ┌── LEGO ───────────────────────────────────────────────
# │ Goal: A100 ridge point for the latency callout below.
# │ Show: The formatted ridge point consumed by the following prose.
# │ How: Read the registry value and format it for inline prose.
class A100RidgeExample:
ridge = Hardware.Cloud.A100.ridge_fp16
ridge_str = fmt_int(round(ridge.magnitude), commas=False)
```
The A100 ridge is `{python} A100RidgeExample.ridge_str` FLOP/byte.
Do
- Keep ≤ 5–8 output values per cell when possible.
- Use registry paths for shared specs —
Hardware.Cloud.H100,Models.Vision.ResNet50,Systems.Reliability.Gpu.mttf_hours,Literature.Training.MfuHigh, andmlsysim.physics.calc_*for architecture formulas. Reservemlsysim.core.unitsfor physics/units only (HOURS_PER_DAY,BYTES_FP16, latency stack). - Put
#| echo: falseas the first line after```{python}(required bybook-check-code). - Use
fmt_int(round(x))for computed integers;precision=0only when the value is already integer-like at the source.
Don't
- Mega-classes — one class whose output values appear in multiple distant sections
or unrelated callouts (anti-pattern:
TrainingDimensionsspanning forward-pass prose and a wave-quantization table hundreds of lines apart). Exception:# │ Scope: chapter-anchorwhen the same scenario thread intentionally reuses the same output values (KWS case-study targets, a lighthouse profile + takeaway, GPT-3 household-year anchor, build/buy TCO + summary). - Cross-cell reads — a later cell referencing
OtherClass.some_strwithout redefining inputs in the same cell (hidden exec-order dependency). - Duplicate classes — two cells for the same story (e.g. separate table and prose calcs for one latency budget); merge or split by narrative beat, not by output type.
- Kitchen-sink output sections — a cell emits ten fields used once each across the chapter; split by callout instead.
- Legacy constant output names — do not mirror removed
constants.pysymbols (H100_FLOPS_FP16_TENSOR_val_str,GPUS_PER_HOST_str, …). Use scenario-descriptive names (h100_peak_fp16_val_str,dgx_h100_gpus_per_node_str) with registry paths on the RHS../binder/binder check registry --scope sourcesenforces this. - Hardcoded walkthrough operands — in callout Problem / Setup / Step prose that
already uses
{python} Class.field_str, do not type scenario inputs or intermediate numbers (100 GPUs,70B × 2,10×,/365,\$2/GPU-hour). Provide setup inputs, operands, rates, and multipliers from the cell../binder/binder check code --scope lego-prose-literalsflags common cases.
Scope boundary (judgment): The gate targets computational callouts—where a
LEGO cell derives {python} *_str from scenario inputs. Pure narrative
(100--1,000× more expensive than arithmetic), teaching asides, and footnotes
stay literal. If a number is an input to or step in worked math, compute it in the cell.
Review checklist
When editing or auditing a chapter:
| Check | Question |
|---|---|
| Locality | Is the cell within ~50–100 lines of the first ref? |
| Span | Do all output values appear in the same callout / ## section? |
| Coupling | Does any other cell read this class's attributes? |
| Dead code | Does lego-dead-code report unused output values? |
| Walkthrough literals | Does lego-prose-literals pass on touched callouts? |
| Fmt | Do prose preview + canonical audit pass (fmt/ workflow)? |
Migration (existing chapters)
Do not big-bang refactor large chapters. When a file is already open for copyedit, fact-check, or fmt audit:
- Split the mega-class at callout boundaries.
- Rename classes to match the narrative beat.
- Inline duplicated constants from
mlsysimwhere it reduces drift. - Re-run
fmt/audit_prose.pyand./binder/binder check math --scope canonicalon that chapter.
Future optional lints (lego-locality, lego-span, lego-cross-ref) may
automate the checklist; until then, apply this contract in review.
Related
binder/tools/audit/fmt/README.md— spurious.0/ fmt precision workflow./binder/binder check math --scope canonical— static fmt and suffix lint./binder/binder check code --scope lego-prose-literals— walkthrough prose must not hardcode computed operands./binder/binder check refs --scope inline-python— chapter exec validationbinder/tools/audit/book_check_registry_sources.py— legacy alias / constant import gatemlsysim/tests/test_constants_allowlist.py— CI lock onconstants.pysurface
Audit runbook (last-minute cleanup)
Use this pass before chapter sign-off or pre-push /precheck.
Phase 0 — Inventory
export PYTHONPATH=mlsysim
python3 binder/tools/audit/lego_focal_verify.py books/vol1 books/vol2
python3 binder/tools/audit/fmt/audit_fmt_usage.py --root books
python3 binder/tools/audit/fmt/fmt_prose_contract.py --root books
Rank chapters by focal-verify failures + {python} ref density. Ledger:
binder/tools/audit/artifacts/lego_audit_ranked_chapters.md.
Phase 1 — Gates (per chapter or corpus)
CH=books/vol1/01_introduction/01_introduction.qmd
./binder/binder check code --scope lego-dead-code --path "$CH"
./binder/binder check code --scope lego-prose-units --path "$CH"
python3 binder/tools/audit/book_check_lego_prose_units.py "$CH"
python3 binder/tools/audit/fmt/fmt_prose_contract.py "$CH"
python3 binder/tools/audit/audit_math_canonical.py "$CH"
./binder/binder check math --scope canonical --path "$CH"
python3 binder/tools/scripts/maintenance/validate_inline_refs.py --path "$CH"
Prose-unit contract (two lanes): lego-prose-units / book_check_lego_prose_units.py
flags domain glyphs (FLOP/byte, TFLOP/s, g/kWh, GB, …) immediately after a
closed `{python} *_str` output. fmt_prose_contract.py flags %, $, scale,
and × duplication. Open fmt() outputs intentionally keep units in prose.
Phase 2 — Naming contract
Every *_str output must match its formatter:
- Closed-fixed:
*_w_str,*_gb_per_s_str→ typed fmt with pinnedunit= - Open: generic
*_str→fmt()and prose supplies unit - Scale:
*_b_str→fmt_params(..., scale="B");*_m_str→fmt_count(..., scale="M") - Avoid false tokens:
top_k_neighbors_strnottop_k_strwhen K is a dimension
Phase 3 — Prose preview (after each fix batch)
PYTHONPATH=mlsysim python3 binder/tools/audit/fmt/audit_prose.py "$CH" --flagged-only
Precision guard triage (when exec fails with Formatting Precision Error):
| Error | Fix |
|---|---|
formatted as '0' |
Raise precision or change display unit |
not integer-like but precision=0 |
Use precision>=1 |
integer-like but precision=1 … '10.0' |
Use precision=0 or omit precision= |
For fmt_arithmetic_intensity, prefer no explicit precision= (auto:
integer-like → 0, fractional → 1). Pass explicit precision only for pinned
scenario literals (e.g. 300 FLOP/byte). 7.0 FLOP/byte is a smell on exact
integers; 153.0 FLOP/byte is normal for fractional ridge points at
precision 1.
Corpus exec sweep (all chapter QMDs, shared namespace):
python3 binder/tools/audit/chapter_html_verify.py --vol1 training # build + full lane
python3 binder/tools/audit/chapter_html_verify.py --report
PYTHONPATH=mlsysim python3 binder/tools/audit/fmt/audit_lego_html.py
Do not commit truncated books/config/_quarto-html-vol*.yml after binder
builds — restore from git if the render list shrinks.
Phase 4 — HTML spot checks
python3 binder/tools/audit/fmt/audit_html.py books/_build/html-audit/vol1/introduction.html
Spot-check certificates for substituted QMD→HTML prose; no literal {python}.
Follow-up: distance / length outputs (P5)
Some cells still use open fmt() for meters while prose supplies the unit:
distance_str = fmt(distance_m, precision=2, commas=False)
# prose: ... `{python} EdgeLatencyDistance.distance_str` meters ...
This pass deferred a corpus-wide decision on:
- Comma rule — no commas for small decimals (2.8 m, 3.33 m); commas when displaying km or values ≥ 1,000 m.
- Closed vs open — compare
LightLatency.distance_str(fmt_qty, closed km) vsBrakingDistance/EdgeLatencyDistance(open m + prose “meters”). fmt_lengthhelper — optional typed formatter (pinnedunit=or auto m/km) if the pattern repeats.