@brandon_m_behring/book-scaffold-astro 3.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CLAUDE.md +179 -0
  2. package/bin/book-scaffold.mjs +61 -0
  3. package/components/CaseStudy.astro +36 -0
  4. package/components/ChapterHeader.astro +61 -0
  5. package/components/ChapterNav.astro +29 -0
  6. package/components/ChapterTOC.astro +33 -0
  7. package/components/Citation.astro +94 -0
  8. package/components/Cite.astro +71 -0
  9. package/components/CodeBlock.astro +115 -0
  10. package/components/CodeRef.astro +49 -0
  11. package/components/ConceptBox.astro +26 -0
  12. package/components/Convergence.astro +41 -0
  13. package/components/CounterBox.astro +15 -0
  14. package/components/Divergence.astro +32 -0
  15. package/components/DynConnect.astro +15 -0
  16. package/components/ExampleBox.astro +15 -0
  17. package/components/Figure.astro +35 -0
  18. package/components/InsightBox.astro +15 -0
  19. package/components/KeyIdea.astro +21 -0
  20. package/components/MarginNote.astro +37 -0
  21. package/components/NoteBox.astro +15 -0
  22. package/components/OpenQuestion.astro +15 -0
  23. package/components/PaperBox.astro +15 -0
  24. package/components/PatternTimeline.astro +133 -0
  25. package/components/Recovery.astro +34 -0
  26. package/components/ResultBox.astro +15 -0
  27. package/components/Sidebar.astro +268 -0
  28. package/components/Sidenote.astro +26 -0
  29. package/components/SkillBox.astro +24 -0
  30. package/components/SourceArchive.astro +285 -0
  31. package/components/StatusBadge.astro +51 -0
  32. package/components/Tag.astro +60 -0
  33. package/components/Theorem.astro +65 -0
  34. package/components/TipBox.astro +15 -0
  35. package/components/ToolFilter.tsx +160 -0
  36. package/components/TryThis.astro +23 -0
  37. package/components/VersionSelector.tsx +85 -0
  38. package/components/WarnBox.astro +15 -0
  39. package/components/WeekRef.astro +51 -0
  40. package/components/XRef.astro +40 -0
  41. package/dist/index.d.ts +135 -0
  42. package/dist/index.mjs +369 -0
  43. package/dist/lib/katex-macros.d.ts +26 -0
  44. package/dist/lib/katex-macros.mjs +98 -0
  45. package/dist/schemas.d.ts +17 -0
  46. package/dist/schemas.mjs +160 -0
  47. package/dist/types-Cz-pwE1N.d.ts +61 -0
  48. package/examples/chapter-template-academic.mdx +100 -0
  49. package/examples/chapter-template-tools.mdx +90 -0
  50. package/layouts/Base.astro +250 -0
  51. package/layouts/Chapter.astro +37 -0
  52. package/package.json +137 -0
  53. package/pages/chapters.astro +371 -0
  54. package/pages/convergence.astro +96 -0
  55. package/pages/print.astro +39 -0
  56. package/pages/references.astro +160 -0
  57. package/pages/search.astro +87 -0
  58. package/pedagogy/kf-chapter-shape.md +96 -0
  59. package/pedagogy/source-tiers.md +121 -0
  60. package/pedagogy/volatility-classes.md +110 -0
  61. package/recipes/00-getting-started.md +77 -0
  62. package/recipes/01-add-math.md +71 -0
  63. package/recipes/02-bibliography-pipeline.md +82 -0
  64. package/recipes/03-asset-pipelines.md +84 -0
  65. package/recipes/04-component-library.md +118 -0
  66. package/recipes/05-deploy-cloudflare.md +74 -0
  67. package/recipes/06-mobile-first-layout.md +73 -0
  68. package/recipes/07-chapter-shapes.md +84 -0
  69. package/recipes/08-decisions-ledger.md +110 -0
  70. package/recipes/09-validation.md +106 -0
  71. package/recipes/10-custom-domain.md +72 -0
  72. package/recipes/README.md +43 -0
  73. package/scripts/build-bib.mjs +99 -0
  74. package/scripts/build-figures.mjs +179 -0
  75. package/scripts/render-notebooks.mjs +223 -0
  76. package/scripts/validate.mjs +179 -0
  77. package/styles/callouts.css +303 -0
  78. package/styles/chapter.css +209 -0
  79. package/styles/convergence.css +349 -0
  80. package/styles/layout.css +156 -0
  81. package/styles/print.css +203 -0
  82. package/styles/tokens.css +194 -0
  83. package/styles/tool-filter.css +135 -0
  84. package/styles/typography.css +147 -0
