Skip to content
Section Authoring Playbook

Section Authoring Playbook

How to author a section in the Hugo build: pure Markdown + shortcodes. The mechanical rules are enforced by the tooling (npm test, npm run verify-section) — author, run the verifier, fix what it reports. You should not have to memorize the mechanical rules; the lints know them.

This playbook describes the repository’s current Hugo architecture. Content is plain Markdown with Hugo shortcodes; React, JSX, MDX, and Nextra metadata are not valid authoring syntax.

0. Source-first workflow (required for AI agents)

Automated checks can prove that syntax renders and an authored answer grades against itself. They cannot prove that the transcription is faithful or that the answer is mathematically correct.

Before writing:

  1. Read AGENTS.md, this playbook, and—when applicable—the Knowledge Check playbook. Inspect git status and preserve unrelated work.
  2. Use the local source PDF as the content authority. Render and visually read the relevant section pages and Answer Key pages. Extracted text or the web edition may help locate prose, but neither is authoritative for formulas, diagrams, part labels, or answer context.
  3. Keep a source ledger in working notes: PDF page range, source section, every retained exercise number, its official answer location, and the component used. Record any response-mode adaptation or source discrepancy.
  4. Preserve the source’s objectives, exposition, definitions, procedures, examples, and mathematics. Omit only material explicitly allowed by the request/playbook. Never silently invent, condense, or “repair” source content; flag a suspected source error and show the evidence.
  5. For chapter-sized work, divide by section. Each worker must read the same playbook, inspect its own PDF pages, run its section verifier, and return its source ledger. The parent reconciles every section and runs the book-wide gates.

Existing sections authored before July 22, 2026 whose attribution footer explicitly discloses light condensation are grandfathered. Do not rewrite them solely to remove that disclosed condensation. New sections and substantive revisions follow the source-fidelity rule above; never use the grandfathering exception to justify new omissions.

1. Where the file goes

content/<subject>/<book>/<NN>-<chapter-slug>/<NN>-<section-slug>.md
  • Section landings (book cover, chapter overview) are _index.md, not index.md.
  • Ordering is by frontmatter weight. Give each section the weight matching its NN prefix. At book level, chapters and Knowledge Checks share one sequential weight order: insert a Knowledge Check immediately after its ending chapter, then continue with the next unused weight. A chapter’s weight therefore may differ from its chapter number after an earlier Knowledge Check.
  • Sidebar label defaults to the frontmatter title. Set linkTitle: only if the sidebar label should differ.
  • Numeric prefixes are zero-padded, start at 01, sequential, no gaps. The validator enforces this.

Frontmatter for a numbered section:

---
title: Add and Subtract Fractions with Common Denominators
description: >-
  One to two sentences summarizing the section.
source_section: "4.4"   # the section number in the SOURCE textbook
weight: 4
---

Book root _index.md also needs license: and source: (and carries cascade: {type: docs} so the whole book gets the docs sidebar). Chapter _index.md needs source_chapter:. Knowledge-check pages need source_chapters: (e.g. "1-6"). npm run validate checks all of this.

Every chapter _index.md contains a reader-facing overview in this exact shape:

## Sections

- **Section title** — concise description derived from the section objectives.

List every authored section once. Do not copy visible print prefixes such as 10.1, and do not fall back to a bare linked outline or title-only bullets. The content lint enforces the descriptive bullet shape.

