From 8b80af144971942630e0d326a4430c30bbf92062 Mon Sep 17 00:00:00 2001 From: StreamDemon Date: Mon, 6 Jul 2026 17:34:10 +0800 Subject: [PATCH 1/2] docs: working practices in CLAUDE.md + rulebook transcription runbook Captures the project's operating discipline so any contributor (or agent session) works the same way: PR/review workflow, definition of done, rules-content fidelity rules, dev-loop gotchas, and the proven vision-extraction pipeline for the scanned rulebook. --- CLAUDE.md | 55 +++++++++++++++++++++++++++++++++++++ docs/rules/TRANSCRIPTION.md | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 docs/rules/TRANSCRIPTION.md diff --git a/CLAUDE.md b/CLAUDE.md index 76ef4fc..4798d43 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,3 +21,58 @@ Always read DESIGN.md before making any visual or UI decisions. All font choices, colors, spacing, and aesthetic direction are defined there (the "Ley Terminal" system). Do not deviate without explicit user approval. In QA mode, flag any code that doesn't match DESIGN.md. + +## Workflow + +- Branch → PR → Cubic AI review → the human maintainer merges. `main` is + protected; never commit to it directly, and never merge PRs yourself. +- **No AI attribution anywhere on this repo**: no generated-with footers in + PRs or issues, no Co-Authored-By/attribution trailers in commits. +- Treat every Cubic finding honestly: reproduce/validate it first. If real, + fix the ROOT CAUSE (the whole bug class, not the flagged instance) and + reply describing what you found; if invalid, explain why with evidence. + Cubic re-reviews on every push — iterate until clean. +- Checkpoint-commit long work (e.g. content transcription) every few units + so partial progress survives. + +## Definition of done + +- `vp check` and `vp test` clean from the root (root `vp test` runs every + package's Vitest project). +- Rules changes assert against printed book values, with the page cited in + content JSON (`page` = printed page number) and exercised in tests. +- User-visible changes are verified LIVE in the browser (roll vitals, deal + damage, cast, navigate between characters) — tests alone don't count. + Watch for the navigation-state bug class: param routes don't remount, so + per-character state must reset on id change, and async guards need + ownership tokens, not booleans. + +## Rules-content fidelity (the heart of the project) + +- Every mechanic comes from the rulebook, transcribed from rendered pages — + NEVER from memory of the Rifts rules, which is unreliable on specifics + (printed costs, dice, page numbers). If memory and the page disagree, the + page wins. +- The rulebook PDF lives at `docs/rules/` (gitignored, local-only). It is a + scanned book with NO text layer — text extraction returns garbage. Use + the vision pipeline in docs/rules/TRANSCRIPTION.md, with sections indexed + in docs/rules/PAGE_MAP.md. +- Engine functions return raw book amounts and stay pure and deterministic + (dice rolls and elapsed time are INPUTS). Clamping and validation live at + the write: the backend revalidates every mutation through `deriveSheet`, + so illegal states are unstorable rather than defensively handled. +- When a printed mechanic doesn't fit the schema, extend the schema (with + refinements that reject contradictions at content-load time) instead of + flattening the rule into prose. + +## Dev loop + +- Local Convex: `pnpm exec convex dev` in packages/backend (anonymous LOCAL + deployment; always `pnpm exec`, never npx). Stop it before pulls — it + rewrites `convex/_generated`. Killing the CLI can orphan + `convex-local-backend` on port 3210 with STALE functions still serving; + kill that process before restarting. +- Web: `vp dev` in apps/web, then verify at localhost:5173. +- Seed test data: `pnpm exec convex run characters:create '{...}'` in + packages/backend (the CLI may crash with a libuv assertion AFTER the + mutation succeeds — a returned id means it worked). diff --git a/docs/rules/TRANSCRIPTION.md b/docs/rules/TRANSCRIPTION.md new file mode 100644 index 0000000..395dd61 --- /dev/null +++ b/docs/rules/TRANSCRIPTION.md @@ -0,0 +1,53 @@ +# Transcribing the rulebook — the vision-extraction pipeline + +The source (`docs/rules/Rifts-Ultimate-Edition-Main-Book.pdf`, gitignored and +local-only) is 382 **scanned pages with no text layer**. `pdftotext` yields ~1 +garbage character per page — do not attempt text extraction. The proven +pipeline is render-and-read: + +## The pipeline + +1. **Locate the section** in [PAGE_MAP.md](./PAGE_MAP.md). + Offset (verified): `PDF page index (0-based) = printed page number + 2`. +2. **Render the page range** to PNG at 2.2x — crisp enough for dense stat + blocks and numeric tables: + + ```bash + python -c " + import fitz + d = fitz.open('docs/rules/Rifts-Ultimate-Edition-Main-Book.pdf') + for i in range(START_IDX, END_IDX): + d[i].get_pixmap(matrix=fitz.Matrix(2.2, 2.2)).save(f'OUT_DIR/p{i}.png') + " + ``` + + (PyMuPDF is installed; `pdftoppm`/poppler is not.) +3. **Read each PNG visually** and transcribe into content JSON: + - keep printed wording for ranges, durations, and notes; + - stamp `page` with the **printed** page number (not the PDF index); + - structured fields (dice, costs) get the structured value, and variable + or conditional printed rules keep the full sentence in a `*Note` field + (`ppeNote`, `savingThrowNote`) beside the number. +4. **Batch per book section** (e.g. one spell level at a time). Merge into + the content file sorted consistently; schemas validate at import via + `.parse`, so a bad transcription fails the whole test suite immediately. +5. **Pin totals in tests** (e.g. per-level spell counts in + `packages/rules/tests/spells.test.ts`) so silent drift or accidental + deletion is caught forever after. +6. **Checkpoint-commit** every few sections; transcription sessions are long + and partial progress should survive anything. + +## Rules of the pipeline + +- Transcribe only what the render shows. Model memory of Palladium rules is + unreliable on specifics (costs, dice, levels, page numbers) — e.g. Heal + Wounds is printed at level 5, not the commonly-remembered 6. **The page + always wins.** +- Completeness is checked against the book, not the existing catalog: the + spell catalog claimed "levels 1-4" for weeks while holding 5 of 13 level-2 + spells. When touching a section, verify the whole section's roster from + the pages. +- When a printed mechanic doesn't fit the current schema, extend the schema + with refinements that reject contradictory shapes at content-load time + (see `spellHealingSchema`'s exclusive/othersOnly/full) rather than + flattening the rule into description prose. From ee1bd146ba6ae458413e3c8183237b4069da7452 Mon Sep 17 00:00:00 2001 From: StreamDemon Date: Mon, 6 Jul 2026 17:35:01 +0800 Subject: [PATCH 2/2] docs: format TRANSCRIPTION.md --- docs/rules/TRANSCRIPTION.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/rules/TRANSCRIPTION.md b/docs/rules/TRANSCRIPTION.md index 395dd61..407b38b 100644 --- a/docs/rules/TRANSCRIPTION.md +++ b/docs/rules/TRANSCRIPTION.md @@ -22,6 +22,7 @@ pipeline is render-and-read: ``` (PyMuPDF is installed; `pdftoppm`/poppler is not.) + 3. **Read each PNG visually** and transcribe into content JSON: - keep printed wording for ranges, durations, and notes; - stamp `page` with the **printed** page number (not the PDF index);