session 2026-06-09 (pm): Wave 5 CLOSED + typographic audit + measure→72ch + soft rag LIVE; PENDING-37/38, REVIEWED-37..40 placed, session memory + skill-harvest proposals

This commit is contained in:
David F Glidden
2026-06-09 21:16:20 +02:00
parent 730deaf25d
commit db84e50c83
6 changed files with 191 additions and 2 deletions
+53
View File
@@ -883,3 +883,56 @@ The Jurist's six-phase authorization map governs sequencing. Cross-cutting succe
**Recommendation:** **Option 1.** It closes the exact failure class (silent glyph drift) with minimal change, byte-identical output, and is precisely the "unknown = build error" the Hakyll north-star and §5 clause 3 call for. Option 2's derive-from-class is the purer single-source but the slug↔class↔sigil naming is already 1:1; making it structural earns its place only if that convention later strains — a clean follow-on, not required now (proportionality). The SVG sprite stays hand-authored either way. Steward to rule between 1 and 2.
**Files affected:** `site.hs` (new build-validation in/near `loadListingDefs`; Option 2 also touches `compassRowsHtml`); `templates/reading-compass-points.html` (read for the symbol-id census; not edited); `data/content-types.yml` + `data/listings.yml` (read, not edited); content-typology §5 cross-reference (records the enforcement; ships with the Stage-G promotion per the spec's own sequencing). Verification: byte-identical compiled-output gate (Option 1) + a deliberate-mismatch test proving the check fires.w
**Awaiting:** Steward authorization (choose Option 1 or 2).
**Status (2026-06-09):** STEWARD AUTHORIZED inline (chat, Option 1). Built `4936fc9`: `validateCompass` binds class→glyph→sigil structurally; `_site` byte-identical (570 files); both checks fire on deliberate mismatch. REVIEWED-36 placed by steward. DONE.
## PENDING-35 — W5.1 / G2: single-source title display (retire the `showTitleField` hardcode) + Dream amendment
**Date:** 2026-06-09
**Tag:** [HARDENING]
**Summary:** Retire the 11-class hardcoded `titleClasses` list in `showTitleField` (`site.hs:1829`), binding the title-shown decision structurally to `content-types.yml` `title_display` (the matrix's executable shadow) so it cannot drift by hand — the durable form the stopgap's own comment names (G2). **Coupled steward amendment:** Dream moves `title_display: shown → not-shown` (steward editorial ruling, 2026-06-09).
**Rationale:** Audit finding G2. `showTitleField` returns `"true"` for a class iff it is in a hand-maintained 11-element list whose own comment (2026-05-30) admits: *"Durable form is a table lookup (Hakyll audit §4A); until then this list must track the matrix `shown` set."* Diagnosis confirmed the list = the 10 matrix-`shown` classes + `chamber`. **`chamber` is a dead entry** — zero corpus pages carry `class: chamber` (the v1-legacy `content/chamber/**` pages carry real classes: essay/meditation/about/offering/deliberation/lex), so retiring the hardcode in favour of `title_display == "shown"` is **byte-identical** (the one definitional divergence — hardcode shows chamber, YAML marks it `n/a` — has no rendered instance). The census also surfaced classes *outside* the 14-type enumeration (`offering`, `deliberation`, `lex`, `sequence-index`, `not-found`); the replacement lookup is **permissive** (absent → not-shown), matching prior behaviour, and must NOT error on absence (unlike `validateCompass` on unknown listing classes), since `showTitle` runs over every routed item.
**Options:**
1. **Thread the shown-set as a typed parameter (recommended).** Extend the existing `content-types.yml` parser (`TypeFacts`) to capture `title_vignette.title_display`; add `loadTitleShownClasses :: IO [String]` (reuses the decode machinery beside `loadListingDefs`); load once in `main` via `preprocess`; make `showTitleField :: [String] -> Context String` and `postCtx :: [String] -> Context String`, passing the set at the ~10 `postCtx` call sites. Compile-time enforcement → the binding cannot be forgotten (the type forces it). One file read at build start; no per-item re-read.
2. **`loadBody` a tracked data item** (the `_build_info` pattern): localizes the dependency inside `showTitleField`, no call-site ripple, runtime (not compile-time) binding. Adds a parallel data path + Item-body typing.
3. Status quo — hand-synced list; rejected by the promotion gate (G2).
**Recommendation:** **Option 1.** Compile-time binding is the stronger single-source guarantee and the truest to G2's intent (drift made *impossible*, not merely *checked*); it reuses the one `content-types.yml` decode rather than adding a second reader. The call-site ripple is mechanical and contained to `main`.
**Dream amendment (coupled, steward-ruled):** `content-typology-specification.md §2` Sub-table 2 line 746: Dream `title_display` `shown → not-shown` (vignette_eligibility unchanged: `no`). Human-canonical matrix edit → `make content-types` regenerates the YAML shadow → `make content-types-check` confirms no drift. This is a steward editorial decision on the steward's own canonical document; it rides into the Stage-G jurist read (the jurist sees Dream=not-shown with this rationale and may raise it — the loop stays intact). Rendered effect: the single `class: dream` page loses its `<h1>` title heading (the one classified delta).
**Verification (two-hat separation):** Commit 1 = the refactor alone (YAML still says dream=shown) → `_site` **byte-identical** vs pre-change baseline (proves the refactor is pure; chamber's 0 instances make the set-difference invisible). Commit 2 = the Dream amendment (spec §2 + regen) → `_site` delta = **only** the dream page's `<h1>` removed, nothing else (expected-delta gate). Plus a liveness check: temporarily flip a class's `title_display` and confirm the rendered title tracks it (proves the binding is live, not vestigial).
**Files affected:** `site.hs` (`TypeFacts`/parser, new `loadTitleShownClasses`, `showTitleField` + `postCtx` signatures + ~10 call sites, `main` load); `docs/AldineXXI-Codex/content-typology-specification.md` §2 Sub-table 2 (Dream row — human-canonical); `data/content-types.yml` (regenerated, not hand-edited); content-typology §5 cross-reference ships with the Stage-G promotion (W6).
**Awaiting:** Steward AUTHORIZED inline (refactor + Dream amendment). Render-review PASSED ("yes, unquestionably — one can't title their dreams").
**Status (2026-06-09):** DONE + DEPLOYED + LIVE. Two-hat: `4ea261a` (refactor, `_site` byte-identical to baseline) + `ac39b26` (Dream amendment, delta = the single dream page's `<h1>` removed). `make rebuild` shipped (CloudFlare uploaded 2 files — the dream page + `_redirects`); pushed both remotes (`f8e00e7`→`ac39b26`); live-verified on animalrationiscapax.org (h1.post-title=0, `<title>`+breadcrumb preserved). REVIEWED-37 drafted for steward placement. Remaining Wave 5: W5.3 (G4 clause-1 gate), W5.4 (G1 wildcard→build-error, lands in the Stage-G promotion commit).
## PENDING-36 — W5.3 / G4 (clause-1 enforcement gate) + W5.4 / G1 (wildcard fallbacks → build errors)
**Date:** 2026-06-09
**Tag:** [HARDENING]
**Summary:** Close the last two Wave-5 enforcement items. **G4 (W5.3):** a build-time census asserting every authored content file's `class:` is in the content-typology §1 enumeration (clause 1), erroring loudly otherwise — which also guards `default.html`'s unguarded `class="$class$-page"` body emission. **G1 (W5.4):** convert the two `classRouteName`/`classDisplayName` wildcard fallbacks (`other -> other ++ "s"`) into build errors (clause 3/4: unknown = build error, not fallback) — patch prepared now, **lands inside the Stage-G promotion commit** per the spec's own sequencing (clause 4 becomes OPERATIVE at promotion).
**Rationale:** §5 clause 1 ("every authored content file declares exactly one `class:` from §1") and clause 4 ("`site.hs` routing may not fall back to `class: essay`; all pages explicitly typed") are the corpus's two unenforced contract clauses (gap-map G4, G1). Today they hold by convention. Diagnosis (per-directory class census): **content/posts/** — the authored corpus that reaches `classRouteName`/`classDisplayName` via `postCtx` — carries only §1-enumerated classes (dream/essay/fragment/glimpse/gloss/heteronym/hommage/meditation/observation/readings/reverie), so the G1 wildcard is **never triggered** → converting it to `error` is byte-identical. The non-enumeration classes live **outside** the authored corpus: `sequence-index` (content/essays/after-the-reply/index.md — a sequence-index view), `not-found` (content/404.md), and `lex`/`offering`/`deliberation` (the content/chamber/** v1-legacy cluster — known-deferred, not drift, per `project-arc-chamber-v1-legacy-cluster.md`). A naïve clause-1 census would break the build on these legitimate pages; the gate therefore validates against **enumeration ∪ an explicit exempt-set**.
**The one decision (steward's): the exempt-set.** It blesses the non-typology classes the corpus legitimately carries. Proposed: `{ sequence-index, not-found, lex, offering, deliberation }` — each commented by reason (infra view · 404 · v1-legacy chamber ×3, deferred per tracker). It is a **self-tightening ratchet**: when the v1-legacy chamber cluster is finally migrated (the deferred to-do), those three entries are removed and the gate tightens automatically; a brand-new unblessed class anywhere → loud build error. Alternative (path-based: exempt `content/chamber/**` wholesale + infra) is coarser — it wouldn't catch a typo'd class inside the legacy tree (which is frozen anyway). Recommend the class-based set: precise everywhere, every blessing visible.
**Options (gate location):**
1. **site.hs build guard (recommended)** — a `preprocess` census beside `validateCompass`/`loadListingDefs`: glob `content/**/*.md`, read each frontmatter `class:`, assert ∈ (`class_enumeration` from content-types.yml ∪ exempt-set), `error` listing any violator. Fails the actual `make rebuild` that ships the site. Consistent with W5.2/W5.1's home. Output-neutral → byte-identical.
2. **content-types-check (Python)** — extend `generate-content-types.py --check` with the corpus census. Cleaner to write, enumeration already derived there — **but** the pre-commit hook is generic (secrets/debug/large-files only) and does *not* run it, and ARC has no CI server, so it would gate nothing automatically. Weaker.
**Recommendation:** **Option 1** for G4 (the build is the real gate; a check nobody runs isn't enforcement). G1's wildcards→`error`: prepare now (byte-identical), **land at W6** inside the promotion commit per the roadmap's precondition list (W5.1–5.3 ✓ *before* Stage G; G1 lands *with* it). G4 lands now (it is a Stage-G precondition, output-neutral).
**Verification:** byte-identical `_site` gate (both are guards/never-triggered fallbacks — no rendered change); deliberate-mismatch test for G4 (inject a bogus `class:` on a scratch file → build errors with the file named; remove → green), mirroring the W5.2 idiom.
**Files affected:** `site.hs` (new census guard in/near `loadListingDefs`; G1 = `classRouteName`/`classDisplayName` `other ->` arms → `error`, staged for W6); `templates/default.html` (read for the body-class emission; guarded transitively by the census — no edit unless steward wants an inline note); content-typology §5 cross-reference + clause-1/clause-4 promotion text ship with the Stage-G commit (W6). `data/content-types.yml` read (enumeration), not edited.
**Awaiting:** Steward authorization — chiefly to ratify the **exempt-set** `{sequence-index, not-found, lex, offering, deliberation}` and confirm Option 1 (build guard). On the nod: build G4 now (byte-identical), prepare G1 for W6.
**Status (2026-06-09):** STEWARD AUTHORIZED inline (exempt-set confirmed; Option 1). Steward refinement folded: **`sequence-index` reframed as a permanent generated-view class, NOT legacy** — after-the-reply is the only sequence today but more are coming; the exempt-set comments split into two kinds (permanent views/infra: sequence-index, not-found · v1-legacy ratchet: lex, offering, deliberation). **G4 DONE** (`23db039`, pushed both remotes): `validateCorpusClasses` build guard; `_site` byte-identical to `ac39b26` (stash-rebuild diff); deliberate-mismatch fires naming the file; output-neutral → no deploy. **G1 PREPARED** (`9699088` roadmap W5.4) — exact wildcard→error patch recorded, **lands inside the Stage-G promotion commit (W6)** per spec sequencing; byte-identical (never-triggered today). REVIEWED-38 drafted for steward placement. **Wave 5 CLOSED (G2/G3/G4 built; G1 staged).** Remaining to Stage G: W4.4 (jurist multilingual v1.0 sign-off) → Wave 6 promotion.
## PENDING-37 — Reading measure → 66 characters (rem), fixing the px/rem root-size bug
**Date:** 2026-06-09
**Tag:** [HARDENING] (deep typographic audit follow-on; jurist disposition #1; steward-authorized inline as a *starting point* for render review)
**Summary:** Set the reading measure to a precise **66 characters** (Bringhurst ideal), expressed in **rem** so the character count holds constant across breakpoints, replacing the two divergent, over-wide px/rem-confused measures.
**Rationale:** The audit measured (font-metric over 91,579 chars of real ARC prose) + render-confirmed (headless Chrome): EB Garamond prose averages **0.377–0.381 em/char**, so the live columns run **~90 characters** (`$narrow-size: 720px`) and **~101 characters** (`$body-column: 38rem` at the 21px root) — both well past Bringhurst's 75–80 "too long" ceiling. Root cause: `$body-column: 38rem` was authored to "38rem ≈ 65 chars" at the **16px** base, but the live `:root` scales to **21px** (137.5/125/131.25% per breakpoint), silently inflating 38rem to 798px (~101 ch). `$narrow-size` is px (720) so its char-count drifts across breakpoints. Two tokens, two confusions, both off-spec — the spec itself says 38rem/"65 characters" (`:165`) but `$narrow-size` comments "68 characters" while rendering ~90.
**Change (provisional — for the steward's eye):** `$narrow-size: 720px → 24.9rem` (`_variables.scss:119`) and `$body-column: 38rem → 24.9rem` (`_sidenotes.scss:33`); 24.9rem = 66 × 0.3771em (measured). Both become the same rem value (they are the same concept — "the reading measure"; a follow-on pass should unify them to one token). NOT a byte-identical change — the reading column intentionally narrows (720px → ~523px @ desktop, a ~27% narrowing; the marginalia-grid body narrows 38→24.9rem).
**Decisions deferred to the render review (steward's "we look, then decide"):** (a) final character target within the Bringhurst band 66–75 (24.9–28.3rem); (b) the **marginalia-grid proportions** — narrowing the body to 24.9rem makes the fixed 14rem sidenote column proportionally much larger (14/24.9 = 56% vs 14/38 = 37%); the sidenote/marker columns may need adjusting, to be judged on the about pages + sidenote/register essays; (c) **spec reconciliation** (AldineXXI §I measure prose: 38rem/65-char → the chosen rem/66) lands after the visual decision; (d) deploy. No deploy in this step — local build + serve only.
**Files affected (this step):** `scss/AldineXXI/_variables.scss` (`$narrow-size`), `scss/AldineXXI/_sidenotes.scss` (`$body-column`). Spec text + token unification deferred.
**Awaiting:** Steward AUTHORIZED inline (66 ch, starting point). Render review of about pages + marginalia essays → then the deferred decisions above.
**Status (2026-06-09):** LOCKED at **72 characters** (`27.2rem`) — steward render-ruling across all content types (66 a touch narrow, 75 a touch wide; 72 "spot on," sidenote/marginalia proportion approved). **Resize bug found + fixed** (the `.page-content` mobile `100vw` override defeated the rem measure <768px — "measure gets longer on resize"; removed). **Proper-homes consolidation** (steward-directed, W3R discipline): one token `$narrow-size` (`$body-column` aliases it), one governing rule (`.page-content`/_base, all viewports), zero measure rules left in `_mobile-responsive` (override removed + a dead `.wrapper main` block deleted). Spec §I Measure reconciled to the measured value + 65/68 inconsistency resolved; §XII grid + token-index updated. Verified: compiles · measure holds across a viewport sweep · no overflow. Committed `06effe4` (behaviour commit; gate = steward render-review, passed). **DEPLOYED + LIVE 2026-06-09** (bundled with the soft-rag pass `a9e5196`): pushed both remotes, `make rebuild` shipped (CloudFlare 2 files = style.css + _redirects — pure CSS, no HTML change), live-verified on animalrationiscapax.org (`max-width:27.2rem` ×9, no 720px). REVIEWED-39 to be drafted. (Deferred token-unification: $body-column now aliases $narrow-size; full removal of the local name is optional tidy, not required.)
## PENDING-38 — Soft rag: hyphenate reading prose (reverses §I.i hyphens:none)
**Date:** 2026-06-09
**Tag:** [HARDENING] (deep typographic audit follow-on; steward-approved inline on render; jurist endorsed `hyphens: auto` as the no-JS "rung (i)" in their audit reading)
**Summary:** Reverse the §I.i posture from *ragged-right, un-hyphenated* (`hyphens: none`) to a **soft rag** — ragged-right with judicious auto-hyphenation of reading prose.
**Rationale:** The prior `hyphens: none` was a **tool-quality** decision (web justification/hyphenation judged poor when ARC was built), not philosophical. The deep typographic audit + masters reopened it: Hochuli ranks the soft rag (ragged + narrow hyphenation zone) **above** the hard rag (the hard rag "looks uneven and unsettled"); Rutter — "hyphenate without justifying… reduces the raggedness"; the engine is the universal Liang/`hyph-utf8` patterns, language-correct via the document `<html lang>`. With the measure now properly set (72 ch, PENDING-37 — the old ~90-char measure was itself a reason hyphenation seemed unneeded), the soft rag reads better than the hard rag. Stays **ragged** (does NOT justify — justification on large viewports remains open, needs the JS Knuth–Plass "educated engine," off the table until a logged gate amendment).
**Implementation (proper home — _base, the reading-body home, every viewport):** `.page-content p/li/blockquote { -webkit-hyphens:auto; hyphens:auto; hyphenate-limit-chars:6 3 3; -webkit-hyphenate-limit-before/-after:3; hyphenate-limit-lines:2; -webkit-hyphenate-limit-lines:2 }`; body default → `hyphens: manual`. **The `-webkit-hyphens` prefix was the bug** that made Safari show nothing first (no autoprefixer in the build → prefixes hand-written; the limits were prefixed but the property wasn't). Cross-browser: `hyphens:auto` is Baseline (all engines); limits via standard (Chrome/Edge) + `-webkit-` (Safari); Firefox honours auto, ignores limits (sane defaults) — not Safari-tailored.
**Verification:** steward render-review on desktop AND phone ("few, but perfect"); the phone "none" was iOS-Safari **cache** (fresh-origin test confirmed; no mobile override exists — rule is viewport-agnostic). Live-verified post-deploy.
**Files affected:** `scss/AldineXXI/_base.scss` (the soft-rag rule + body `hyphens: manual`); `scss/AldineXXI/_mobile-responsive.scss` (stale per-phone hyphens comment corrected); `docs/AldineXXI-Codex/AldineXXI-specification.md` §I.i (posture reconciled).
**Awaiting:** Steward APPROVED inline (deploy now). **DONE + DEPLOYED + LIVE** `a9e5196`. **§I.i posture reversal rides the Stage-G read for formal jurist concurrence** (substance already jurist-endorsed). REVIEWED-40 to be drafted.