A bold lead-in paragraph (e.g. **Step 2**: ...) immediately followed by a
list item with no blank line between them is parsed by Pandoc as one
paragraph, so the list marker can render inline. Add a markup check
(check markup --scope list-spacing) that flags this, extend the
format lists fixer to insert the blank line for bold lead-ins (not just
colon-terminated lead-ins), and cover both with tests.
Add native Binder checks for LEGO formatting/unit/prose contracts, extend mlsysim formatter helpers, and normalize Vol1/Vol2 LEGO output strings through typed formatters.
Validate precision-sensitive prose rendering across both volumes and promote direct math/string assembly to fmt_math, fmt_display_math, fmt_text, and domain-specific helpers.
Adds 'binder check cli --scope binder-canonical' (default=yes): parses
.pre-commit-config.yaml and fails if any local hook scoped to book/quarto/
contents/ calls a raw script instead of ./book/binder. Repo-wide guards, CI
hygiene, third-party hooks, and other subprojects are not book content and
are not inspected; an explicit ALLOWLIST covers any justified exception.
This makes Binder-as-single-front-door a mechanically enforced invariant
instead of a convention — the day someone wires an off-Binder book hook (as
book-check-lego-units once was), the commit fails with an actionable fix.
Runs via the existing always-run book-check-cli-contract hook; no new hook.
Also hardens the cli-contract 'check cli help' case to assert on stable
tokens (group header + scope names) rather than the note phrase, which Rich
wraps once a group gains a scope. Extends it to cover the new scope.
Automated fixers consume 'binder check <group> --json', so every issue must
carry the exact fix. Populate ValidationIssue.suggestion as '<wrong> → <right>'
for both percent checks:
- percent_in_table: '50 percent → 50%'
- percent_in_prose: '98.5% → 98.5 percent'; range hits get a judgment note
('30–50% → (range) X to Y percent') instead of a misleading per-value fix.
percent_prose.Hit now carries an explicit replacement + is_range flag.
Documented the contract (file/line/code/suggestion/context) in the
ValidateCommand 'adding a check' docstring so future checks inherit it.
The _run_mitpress_* prefix already marks checks that trace to the MIT Press
copyedit (em-dash, slash, vs., e.g./i.e., percent-in-captions, etc.). Extend
it to six more that derive from the copy editor's style sheet / AU query
categories but were not yet labeled, so provenance is visible in the check
registry:
_run_abbreviation_first_use -> _run_mitpress_abbreviation_first_use (Cat F + abbrev list)
_run_concept_term_capitalization -> _run_mitpress_concept_term_capitalization (Cat G + glossary cap)
_run_percent_in_prose -> _run_mitpress_percent_in_prose (style sheet: spell out %)
_run_percent_in_tables -> _run_mitpress_percent_in_tables (style sheet: % kept in tables)
_run_latin_running_text -> _run_mitpress_latin_running_text (spell out e.g./i.e./viz.)
_run_compound_prefix -> _run_mitpress_compound_prefix (close up non-/re-/pre-)
Scope names (CLI surface) are unchanged, so pre-commit, CI, and the CLI
contract are unaffected; only the internal method identifiers move.
New 'percent-in-prose' scope (numbers group, default=True) flags the %
symbol where body prose should spell out 'percent', the symmetric inverse of
percent-in-tables. Per MIT Press AU response Category H / Chicago §9.18,
running prose (body, caption sentences, alt text) uses the word; % is for
tables, equations, code, and in-figure data.
Detection (cli/checks/percent_prose.py) reuses the pipe-table walk to exclude
tables and exempts every legitimate non-prose context: code, math, Quarto
attributes ({width=100%}), link/image targets and URLs, <style> CSS, HTML
comments, caption/alt lines, and quoted material (a % inside double quotes is
a verbatim quotation). No auto-fixer: prose ranges need judgment.
Tables invert the prose convention and use the % symbol, not the word
'percent'. This adds enforcement on both sides, sharing one implementation
in cli/checks/percent_tables.py (matching the cli/checks/currency_style.py
pattern) so the fixer fixes exactly what the check flags:
- check: 'percent-in-tables' scope in the numbers group (validate.py),
default=True → runs in book-check-numbers pre-commit + validate-dev
CI automatically.
- fix: native 'percent-tables' target in the format command
(./book/binder format percent-tables), wired as the
book-format-percent-tables pre-commit hook ahead of prettify so
columns realign after the substitution shortens cells.
Detection reuses the header+separator pipe-table walk shared by the other
table checks, gated by a number-token before 'percent' so header labels and
'percentage points / pp' are never touched.
Fix naming mismatches, migrate ridge/intensity exports to fmt_arithmetic_intensity, adopt fmt_length and fmt_rate for distance and speed, and register fmt_length in binder checks.
Pre-commit lego-units now lints only staged QMDs with per-file baseline so
parallel WIP does not block atomic migration commits; full-repo baseline remains for CI.
math_canonical's canonical-helper allowlist predated the typed formatters, so
fmt_multiple/fmt_percent/fmt_pp/fmt_count/fmt_ratio/fmt_range assignments were
wrongly flagged as noncanonical_str_assign. Add them to CANONICAL_STR_CALL,
FMT_FAMILY_USE, and MLSYSIM_STAR_FMT_NAMES (and the error message). Required
for any typed-formatter migration to be committable.
Introduce semantic value-kind formatters (fmt_percent with 0-1 ratio
guard, fmt_pp, fmt_multiple, fmt_count, fmt_ratio) plus a universal
finite guard, moving away from generic fmt(prefix=/suffix=). Add the
fmt_semantic_suffix checker (forbids %/percent/x/scale glyphs in
suffix=) wired as an opt-in math scope, update math_canonical messaging
to the new style= API, and land the AST audit tooling.
Foundation for the corpus-wide formatting migration; no .qmd changes yet.
Currency was the only fmt_* value-kind without its own helper, so every
dollar amount carried a hand-escaped prefix="\$" -- the exact pattern that
silently breaks Pandoc/LaTeX when the escape is forgotten. Introduce
fmt_usd() (the currency sibling of fmt_percent): it owns the escaped \$,
the ~ approximation marker, and whole-dollar rounding, and never emits a
literal "USD".
- mlsysim.fmt: add fmt_usd; fmt_qty now refuses currency units (was a
dormant " USD" leak); export fmt_usd from mlsysim.
- Migrate all 271 currency call sites across both volumes to fmt_usd.
- currency_style check: forbid $ in any prefix=/suffix= and enforce the
fmt_usd path; register fmt_usd as a canonical helper in math_canonical.
- units.py: document dollar (magnitude) vs fmt_usd (display) split.
- Restore canonical $\times$ math notation in the vol2 glossary.
Validated: numbers/currency/math/code checks clean; 22 fmt + 9 checker +
499 mlsysim tests pass; 4 currency-heavy chapters render with $ output
and no leaked \$ or visible USD.
Replace pointers to the private project rules/docs tree (relative .claude/rules
and .claude/docs paths) in code comments and docstrings with neutral phrasing
("the project prose style guide", etc.). Load-bearing runtime paths that the
tooling reads or writes are left intact.
1. python-dollar-collision (code, default=True): flag \${python} in QMD
prose — escaped dollar before {python} silently kills inline expression
rendering. Correct form is \$\`{python}. Zero violations on current
corpus; prevents regression.
2. suffix-consistency (math, default=True): blocklist for banned suffix=
values in fmt() calls — wrong unit conventions like TFLOPS, Gbps, TBps
that should use TFLOP/s, Gb/s, TB/s slash-s form.
3. pdf-warnings (pdf, default=False): post-build scan of PDF text for
UserWarning strings that leaked into rendered output. Reports as
warnings since the content may be legitimate.
4. Cell execution warning capture (audit_lego_cells.py): captures Python
UserWarning during LEGO cell execution using warnings.catch_warnings.
Flags Precision, not found, and deprecated warnings. Reports as
WARN vol/chapter: CellClass emitted UserWarning: <message>.
5. fmt_percent_suffix (math-canonical scope): detects fmt_percent() calls
with suffix= keyword — fmt_percent does not accept suffix= and would
raise TypeError at render time. Suggests fmt(x * 100, ...) instead.
6. rendered-python-leak (code, default=False): scans _build/html-audit/
HTML files for literal {python} text that escaped Quarto evaluation.
Excludes {python} inside <code> tags (legitimate code listings).
Strip 2758 redundant unit tokens after {python} *_str refs;
add migration journal and baseline; OUTPUT-only units policy;
fix MobileServingCalc pint path (66 mW); recognize fmt_qty in
math-canonical; defer new linters from default pre-commit.