2. Writing patterns

  • Math: KaTeX — $...$ inline, $$...$$ display (multi-line $$ fenced on their own lines). Group digits with {,}: $37{,}519{,}248$. Plain prose numbers need no $. A literal dollar sign (money) is \$.
  • Fractions: \tfrac everywhere — inline, in Check: sentences, and inside $$ arrays. \dfrac is banned and plain \frac inside an array renders too tall; the lint enforces both. Nested fractions use \cfrac in a display block.
  • Worked-example step tables: align relations with \begin{array}{lrcl}, rows explanation & LHS &=& RHS \\[4pt]. Separate steps with \\[4pt] (the lint rejects bare \\ in {lrcl} arrays).
  • Prose inside math needs TeX spacing: ordinary spaces outside \text{...} disappear. Write \text{If }n^2=m or \text{If}\ n^2=m, and use \ between adjacent text commands. Never rely on source whitespace in \text{If} n, m \text{is}, or \text{square}\text{root}. The lint catches high-confidence joins; the rendered page is the final check.
  • Unlike MDX, a literal { in prose is harmless in Hugo — only {{ starts a shortcode. So set notation like {3, 6, 9} is fine in prose (though math sets still read best as $\{3,6,9\}$).

3. Exercises and components (shortcodes)

Fill-in (free-response math, graded in the browser):

{{< fillin
  question="Find the sum: $\tfrac{3}{6} + \tfrac{2}{6}$"
  answer="\frac{5}{6}"
  answerDisplay="$\tfrac{5}{6}$"
  hint="Add the numerators and keep the same denominator."
>}}

LaTeX in answer uses a single backslash (Hugo passes shortcode params verbatim — no MDX doubling). question/hint/answerDisplay support inline $...$. The verifier runs every answer through the real grader: it must self-grade correct.

Every regular-section exercise needs a concise, strategy-oriented hint. Knowledge Checks deliberately omit hints.

When a prompt asks for an unordered collection of roots or solutions, add answerMode="unordered":

{{< fillin
  question="Solve $x^2=9$. Enter both solutions, separated by a comma."
  answer="-3,3"
  answerMode="unordered"
  answerDisplay="$x=-3$ or $x=3$"
  hint="Use the Square Root Property and include both signs."
>}}

Use this mode only when order has no mathematical meaning. Ordered pairs, ordered triples, coordinate lists, sequences, and prompts that explicitly ask for larger-first/smaller-first order must keep the default ordered grading.

Every retained source “Try It” or check must become a real fillin, multiplechoice, or graphplot component. Never ship a static prompt followed by <details><summary>Check answer</summary> or plain answer prose. Choose the component that matches the learner’s response; if the source interaction cannot be represented faithfully, choose another source item or record the smallest response-mode adaptation in the source ledger.

Learner-facing fields contain the exercise itself, not print UI:

  • Remove prefixes such as Try It 10.17. from question, hint, and answerDisplay; retain the source number in the ledger.
  • A standalone prompt does not keep a source part marker: write Simplify $x^2$, not Simplify: (a) $x^2$.
  • Split a genuine multipart task into separate components unless one combined response is pedagogically necessary and supported by the grader.

Keep each consecutive interactive practice set to 2–3 questions maximum, counting fill-ins, multiple-choice questions, and graphing questions together. Choose prompts that cover the example’s distinct skills instead of repeating near-identical arithmetic. Dedicated knowledge-check-XX-YY.md pages are exempt because they are cumulative assessments.

Multiple choice (text mode) — options are one per line in the body; answer must match an option string exactly:

{{< multiplechoice
  question="Which symbol goes at the endpoint of $x > 2$?"
  answer="parenthesis"
  hint="Is $2 > 2$ true?"
>}}
parenthesis
bracket
{{< /multiplechoice >}}

Multiple choice (graph mode) — “which graph is correct?”. Options are prerendered <svg> blocks separated by a line ===OPT===, with answerIndex (0-based). Generate each option SVG with the figure helper (below). Every option needs an aria-label.

Graph it yourself (GraphPlot): config (answer + grid) is JSON in the body.

{{< graphplot
  question="Graph the line $y = -4x$ by placing two points on it."
  answerDisplay="$y = -4x$"
  ariaLabel="A blank grid from −7 to 7 on both axes."
  hint="The line passes through the origin."
>}}
{"answer": {"slope": -4, "intercept": 0}, "grid": {"xMin": -7, "xMax": 7, "yMin": -7, "yMax": 7}}
{{< /graphplot >}}

answer shapes: {slope,intercept}, {x}, {y}, {system:[…]}, {quadratic:{a,b,c}}.

GraphPlot configuration is validated during npm test: grid bounds and steps must be finite, minimums must be below maximums, snap/grid/tick steps must be positive, and the answer shape must match one of the supported forms.

Callouts / cards (Hextra shortcodes):

{{< callout type="info" >}}
**Note.** Body markdown, including $math$.
{{< /callout >}}

Static figures (Graph / NumberLine / Figure): these are prerendered SVG. Hugo can’t run the geometry at build time, so generate the SVG with the helper and paste the <div class="ap-figure">…</div> block it prints:

node tools/render-figure.mjs graph '{"ariaLabel":"The line y = 2x + 1.","lines":[{"slope":2,"intercept":1,"label":"y = 2x + 1"}]}'

The accepted JSON properties map to the buildGraph, buildNumberLine, and buildFigure helpers in assets/js/lib/graph-core.mjs. Every figure MUST carry an ariaLabel.

Do not paste or download textbook equation images into content. Write equations as KaTeX, recreate tabular relationships as Markdown tables, and use the figure helper for diagrams and graphs so its accessible SVG is embedded directly in the page. File-backed Markdown/HTML images, raster formats (.png, .jpg, .webp, and similar), and inaccessible inline SVGs are rejected by the content lint.

Recreated graphs and diagrams still require visual comparison with the PDF. Check geometry, labels, axes, endpoints, direction, and dark-mode contrast; a valid accessible SVG can still be mathematically wrong.

4. Verify (the workflow)

From the repository root:

  1. Reconcile the finished page against the source ledger and PDF images: objectives, examples, math, figures, retained exercises, and official answers must all match.
  2. Independently solve every touched interactive question and compare both the prompt and answer with the source Answer Key. Do not infer correctness from self-grading.
  3. npm run verify-section -- content/<subject>/<book>/<ch>/<sec>.md — lints, renders every math run, and confirms that each fill-in answer is parseable by the real grader. Fix every ✗ and investigate warnings.
  4. Run npm test. It includes whole-repository structure validation, per-page real-grader verification, unit tests, repo-wide authoring lints, documentation consistency checks, and KaTeX parsing. npm run validate remains available as a focused structure-only command.
  5. For chapter/bulk work, run npm run build and npm run check:build (or npm run ci). The production audit checks, among other things, that math pages load the pinned KaTeX CSS and that its hidden accessibility layer cannot appear as duplicate visible math.
  6. Open every changed page with npm run serve. Confirm real components render and grade, prose/math spacing is visible, formulas appear once, and figures match the PDF. Also open a changed chapter landing page.
  7. Run git diff --check and report the changed/untracked files plus the source ledger and pages visually checked. Do not commit unless asked.

Done checklist

  • PDF section and Answer Key pages visually inspected; source ledger complete.
  • Source fidelity reconciled; any discrepancy/adaptation explicitly recorded.
  • Frontmatter complete (title, description, source_section, weight).
  • Chapter landing uses descriptive **Title** — summary section bullets.
  • Every retained practice item is a real component with no print-only label.
  • Every touched answer independently solved and checked against the source key.
  • No file-backed instructional images; recreated figures compared visually.
  • verify-section clean (✗ = 0).
  • npm test green.
  • Production build/audit green for chapter or bulk work.
  • Browser check confirms math appears once, spacing is correct, components work, and figures match the source.
  • Handoff lists source evidence, visual pages, and all uncommitted files.