@@ -0,0 +1,84 @@
1
+ # Recipe 03 — Asset pipelines (figures + notebooks)
2
+
3
+ **Profile**: any (build:figures and build:notebooks both graceful-skip when source dirs / tools are absent).
4
+
5
+ **TL;DR**: Put PDFs in `figures/`, Jupyter notebooks in `notebooks/`. `npm run dev` / `npm run build` runs both pipelines via the `build:assets` prebuild hook. Output: `public/figures/*.svg` (PDF→SVG via `pdftocairo`) and `public/notebooks/*.html` (ipynb→HTML via `uv run jupyter nbconvert`).
6
+
7
+ ## How each pipeline works
8
+
9
+ ### Figures — `scripts/build-figures.mjs`
10
+
11
+ - **Source**: `figures/` at scaffold root (override via `BOOK_FIGURES_PATH` env var; e.g. `BOOK_FIGURES_PATH=../shared/figures` to share with a LaTeX sibling)
12
+ - **Output**: `public/figures/<same-subdir-structure>/<stem>.svg`
13
+ - **Tool**: `pdftocairo` (poppler-utils) with `pdftoppm` fallback for malformed SVG
14
+ - **Idempotency**: skips when SVG mtime >= PDF mtime
15
+ - **Graceful skip**: if `pdftocairo` or `pdftoppm` not on PATH (Cloudflare build container), warns and exits 0 — committed SVGs under `public/figures/` are served as-is
16
+
17
+ ### Notebooks — `scripts/render-notebooks.mjs`
18
+
19
+ - **Source**: `notebooks/*.ipynb` at scaffold root (override via `BOOK_NOTEBOOKS_PATH`)
20
+ - **Output**: `public/notebooks/<stem>.html`
21
+ - **Tool**: `uv run jupyter nbconvert --template=basic` (override the `uv` working dir via `BOOK_UV_CWD` if your venv lives elsewhere)
22
+ - **Style scoping**: each rendered notebook wrapped in `<div class="notebook-frame">` to prevent nbconvert's embedded `<style>` from bleeding into site CSS
23
+ - **Stub skipping**: notebooks under 1500 bytes (configurable via `NOTEBOOK_STUB_BYTES`) are skipped — useful for placeholder notebooks that haven't been authored yet
24
+ - **Graceful skip**: if `uv` not on PATH, warns and exits 0
25
+
26
+ ## Use figures in chapter MDX
27
+
28
+ After build, link to the SVG via `<Figure>`:
29
+
30
+ ```mdx
31
+ import Figure from '../../components/Figure.astro';
32
+
33
+ <Figure
34
+ src="/figures/intro/eigenvalues.svg"
35
+ caption="Eigenvalue structure of the example matrix."
36
+ id="intro-fig-eigenvalues"
37
+ />
38
+ ```
39
+
40
+ `Figure` is a CORE component (recipe 04). The validator (recipe 09) checks that every `<Figure src="...">` exists on disk after build.
41
+
42
+ ## Link to notebook companions
43
+
44
+ The `ChapterHeader` component (recipe 04) accepts a `notebook_path` frontmatter field that auto-links to the rendered HTML:
45
+
46
+ ```yaml
47
+ ---
48
+ title: "Chapter 1"
49
+ notebook_path: notebooks/chapter01.ipynb
50
+ ---
51
+ ```
52
+
53
+ Renders a "View executable companion" link in the chapter header.
54
+
55
+ ## Cloudflare deploy quirk: committed artifacts
56
+
57
+ Cloudflare Workers build containers don't have `pdftocairo` or `uv` installed. Two options for academic books deploying there:
58
+
59
+ 1. **Commit derived artifacts** (recommended for low-friction deploy):
60
+ - Edit `.gitignore`: remove the `public/figures/` and `public/notebooks/` lines.
61
+ - Run `npm run build:assets` locally; commit the generated outputs.
62
+ - CI's prebuild gracefully skips (tools missing), serves committed artifacts.
63
+ - Trade-off: ~3 MB of binary artifacts in git history.
64
+
65
+ 2. **Install poppler + uv in CI**: prepend `apt-get install -y poppler-utils && curl -LsSf https://astral.sh/uv/install.sh | sh && ...` to the build command. More setup; cleaner repo.
66
+
67
+ post_transformers chose option 1 (see commit `f7fa75d`).
68
+
69
+ ## Common gotchas
70
+
71
+ - **Notebooks should be output-free** for a clean rendered HTML — clear outputs before committing, or use `nbstripout` as a pre-commit hook. Cells with embedded outputs render those outputs in the HTML; this may or may not be what you want.
72
+ - **Stub-size threshold** at 1500 bytes is empirical from post_transformers — adjust via `NOTEBOOK_STUB_BYTES` if your placeholder notebooks are larger.
73
+ - **`pdftocairo` produces a tiny SVG** for some PDF inputs (vector layers ungrouped, etc.). The script auto-falls back to `pdftoppm -r 200 -png` at 200 DPI when SVG output is < 200 bytes.
74
+
75
+ ## Canonical files
76
+
77
+ - `scripts/build-figures.mjs:23-32` — path resolution + env overrides
78
+ - `scripts/render-notebooks.mjs:30-46` — same
79
+ - `package.json` `prebuild` / `predev` / `build:assets` — orchestration
80
+ - `.gitignore` — toggles whether artifacts are committed
81
+
82
+ ## Reference implementation
83
+
84
+ [`~/Claude/post_transformers/guides/web/`](../) ships both pipelines pointed at `../figures/` and `../notebooks/` via env vars in its `package.json` scripts. The post_transformers repo commits its generated `public/figures/` and `public/notebooks/` (the Cloudflare workaround), see commit `f7fa75d`.
@@ -0,0 +1,118 @@
1
+ # Recipe 04 — Component library
2
+
3
+ **Profile**: components are profile-flavored. Tools family always available; academic family available when `BOOK_PROFILE=academic`. Utility components (Cite, XRef, Figure, …) work in any profile.
4
+
5
+ **TL;DR**: Two callout families coexist (per Q3). Authors `import` what they need. The scaffold doesn't force migration of existing tools-profile books to academic, or vice versa.
6
+
7
+ ## Callout families
8
+
9
+ ### Tools family (8 components, default in `BOOK_PROFILE=tools`)
10
+
11
+ `src/components/callouts/`:
12
+
13
+ | Component | Use for | Visual |
14
+ |---|---|---|
15
+ | `SkillBox` | A skill the reader practices | green left-bar (tip) |
16
+ | `CaseStudy` | Concrete worked example with date stamp | blue left-bar (info) |
17
+ | `ConceptBox` | Term + crisp definition | plum left-bar (authority) |
18
+ | `KeyIdea` | Bold short principle | gold left-bar (insight) |
19
+ | `TryThis` | Reader exercise / activity | green left-bar (tip) |
20
+ | `Recovery` | Anti-pattern + fix | rose left-bar (warning) |
21
+ | `Convergence` | Multiple tools agree (with `tools` array) | gold left-bar |
22
+ | `Divergence` | Tools disagree (with `axis` label) | gold dashed left-bar |
23
+
24
+ ### Academic family (10 components, default in `BOOK_PROFILE=academic`)
25
+
26
+ `src/components/callouts/`:
27
+
28
+ | Component | Use for |
29
+ |---|---|
30
+ | `NoteBox` | Aside / clarification |
31
+ | `ExampleBox` | Worked example |
32
+ | `DynConnect` | Cross-chapter conceptual link |
33
+ | `InsightBox` | High-level "why this matters" |
34
+ | `WarnBox` | Reader pitfall |
35
+ | `CounterBox` | Counter-example / wrong-but-instructive |
36
+ | `TipBox` | Practical advice |
37
+ | `OpenQuestion` | Research gap |
38
+ | `PaperBox` | Reference to a specific paper |
39
+ | `ResultBox` | Theorem / proposition / lemma headline |
40
+
41
+ For full theorem-like environments (proof scaffolding, numbering), use `<Theorem>` (below).
42
+
43
+ ## Theorem family (academic profile)
44
+
45
+ `src/components/Theorem.astro` — unified component for nine LaTeX-style environments via the `type` prop:
46
+
47
+ ```mdx
48
+ <Theorem type="theorem" id="thm:zoh-stability" label="ZOH stability">
49
+ The bilinear discretization preserves stability iff $|\lambda \Delta t| < 1$.
50
+ </Theorem>
51
+
52
+ <Theorem type="proof">
53
+ Direct algebra on the bilinear map.
54
+ </Theorem>
55
+ ```
56
+
57
+ Supported `type` values: `theorem`, `proposition`, `lemma`, `corollary`, `definition`, `example`, `exercise`, `remark`, `proof`. Each gets its own bar color and numbering counter.
58
+
59
+ ## Utility components (any profile)
60
+
61
+ `src/components/`:
62
+
63
+ | Component | Purpose | Example |
64
+ |---|---|---|
65
+ | `Cite` | Inline citation linked to `/references` | `<Cite key="gu2024mamba" page="3" />` |
66
+ | `XRef` | Cross-reference to a labeled element | `<XRef id="thm:zoh-stability" />` |
67
+ | `Figure` | Image + caption + id | `<Figure src="/figures/week04/eigenvalues.svg" caption="…" id="fig-eig" />` |
68
+ | `MarginNote` | Right-margin annotation (Tufte-style) | `<MarginNote>side comment</MarginNote>` |
69
+ | `Sidenote` | Auto-numbered marginalia | `<Sidenote>numbered note</Sidenote>` |
70
+ | `WeekRef` | Jump-link to a week chapter | `<WeekRef week={4} />` |
71
+ | `CodeRef` | GitHub-deep-link to file:line | `<CodeRef path="experiments/jax/foo.py" line={42} />` |
72
+ | `CodeBlock` | Embed code-file range with syntax highlight | `<CodeBlock src="…" lines="10-30" />` |
73
+ | `Tag` | Inline volatility/topic tag | `<Tag>stable-principle</Tag>` |
74
+ | `StatusBadge` | Render frontmatter `status` value with color | `<StatusBadge status={frontmatter.status} />` |
75
+ | `ChapterHeader` | Auto-rendered metadata block (week, part, status, companion links) | placed at top of each chapter automatically by Chapter.astro |
76
+
77
+ ## Conditional imports
78
+
79
+ Authors are responsible for importing what they use. The scaffold doesn't auto-import; this keeps build output clean and makes intent explicit:
80
+
81
+ ```mdx
82
+ ---
83
+ title: "Week 4 — Discretization"
84
+ week: 4
85
+ part: ssm-core
86
+ status: implemented
87
+ ---
88
+ import NoteBox from '../../components/callouts/NoteBox.astro';
89
+ import Theorem from '../../components/Theorem.astro';
90
+ import Cite from '../../components/Cite.astro';
91
+
92
+ <NoteBox>This is the heart of S4.</NoteBox>
93
+
94
+ <Theorem type="theorem">…</Theorem>
95
+
96
+ The HiPPO theory <Cite key="gu2020hippo" /> shows that …
97
+ ```
98
+
99
+ ## Mixing families
100
+
101
+ You can use tools-family callouts in an academic book or vice versa — nothing stops you. The "default family" per profile is only about what `examples/chapter-template-*.mdx` import for you. Drop in a `<SkillBox>` in an academic chapter when it fits.
102
+
103
+ ## Common gotchas
104
+
105
+ - **Path depth**: chapters in `src/content/chapters/foo.mdx` import via `../../components/...`. If you nest chapters in a subfolder (`src/content/chapters/week04/intro.mdx`), use `../../../components/...`.
106
+ - **Cite throws on unknown bibkey**: this is intentional (recipe 02). Run `npm run build:bib` after editing `bibliography.bib`.
107
+ - **XRef silently renders `[?label]` for unknown ids**: the validator (recipe 09) catches these — don't rely on visual inspection.
108
+ - **Theorem id collisions across chapters**: include a chapter prefix (e.g. `id="w4:thm:zoh"` not `id="thm:zoh"`).
109
+
110
+ ## Canonical files
111
+
112
+ - `src/components/callouts/` — both families, 18 components total
113
+ - `src/components/Theorem.astro` — unified theorem-like environment
114
+ - `src/components/{Cite,XRef,Figure,…}.astro` — utility components (10 total)
115
+
116
+ ## Reference implementation
117
+
118
+ [`~/Claude/post_transformers/guides/web/src/content/chapters/`](../../post_transformers/guides/web/src/content/chapters/) — 6 chapters exercising the academic family. [`~/Claude/book-template-astro/src/content/chapters/`](../../book-template-astro/src/content/chapters/) — 23 chapters exercising the tools family.
@@ -0,0 +1,74 @@
1
+ # Recipe 05 — Deploy to Cloudflare Workers + Static Assets
2
+
3
+ **Profile**: any (deploy mechanism is profile-agnostic).
4
+
5
+ **TL;DR**: Cloudflare unified Pages → Workers + Static Assets (late 2025). For new static sites, use the Workers flow: `wrangler.toml` + `npx wrangler deploy`. Legacy Pages OAuth still works for existing projects; see "Legacy Pages alternative" below.
6
+
7
+ ## Setup (one-time, ~10 minutes in the dashboard)
8
+
9
+ 1. **Sign up** at cloudflare.com (free tier covers everything: 500 builds/month, unlimited bandwidth, unlimited sites).
10
+
11
+ 2. **Workers & Pages → Create → Connect to Git**:
12
+ - Authorize Cloudflare's GitHub app
13
+ - Grant repo access (scope to just your book repo)
14
+
15
+ 3. **Configure build**:
16
+ - **Project name**: hyphens only (e.g. `my-book-guide`) — becomes the subdomain
17
+ - **Production branch**: `main`
18
+ - **Build command**: `npm install && npm run build`
19
+ (or `cd guides/web && npm install && npm run build` for monorepo setups)
20
+ - **Deploy command**: `npx wrangler deploy`
21
+ (or `cd guides/web && npx wrangler deploy` for monorepo)
22
+
23
+ 4. **Customize `wrangler.toml`** at scaffold root (or your Astro project root):
24
+ - Edit `name = "your-book-name"` to match the dashboard project name
25
+ - `[assets] directory = "./dist/"` — should match Astro's build output
26
+
27
+ 5. **Save and Deploy.** First build takes ~5-7 minutes (mostly npm install). After: every push to main auto-deploys.
28
+
29
+ URL: `https://<project-name>.<your-account>.workers.dev`.
30
+
31
+ ## The `cd` prefix for monorepo Astro projects
32
+
33
+ When `wrangler.toml` lives in a subdirectory (e.g. `guides/web/wrangler.toml`), Cloudflare runs commands from the repo root by default. Wrangler can't find its config there → deploy fails with "Could not detect a directory containing static files".
34
+
35
+ Fix: prefix both **build** and **deploy** commands with `cd <subdir> &&` so they execute in the same working directory as `wrangler.toml`. Build commands also need this since they produce the `./dist/` that wrangler.toml's `[assets]` reads.
36
+
37
+ ## Build-container quirks: poppler + uv missing
38
+
39
+ Cloudflare's build container ships Node + npm but **not** `pdftocairo` (poppler-utils) or `uv` (Python venv manager). If your book uses the figure or notebook pipelines (recipe 03):
40
+
41
+ - The scripts already graceful-skip on missing tools — build won't fail
42
+ - **But** the public/figures/ and public/notebooks/ directories will be empty
43
+ - Two solutions documented in recipe 03; the recommended one is committing the derived artifacts (remove their lines from .gitignore)
44
+
45
+ ## Legacy Pages alternative
46
+
47
+ For accounts still on the Pages-only flow (or by explicit preference):
48
+
49
+ - Use the `.github/workflows/deploy.yml` template that ships in v2.0 (legacy path)
50
+ - Add `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` GitHub secrets
51
+ - Token created at **My Profile → API Tokens → Create Custom Token** with permissions: `Account / Cloudflare Pages / Edit`
52
+ - Push to main triggers the workflow
53
+
54
+ This path doesn't use `wrangler.toml`. URL: `<project>.pages.dev`.
55
+
56
+ ## Common gotchas
57
+
58
+ - **First deploy 404s** — Cloudflare takes ~30s after the deploy completes to propagate DNS. Refresh after a minute.
59
+ - **Build succeeds but deploy fails with "Could not detect static files"** — `wrangler.toml` is in a subdir; the deploy command needs the `cd` prefix.
60
+ - **Project name with underscores** breaks the subdomain. Always hyphens (e.g. `my-book` not `my_book`).
61
+ - **Deploy command field doesn't exist in older dashboard UI** — Cloudflare migrated the dashboard; some older accounts see only "Build command" and skip Deploy. In that case, `npx wrangler deploy` runs automatically after the build command if `wrangler.toml` is present.
62
+
63
+ ## Custom domain
64
+
65
+ See `recipes/10-custom-domain.md`.
66
+
67
+ ## Canonical files
68
+
69
+ - `wrangler.toml` at scaffold root — Workers + Static Assets config
70
+ - `.github/workflows/deploy.yml` (preserved from v1) — legacy Pages OAuth path
71
+
72
+ ## Reference implementation
73
+
74
+ [`~/Claude/post_transformers/guides/web/wrangler.toml`](../../post_transformers/guides/web/wrangler.toml) — production-deployed to `post-transformers-guide.brandon-m-behring.workers.dev` since 2026-05-18.
@@ -0,0 +1,73 @@
1
+ # Recipe 06 — Mobile-first Tufte layout + sidebar
2
+
3
+ **Profile**: any (layout is profile-agnostic; sidebar groups chapters differently per profile).
4
+
5
+ **TL;DR**: Three-tier Tufte width (65/80/90ch) + 28/24/26ch sidenote column + left chapter-nav sidebar at ≥1024px. Mobile (<48rem) collapses to single column with inline sidenote asides; sidebar hides. All pure CSS, zero JS.
6
+
7
+ ## The three layers
8
+
9
+ 1. **Floating chrome** (top-right, `position: fixed`): theme toggle + search + tools-profile islands (`ToolFilter`, `VersionSelector`) when `BOOK_PROFILE !== 'academic'`.
10
+ 2. **Left sidebar** (`Sidebar.astro`): chapter nav grouped by part. Sticky to top, ≤100vh, independently scrollable. Hidden below 64rem (1024px).
11
+ 3. **Main content** (`.prose`): Tufte 2-column. Main text at `--measure-main`, sidenote column at `--measure-side`, both responsive to viewport tier.
12
+
13
+ ## Three-tier width strategy
14
+
15
+ Stock Tufte fixes `--measure-main` at 65ch (~520px). On 1440px+ monitors with a 16rem sidebar, that leaves ~600px of empty page margin — readable but wasteful. The scaffold tiers the measure:
16
+
17
+ | Tier | Trigger | `--measure-main` | `--measure-side` | Sidebar | Notes |
18
+ |---|---|---|---|---|---|
19
+ | 1 (default) | ≥48rem | 65ch | 28ch | hidden | Laptop-friendly typographic measure |
20
+ | 2 (sidebar) | ≥64rem | 65ch | 28ch | 16rem | Sidebar appears; main column unchanged |
21
+ | 3 (wide) | ≥90rem (1440px) | 80ch | 24ch | 18rem | Wider sidebar + main column |
22
+ | 4 (ultrawide) | ≥120rem (1920px) | 90ch | 26ch | 18rem | At Tufte's 90ch upper limit of comfortable reading |
23
+
24
+ The 90ch ceiling is firm — beyond that, line length crosses the eye's tracking limit and readers lose place. Don't push higher.
25
+
26
+ **Mobile (<48rem)**: sidenotes reflow inline as colored asides via `@media (max-width: 48rem)` in `layout.css`. Sidebar is `display: none` below 64rem so phones get pure single-column content.
27
+
28
+ ## Verification methodology (Playwright)
29
+
30
+ Width tuning was empirical, not theoretical. The breakpoint values came from comparing rendered chapters at four viewports (375 / 1280 / 1440 / 1920px) and measuring main-column utilization. Final ratios:
31
+
32
+ - 1280px: 70% (no sidebar) — comfortable
33
+ - 1440px: 89% (with sidebar) — was 62% pre-fix
34
+ - 1920px: 72% (with sidebar) — was 47% pre-fix
35
+
36
+ Re-tune with Playwright + `browser_take_screenshot` at the four viewports above whenever the design changes. Snapshots committed to `audits/` if a major retune.
37
+
38
+ ## Per-page sidebar toggle
39
+
40
+ ```astro
41
+ <Base title="..." showSidebar={false}>
42
+ <!-- landing pages, splash screens, search results — no nav -->
43
+ </Base>
44
+ ```
45
+
46
+ Default is `showSidebar={true}`. The landing page (`src/pages/index.astro`) sets false because it's a one-page demo with no chapter context. Every chapter route inherits the default (true).
47
+
48
+ ## Customizing the sidebar
49
+
50
+ `Sidebar.astro` reads `import.meta.env.BOOK_PROFILE` to decide grouping:
51
+
52
+ - **Academic profile**: groups by string-enum `part` (`foundations`/`ssm-core`/`beyond-ssm`/`integration`/`synthesis`). Sorts by `week`. Renders `W01`, `W02` prefixes.
53
+ - **Tools/minimal profile**: groups by numeric `part`. Sorts by `chapter`. Renders `Ch1`, `Ch2` prefixes.
54
+
55
+ Edit `siteTitle` / `siteSubtitle` constants at top of `Sidebar.astro` for branding. For per-page overrides, lift to Astro.props.
56
+
57
+ ## Common gotchas
58
+
59
+ - **Sidebar shows under chapter content at exactly 1023px**: the breakpoint is `min-width: 64rem` = 1024px. At 1023px sidebar is hidden, chapter is full width. This is intentional — the cutoff is the rough boundary between tablet and laptop.
60
+ - **Sidebar overflow on long chapter titles**: `.sidebar-chapter-title` wraps; the 2.4em prefix grid column stays fixed. If you need shorter, edit `Sidebar.astro` line `grid-template-columns: 2.4em 1fr` and increase the prefix column.
61
+ - **Forgetting `class="layout-main"` on the main wrapper**: causes `.prose` to fight the grid. `Base.astro` handles this; don't bypass the layout wrapper.
62
+ - **Custom-width pages**: if you need a layout outside Tufte (e.g. a dashboard), use `<Base showSidebar={false}>` and write your own container CSS. Don't reach into `.prose`.
63
+
64
+ ## Canonical files
65
+
66
+ - `src/styles/tokens.css` — three-tier `--measure-main` / `--measure-side` via `@media (min-width: 90rem|120rem)`
67
+ - `src/styles/layout.css` — `.layout-with-sidebar` grid + `.prose` Tufte rules + mobile sidenote reflow
68
+ - `src/components/Sidebar.astro` — profile-aware chapter nav
69
+ - `src/layouts/Base.astro` — `showSidebar` prop wiring
70
+
71
+ ## Reference implementation
72
+
73
+ [`~/Claude/post_transformers/guides/web/`](../../post_transformers/guides/web/) deployed at `post-transformers-guide.brandon-m-behring.workers.dev`. Three-tier breakpoint tuned via commits `d9d085b` (initial Tufte) → `8f3c6b1` (sidebar) → final ratios verified at four viewports.
@@ -0,0 +1,84 @@
1
+ # Recipe 07 — Chapter shapes (skeleton patterns)
2
+
3
+ **Profile**: profile-aware; pick the chapter shape that matches your book's pedagogy.
4
+
5
+ **TL;DR**: Two opinionated chapter skeletons ship in `examples/`. Copy whichever matches your profile to `src/content/chapters/` and edit. Both are 1-file MDX templates with frontmatter pre-filled.
6
+
7
+ ## The two shapes
8
+
9
+ ### Academic (week-based)
10
+
11
+ `examples/chapter-template-academic.mdx`:
12
+
13
+ ```
14
+ 1. Overview (½ page) — what this week covers + why
15
+ 2. Theory — core math/definitions/theorems
16
+ 3. Examples (worked) — concrete instances
17
+ 4. Reflections — what the reader should walk away with
18
+ 5. Forward-map — how this chapter connects to next week
19
+ ```
20
+
21
+ Designed for textbook-style sequencing. Frontmatter required: `week`, `part` (enum), `status` (7-state).
22
+
23
+ When to use: lecture-note format, curricular sequencing (W01 → W02 → … → W21), every chapter teaches a single body of math.
24
+
25
+ ### Tools (Koller-Friedman)
26
+
27
+ `examples/chapter-template-tools.mdx`:
28
+
29
+ ```
30
+ 1. Representation — what the artifact / concept LOOKS LIKE
31
+ 2. Operation — what you DO with it
32
+ 3. Evolution — what's converging / diverging across the field
33
+ ```
34
+
35
+ Designed for comparative-practitioner books where multiple tools (Claude Code, Gemini CLI, Codex CLI, …) share an underlying capability that you're tracking over time.
36
+
37
+ When to use: cross-tool reviews, "what does X look like in each tool", evolving-technology books with versioned content.
38
+
39
+ Source: Koller & Friedman, *Probabilistic Graphical Models*, 2009 — chapter structure that separates static representation from operations on it from evolutionary dynamics. Adapted from PGM models to apply to tools.
40
+
41
+ ## Picking a shape
42
+
43
+ | If your book is … | Use shape | Profile |
44
+ |---|---|---|
45
+ | A textbook with sequential dependency | Academic | `academic` |
46
+ | Lecture notes (one topic per chapter) | Academic | `academic` |
47
+ | Research synthesis (one paper or theorem per chapter) | Academic | `academic` |
48
+ | A practitioner field-guide across multiple tools | Tools | `tools` |
49
+ | A versioned tech survey with convergence tracking | Tools | `tools` |
50
+ | A solo essay collection | either, lean Academic | `minimal` (uses tools schema) |
51
+
52
+ ## Hybrid books
53
+
54
+ You're allowed to mix. The frontmatter Zod schema is what's enforced; the chapter shape is only a template. Build a chapter however you want as long as:
55
+
56
+ 1. Frontmatter passes its schema (`academicChapterSchema` or `toolsChapterSchema`).
57
+ 2. The rendered output fits the layout (don't break out of `.prose` without `.wide`).
58
+
59
+ post_transformers uses academic for all 6 published chapters. book-template-astro uses tools for all 23. Mixing has not been tested in production — proceed with sense.
60
+
61
+ ## Customizing a shape
62
+
63
+ Both templates are starting points. Common customizations:
64
+
65
+ - **Add more sections**: append after Forward-map (academic) or after Evolution (tools). Use `<h2>` for top-level sections.
66
+ - **Skip a section**: just delete it. Nothing in the scaffold requires Theory or Operation to exist.
67
+ - **Add a sidebar of running examples**: use `<MarginNote>` for short notes, or `<aside class="wide">` for full-width sidebars.
68
+
69
+ ## Common gotchas
70
+
71
+ - **Forgetting the import block**: each template imports the components it uses (NoteBox, Theorem, Cite for academic; SkillBox, KeyIdea, Convergence for tools). If you delete a section, also delete the unused import — astro warns but doesn't fail.
72
+ - **Frontmatter `week` vs `chapter`**: academic uses `week`; tools uses `chapter`. The wrong key under the wrong profile is a Zod error caught at content-sync time.
73
+ - **`status` field is required under academic**: pick one of `implemented` / `chapter_only` / `prose_only` / `code_only` / `reading_only` / `scaffolded` / `planned`. See `~/Claude/post_transformers/docs/STATUS.md` for the canonical state-machine.
74
+
75
+ ## Canonical files
76
+
77
+ - `examples/chapter-template-academic.mdx` — week-based skeleton
78
+ - `examples/chapter-template-tools.mdx` — Koller-Friedman skeleton
79
+ - `pedagogy/kf-chapter-shape.md` — full KF pedagogy methodology
80
+
81
+ ## Reference implementation
82
+
83
+ - Academic: [`~/Claude/post_transformers/guides/web/src/content/chapters/week04.mdx`](../../post_transformers/guides/web/src/content/chapters/week04.mdx) — exemplar
84
+ - Tools: [`~/Claude/book-template-astro/src/content/chapters/05-context-as-currency.mdx`](../../book-template-astro/src/content/chapters/05-context-as-currency.mdx) — exemplar
@@ -0,0 +1,110 @@
1
+ # Recipe 08 — Decisions ledger
2
+
3
+ **Profile**: any (this recipe documents the scaffold-level decisions, not per-book).
4
+
5
+ **TL;DR**: The scaffold encodes 15 design decisions from the v2.0 cross-book consolidation (2026-05-18). This recipe lists each decision, the reasoning, and when to deviate.
6
+
7
+ The full master plan with discussion lives at `~/.claude/plans/i-want-to-investigate-recursive-yao.md`. The decisions ledger below is the operational summary.
8
+
9
+ ## Strategic decisions (Round 1)
10
+
11
+ ### D1. Canonical repo
12
+ **Decision**: `book-scaffold-astro` is the evolved canonical template (not a new repo).
13
+ **Reasoning**: One source of truth across multiple book projects.
14
+ **Deviate when**: Never, while v2.0 is current. If you want a different scaffold, fork.
15
+
16
+ ### D2. Scope
17
+ **Decision**: Full backport — KaTeX + asset pipelines + academic callouts. Opt-in via `BOOK_PROFILE`.
18
+ **Reasoning**: Future academic books would otherwise have to re-invent the wheel.
19
+ **Deviate when**: You want a strictly-minimal scaffold without the academic surface — pick `BOOK_PROFILE=tools` or `minimal`.
20
+
21
+ ## Cross-book decisions (Round 2)
22
+
23
+ ### D3 — Recipe shape (Q1)
24
+ **Decision**: `BOOK_PROFILE` env var + `recipes/` markdown how-tos.
25
+ **Reasoning**: Single repo, profile-aware integrations, human-readable docs.
26
+ **Deviate when**: You need a `book.config.ts` file — document the migration in this ledger.
27
+
28
+ ### D4 — Citation workflow (Q2)
29
+ **Decision**: Both BibTeX pipeline and YAML source manifest ship. `academic` defaults to BibTeX; `tools` defaults to YAML.
30
+ **Deviate when**: You can override the profile default at any time — both are always available.
31
+
32
+ ### D5 — Callouts (Q3)
33
+ **Decision**: Two callout families coexist. Authors `import` what they want.
34
+ **Reasoning**: Don't force migration of book-template-astro's tools family or post_transformers' academic family.
35
+ **Deviate when**: You want a single family — just don't import from the other.
36
+
37
+ ### D6 — Status taxonomy (Q4)
38
+ **Decision**: Both schemas. `academic` → 7-state status; `tools` → volatility + T1-T4 source tiers.
39
+ **Deviate when**: Your book needs neither — pick `minimal` and add your own frontmatter (schema is forgiving).
40
+
41
+ ### D7 — Chapter shapes (Q5)
42
+ **Decision**: Two skeleton templates in `examples/`. Recipe 07 explains when to use which.
43
+ **Deviate when**: Build your own shape — both templates are starting points, not laws.
44
+
45
+ ### D8 — Tufte width (Q6)
46
+ **Decision**: Three-tier breakpoints: 65ch (default) → 80ch at 90rem → 90ch at 120rem.
47
+ **Reasoning**: Empirically tuned via Playwright at 1280/1440/1920px — see recipe 06.
48
+ **Deviate when**: Your book has unusually short or long content — edit `tokens.css` directly.
49
+
50
+ ### D9 — Deploy default (Q7)
51
+ **Decision**: Cloudflare Workers + Static Assets via `wrangler.toml`.
52
+ **Reasoning**: Cloudflare's official path post-Pages deprecation.
53
+ **Deviate when**: Self-host / GitHub Pages / Vercel — `dist/` is a vanilla static bundle, swap the deploy mechanism. Legacy Pages OAuth flow still documented in recipe 05.
54
+
55
+ ### D10 — License (Q8)
56
+ **Decision**: Dual — `LICENSE` (MIT, code) + `LICENSE-CONTENT` (CC-BY-4.0, prose).
57
+ **Reasoning**: Matches post-transformers; reasonable defaults for tech books.
58
+ **Deviate when**: You want different license — overwrite both files.
59
+
60
+ ## Distribution decisions (Round 3)
61
+
62
+ ### D11 — Migration path (Q9)
63
+ **Decision**: Existing books (post-transformers, book-template-astro) freeze on current code. v2.0 is for new books only.
64
+ **Reasoning**: Zero churn for existing books; low cost of bug-fix divergence.
65
+ **Deviate when**: A third book project needs to consume scaffold patterns → trigger v3.0 (npm package).
66
+
67
+ ### D12 — AI authoring guides (Q10)
68
+ **Decision**: Both `CLAUDE.md` (Claude Code auto-loads) + `AGENTS.md` (cross-tool) at scaffold root.
69
+ **Deviate when**: Never — both stay. AGENTS.md can be a 3-line pointer to CLAUDE.md to avoid drift.
70
+
71
+ ### D13 — Recipe format (Q11)
72
+ **Decision**: Terse pointers, 50-100 lines per recipe.
73
+ **Reasoning**: Solo-author time budget; readers follow pointers into canonical code.
74
+ **Deviate when**: A recipe genuinely needs more depth (the validator one is long because the design rationale matters).
75
+
76
+ ### D14 — Sample content (Q12)
77
+ **Decision**: One profile-aware demo chapter (`week01-hello-world.mdx`) shipped by `create-book.sh`.
78
+ **Reasoning**: Concrete starting point; reader deletes and replaces.
79
+ **Deviate when**: Never — the demo is intentional even if the user deletes it immediately.
80
+
81
+ ## Polish decisions (Round 4)
82
+
83
+ ### D15 — Cross-project conventions (Q13)
84
+ **Decision**: Scaffold's `CLAUDE.md` references the hub at `~/Claude/lever_of_archimedes/patterns/`.
85
+ **Reasoning**: Hub stays canonical; scaffold inherits via reference.
86
+ **Deviate when**: Your environment doesn't use the lever-of-archimedes hub — strip the inheritance block from CLAUDE.md.
87
+
88
+ ### D16 — Validation (Q14)
89
+ **Decision**: `scripts/validate.mjs` ships at v2.0 (not deferred). ~150 lines. Wired into prebuild + recommended pre-commit.
90
+ **Reasoning**: XRef/Figure/bibkey typos are common and silent — the validator is the only safety net.
91
+ **Deviate when**: Never — disable individual checks via environment if needed (e.g. unset `BOOK_REPO_ROOT` to skip CodeRef path checks).
92
+
93
+ ### D17 — Custom domain (Q15)
94
+ **Decision**: `recipes/10-custom-domain.md` ships at v2.0.
95
+ **Deviate when**: Always include this recipe — custom domain is the most common post-deploy ask.
96
+
97
+ ## v3.0 (deferred)
98
+
99
+ Not in v2.0:
100
+ - npm package distribution (`@brandon-behring/book-scaffold-astro`) — trigger: third book OR external consumer
101
+ - Profile-conditional subpath exports
102
+ - Migration of post-transformers + book-template-astro to consume the package
103
+
104
+ Until v3.0 triggers, accept that bug fixes don't auto-flow to existing books. Both books are currently low-churn.
105
+
106
+ ## How to use this ledger
107
+
108
+ When a future change in the scaffold contradicts a decision above, update this ledger first. Don't change behavior silently — the ledger is the durable record of "why this is shaped like it is."
109
+
110
+ Each decision has an issue-trail in `~/.claude/plans/i-want-to-investigate-recursive-yao.md` if you need full reasoning.
@@ -0,0 +1,106 @@
1
+ # Recipe 09 — Pre-flight validation
2
+
3
+ **Profile**: any (Cite checks skip under non-academic).
4
+
5
+ **TL;DR**: `npm run validate` runs `scripts/validate.mjs` against all chapter MDX files. Catches typo'd bibkeys / XRef ids / Figure paths / internal links that `astro build` would either miss or surface with poor context. Auto-runs as `prebuild`; recommend wiring into pre-commit too.
6
+
7
+ ## What gets checked
8
+
9
+ | Check | Profile | Why this is needed (vs astro build alone) |
10
+ |---|---|---|
11
+ | `<Cite key="...">` resolves in `src/data/references.json` | academic | Cite.astro throws on the first unknown key. Validator surfaces ALL bad keys at once. |
12
+ | `<XRef id="...">` resolves in `src/data/labels.json` | all | XRef.astro silently renders `[?label]` placeholders. Without this check, typos ship to readers. |
13
+ | `<Figure src="/...">` file exists under `public/` | all | Figure.astro emits a broken-image icon for missing files; build doesn't fail. |
14
+ | `[text](/internal-link)` resolves to known chapter slug or top-level route | all | Astro won't fail on dead internal links. Warning, not error (regex misses dynamic routes). |
15
+ | `<CodeRef path="..." line={N} />` path exists + line in bounds | all, if `BOOK_REPO_ROOT` set | Catches stale line numbers after code refactors in the paired experiments/ repo. |
16
+
17
+ ## What is NOT checked (already covered elsewhere)
18
+
19
+ - **Frontmatter Zod validation** — `astro build` syncs content collections first; Zod errors there.
20
+ - **MDX renders** — `astro build` is the source of truth.
21
+ - **KaTeX strict-mode** (academic profile) — rehype-katex throws on undefined macros during build.
22
+
23
+ The validator's job is to fill the gaps `astro build` leaves, not duplicate it.
24
+
25
+ ## Wiring
26
+
27
+ ```jsonc
28
+ // package.json
29
+ {
30
+ "scripts": {
31
+ "validate": "node scripts/validate.mjs",
32
+ "prebuild": "npm run build:assets && npm run validate"
33
+ }
34
+ }
35
+ ```
36
+
37
+ For pre-commit: add to `.pre-commit-config.yaml`:
38
+
39
+ ```yaml
40
+ - repo: local
41
+ hooks:
42
+ - id: book-validate
43
+ name: validate book content
44
+ entry: npm run validate --silent
45
+ language: system
46
+ pass_filenames: false
47
+ files: 'src/content/chapters/.*\.(md|mdx)$'
48
+ ```
49
+
50
+ ## Environment variables
51
+
52
+ - `BOOK_PROFILE` — `academic` enables Cite-key checking; otherwise skipped.
53
+ - `BOOK_REPO_ROOT` — absolute path to the paired code repo for CodeRef checks. Unset → skipped (the scaffold default; minimal/tools books rarely have a paired code repo).
54
+
55
+ ## Output
56
+
57
+ Exit code = total error count. On success:
58
+
59
+ ```
60
+ validate: ✓ 6 chapter(s) checked (profile=academic); no errors.
61
+ ```
62
+
63
+ On failure, all issues listed at once with `file:line msg`:
64
+
65
+ ```
66
+ validate: ✗ 17 error(s) in 6 chapter(s) (profile=academic):
67
+ week05.mdx:102 Unknown XRef id "w4:prop:zoh-stability" — not in labels.json
68
+ week11.mdx:37 Unknown XRef id "ch:week13" — not in labels.json
69
+ ...
70
+ ```
71
+
72
+ Warnings (currently: internal-link unresolved) are printed to stderr but don't affect exit code.
73
+
74
+ ## Extending the validator
75
+
76
+ The script is ~150 lines of regex-driven scanning. To add a check:
77
+
78
+ 1. Define a regex (`RE_FOO`).
79
+ 2. Loop `content.matchAll(RE_FOO)` per chapter.
80
+ 3. Push to `errors` (build-blocking) or `warnings` (informational).
81
+
82
+ Examples of checks worth adding for specific books:
83
+
84
+ - **Word-count budget** per chapter (catch runaway chapters early)
85
+ - **Mandatory frontmatter fields** beyond the Zod schema (e.g. `last_verified` date older than 6 months → warning)
86
+ - **Image alt text presence** (accessibility)
87
+ - **No `TODO` strings in published chapters** (with frontmatter `status: implemented`)
88
+
89
+ Keep the script regex-based; resist the urge to pull in MDX AST parsing. The script must stay <2 s for the pre-commit-hook use case.
90
+
91
+ ## Common gotchas
92
+
93
+ - **Regex false negatives**: multi-line `<Cite\n key="...">` won't match. Authors should keep component attributes on one line; the build catches the residual cases.
94
+ - **`<Figure src>` with `BOOK_FIGURES_PATH` override**: the validator checks `public/figures/<...>` (post-build location), not the source `figures/` directory. Run `npm run build:figures` before `npm run validate` if assets are stale.
95
+ - **`labels.json` empty**: every XRef fires an error. Until you have a labels-building step (Phase 2.6 in post-transformers, deferred at scaffold v2.0), avoid `<XRef>` in chapter content — use direct markdown links instead.
96
+
97
+ ## Canonical files
98
+
99
+ - `scripts/validate.mjs` — the validator
100
+ - `src/data/references.json` — emitted by `scripts/build-bib.mjs` (recipe 02)
101
+ - `src/data/labels.json` — placeholder; populated by future labels-building step
102
+ - `src/components/{Cite,XRef,Figure,CodeRef}.astro` — components whose contracts validate.mjs enforces
103
+
104
+ ## Reference implementation
105
+
106
+ Tested against `~/Claude/post_transformers/guides/web/` (6 chapters, ~3000 lines of MDX): caught 17 unknown XRef ids that the empty labels.json had been hiding. Runtime: ~80 ms.