Skip to content

Commit e43fff9

Browse files
committed
Add unique content, figure, and doc fixes on top of audit follow-ups
Layers the non-overlapping parts of a parallel audit-remediation pass onto the editorial-registry/security/CSP work already in HEAD. Only the items the follow-up commit did not cover are included here; redundant work (confusable code-scoping, broad-tour focused_neighbors, security headers, the score-registry move) is intentionally omitted in favour of the existing implementation. Content correctness: - special-methods: __lt__ now orders by contents (consistent with __eq__, which the old length-based __lt__ contradicted) and the __hash__ cell demonstrates the mutable-hash hazard it warns about — a Bag found in a set becomes unfindable after its items change. - subprocesses / threads-and-processes / networking: drop the duplicated :::unsupported block that restated the executable cell's code; each page keeps one verified cell whose prose carries the mechanism plus one honest sentence about the sandbox boundary. Figures: - Remove dead grammar vocabulary (open_arrow, dispatch, register(between=), the orphaned ARROW_OPEN constant) — no callers. - FigureSizeContract gains a banner-height check (<=440px displayed at banner width); new FigureLineTextCollisionContract (Contract 12) rejects a solid line striking a label. The latter caught a real collision in control-stop-boundary (the first-true arrow clipped the 'd' label), fixed by starting the arrow below the cell row. Docs: - rubric-saturation: 124 figures / 7 reused (was 109 / 12). - observability-spec: cleanup snippet gains the not-used guard. - rubric-audit-2026-05-12: marked superseded; journey count 24 -> 21. - figure-rubric documents Contract 12 and the height check. https://claude.ai/code/session_018EpcfENTg7herPR712vZeE
1 parent a7f0125 commit e43fff9

15 files changed

Lines changed: 102 additions & 88 deletions

docs/example-figure-rubric.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,13 @@ the figure can merge.
161161
- **Banner-fit, enforced.** Every figure's RENDERED width —
162162
INTRINSIC_SCALE · (Canvas.w + 2 · PAD_X) — must fit the 640px
163163
ceiling of `.cell-banner--1` / `.journey-section-figure`
164-
(`clamp(280px, 65-70vw, 640px)` in site.css). *Contract 8.*
164+
(`clamp(280px, 65-70vw, 640px)` in site.css). The figure must also not
165+
display taller than 440px at that banner width, or it reads as a
166+
portrait column rather than a margin banner. *Contract 8.*
167+
- **No solid line strikes a label.** A solid `<line>` may not cross a
168+
text label's interior (reads as an accidental strike-through);
169+
deliberate *dashed* strikes through a label are allowed. *Contract 12
170+
— FigureLineTextCollisionContract.*
165171
- **Twin consistency.** When two figures depict parallel concepts
166172
(`kw-only-separator``positional-only-separator`,
167173
`class-triangle``metaclass-triangle`), their metrics must

docs/observability-spec.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,10 @@ Do not emit a second per-request log. Attach the cleanup failure to the same wid
503503

