# Puzzle Kits - Build Report

Session: 2026-07-08, autonomous (~3h grant). Plan: PLAN.md (committed first,
db0b368). Everything below was built and verified in-session; canon/data was
read-only throughout; all commits local and narrow to products/puzzle-kits/.

## Deliverables

16 printable kits in kits/ (US Letter, text PDFs, 10-13 KB each):

| Kit | Label | Pages | Canon records cited | Cover title source |
|-----|-------|-------|---------------------|--------------------|
| book01 | Book 1 | 8 | 40 | f_al1_399 quote: "Monday Into the Cave of Thieves" |
| book02 | Book 2 | 7 | 36 | f_al1_399 quote: "Tuesday The Curse of the Blue Spots" |
| book03 | Book 3 | 8 | 37 | f_al1_399 quote: "Wednesday The Forest of Secrets" |
| book04 | Book 4 | 8 | 38 | f_al1_399 quote: "Thursday War of the Waterslides" |
| book05 | Book 5 | 7 | 32 | f_al1_399 quote: "Friday Total Ice Cream Meltdown" |
| book06 | Book 6 | 7 | 34 | f_al1_399 quote: "Saturday Dash Candoo and the Missing Ducks" |
| book07 | Book 7 | 7 | 35 | f_al1_399 quote: "Sunday Dash Candoo and the Forbidden Island" |
| book08 | Book 8 | 8 | 38 | none in canon (OPEN_QUESTIONS #3.1) |
| book09 | Book 9 | 8 | 40 | none in canon |
| book10 | Book 10 | 7 | 34 | none in canon |
| book11 | Book 11 | 7 | 35 | none in canon |
| book12 | Book 12 (b12a+b12b) | 7 | 35 | none in canon |
| book13 | Book 13 | 7 | 33 | none in canon |
| book14 | Book 14 | 7 | 33 | none in canon |
| theme_moves | Moves and Scallywags | 6 | 32 | - |
| theme_codes | Gadgets and Codes | 7 | 59 | - |

Each book kit: cover, 15x15 word search (14 names), match-up (10 pairs),
fill-in-the-blanks (8 canon quotes + word bank), code-breaking (4 Morse +
3 NATO items with decoder tables; KB-15 flash-code matching where the book
prints the table), full answer key with a per-item citation (record id +
human ref + raw ref) and a solution grid (answers bold, fillers gray).
Theme kits swap in: scallywag word search (18x18) + Move#/name matching +
stats fill-ins; gadget word search + invention/inventor matching (via
invented_by relationships) + an extended codes page.

## Provenance model (why these kits can be trusted)

Every pupil-visible string is either (a) copied verbatim by code from one of
these canon fields: entity name/aliases/description_as_written, fact quote,
relationship evidence_quote - or (b) a declared mechanical transformation of
one: blank-one-token, substring-at-printed-delimiter, letterize-for-grid,
Morse/NATO encoding - or (c) functional instruction text, fully inventoried
in OPEN_QUESTIONS.md for review. No AI-written prose, no invented story text,
no invented distractors (fill-in word banks are exactly the blanked words).

The NATO decoder table is canon itself (f_al1_250, Almanac p. 209, carrying
Ralph's F=Foxtrot correction ruling). The Morse decoder table is the
real-world standard the books demonstrably use (f_b01_011 and f_al1_300
decode with it; both cited on every codes page).

tools/verify_kits.py re-derives every string from its cited record under its
declared transformation and fails the build otherwise.

## Verification results (final run, after full rebuild)

- Provenance audit: CLEAN. 16 kits, 874 items audited, 377 distinct canon
  records cited. Transformation counts: verbatim 345, letterize 222,
  blank 120, substring 79, letterize+morse 62, letterize+nato 46.
- Puzzle integrity (same auditor): every word-search answer occurs exactly
  once in its grid (all 8 directions checked); placements match grids;
  word banks are exact permutations of answer sets with no duplicate
  answers; matching permutations valid; matching left/right sides always
  cite the same record (or a validated invented_by relationship);
  flash-code colon splits reconstruct the printed quote.
- Citation integrity: every cited ref is on the cited record (or is its
  first_appearance); every record id resolves in canon/data.
- Encoding: every string WinAnsi-clean (canon strings keep printed en/em
  dashes, ø, accents; non-encodable records are excluded by selection).
- PDF smoke: pdftotext of all 16 PDFs contains the expected sections and
  every fill-in answer (answer-key completeness); page renders spot-checked
  visually at 70dpi (cover, word search, match-up, fill-in, codes, flash,
  key pages of book01; theme kit grids and matchings).
- Determinism: fixed seed per kit id; rebuilding from the same canon
  produces identical content JSON.

## Pipeline (products/puzzle-kits/tools/)

canon_lib.py (read-only canon access, ref formatting, letterize) ->
select_content.py (deterministic selection heuristics -> content/<kit>.json
with per-item provenance) -> puzzle_gen.py (word-search placement with
uniqueness proof, blanking, shuffles, Morse/NATO encoders - the NATO encoder
parses the canon table at runtime) -> pdflib.py (dependency-free PDF writer:
US Letter, Courier core fonts to echo the books' mono typesetting, WinAnsi,
Flate compression) -> render_kit.py (layout + answer keys) ->
verify_kits.py (audit) -> build_all.py (one-shot rebuild).

No new dependencies: system Python 3.9 + poppler (QA only). reportlab was
considered and skipped (not installed; project tooling stays dependency-free).

## Deviations from PLAN.md (as-built log)

1. letterize() now maps accented letters to base letters (Footmøld ->
   FOOTMOLD) - plain dropping produced an unsolvable grid word (FOOTMLD...).
   Instruction line updated accordingly.
2. Cover titles for Books 1-7 use the PRINTED quote lines of f_al1_399
   ("Monday Into the Cave of Thieves"), not the assertion paraphrase the
   plan anticipated - strictly better provenance.
3. Answer-key word-search citations moved below the solution grid (the
   beside-the-grid layout truncated the list).
4. Almanac content feeds the theme kits rather than a 17th standalone kit
   (raised as OPEN_QUESTIONS #3.8).
5. Post-review restyle (Ralph: "more on brand, less municipal", then
   "print friendly, no block black"): double-frame cover masthead,
   line-drawn hazard-stripe bands, double-outline starburst badge,
   outlined label tabs, square-bullet titles over heavy rules, and a drawn
   Morse motif of Dash's printed code name on every cover
   (cover.morse_strip - a cited f_b01_011 substring, so even the decoration
   is canon with provenance). No large solid fills anywhere (photocopier
   safe); the only filled shapes are small (morse dots/dashes, bullets).
   Content model otherwise unchanged; audit re-run clean after each pass.

## Known limitations

- Match-up left column spreads down the full page while descriptions
  top-align (readable, slightly airy).
- Multi-word Morse items carry no word separator (OPEN_QUESTIONS #3.3).
- Semantic guessability of fill-in blanks is heuristic, not human-curated
  (OPEN_QUESTIONS #3.5).
- Kit difficulty is one-size (~grades 3-5); difficulty knobs exist but only
  one setting is built (OPEN_QUESTIONS #3.6).

## How to rebuild

    cd products/puzzle-kits
    python3 tools/build_all.py        # select + render + verify, all 16 kits
    python3 tools/build_all.py book03 # one kit

## Commits (this session, local only)

- db0b368 plan
- pipeline + first full build of content and PDFs
- verify_kits auditor + accent-safe letterize (audit clean)
- docs (this report, OPEN_QUESTIONS, README) + final rebuild
