/kdp-epub — Build the Kindle/KDP EPUB
Produce the Amazon KDP edition as a reflowable EPUB 3, compiled from the v5
canonical text and validated to the gates in .claude/rules/16-kdp-epub.md.
The EPUB is a derived artifact — fix text in book/chapters-v5/, never in the EPUB.
When to use
- "compile the epub", "build the Kindle edition", "export for KDP", "validate the ebook".
- After any v5 text change that should reach the Kindle edition (rebuild from source).
Prerequisites (the build script enforces all of these — fail-loud)
- Source clean.
dist/manuscript-v6.md is current (re-run
pipelines/epub/assemble_v6_manuscript.py if v6 chapters changed) and contains no raw
[CITE:] markers and no [EVIDENCE NEEDED]. Resolve cites to endnotes first
(Stephen / the source-ledger pipeline). Current state: 3 [CITE:] markers remain
(ch08 ×1, ch12 ×2) — these block a non-draft build until resolved.
- Metadata filled.
book/design/epub/metadata.yml has no TODO tokens (author,
publisher, identifier). Generate a stable identifier once:
python3 -c 'import uuid;print("urn:uuid:"+str(uuid.uuid4()))'.
- Cover present and conforming.
book/design/epub/cover.jpg = 2560×1600 JPEG, 1.6:1,
sRGB, title + author composited (rule 16). Current state: the design delivery has
no title-composited ≥1600px cover — a conforming cover must be produced first. Use
--draft to build a coverless proof in the meantime.
Tooling (all present on this machine)
pandoc 3.9 (build) · epubcheck v5.3.0 (gate 1) · Kindle Previewer 3 CLI +
app (gate 2) · sips (cover dimension check) · java.
Procedure
1. Assemble + clean the source
python3 pipelines/epub/assemble_v6_manuscript.py # refresh the manuscript from v6
# (the build script also runs this automatically when the cache is stale)
grep -rn '\[CITE:' dist/manuscript-v6.md # must be empty before a real build
Resolve any [CITE:] to endnotes via the source-ledger cards (Stephen locks the
anchor, then the cite becomes a numbered note). Do not hand-delete cites.
2. Prepare cover + metadata
- Drop the conforming cover at
book/design/epub/cover.jpg (or pass --draft to skip).
- Fill
book/design/epub/metadata.yml (author / publisher / identifier).
3. Build
python3 pipelines/epub/build_kdp_epub.py # full, gated build -> dist/no-one-did-it.epub
python3 pipelines/epub/build_kdp_epub.py --draft # coverless text proof for review
What the build does: guards source + metadata + cover; folds the back-of-book
# Notes definitions into pandoc popup footnotes (epub:type="noteref"/"footnote")
while keeping the Selected Bibliography; runs pandoc --to epub3 --split-level=1 --toc
with kdp.css, the metadata, and the cover.
4. Validate (both hard gates must pass before upload)
python3 pipelines/epub/validate_kdp_epub.py dist/no-one-did-it.epub
- Gate 1 — epubcheck: zero fatals/errors (triage warnings).
- Gate 2 — Kindle Previewer 3: converts to KFX/KPF cleanly. Authoritative Kindle
gate — a file can pass epubcheck and still break on a Paperwhite. If the CLI isn't on
PATH, open
dist/no-one-did-it.epub in the Kindle Previewer 3 app and run the convert.
- Structure checks: cover declared, footnote popups present, no leaked
[CITE:]/[^N].
5. Pre-flight checklist (manual, in Kindle Previewer)
6. Upload
Upload dist/no-one-did-it.epub to KDP as the manuscript; upload the 2560×1600 JPEG
as the marketing cover separately. KDP assigns the ASIN.
Guardrails
- The
scan-kdp-epub-css.py hook warns at edit time if book/design/epub/*.css introduces
KDP-unsupported constructs (position:, fixed pt/px type/margins, height on
text, forced black/white backgrounds).
- Never produce
.mobi (dead format). Never edit the .epub as the master.
- Reflowable only: the fixed-layout dossier interiors do not survive reflow — a
separate fixed-layout edition would be a different pipeline.
Troubleshooting
| Symptom |
Cause / fix |
FAIL: N unresolved [CITE:] |
resolve cites to endnotes in v5, re-assemble, rebuild |
FAIL: metadata.yml ... TODO |
fill author/publisher/identifier in book/design/epub/metadata.yml |
FAIL: cover ... non-conforming |
export a 2560×1600 sRGB JPEG (1.6:1) with title+author, or --draft |
| epubcheck errors on fonts |
embed only OTF/TTF, or drop embedded fonts and let Kindle use reader font |
| notes render inline, not popup |
confirm epub:type markup survived; check pandoc version ≥3.0 |
pandoc: --split-level unknown |
pandoc < 3.0; upgrade (this repo expects 3.9) |
1---2name: kdp-epub3description: Build and validate a KDP-ready reflowable EPUB 3 of *No One Did It* from the v5 source of truth, conforming to the Amazon Kindle Publishing Guidelines. Use when the user wants to compile, build, export, or validate the Kindle / EPUB / KDP edition of the book. Governed by rule 16-kdp-epub. Produces dist/no-one-did-it.epub.4---56# /kdp-epub — Build the Kindle/KDP EPUB78Produce the Amazon KDP edition as a **reflowable EPUB 3**, compiled from the v59canonical text and validated to the gates in `.claude/rules/16-kdp-epub.md`.10The EPUB is a **derived artifact** — fix text in `book/chapters-v5/`, never in the EPUB.1112## When to use1314- "compile the epub", "build the Kindle edition", "export for KDP", "validate the ebook".15- After any v5 text change that should reach the Kindle edition (rebuild from source).1617## Prerequisites (the build script enforces all of these — fail-loud)18191. **Source clean.** `dist/manuscript-v6.md` is current (re-run20 `pipelines/epub/assemble_v6_manuscript.py` if v6 chapters changed) and contains **no raw21 `[CITE:]` markers and no `[EVIDENCE NEEDED]`**. Resolve cites to endnotes first22 (Stephen / the source-ledger pipeline). *Current state: 3 `[CITE:]` markers remain23 (ch08 ×1, ch12 ×2) — these block a non-draft build until resolved.*242. **Metadata filled.** `book/design/epub/metadata.yml` has no `TODO` tokens (author,25 publisher, identifier). Generate a stable identifier once:26 `python3 -c 'import uuid;print("urn:uuid:"+str(uuid.uuid4()))'`.273. **Cover present and conforming.** `book/design/epub/cover.jpg` = **2560×1600 JPEG, 1.6:1,28 sRGB, title + author composited** (rule 16). *Current state: the design delivery has29 no title-composited ≥1600px cover — a conforming cover must be produced first. Use30 `--draft` to build a coverless proof in the meantime.*3132## Tooling (all present on this machine)3334`pandoc 3.9` (build) · `epubcheck v5.3.0` (gate 1) · `Kindle Previewer 3` CLI +35app (gate 2) · `sips` (cover dimension check) · `java`.3637## Procedure3839### 1. Assemble + clean the source40```bash41python3 pipelines/epub/assemble_v6_manuscript.py # refresh the manuscript from v642# (the build script also runs this automatically when the cache is stale)43grep -rn '\[CITE:' dist/manuscript-v6.md # must be empty before a real build44```45Resolve any `[CITE:]` to endnotes via the source-ledger cards (Stephen locks the46anchor, then the cite becomes a numbered note). Do **not** hand-delete cites.4748### 2. Prepare cover + metadata49- Drop the conforming cover at `book/design/epub/cover.jpg` (or pass `--draft` to skip).50- Fill `book/design/epub/metadata.yml` (author / publisher / identifier).5152### 3. Build53```bash54python3 pipelines/epub/build_kdp_epub.py # full, gated build -> dist/no-one-did-it.epub55python3 pipelines/epub/build_kdp_epub.py --draft # coverless text proof for review56```57What the build does: guards source + metadata + cover; folds the back-of-book58`# Notes` definitions into **pandoc popup footnotes** (`epub:type="noteref"/"footnote"`)59while keeping the Selected Bibliography; runs `pandoc --to epub3 --split-level=1 --toc`60with `kdp.css`, the metadata, and the cover.6162### 4. Validate (both hard gates must pass before upload)63```bash64python3 pipelines/epub/validate_kdp_epub.py dist/no-one-did-it.epub65```66- **Gate 1 — epubcheck:** zero fatals/errors (triage warnings).67- **Gate 2 — Kindle Previewer 3:** converts to KFX/KPF cleanly. *Authoritative Kindle68 gate — a file can pass epubcheck and still break on a Paperwhite.* If the CLI isn't on69 PATH, open `dist/no-one-did-it.epub` in the Kindle Previewer 3 app and run the convert.70- **Structure checks:** cover declared, footnote popups present, no leaked `[CITE:]`/`[^N]`.7172### 5. Pre-flight checklist (manual, in Kindle Previewer)73- [ ] Cover renders crisp; title + author legible at thumbnail size.74- [ ] TOC navigates to every chapter + the Bibliography; no page numbers in TOC.75- [ ] A footnote tap opens a **pop-up** (not a jump to the back); back-link returns.76- [ ] A part-divider image displays with alt text; single-column everywhere.77- [ ] Reader font-size changes reflow cleanly (no fixed-px overflow).78- [ ] Epigraphs (incl. CJK/Greek/Arabic/Sanskrit) render; no tofu boxes.79- [ ] No raw `[CITE:]`, `[^N]`, or `## beat-name` text anywhere.8081### 6. Upload82Upload `dist/no-one-did-it.epub` to KDP as the manuscript; upload the 2560×1600 JPEG83as the marketing cover separately. KDP assigns the ASIN.8485## Guardrails8687- The `scan-kdp-epub-css.py` hook warns at edit time if `book/design/epub/*.css` introduces88 KDP-unsupported constructs (`position:`, fixed `pt`/`px` type/margins, `height` on89 text, forced black/white backgrounds).90- Never produce `.mobi` (dead format). Never edit the `.epub` as the master.91- Reflowable only: the fixed-layout dossier interiors do not survive reflow — a92 separate fixed-layout edition would be a different pipeline.9394## Troubleshooting9596| Symptom | Cause / fix |97|---|---|98| `FAIL: N unresolved [CITE:]` | resolve cites to endnotes in v5, re-assemble, rebuild |99| `FAIL: metadata.yml ... TODO` | fill author/publisher/identifier in `book/design/epub/metadata.yml` |100| `FAIL: cover ... non-conforming` | export a 2560×1600 sRGB JPEG (1.6:1) with title+author, or `--draft` |101| epubcheck errors on fonts | embed only OTF/TTF, or drop embedded fonts and let Kindle use reader font |102| notes render inline, not popup | confirm `epub:type` markup survived; check pandoc version ≥3.0 |103| pandoc: `--split-level` unknown | pandoc < 3.0; upgrade (this repo expects 3.9) |