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.
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.
_collapse_blank_lines toggled code-block state on every ``` line,
but commented-out figures (<!-- ... -->) often contain partial code
blocks with unbalanced fences. One stray fence inverted the toggle for
the rest of the file, so real python cells were treated as prose and
their blank lines collapsed (observed in security_privacy.qmd, which
desynced at its first commented-out tikz block).
Skip fence tracking and blank collapsing inside HTML comment regions;
commented-out content passes through verbatim.
The book-format-python hook's fence regex matched display blocks
(```python, ```{.python}) but skipped executable ```{python} cells,
so hand-aligned assignment columns accumulated unchecked in LEGO code
(931 aligned lines across 22 files).
Add a second formatting profile for executable cells:
- Black at 150 chars (keeps one-line fmt_* exports; the display-block
70-char profile would explode them into multiline calls)
- --skip-string-normalization (preserves the corpus quote-style mix)
- #| option lines shielded with a sentinel (Black rewrites '#|' to
'# |', which breaks Quarto cell options)
- no comment wrapping (LEGO box-drawing headers must not rewrap)
- blank-line runs collapsed to one inside cells, keeping cells compact
and making output a fixed point for book-format-blanks, whose fence
toggle can desync on commented-out partial code blocks
Apply the one-time mechanical reformat across both volumes. Black
verifies AST equivalence, so all cells are semantically unchanged;
binder math/code checks and the LEGO test suites pass.
Remove redundant ml_ prefix from ml_workflow chapter files and update all
Quarto config references. Consolidate custom scripts into native binder
subcommands and archive obsolete tooling.