504504
```python
505505
finally:
506-
if code_callback is not None and hasattr(code_callback, "destroy"):
506+
# create_once_callable destroys itself after the Dynamic Loader invokes it,
507+
# so destroy ONLY callbacks that went unused (e.g. cache-hit paths) to avoid
508+
# false "OnceProxy has already been destroyed" cleanup errors.
509+
if code_callback is not None and not code_callback_used and hasattr(code_callback, "destroy"):
507510
try:
508511
code_callback.destroy()
509512
except Exception as exc:

docs/rubric-audit-2026-05-12.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# Rubric audit snapshot — 2026-05-12
22

3+
> **Superseded.** This is a point-in-time snapshot. The catalog has since dropped the Workers journey (journey sections went from 24 to 21) and the figure registry grew past a one-per-slug count. The journey-section counts below have been corrected to the current 21; regenerate a fresh snapshot with `make rubric-audit` rather than trusting the rest of these frozen numbers.
4+
35
This snapshot audits the shipped catalog against the example, example-figure, and journey-visualisation rubrics. It records the green baseline and the accepted exception so future passes can hunt for semantic drift instead of rediscovering the same ledgers.
46

57
## Scoreboard
68

79
- Examples: count=109, min=7.1, avg=8.98, median=9, below9=1, distribution=7.1 × 1, 9 × 108
810
- Example diagrams: count=109, min=9, avg=9.01, median=9, below9=0, distribution=9 × 106, 9.5 × 3
9-
- Journey diagrams: count=24, min=9, avg=9.02, median=9, below9=0, distribution=9 × 23, 9.5 × 1
11+
- Journey diagrams: count=21, min=9, avg=9.02, median=9, below9=0, distribution=9 × 20, 9.5 × 1
1012
- Accepted waiver: `hello-world` remains intentionally tiny at 7.1.
1113
- Graph health: 109 linked sources, 361 edges, 0 orphaned examples.
1214

@@ -54,7 +56,7 @@ This snapshot audits the shipped catalog against the example, example-figure, an
5456
| Grammar conformance | Shared geometry contracts | PASS |
5557
| Independence from lesson figures | Section figures compared with example attachments | PASS |
5658
| Layout fit | Journey figure dimensions within production column | PASS |
57-
| Outcome support | `check_journey_outcomes.py` over all 24 sections | PASS |
59+
| Outcome support | `check_journey_outcomes.py` over all 21 sections | PASS |
5860
| Prerequisite order | Journey order reviewed against lesson dependencies | PASS |
5961

6062
## Example and diagram inventory

docs/rubric-saturation.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# Rubric saturation analysis
22

3-
After six iteration passes, the figure system has 109 examples
4-
attached (one per slug on `main`) and 109 figures in
5-
`src/marginalia.py FIGURES`. Coverage is 100%. Distribution against
6-
`docs/example-figure-rubric.md`:
3+
After six iteration passes, the figure system attaches a figure to
4+
every example (one banner per slug on `main`) from the 124 paint
5+
functions in `src/marginalia.py FIGURES`. Coverage is 100%.
6+
Distribution against `docs/example-figure-rubric.md`:
77

88
| band | count | composition |
99
|---|---:|---|
@@ -27,10 +27,10 @@ at 8.5 by design:
2727
A figure loses up to 1.0 when its placeholders (`a`, `b`, `xs`) do
2828
not match the cell's specific names (`first`, `second`, `factor`,
2929
`numbers`). For a library of 109 figures across 109 cells, matching
30-
running variables one-for-one would require 109 bespoke paint
31-
functions; reuse becomes impossible. Today 12 figures are reused
32-
across multiple slugs precisely because they capture a *general*
33-
mechanism (`iter-protocol` covers `iterators`,
30+
running variables one-for-one would require a bespoke paint
31+
function per cell; reuse becomes impossible. Today 7 figures are
32+
reused across multiple slugs precisely because they capture a
33+
*general* mechanism (`iter-protocol` covers `iterators`,
3434
`iterator-vs-iterable`, `iterating-over-iterables`,
3535
`container-protocols`). Every reuse pays a tax against this
3636
criterion.

public/prototyping/journey-figures-gestalt.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

public/prototyping/production-figures-gestalt.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

src/asset_manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Generated by scripts/fingerprint_assets.py. Do not edit by hand.
22
ASSET_PATHS = {'SITE_CSS': '/site.0c2adaa2c94a.css', 'SYNTAX_JS': '/syntax-highlight.3b6c7f730d46.js', 'EDITOR_JS': '/editor.a4a7766e1b9b.js'}
3-
HTML_CACHE_VERSION = '9aace8e7f09e'
3+
HTML_CACHE_VERSION = 'ae825b1de0bc'

src/example_sources/networking.md

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,8 @@ finally:
3535
```
3636
:::
3737

38-
:::unsupported
39-
`socketpair()` returns two connected endpoints. `sendall` writes encoded bytes into one end, and `recv` reads up to 16 bytes off the other. The byte boundary is the whole point: `"ping".encode("utf-8")` produces `b'ping'`, which is what the socket actually moves. (The in-browser Run button cannot open sockets — the sandbox disables outbound access — so pressing Run on this page fails; the verified output below comes from a real socket pair under standard CPython at build time.)
40-
41-
```python
42-
left, right = socket.socketpair()
43-
left.sendall("ping".encode("utf-8"))
44-
data = right.recv(16)
45-
```
46-
:::
47-
4838
:::cell
49-
The complete version adds two things: a `try`/`finally` so both endpoints close even if `recv` or the surrounding work raises, and a second `print` that `decode`s the received bytes back into a Python `str` for display. The first `print` shows the raw bytes `b'ping'`; the second shows the decoded text `ping`.
39+
`socketpair()` returns two connected endpoints. `sendall` writes encoded bytes into one end and `recv` reads up to 16 bytes off the other — the byte boundary is the whole point: `"ping".encode("utf-8")` produces `b'ping'`, which is what the socket actually moves. The `try`/`finally` closes both endpoints even if `recv` raises, and the second `print` `decode`s the bytes back into a Python `str`. The in-browser sandbox cannot open sockets, so pressing Run here fails; this output came from a real socket pair under standard CPython at build time.
5040

