Files

260 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
````markdown
```{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.
## Related
- `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
```bash
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)
```bash
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 `*_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_str` not `top_k_str` when K is a dimension
### Phase 3 — Prose preview (after each fix batch)
```bash
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):
```bash
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/`.
```bash
./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):
```bash
./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
```bash
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
```bash
./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:
```python
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`.