You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: make the orientation true again, for the next instance to inherit
The same pass this repository ran on 2026-07-25, and needed again: the last six
commits moved things the documentation had not caught up with. Everything below
was checked against a command's output, not against memory.
**The specification did not know about `words:`** — nought occurrences. § 7.12
described a calendar whose own vocabulary was English and unchangeable, which
stopped being true two commits ago. It now has a *Sprache des Blattes* section
covering `words:`, `font:` on a document generator, and the Latin-1 boundary that
makes the second necessary. This project's rule is to update the spec in the same
breath as the code; that did not happen, and this is the repair.
**§ 15's question 2 changed shape and now says so.** "Whether a font must be
shipped" was theoretical while a missing glyph in a generator's text went
unnoticed and a document could name no font. Both are fixed, so stage 2 is
reachable everywhere and the question is now whether it is *reasonable to ask* —
a decision for use, but one the user is finally shown rather than handed a box.
**Decision 53** records the three changes as the one decision they were, in the
order that is the actual lesson: refusing the silent failure before supplying the
remedy left a window in which a Polish calendar was less usable than before.
**Three stale numbers in `docs/CLAUDE.md`**, each verified against the tree:
the specification is ~2800 lines and not ~2330; it covers thirteen generators and
not ten; there are fifty-three implementation decisions and not fifty-one. Also
checked and correct: 1231 tests, 19 presets (twelve generators, seven `lines`
papers — the eighth `generator: lines` is a *section* inside `notebook-a4`),
18 examples, 45 modules, two tags, `0.11.1`.
The Done table gains the row for everything after the audit, the module table
names all three functions both page paths now share, and the release paragraph
says two releases rather than one first tag.
The habit about probes is rewritten with its real count: five times over these
sessions I concluded something was unchecked, and five times the probe was wrong
and the code was right — including twice while writing *this* commit's checks.
1231 tests green, ruff clean, every committed example byte-identical.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/CLAUDE.md
+38-14Lines changed: 38 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -126,6 +126,7 @@ edge, so a tick now carries its position *and* its value separately. `unit` says
126
126
| post-M9 `notebook`| the **second document generator**, and the first that composes blades (§ 7.13, decision 50): sections, each filled by an ordinary generator, with a linked contents page, opt-in dividers and a cover. The seam: a `DocumentPage` may carry a `Fill` (generator name + validated config) instead of marks, and **the handle calls the blade** — `pages.document_page_marks` is the one function that says what is on a page, used by the writer, the capability pre-flight and the media check. A section is a definition in miniature, validated by that blade's own `config_model` with the loader's context (so `px`/`%w` resolve there too). Document bands are now laid out **per page**, so `{page}` counts and a per-page `{section}` names the section; `calendar-a4` stayed byte-identical. `page_layout.Page` extracted from `calendar_layout` and shared. Preset `notebook-a4`, example `15-notebook`|
127
127
| post-M9 band colour | a header/footer `Band` takes `background` (a full-width strip, band height only) and `text_color`, both default off — drawn in `layout_band` so both the blade and document paths get them; resolves the title-page contrast (§ 8.9) |
128
128
| pulled forward into M1 | format table, presets, `check`, overwrite protection, placeholders — the M1 acceptance criteria needed them |
129
+
| 0.11.x — after the audit |**`tests/test_geometry_readback.py`**: every blade's geometry measured out of a finished PDF against numbers taken from the definition, never from the code — a hexagon's every edge exactly `size`, ring radii against the cumulative series, a mandala mapped onto itself by a twelfth of a turn, a perspective fan concurrent within a millimetre, `form`'s 8 mm ruling identical on A4 and A5, log positions against `math.log10`. **`check_page_furniture`**: the frame is checked on both page paths, not only drawn on both. **`check_text_glyphs`**: a generator's text is checked for missing glyphs (§ 12 point 13), which no code had ever done. **`words:` and `font:`** on the document generators, so every word on the sheet comes from the definition and there is a way out of Latin-1 for it (decision 53). **`labels: none`** accepted on `grid` and `polar`, the spelling § 7.10 documents |
129
130
| 0.10.0 release-readiness | the audit above, in five commits: three degenerate values that hung or crashed instead of refusing; **`page_furniture`**, so the document path carries the page model (duplex, border, hole marks, ruler, stamp, background) instead of dropping it silently, plus three keys refused by name and a fourth (a multi-sheet blade in a notebook section) refused for now (decision 52); five messages made actionable and `--version` added; a ruler tick that ran off the edge, a ruler font that was never opened, an unreachable bookmark guard; and twenty-six stale comments |
130
131
131
132
### Not done
@@ -146,8 +147,9 @@ edge, so a tick now carries its position *and* its value separately. `unit` says
146
147
147
148
**Two more things wait, and neither of them is code:**
148
149
149
-
-~~**PyPI.**~~**Published on 2026-07-26 — `ctrlgrid 0.11.0`, MIT, Python ≥ 3.11,
150
-
wheel and sdist, under the repository's first tag `v0.11.0`.** M1's first
150
+
-~~**PyPI.**~~**Published on 2026-07-26 — `ctrlgrid`, MIT, Python ≥ 3.11, wheel
151
+
and sdist. Two releases so far: `v0.11.0`, the repository's first tag ever, and
152
+
`v0.11.1` for the metadata it should have carried.** M1's first
151
153
acceptance criterion is met at last, and measured rather than assumed: `uvx
152
154
ctrlgrid millimeter-a4 --pages 3 -o out.pdf` installs from the network and
153
155
produces a sheet whose MediaBox reads 210.000 × 297.000 mm with a 1.000 mm line
@@ -234,7 +236,8 @@ of the five serious findings were there. That is where to look first.
234
236
## Read this before writing anything
235
237
236
238
**The specification is the source of truth, and it is unusually complete** —
237
-
~2330 lines covering the architecture, all ten generators, the page model,
239
+
~2800 lines covering the architecture, all thirteen generators — eleven blades
240
+
and two documents — the page model,
238
241
validation, milestones and the decisions that were explicitly rejected and why.
239
242
240
243
Most questions that come up while implementing are already answered there, with
@@ -251,7 +254,7 @@ work around it.
251
254
252
255
**Where the specification was genuinely silent**, the resolution is recorded in
fifty-one of them so far, each with the section it belongs to and the
257
+
fifty-three of them so far, each with the section it belongs to and the
255
258
reasoning. Read it before changing a default; several look arbitrary and are not.
256
259
257
260
## Language split
@@ -278,7 +281,7 @@ and knows nothing about margins.
278
281
|`axes.py`|`AxisPeriod` — what the handle needs from a blade for § 8.3/§ 8.5 |
279
282
|`model.py`| pydantic sections; `Section` base with `extra="forbid"` + `deferred`|
280
283
|`loader.py`| YAML → `Document`; formats, presets, devices, name lists |
281
-
|`pages.py`|`Geometry`, page loop, placeholders, `preflight`, `build`, `snap: pixel`, and **`page_furniture`**— the one function that says what the handle draws around a page's own marks, used by the blade path *and* the document path |
284
+
|`pages.py`|`Geometry`, page loop, placeholders, `preflight`, `build`, `snap: pixel`, and the three functions both page paths share: **`page_furniture`**(what the handle draws around a page's own marks), **`check_page_furniture`** (whether it fits) and **`check_text_glyphs`** (whether the font can draw it). Each exists because the blade path had it and the document path did not|
282
285
|`frame.py`| header/footer layout, border, background, hole marks, the edge ruler's marks and its fit check, stamp |
carries a *done* note on each of its phases saying what was built and what was
401
412
decided against; the designs and plans behind the larger ones live beside it in
@@ -410,7 +421,7 @@ supply:
410
421
|---|---|
411
422
| a device | the empty `quirks` (decision 31), and the rM2 figures nobody has measured on the device — the Paper Pro is owner-checked, the rM2 is not |
412
423
| a pair of scissors | print `examples/16-net-tray.yaml` and `box-tuck-a4` at 100 %, cut, fold. The tray checks the geometry; the tuck-top also checks the thickness rule. **No test can do this**, and until someone does, the tests only agree with themselves |
413
-
| use | the three § 15 questions below, and 1.0.0 itself. 0.11.0 is *published*, so the DSL can now meet someone other than the test suite — which is the one thing 1.0.0 has always been waiting for |
424
+
| use | the three § 15 questions below, and 1.0.0 itself. The DSL can now meet someone other than the test suite, which is the one thing 1.0.0 has always been waiting for |
414
425
415
426
If a new feature *is* wanted, the recipe has not changed since M1: a design
416
427
settled with the user first when there is a real fork in it, then a plan, then
@@ -431,11 +442,15 @@ Three habits earned their place the hard way and are worth keeping:
431
442
the same centre. The third quantity, the grid, was the only one that could
432
443
have contradicted it, and it was not looked at until the user did.
433
444
-**A probe that does not fire proves nothing either — suspect the probe first.**
434
-
During the release audit I twice concluded a check was missing, and twice the
435
-
check was there and my test was wrong: a name list without `{name}` in any band
436
-
draws no text, so the glyph check has nothing to refuse; and a pipeline's `$?`
437
-
is `head`'s exit code, not the tool's. Both would have been reported as bugs.
438
-
Before writing "X is not checked", make the probe fail on purpose.
445
+
This happened *five* times over the release sessions, and not once was the code
446
+
at fault: a name list without `{name}` in any band draws no text, so the glyph
447
+
check had nothing to refuse; a pipeline's `$?` is `head`'s exit code, not the
448
+
tool's; a `--seed` that never reached the loop made the maze booklet look like
449
+
a regression; Vera has 256 glyphs and no Polish, so "a font file fixes it"
450
+
failed for the fixture's reason and not the check's; and a grep for a claim in
451
+
`docs/CLAUDE.md` missed it because the sentence wrapped across two lines.
452
+
Every one of them was about to be written up as a bug. **Before writing "X is
453
+
not checked", make the probe fail on purpose.**
439
454
440
455
A specific paper aeroplane was considered and **refused**: it is a drawing, not
441
456
a structure, and § 2 rules out a drawing language.
@@ -465,3 +480,12 @@ than on a design answer — **q4 was put to the user in July 2026 while building
465
480
the slanted families and deliberately left out**: the angle is what unlocks
466
481
calligraphy guides and origami pre-creasing, the unit would be a convenience on
467
482
top of it (§ 7.1).
483
+
484
+
**q2 changed shape on 2026-07-26 and is worth re-reading in § 15.** It used to be
485
+
theoretical, because a missing glyph in a generator's text was never noticed: the
486
+
check ran over the bands only, a Polish month name printed as a box, and a
487
+
document generator took no font at all — so the documented remedy did not exist
488
+
for it. Both are fixed (decision 53). Stage 2 is now *reachable* everywhere, and
489
+
the question is no longer "does it work" but "is it reasonable to ask, or should
490
+
a broad OFL font travel with the tool?". Still for use to answer — but now the
0 commit comments