|
| 1 | +# Diagram upgrade plan |
| 2 | + |
| 3 | +Written 2026-09-27 from a review of every attached figure on the live |
| 4 | +site, the gestalt review pages, the grammar, both rubrics and the |
| 5 | +registry. The rubrics remain the scoring instruments. This document |
| 6 | +records what the review found, the principles that follow from it, |
| 7 | +what the first pull request changed, and the queue after that. |
| 8 | + |
| 9 | +## What the review found |
| 10 | + |
| 11 | +The library's strengths are enforced rather than hoped for: the palette, |
| 12 | +stroke weights and typography are locked by contracts, captions assert |
| 13 | +what is true instead of narrating the picture, and the gestalt review |
| 14 | +page paints from the production paint functions. The figures that teach |
| 15 | +best show a mechanism changing state, such as aliasing before and after |
| 16 | +an append, the iterator caret advancing one row at a time, and the MRO |
| 17 | +diamond flattening into a chain. |
| 18 | + |
| 19 | +The weaknesses are measurable. |
| 20 | + |
| 21 | +| finding | measure | |
| 22 | +|---|---:| |
| 23 | +| paint functions using only cell, arrow, dashed line and text | 66 of 124 | |
| 24 | +| `cell` calls / `closed_arrow` calls / `lanes` calls | 236 / 129 / 1 | |
| 25 | +| attachments anchored after cell 0 | 106 of 115 | |
| 26 | +| example figures scoring exactly 9.0 (the rest 9.5, none lower) | 106 of 109 | |
| 27 | +| journey figures scoring exactly 9.0 | 21 of 21 | |
| 28 | +| arrows shorter than 20 units | 9 | |
| 29 | + |
| 30 | +Behind the numbers: figures hard-coded the light palette, so in dark |
| 31 | +mode each one sat on a cream chip; a paired banner had no shared |
| 32 | +baseline; the SVG asked for JetBrains Mono and Source Sans while the |
| 33 | +page loaded no web fonts, so the visitor's operating system chose the |
| 34 | +faces; several selector figures put the accent on whichever row the |
| 35 | +loop reached last; and because no contract read figure text, the |
| 36 | +bytes-and-bytearray figure rendered doubled backslashes for months and |
| 37 | +the regular-expressions figure drew a pattern the example never uses. |
| 38 | + |
| 39 | +## Principles |
| 40 | + |
| 41 | +1. **Show state over time.** One frame is a label. The same frame after |
| 42 | + each step is a mechanism. Repeating one drawing is cheap in this |
| 43 | + grammar; `iterator-unroll` and `generator-resume` do it. |
| 44 | +2. **Draw the heap.** Python's picture is frames on the left and objects |
| 45 | + on the right with arrows crossing between them. The grammar now has |
| 46 | + an `env` phrase for the frame, and closures uses it. |
| 47 | +3. **Redraw a weak figure; never replace it with a note.** A drawing |
| 48 | + that would be no worse as a two-column table needs a mechanism |
| 49 | + drawn in its place. Removing it and recording a rationale leaves the |
| 50 | + page with less than it had, which the first draft of this plan got |
| 51 | + wrong for four pages. |
| 52 | +4. **The figure carries the lesson's own names.** Reuse the mechanism |
| 53 | + by parametrising the paint function, so `variables` shows `message` |
| 54 | + and `constants` shows `MAX_RETRIES`. |
| 55 | +5. **Accent only what the caption names.** When the caption names no |
| 56 | + single element, the figure has no accent. |
| 57 | +6. **Check figure content against the example.** Geometry contracts |
| 58 | + cannot see a wrong label. A content contract can. |
| 59 | +7. **Paint with tokens.** The grammar emits `var(--fig-*)`; the page's |
| 60 | + stylesheet decides the ink in each colour scheme. |
| 61 | +8. **Typography follows the page.** Mono and sans use the page's own |
| 62 | + stacks, and mono runs carry `textLength` so divider maths holds on |
| 63 | + every platform. The italic serif stays for identifiers. |
| 64 | +9. **Rank before scoring.** With 106 of 109 figures at 9.0, the score |
| 65 | + is a ship gate. Choosing what to redesign next needs a ranking. |
| 66 | + |
| 67 | +## What the first pull request changed |
| 68 | + |
| 69 | +Grammar (`src/marginalia_grammar.py`): |
| 70 | + |
| 71 | +- `INK`, `INK_SOFT`, `EMPHASIS` and `SOFT_FILL` are `var(--fig-*)` |
| 72 | + references. `FIGURE_TOKENS_LIGHT` and `FIGURE_TOKENS_DARK` hold the |
| 73 | + values; `figure_token_css()` emits them for the gestalt pages and the |
| 74 | + social-card shell. |
| 75 | +- `FONT_MONO` and `FONT_SANS` match `public/site.css`; every mono run is |
| 76 | + pinned to `MONO_ADVANCE` with `textLength`. |
| 77 | +- `ARROW_MIN = 20`; `Canvas` records every arrow's length. |
| 78 | +- `name_box` and `bind` take a width so long names fit. |
| 79 | +- The `env` phrase draws a frame with bindings, optionally ghosted, and |
| 80 | + returns anchors for heap arrows. |
| 81 | + |
| 82 | +Figures (`src/marginalia.py`): |
| 83 | + |
| 84 | +- `closure-cell` is an environment diagram: the returned frame ghosted, |
| 85 | + `double` naming a heap function object, one accented `__closure__` |
| 86 | + reference back into the frame. |
| 87 | +- `generator-resume` replaces `generator-ribbon`: three ribbons for |
| 88 | + three `next()` calls on the cell's own `countdown(3)`, the surviving |
| 89 | + local beside each. |
| 90 | +- `format-spec` is a railroad tracing the cell's own `05.1f`: visited |
| 91 | + stations shaded, skipped stations ghosted under a bypass rail. |
| 92 | +- `bytes-vs-bytearray` draws the example's bytes as integer slots and |
| 93 | + the in-place write `packet[0] = ord("P")`, anchored to the cell that |
| 94 | + performs it. |
| 95 | +- `regex-groups` replaces `regex-anchors`: the cell's two capture |
| 96 | + groups dropped onto the record `Ada: 10`. |
| 97 | +- `value-type-lookup` replaces `value-types`: `text` bound to a `str` |
| 98 | + object, then the `type()` hop to the class where `upper()` lives. |
| 99 | +- `logging-threshold` replaces `logging-levels`: the cell's three |
| 100 | + records crossing the handler's INFO gate, `debug` dropped. |
| 101 | +- `variables-bind` and `constants-bind` come from one parametrised |
| 102 | + factory. |
| 103 | +- Nine arrows lengthened to the minimum; five figures lost an accent |
| 104 | + that marked an arbitrary row. |
| 105 | +- `literal-forms` and `collections-containers` stay as they were, scored |
| 106 | + 8.5, until a mechanism replaces them. |
| 107 | + |
| 108 | +Page (`public/site.css`, `src/templates/about.html`): |
| 109 | + |
| 110 | +- `--fig-ink`, `--fig-ink-soft`, `--fig-accent` and `--fig-soft` defined |
| 111 | + for light and dark; the dark-mode paper chip removed. |
| 112 | +- `.cell-banner figure` is a two-row subgrid, so paired figures |
| 113 | + bottom-align their drawings and start both captions on one line. |
| 114 | + |
| 115 | +Contracts (`tests/test_marginalia_geometry.py`): |
| 116 | + |
| 117 | +- 13, content: no doubled backslash anywhere; an escape sequence in an |
| 118 | + attached figure must appear in that example's source. |
| 119 | +- 14, arrow length: every arrow at least `ARROW_MIN`. |
| 120 | +- 15, theme tokens: `site.css` defines all four tokens in both schemes |
| 121 | + with the grammar's values, and every grammar colour is a token. |
| 122 | + |
| 123 | +Rubrics: the redraw rule, the zero-accent rule, the placement note and |
| 124 | +the three contracts are in `docs/example-figure-rubric.md` and |
| 125 | +`docs/journey-visualisation-rubric.md`; the token and typography |
| 126 | +invariants are in `docs/visual-explainer-spec.md`. |
| 127 | + |
| 128 | +## Queue |
| 129 | + |
| 130 | +Ordered by payoff. Each item is one pull request. |
| 131 | + |
| 132 | +1. **State over time** for `context-managers` (enter, body, exit, with |
| 133 | + the exception path as a second strip), `decorators` (two calls, two |
| 134 | + wrappers) and `object-lifecycle`. |
| 135 | +2. **Heap drawings** with `env` for `scope-global-nonlocal`, `recursion` |
| 136 | + (frames with `n` bindings instead of labelled cells) and `unpacking`. |
| 137 | +3. **Mechanisms for the two table figures.** `literals`: the same value |
| 138 | + spelled three ways converging on one `int` object. `collections-module`: |
| 139 | + one container's operation, or split the page. |
| 140 | +4. **Parametrise the remaining reused figures**: `iterator-unroll`, |
| 141 | + `iter-protocol`, `class-triangle`, `operator-dispatch`, |
| 142 | + `comprehension-equivalence`. |
| 143 | +5. **Move anchors to the cell where the move happens.** For each cell-0 |
| 144 | + attachment, ask whether a later cell holds the mutation, dispatch or |
| 145 | + resume the figure depicts. Rewrite criterion 10 once the distribution |
| 146 | + is honest. |
| 147 | +6. **Rank the gestalt.** A pairwise ranking table in the registry, and |
| 148 | + redesign from the bottom ten. The 8.5 score stays as the gate. |
| 149 | +7. **Widen the content contract** from escape sequences to every mono |
| 150 | + literal that looks like Python source: it must appear in the |
| 151 | + example's code or output. |
| 152 | +8. **Buffers and hashes.** `dict-buckets` and `set-buckets` with hash |
| 153 | + values from the example; `list-append` and `slice-ruler` as header |
| 154 | + plus buffer. |
| 155 | + |
| 156 | +## What to study |
| 157 | + |
| 158 | +- **Composing Programs** (composingprograms.com) and **Python Tutor** |
| 159 | + (pythontutor.com) for the environment diagram: frames left, objects |
| 160 | + right, and how a closure and a recursive call are drawn. |
| 161 | +- **Ned Batchelder, "Facts and myths about Python names and values"** |
| 162 | + for name-to-value drawings done the same way every time. |
| 163 | +- **Crafting Interpreters** (craftinginterpreters.com) for warm ink on |
| 164 | + cream, one idea per figure, held consistent across a whole book. |
| 165 | +- **Sam Rose, samwho.dev** ("Hashing", "Memory Allocation") for |
| 166 | + programmer-facing figures with a locked grammar; the model for the |
| 167 | + bucket figures. |
| 168 | +- **Lydia Hallie, "JavaScript Visualized"** for the frame designs of |
| 169 | + scope chain and closures. |
| 170 | +- **Bartosz Ciechanowski** (ciechanow.ski) for figures where exactly |
| 171 | + one variable changes between frames. |
| 172 | +- **SQLite syntax diagrams** for what a railroad is; `format-spec` |
| 173 | + follows them. |
| 174 | +- **The Rust Book, chapter 4** and **"Go Slices: usage and internals"** |
| 175 | + for header-plus-buffer drawings of list, bytearray and slice. |
| 176 | +- **Tufte, Visual Explanations, "the smallest effective difference"** |
| 177 | + for how little contrast emphasis needs. |
| 178 | +- **Maggie Appleton** and **Julia Evans** for mechanism drawings that stay playful without adding marks. |
| 179 | +- **Distill.pub** for captions that carry the claim. |
0 commit comments