Files
cs249r_book/book/docs/LEGO_CELLS.md
2026-06-20 22:07:48 -04:00

12 KiB
Raw Permalink Blame History

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 (book/tools/audit/fmt/README.md), the agent verify playbook (.claude/rules/lego-verify.md), and pre-commit checks (lego-dead-code, ./book/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:

  1. Header comment — Context (section/callout ID), Goal, Show, and How.
  2. Class name — Scenario-specific (Gpt4ClusterMtbf), not generic (Calc).
  3. Four blocks (when applicable): LOAD → EXECUTE → GUARD → OUTPUT.
  4. Output values — Formatted strings (*_str) via typed helpers such as fmt_qty(), fmt_usd(), fmt_percent(), and fmt_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 semantic mult token, for example speedup_mult_str or speedup_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 ≤ 58 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, and mlsysim.physics.calc_* for architecture formulas. Reserve mlsysim.core.units for physics/units only (HOURS_PER_DAY, BYTES_FP16, latency stack).
  • Put #| echo: false as the first line after ```{python} (required by book-check-code).
  • Use fmt_int(round(x)) for computed integers; precision=0 only 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: TrainingDimensions spanning forward-pass prose and a wave-quantization table hundreds of lines apart). Exception: # │ Scope: chapter-anchor when 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_str without 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.py symbols (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 check registry --scope sources enforces 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 check code --scope lego-prose-literals flags 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 ~50100 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:

  1. Split the mega-class at callout boundaries.
  2. Rename classes to match the narrative beat.
  3. Inline duplicated constants from mlsysim where it reduces drift.
  4. Re-run fmt/audit_prose.py and ./book/binder check math --scope canonical on that chapter.

Future optional lints (lego-locality, lego-span, lego-cross-ref) may automate the checklist; until then, apply this contract in review.

  • book/tools/audit/fmt/README.md — spurious .0 / fmt precision workflow
  • ./book/binder check math --scope canonical — static fmt and suffix lint
  • ./book/binder check code --scope lego-prose-literals — walkthrough prose must not hardcode computed operands
  • ./book/binder check refs --scope inline-python — chapter exec validation
  • book/tools/audit/book_check_registry_sources.py — legacy alias / constant import gate
  • mlsysim/tests/test_constants_allowlist.py — CI lock on constants.py surface

Audit runbook (last-minute cleanup)

Use this pass before chapter sign-off or pre-push /precheck.

Phase 0 — Inventory

export PYTHONPATH=mlsysim
python3 book/tools/audit/lego_focal_verify.py book/quarto/contents/vol1 book/quarto/contents/vol2
python3 book/tools/audit/fmt/audit_fmt_usage.py --root book/quarto/contents
python3 book/tools/audit/fmt/fmt_prose_contract.py --root book/quarto/contents

Rank chapters by focal-verify failures + {python} ref density. Ledger: book/tools/audit/artifacts/lego_audit_ranked_chapters.md.

Phase 1 — Gates (per chapter or corpus)

CH=book/quarto/contents/vol1/introduction/introduction.qmd
./book/binder check code --scope lego-dead-code --path "$CH"
./book/binder check code --scope lego-prose-units --path "$CH"
python3 book/tools/audit/book_check_lego_prose_units.py "$CH"
python3 book/tools/audit/fmt/fmt_prose_contract.py "$CH"
python3 book/tools/audit/audit_math_canonical.py "$CH"
./book/binder check math --scope canonical --path "$CH"
python3 book/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. See book/tools/audit/artifacts/lego_closed_prose_audit.md.

Phase 2 — Naming contract

Every *_str output must match its formatter (see .claude/rules/lego-units.md):

  • Closed-fixed: *_w_str, *_gb_per_s_str → typed fmt with pinned unit=
  • Open: generic *_strfmt() and prose supplies unit
  • Scale: *_b_strfmt_params(..., scale="B"); *_m_strfmt_count(..., scale="M")
  • Avoid false tokens: top_k_neighbors_str not top_k_str when K is a dimension

Phase 3 — Prose preview (after each fix batch)

PYTHONPATH=mlsysim python3 book/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. Agent copy: .claude/rules/lego-verify.md.

Corpus exec sweep (all chapter QMDs, shared namespace):

python3 book/tools/audit/chapter_html_verify.py --vol1 training   # build + full lane
python3 book/tools/audit/chapter_html_verify.py --report
PYTHONPATH=mlsysim python3 book/tools/audit/fmt/audit_lego_html.py

Do not commit truncated book/quarto/config/_quarto-html-vol*.yml after binder builds — restore from git if the render list shrinks.

Phase 4 — Per-chapter replayable verify (PASS bar)

One chapter at a time: binder HTML build, every LEGO cell, every inline ref in rendered HTML prose, optional coherence review. Writes a certificate under book/tools/audit/artifacts/lego_chapter_reports/.

./book/tools/audit/verify_lego_chapter.sh vol1 introduction
./book/tools/audit/verify_lego_chapter.sh vol2 network_fabrics

PASS requires: HTML build clean; cells N/N; rendered prose refs N/N (value

  • HTML context); coherence review not FAIL. Re-run the same command after fixes.

Corpus sweep (sequential, resumable):

./book/tools/audit/run_all_lego_chapters.sh
# failures → book/tools/audit/artifacts/lego_chapter_failures.txt
# progress  → book/tools/audit/artifacts/lego_chapter_progress.md

Phase 5 — HTML spot checks

python3 book/tools/audit/fmt/audit_html.py book/quarto/_build/html-audit/vol1/introduction.html

Spot-check certificates for substituted QMD→HTML prose; no literal {python}.

Phase 6 — Capstone

./book/tools/audit/verify_lego_pipeline.sh   # or /precheck before push

Sign-off template: book/tools/audit/artifacts/lego_audit_signoff.md.

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) vs BrakingDistance / EdgeLatencyDistance (open m + prose “meters”).
  • fmt_length helper — optional typed formatter (pinned unit= or auto m/km) if the pattern repeats; see P5 in lego_audit_signoff.md.