Releases: brandon-behring/book-scaffold-astro
v4.9.0 — universal slug + XRef crash & build-labels slug-404 fixes
Minor release. Makes the optional slug URL-override field universal across all profiles, and fixes two latent bugs surfaced by the mathematical-guides / claude-books consumers:
- fix(XRef.astro): the frontmatter doc comment's
{/* */}example, inside a/** */JSDoc block, closed the block early — esbuild threwUnexpected "}"on any MDX import of<XRef>. Now//line comments; the MDX-correct example is restored. - fix(build-labels): cross-reference hrefs now prefer frontmatter
slug:over the filename, matching Astro'sentry.id(no more 404s under a custom slug). - feat(schemas):
slugadded to academic / tools / minimal / course-notes (research-portfolio already had it).
288 tests (was 275). Full notes in CHANGELOG.md.
🤖 Generated with Claude Code
v4.4.0 — book-genre visual baselines + exercises auto-collection + /exercises route
[4.4.0] — 2026-05-25
Minor release. Polish closure of v4.3.0 deferred items. No new consumer issues since v4.3.0 ship — this release closes internal backlog before the next consumer-feedback cycle begins. All additive; consumers upgrade by bumping version with zero config changes.
Added
fixture-book-genrevisual regression fixture — 5 routes × 4 viewport widths = 20 new baseline PNGs at AE=0. Exercises all 6 v4.3.0 book-genre components (<Tip>,<TipsCard>,<Exercise>,<Practice>,<Solution>,<ExerciseSolutions>) plus both/tipsand/exercisesauto-routes. Closes the visual-coverage gap deferred from v4.3.0 to keep velocity.<ExerciseSolutions auto />mode — new optional boolean prop on the v4.3.0 component. Whenautois true, the component readssrc/data/exercises.json(emitted bybook-scaffold build-exercises), scopes by the current chapter viaAstro.url.pathname(matching the/chapters/<slug>/route pattern auto-injected since v4.3.0 #69), and auto-renders a list of exercise problem statements with placeholder solution lines (_Add your solution here._). Defaultauto: falsepreserves v4.3.0 manual-<Solution>behavior — no regression for existing consumers. Graceful skip + clear hint message when exercises.json is missing or the chapter URL pattern doesn't match.book-scaffold build-exercisesscript — sister to v4.3.0'sbuild-tips. Scans chapter MDX (honoringloader.baseoverrides viareadChaptersBasefrom v4.1.2) for<Exercise id="X">body</Exercise>instances via 2-branch regex (single + double quote portability, no backreference — same v4.1.2 cross-runtime lesson). Emitssrc/data/exercises.jsonkeyed by chapter slug. Run onprebuild. Wired into thebin/book-scaffolddispatcher./exercisesauto-route — opt-in viaroutes.exercises: trueindefineBookConfig(defaultfalseper profile, mirroringroutes.tips). Newpages/exercises.astroreadssrc/data/exercises.jsonand renders an index grouped by chapter with/chapters/<slug>/#exercise-<id>deep links + per-exercise problem-text previews (first 120 chars). Auto-injected viabookScaffoldIntegration(ROUTE_REGISTRY.exercisesentry).RouteToggles.exercises: booleanadded toprofile-kit.ts(defaultfalsefor all 5 built-in profiles — academic / tools / minimal / course-notes / research-portfolio).- 9 new build-exercises extractor tests (
tests/build-exercises.test.mjs) — mirror the v4.3.0 build-tips test suite: double-quoted / single-quoted / multiple / whitespace normalization / full body preserved / no-id skip / empty-id skip / empty source / no-Exercise source.
Changed
ExerciseSolutions.astronow branches onautoprop. Manual mode (no prop) renders the slot content exactly as v4.3.0 (regression-safe). Auto mode renders the auto-generated section.bin/book-scaffold.mjsregisters the newbuild-exercisessub-command + updates help text.
Migration
None. Pure additive minor release.
Out of scope / next sessions
- MDX AST-based solution-slot extraction (
<Fragment slot="solution">...</Fragment>inside<Exercise>) — v4.4.0 auto-collection captures problem text only, not solutions. Full solution-slot extraction would require MDX AST traversal at build time. Defer until a consumer asks; file a request if you'd like it in v4.5.0. - #15 Multibook routing, #16 AnkiCard + extract-cards CLI — still no consumer signal; deferred.
Release policy
- D12 lock-step preserved:
@brandon_m_behring/create-book@4.4.0ships alongside. - Pre-publish smoke gate (v3.6.5) ran end-to-end against academic before publish.
- 215 unit tests pass (206 existing + 9 new build-exercises).
- 96 visual baselines at AE=0 (76 existing + 20 new fixture-book-genre).
- Feedback loop: file friction at https://github.com/brandon-behring/book-scaffold-astro/issues with the
consumer:<workspace>label. If you'd like<Fragment slot="solution">extraction for auto-rendered actual solutions, file a v4.5.0 request.
v4.3.0 — chapters bug + pedagogy infrastructure batch
[4.3.0] — 2026-05-24
Minor release. Bundles 4 issues filed since v4.2.0 shipped (within ~36 hours): one real bug (#69), one docs gap (#68), and two pedagogy-component requests from the claude-books supplement-format decision round (#70 + #71). All additive; consumers upgrade by bumping version with zero config changes.
Fixed
-
/chapters/index links + auto-injected per-chapter route (#69). The shipped/chapters/index page previously linked to/<slug>/(root-level) instead of/chapters/<slug>/(under the chapters prefix). Every link on the academic chapters index 404'd. Two-part fix:package/pages/chapters.astro:115— href corrected to/chapters/${id}/- NEW
package/pages/chapters/[...slug].astro— per-chapter dynamic route auto-injected bybookScaffoldIntegrationwheneverroutes.chapters: true. Mirrors the v3.4.0 frontmatter pattern (toolkit ships BOTH index + dynamic route together). Layout switches by preset:Chapter.astrofor academic + research-portfolio (KaTeX + theorem chrome);Base.astrofor tools + minimal + course-notes (lighter).
Pre-v4.3.0 every academic consumer wrote the same dynamic-route boilerplate in their own
src/pages/chapters/[...slug].astro. v4.3.0 removes that boilerplate. Migration note for existing consumers: if you have your ownsrc/pages/chapters/[...slug].astroAND upgrade to v4.3.0, Astro will error on duplicate routes — either delete your local file (recommended) or keep yours and don't enableroutes.chapters: true(your file takes precedence as a consumer route).
Added
<Tip n="..." title="...">numbered-tips component (#70) — Pragmatic Programmer-style pull-quotable rules. Author provides the number; registry doesn't auto-number. Gold border +Tip {n}badge +#tip-{n}anchor for cross-references.defineTips({ volumeOffset, volumeLabel })API — cross-volume coordination helper. Branded type (matchesdefineStylepattern); lets a multi-volume series offset displayed numbers without renumbering source tags. Seepackage/src/lib/define-tips.ts.<TipsCard>component — print-friendly pull-out card listing all tips. Readssrc/data/tips.json. Graceful skip when tips.json missing./tipsauto-injected route — opt-in viaroutes.tips: true. Renders all tips with#tip-{n}permalinks, chapter backlinks, and 80-char body previews. Readssrc/data/tips.jsonfrom the new build script.book-scaffold build-tipsscript — scans chapter MDX for<Tip>instances via 4-branch regex (handles single + double + mixed quote styles, no backreference for cross-runtime portability — same lesson as v4.1.2 regex fix). Emitssrc/data/tips.jsonsorted byn; warns (doesn't fail) on duplicate numbers. Wired intobin/book-scaffold.mjsdispatcher.<Exercise id="...">inline-at-concept-introduction component (#71) — CS:APP precedent. Light treatment;#exercise-{id}anchor for cross-linking from<Solution>.<Practice id="..." difficulty="1-4">end-of-chapter component — diamond markers (◆◆◇◇ for difficulty=2). Closed TS literal union on difficulty (inlined single-line per v4.1.0 PocLayout lesson).#practice-{id}anchor.<Solution for="...">companion paired by id — backlinks to#exercise-{id}. Manual pairing (no build-time auto-collection in v4.3.0).<ExerciseSolutions>chapter-end wrapper — provides## Exercise solutionsheading + container for nested<Solution>elements. Author places<Solution>items inside manually.- New "book-genre" component family (cross-profile, 6 components) — documented in
package/CLAUDE.mdalongside the v4.1.0 pedagogy family. Names trace genre lineage (Pragmatic Programmer for Tip; CS:APP for Exercise/Practice). RouteToggles.tips: booleanfield added (all profiles defaulttips: false).package/recipes/17-draft-chapter-workflow.md(#68) — documents the canonicalgetCollection('chapters', (e) => !e.data.draft)filter pattern, when to use draft vs delete vs reorder, and aBOOK_INCLUDE_DRAFTSpreview-env pattern consumers can wire into their own override route. Closes the docs-discoverability loose end from #63's resolution.- 27 new tests — 9 build-tips extractor tests, 6 defineTips identity/branding tests, 12 book-genre component contract tests.
Changed
bin/book-scaffold.mjsdispatcher gainsbuild-tipssub-command + updated help text.build-figuresrow in CLI table notes that TikZ stage (v4.2.0) ships in build-figures.
Migration
None required for the additive changes (#68, #70, #71). For #69 specifically: if you've been hand-maintaining src/pages/chapters/[...slug].astro AND you opt into routes.chapters: true (the academic preset doesn't by default), DELETE your local file before upgrading or Astro will error on duplicate routes. v3 consumers who never used routes.chapters: true are unaffected.
Release policy
- D12 lock-step preserved:
@brandon_m_behring/create-book@4.3.0ships alongside. - Pre-publish smoke gate (v3.6.5) ran end-to-end against academic + research-portfolio before publish.
- 206 unit tests pass (171 existing + 27 new from this release + 8 carry-over from earlier patches).
- 76 visual baselines at AE=0 (existing fixtures unaffected by the #69 href fix — rendered output is byte-equivalent).
- Feedback loop: file friction at https://github.com/brandon-behring/book-scaffold-astro/issues with the
consumer:<workspace>label.
v4.2.0 — TikZ standalone → SVG via build-figures (#17)
[4.2.0] — 2026-05-23
Minor release. Closes #17 — book-scaffold build-figures now auto-compiles TikZ standalone .tex sources to .pdf via pdflatex before the existing PDF→SVG conversion runs. Additive only; consumers with .pdf-only figures are unaffected; consumers without TeX Live continue to work (clear ERROR + skip for .tex files; pre-built .pdf files still convert normally).
Added
- TikZ standalone → PDF → SVG pipeline (#17).
package/scripts/build-figures.mjsnow scansfigures/for both.pdfAND.texfiles. For each.texsource: if no sibling.pdfexists OR the.texis newer than the.pdf, runspdflatex -halt-on-error -interaction=nonstopmode -output-directory=. <name>.texin the source directory. The generated.pdfthen flows into the existing pdf2svg conversion stage unchanged. Missing-pdflatex case: emits clear ERROR with TeX Live install link (https://www.tug.org/texlive/) and skips.texfiles; continues processing any.pdf-only topics. Doesn't crash the build. package/recipes/16-tikz-figures.md— end-to-end recipe covering the\documentclass[tikz,border=2mm]{standalone}convention, discovery rule (when pdflatex runs vs skips), working-directory semantics (compiles infigures/<topic>/so\input{}relative paths work),.gitignoresnippet for intermediate.aux/.log/.fls/.fdb_latexmk/.synctex.gzfiles, TeX Live install instructions per OS, CI workflow snippet for runners that need to regenerate from source, and common debugging tips.tests/build-figures-tikz.test.mjs— 2 tests. (1) End-to-end: copy a minimaltikz-basic.texfixture to a temp project, runbuild-figures, assert thatpdfexists alongside.texAND that.svgexists underpublic/figures/sample/AND that the SVG contains valid<svg>markup. (2) Missing-pdflatex error message format. Both testsskipcleanly whenpdflatex/pdftocairoaren't on PATH (uses{ skip: undefined }semantics — Node 22's test runner treatsnullas a skip-with-reason, soundefinedis required for "run the test").tests/fixtures/figures/tikz-basic.tex— minimal TikZ standalone source for the pipeline test.- PACKAGE_DESIGN.md §7 — new "Optional system dependencies" subsection documenting
pdflatex+pdftocairoinstall paths. - PACKAGE_DESIGN.md §8 —
build-figuresrow updated to note the v4.2.0 TikZ stage.
Changed
build-figuresoutput line — now includestikz→pdfcount when stage 1 ran on at least one source (e.g.,build-figures: 3 total, 3 converted (0 png fallback), 0 cached, 1 tikz→pdf).
Migration
None. Pure additive minor release. Upgrade by bumping @brandon_m_behring/book-scaffold-astro and @brandon_m_behring/create-book to ^4.2.0 (lock-step). Consumers without .tex figures see zero behavior change. Consumers WITH .tex figures now get automatic compilation IF they have TeX Live installed; otherwise see a clear ERROR + continued processing of .pdf figures.
Release policy
- D12 lock-step preserved:
@brandon_m_behring/create-book@4.2.0ships alongside. - Pre-publish smoke gate (v3.6.5) ran end-to-end against academic + research-portfolio before publish.
- 171 unit tests pass (169 existing + 2 new TikZ tests; latter skip cleanly when TeX Live unavailable).
- Feedback loop: file friction at https://github.com/brandon-behring/book-scaffold-astro/issues with the
consumer:<workspace>label.
v4.1.2 — readChaptersBase regex CI compatibility
[4.1.2] — 2026-05-23
Hotfix release. Republishes v4.1.1's scope (#63 chapter discovery fix + fixture-pedagogy baselines) with a more permissive regex in readChaptersBase. The v4.1.1 tag exists on GitHub but was never published to npm because the readChaptersBase: double-quoted loader.base override works unit test failed in CI's Node 22 environment — the regex's (['"])([^'"]+)\1 backreference form behaved differently on the runner than locally. v4.1.2 uses two separate alternation branches (one per quote style) instead of a backreference; the test passes locally AND in CI. No consumer impact since v4.1.1 never reached npm.
Fixed
readChaptersBaseregex rewritten without backreference — replacedchapters\s*[:=][\s\S]{0,400}?loader\s*:[\s\S]{0,200}?base\s*:\s*(['"])([^'"]+)\1with\bchapters\b[\s\S]{0,500}?\bbase\s*:\s*'([^']+)'|\bchapters\b[\s\S]{0,500}?\bbase\s*:\s*"([^"]+)". Two consequences: (1) no more backreference (more portable across regex engines), (2) more permissive matching pattern — relaxes the requirement thatchaptersbe immediately followed by:or=. All 8 existingchapters-base-resolution.test.mjstests pass; behavior is conceptually unchanged.
Carries forward from the unreleased v4.1.1
book-scaffold validate+book-scaffold build-labelshonorloader.baseoverrides (#63)fixture-pedagogyvisual regression fixture — 20 new baseline PNGs at AE=0PocLayout.astrotype union flattened to single line
See the v4.1.1 entry below for the full details of those changes.
Release policy
- D12 lock-step preserved:
@brandon_m_behring/create-book@4.1.2ships alongside. - Pre-publish smoke gate (v3.6.5) ran end-to-end against academic + research-portfolio before publish.
- Test gate caught the regex regression in CI before publish — exactly the gate's intended purpose.
v4.1.1 — #63 chapter discovery fix + fixture-pedagogy baselines
v4.1.0 — claude-books pedagogy consumer batch
[4.1.0] — 2026-05-23
Consumer-batch minor release. Bundles 6 issues filed by the claude-books consumer during its 2026-05-23 pedagogy PoC round. All additive; no breaking changes; v4.0.0 consumers upgrade by bumping the version with no config edits required.
Why
The 2026-05-23 PoC round at claude-books/handbook rendered 5 supplement formats of Chapter 1 side-by-side (tutorial / how-to / TL;DR / part-summary / cheat-sheet) backed by research at claude-books/docs/research/11-pedagogy/ (Sweller cognitive-load, Bloom's-taxonomy, React.dev callout vocabulary). The round surfaced 4 missing components + 1 layout primitive + 1 build-noise bug + 1 docs gap, all at once. This release ships all 6.
Added
<Pitfall>component (#58) — React.dev "Pitfall" vocabulary for retrospective "common mistake" callouts. Distinct from<WarnBox>(which is preemptive). Crimson border + tinted background (new--warm-crimsontoken).<WorkedExample>component (#57) — collapsible demonstration block backed by Sweller/Cooper's worked-example-effect theory. Native<details>(no JS);idprop becomes#worked-example-{id}anchor (prefixed to avoid heading-anchor collisions); optionalexpandedprop. Plum border + chip in the summary.<YouWillLearn>component (#59) — chapter-opener "what this chapter delivers" callout (Bloom's-taxonomy framing). Slotted body (MDX bullets); optionalprerequisitesprop renders a "Before you start" sub-block. Gold border.<PocLayout>component (#56) — per-PoC-kind layout selector. Closed discriminatedkindunion:'tutorial' | 'how-to' | 'tldr' | 'part-summary' | 'cheat-sheet'. Each kind swaps 3 CSS variables (--bs-content-line-length,--bs-content-vertical-rhythm,--bs-heading-emphasis) on a.poc-layout-{kind}wrapper. Newpackage/styles/poc-layouts.cssships the variant table; consumers override via:where(.poc-layout-X)selectors.- Pedagogy family —
Pitfall/WorkedExample/YouWillLearnform a new "pedagogy" component family (any preset can use them). Documented inpackage/CLAUDE.md. PACKAGE_DESIGN.md §5a(#61) — new section "Custom collections + YAML date types" covers thez.date()vsz.string()gotcha + 2 safe patterns + anti-pattern. NozodDateStringhelper export in this release (one consumer hit the issue; docs solve it).- 17 new contract tests (
tests/pedagogy-callouts.test.mjs+tests/poc-layout-css.test.mjs) — assert each component's Props interface, default values, CSS class names; assert each PocLayout kind's CSS variable set. - 9 new isYamlEmpty tests (
tests/sources-empty-detection.test.mjs) — empty / whitespace / comment-only /[]/ single-entry / multi-entry / missing-file / malformed-yaml.
Changed
- Empty
sources/manifest.yamlno longer emits noisy WARN (#60). Astro'sfile()content loader previously logged[file-loader] No items found in sources/manifest.yamlfor every build — including when the file existed with[]content (valid pre-bibliography state in early Phase 1 chapter development).package/src/schemas-entry.tsnow detects empty manifests (after stripping#-comment lines + whitespace; treats[]and empty as empty) and skips registering the collection entirely. Distinguished states:- File missing → collection not registered; build is silent (preserves existing behavior).
- File exists, parses empty → collection not registered; build is silent (new).
- File exists with entries → collection registered; loader runs normally (unchanged).
- File exists, malformed YAML → Astro's loader surfaces the real ERROR (unchanged).
Migration
None. Pure additive minor release. Upgrade by bumping @brandon_m_behring/book-scaffold-astro and @brandon_m_behring/create-book to ^4.1.0 (lock-step).
Out of scope / next sessions
- Visual regression baselines for new components deferred to v4.1.1 (component contract tests cover the API; visual-pixel verification follows when fixture-pedagogy gets captured).
zodDateStringhelper export — wait for a second consumer to ask (avoids ad-hoc API surface).<PocLayout kind: string>escape hatch — closed union enforces vocabulary discipline; revisit if a sixth kind is needed.
Release policy
- D12 lock-step preserved:
@brandon_m_behring/create-book@4.1.0ships alongside the toolkit. - Pre-publish smoke gate (v3.6.5) ran end-to-end against academic + research-portfolio before publish.
- 161 unit tests pass (124 existing + 17 new pedagogy + 9 new empty-manifest + 11 existing katex/define-style).
- Visual regression baselines (56 existing) unaffected — no v4.0.0 components changed.
- Feedback loop: file friction at https://github.com/brandon-behring/book-scaffold-astro/issues with the
consumer:<workspace>label. v4.x is the iteration window for this API.
v4.0.0 — defineStyle architecture (BREAKING)
[4.0.0] — 2026-05-23
BREAKING: the v3.x preset: / profile: shorthand on defineBookConfig is removed. Replaced by typed defineStyle() composition via the new styles: [...] field. Migration is ~2 lines per book. Full migration recipe in package/MIGRATION-v3-to-v4.md; composition patterns in package/recipes/15-defining-styles.md.
Why
The v3.x API accumulated through 7 consumer-pilot releases. Each release added one or two top-level BookConfigOptions fields to address a specific consumer ask (routes, katexMacros, extraStyles, extraIntegrations, mdxComponentsModule, ...). v4 unifies that surface around a typed, branded, composable Style object: define a style once, import it across many books, override per-book explicitly. The user can now build style clusters (guidesFamilyStyle, coursebookStyle) once and reuse them across workspace siblings without per-book repetition.
Foundational design choices (locked during v4.0.0 design session, see /.claude/plans/examine-what-has-happened-peppy-scroll.md):
- Explicit over silent — no profile-level magic defaults; every config decision visible in the call site or the imported Style.
- No legacy debt — hard break at v4; sunset of v3 API is immediate.
- TypeScript strict-mode best practices — branded types via
unique symbol, closed shape (no public index signature; scopedextra?field instead — preserves typo protection on toolkit fields),satisfiesfor narrow registry inference,readonlyon all DTO fields,.jsextensions in imports.
Added
defineStyle(opts: StyleInput): Style— identity helper that creates a typed, branded, composable Style. Zero runtime overhead beyond an object spread + version marker. Branded type prevents confusion withPartial<BookConfigOptions>. See PACKAGE_DESIGN.md §4a.- 5 built-in Style exports —
academicStyle,toolsStyle,minimalStyle,courseNotesStyle,researchPortfolioStyle. One per preset. PlusBUILTIN_STYLES: Record<BookPreset, Style>(as const satisfies— narrow inferred lookup). composeStyles(styles)— public helper for advanced consumer composition. Per-key merge strategy documented in PACKAGE_DESIGN.md §4a table.styles?: readonly Style[]onBookConfigOptions— array of Styles composed left-to-right; top-level fields win over composed style chain.deploy?: 'pages' | 'workers'onBookConfigOptionsandStyle(#50) — drives create-book'swrangler.tomlshape. Inherited from chosen style; academic/tools/minimal default to'workers', course-notes/research-portfolio default to'pages'.routes.frontmatterwidened toboolean | { enabled: boolean; prefix?: string }(#49) — object form lets consumers control the URL prefix (prefix: ''mounts pages at root). Boolean form keeps working with default prefix'frontmatter'.extra?: Readonly<Record<string, unknown>>onStyle— scoped consumer-side metadata namespace. Ignored by toolkit; survives composition as per-key spread. Preserves typo protection on known toolkit fields (closed shape — no public index signature).__styleVersion: 1marker on every Style (auto-set bydefineStyle) — forward-compatibility hook for future API-shape evolution.package/recipes/15-defining-styles.md— new recipe covering workspace-local vs npm-package patterns, per-key merge semantics, escape hatches, feedback loop.package/MIGRATION-v3-to-v4.md— step-by-step migration recipe for the 4 known consumers + external users.PACKAGE_DESIGN.md §4rewritten + new §4a — fulldefineBookConfigv4 API contract +defineStyleAPI reference.- 40 unit tests in
package/tests/define-style.test.mjs— identity, branding, merge semantics for each field, BUILTIN_STYLES integrity, composition edge cases. - 5 new create-book scaffold tests — per-preset
wrangler.toml+ new v4astro.config.mjsshape.
Removed (BREAKING)
preset?: BookPresetfield onBookConfigOptions— replaced bystyles: [<presetName>Style]. Runtime check detects v3 usage and throwsBookConfigErrorwith auto-suggested replacement code + missing import line + link to MIGRATION-v3-to-v4.md.profile?: BookPresetfield (v3.4.0 backward-compat alias) — same treatment aspreset. Both throw the same migration error.- No backward-compatibility shim. Consumers in early pilot phase (~4 known + workspace siblings, all maintained by same author); migration is ~2 lines per book. The v3.7.1 line stays installable indefinitely via npm for consumers who need more time.
Changed
book-scaffold build-bibstrips%-prefixed comment lines before parsing (#54). Permanent fix for the citation-js parse-error class that drove the v3.6.1 → v3.6.4 hotfix chain. The library treats line-leading@TYPEtokens (e.g.,% @article{...}) as entry starts even inside comments; the pre-passstripBibtexLineCommentsremoves any line whose first non-whitespace character is%before passing to@citation-js/plugin-bibtex. No consumer action needed; all existing.bibfiles continue to work.create-bookgeneratedastro.config.mjsuses v4 API — emitsimport { defineBookConfig, <preset>Style } from '@brandon_m_behring/book-scaffold-astro'; export default await defineBookConfig({ styles: [<preset>Style], site: '...' });instead of v3preset: '<preset>'form.create-bookemits per-presetwrangler.toml— Workers shape (academic/tools/minimal) vs Pages shape (course-notes/research-portfolio).PROFILES[preset]?.katex === truegate preserved from v3.7.1 — KaTeX wiring activates for bothacademicandresearch-portfoliobased on the profile registry, not the preset literal. The fix is unchanged by the v4 API redesign.
Migration
For each book using @brandon_m_behring/book-scaffold-astro@^3.x, edit astro.config.mjs:
- import { defineBookConfig } from '@brandon_m_behring/book-scaffold-astro';
+ import { defineBookConfig, academicStyle } from '@brandon_m_behring/book-scaffold-astro';
export default await defineBookConfig({
- preset: 'academic',
+ styles: [academicStyle],
site: 'https://my-book.example.com/',
});| v3 preset | v4 import | v4 styles field |
|---|---|---|
'academic' |
academicStyle |
[academicStyle] |
'tools' |
toolsStyle |
[toolsStyle] |
'minimal' |
minimalStyle |
[minimalStyle] |
'course-notes' |
courseNotesStyle |
[courseNotesStyle] |
'research-portfolio' |
researchPortfolioStyle |
[researchPortfolioStyle] |
If migration friction surfaces, file an issue at https://github.com/brandon-behring/book-scaffold-astro/issues with the consumer:<your-workspace> label. The v4.x release line is explicitly the iteration window for this API.
Release policy
- D12 lock-step preserved:
@brandon_m_behring/create-book@4.0.0ships alongside the toolkit. - Pre-publish smoke gate (v3.6.5) ran end-to-end against academic + research-portfolio before publish.
- 124 unit tests + 56 visual baselines (AE=0) verify DOM output is unchanged by the API redesign.
v3.7.1 — consumer:guides bug-fix patch (#52 + #51 + #48)
Patch release closing 3 issues from the brandon-behring/guides + brandon-behring/guides-experimentation Phase 0b batch (2026-05-22→23). Unblocks that workspace's CI (was crashing on validate under Node 20) and fixes brace-math rendering in research-portfolio.
Fixed
book-scaffold validateno longer requires Node 22 (#52).scripts/validate.mjspreviously importedglobfromnode:fs/promises, an API added in Node 22. The scaffold's generated consumer CI templates shipnode-version: '20', sonpm run validatecrashed on every consumer's prebuild hook withSyntaxError: The requested module 'node:fs/promises' does not provide an export named 'glob'. Replaced with a recursivereaddirwalker (extracted toscripts/walk-mdx.mjsfor unit-testability). Works on Node 18+; output format matches the previousglobshape.- MDX math with curly braces now renders in
research-portfoliopreset (#51). Expressions like$\mathbb{E}\{X\}$,$\mathbb{P}\{X|Y\}$,$\mathrm{Cov}\{X, Y\}$previously failed becausesrc/config.tsgated the KaTeX wiring on the literalprofile === 'academic', ignoring thekatex: trueflag thatresearch-portfoliosets in its profile definition. Withoutremark-mathintercepting first, MDX parsed{X}as a JSX expression containing undefined variableX. Fix: gate onPROFILES[profile]?.katex === true(single source of truth: the profile registry). New visual fixturefixture-research-portfolio/.../math.mdxcovers brace-math at 4 viewport widths. create-booknow adds KaTeX peer deps forresearch-portfolioscaffolds (paired with the above). Previously only academic scaffolds gotkatex/rehype-katex/remark-mathin their generatedpackage.json.create-booknow acceptscourse-notesandresearch-portfoliopresets (latent bug fix).VALID_PROFILEShad been rejecting both since they shipped (v3.3.0 and v3.5.0 respectively) — they could only be selected by editing a scaffolded book manually.
Added
- Component prop tables for v3.5.0 components (#48) in
PACKAGE_DESIGN.md §10. CoversPreReleaseBanner,PolicyRef,AICollaborationDisclosure,BlockedByCalloutwith prop signatures + default values + slot semantics.
Release policy
- D12 lock-step preserved:
@brandon_m_behring/create-book@3.7.1ships alongside the toolkit. - Pre-publish smoke gate (v3.6.5) ran for both academic and research-portfolio scaffolds before publish.
v3.7.0 — chapters profile-strategy refactor (#35) + academic visual baseline (#36)
Minor release. Refactors the /chapters route from a field-presence discriminator into a per-profile renderer strategy plugged into the existing PROFILES registry. Adds a visual-regression baseline for the academic /chapters route. Closes #35 and #36.
No breaking API changes. Consumer DOM output is byte-equivalent for tools profile (verified at AE=0 against all 3 pre-existing visual fixtures).
Architecture
| Layer | Owns |
|---|---|
Route (pages/chapters.astro) |
Data fetch, byPart grouping, ToolFilter wiring, CSS, inline filter script |
Renderer (per profile, src/profiles/renderers/*-chapters.ts) |
Numbering format, badge selection, sort key, data-tools value |
Registry (PROFILES[profile].chaptersRenderer) |
Dispatch point read at route render time |
Pure-function strategy — no Astro imports in renderer modules, preserving the chapter-sort.ts pattern that keeps tsup's DTS bundler stable.
Added
ChaptersRendererinterface and three implementations:toolsChaptersRenderer— numeric Part/Chapter, volatility badge, freshness affordance, tools-compared tags.academicChaptersRenderer— string-enum Part, Week N numbering, status badge.fallbackChaptersRenderer— field-presence dispatch (v3.5.2 logic, preserved for minimal / course-notes / research-portfolio safety net).
ProfileDefinition.chaptersRenderer?: ChaptersRenderer— optional field; each of the 5 shipped profiles wired to its renderer.package/tests/visual/fixture-academic-chapters/— new visual-regression fixture exercisingdefineBookConfig({ profile: 'academic', routes: { chapters: true } })end-to-end. 5 chapters across foundations / ssm-core / beyond-ssm / synthesis parts; 4 routes screenshotted at 4 viewport widths = 16 baselines.package/tests/chapters-renderer.test.mjs— 31 cases across all 3 renderers + cross-renderer agreement +chapterSortKeybackward-compat.
Changed
pages/chapters.astroprecomputes per-card render data in the frontmatter---block (sidesteps an Astro-compiler limitation where TypeScript generic casts inside{...}JSX expressions get parsed as tag-start tokens).package.jsonfilesallowlist addssrc/profiles/,src/profile-kit.ts,src/schemas.ts. Required for the refactored route's transitive imports to resolve at consumer build time — uncovered by the v3.6.5 pre-publish smoke gate before any npm ship.
Pre-publish smoke (v3.6.5 gate) caught two real bugs
- Pre-publish smoke surfaced both missing-file-in-tarball bugs before any npm publish. Process win — exactly the failure class v3.6.5 was designed to catch. Without it, v3.7.0 would have shipped a broken first build for every new consumer.
Backward compatibility
- Public API:
chapterSortKey(data)export retained; behavior unchanged. - New types added (additive):
ChaptersRenderer,PartKey,VolatilityBadge,StatusBadge,FreshnessAffordance. Three renderer instances exported. - Frontmatter schemas unchanged.
- DOM output byte-equivalent for tools profile (verified by visual regression).
Release policy
- D12 lock-step preserved:
@brandon_m_behring/create-book@3.7.0ships alongside the toolkit.