5141
```python
5242
import socket
@@ -73,5 +63,5 @@ ping
7363
- Network protocols move bytes, not Python `str` objects.
7464
- Close real sockets when finished, usually with a context manager or `finally` block.
7565
- Use high-level HTTP libraries for application HTTP unless socket-level control is the lesson.
76-
- Cloudflare Workers support HTTP-style networking through platform APIs; this example avoids outbound calls so the editable lesson stays deterministic and safe.
66+
- The verified output came from a real `socketpair()` under standard CPython at build time; the in-browser sandbox cannot open sockets, so live runs of this page fail there.
7767
:::

src/example_sources/special-methods.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Bag:
4343
return hash(tuple(self.items))
4444

4545
def __lt__(self, other):
46-
return len(self.items) < len(other.items)
46+
return self.items < other.items
4747

4848
def __contains__(self, item):
4949
return item in self.items
@@ -209,7 +209,7 @@ Bag(['a', 'b'])
209209
:::
210210

211211
:::cell
212-
`__eq__` decides what equality means for the type. Defining `__eq__` removes the default `__hash__`, so add `__hash__` back when instances should work in sets or as dict keys — but only for types treated as immutable: this `Bag` hashes its current items, so mutating one after adding it to a set makes it unfindable. `__lt__` alone is enough for `<` and for `sorted()`.
212+
`__eq__` decides what equality means for the type, comparing contents. `__lt__` orders by those same contents, so ordering stays consistent with equality (define one comparison and `functools.total_ordering` can fill in the rest; `__lt__` alone is enough for `<` and `sorted()`). Defining `__eq__` removes the default `__hash__`, so add it back only for types you treat as immutable: this `Bag` hashes its current items, so the last two lines show the hazard — a `Bag` found in a set becomes unfindable once its items change, because its hash no longer points at the bucket it was stored in.
213213

214214
```python
215215
class Bag:
@@ -223,17 +223,23 @@ class Bag:
223223
return hash(tuple(self.items))
224224

225225
def __lt__(self, other):
226-
return len(self.items) < len(other.items)
226+
return self.items < other.items
227227

228228
print(Bag(["a", "b"]) == Bag(["a", "b"]))
229229
print(Bag(["a"]) < Bag(["a", "b"]))
230-
print(hash(Bag(["a"])) == hash(Bag(["a"])))
230+
231+
bag = Bag(["a"])
232+
seen = {bag}
233+
print(bag in seen)
234+
bag.items.append("b")
235+
print(bag in seen)
231236
```
232237

233238
```output
234239
True
235240
True
236241
True
242+
False
237243
```
238244
:::
239245

src/example_sources/subprocesses.md

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,8 @@ print(result.returncode)
3535
```
3636
:::
3737

38-
:::unsupported
39-
`subprocess.run` spawns a child Python interpreter, captures its stdout and stderr (`capture_output=True`), decodes them as text (`text=True`), and raises `CalledProcessError` if the child exits non-zero (`check=True`). The returned `result` holds the captured streams and exit code as portable evidence the child ran. (The in-browser Run button cannot spawn child processes, so pressing Run on this page fails in the sandbox; the verified output below comes from standard CPython at build time.)
40-
41-
```python
42-
result = subprocess.run(
43-
[sys.executable, "-c", "print('child process')"],
44-
text=True,
45-
capture_output=True,
46-
check=True,
47-
)
48-
```
49-
:::
50-
5138
:::cell
52-
`subprocess.run()` starts a child process and waits for it. `capture_output=True` stores the child's standard output and error streams on the result object.
39+
`subprocess.run()` spawns a child Python interpreter and waits for it: `capture_output=True` stores the child's stdout and stderr on the result, `text=True` decodes them as strings, and `check=True` raises `CalledProcessError` on a non-zero exit. The result object carries the captured streams and exit code as portable evidence the child ran. The in-browser Run button cannot spawn processes, so pressing Run here fails in the sandbox; the output below was produced by really spawning the child under standard CPython when the example was verified.
5340

5441
```python
5542
import subprocess
@@ -76,5 +63,5 @@ child process
7663
- Use a list of arguments instead of shell strings when possible.
7764
- Capture output when the parent program needs to inspect it.
7865
- `check=True` turns non-zero exits into exceptions.
79-
- The output shown here was produced by really spawning the child under standard CPython when the example was verified; the site's in-browser sandbox cannot create processes, so live runs of this page fail there.
66+
- The verified output came from a real child process under standard CPython at build time; the in-browser sandbox has no process table, so live runs of this page fail there.
8067
:::

0 commit comments

Comments
 (0)