docs(01-03): complete Plan 03 — Foundry seed pipeline + Wizard worked example
Add SUMMARY.md documenting the seed pipeline (320 ClassProgression rows + 1 ClassFeatureOption row), the four auto-fix deviations resolved during execution (env setup, v8 path, Prisma 7 typed-input, third-party tsc exclusion), and the contract Plan 03b consumes when appending overlay data.
This commit is contained in:
246
.planning/phases/01-level-up-pf2e-regelkonform/01-03-SUMMARY.md
Normal file
246
.planning/phases/01-level-up-pf2e-regelkonform/01-03-SUMMARY.md
Normal file
@@ -0,0 +1,246 @@
|
||||
---
|
||||
phase: 01-level-up-pf2e-regelkonform
|
||||
plan: 03
|
||||
subsystem: seeding
|
||||
tags: [seeding, prisma, foundry-pf2e, class-progression, spellcaster, wizard, level-up, pipeline]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 01-level-up-pf2e-regelkonform
|
||||
provides: ClassProgression + ClassFeatureOption Prisma tables (Plan 01-01); Proficiency type vocabulary (Plan 01-02 lib/types.ts)
|
||||
provides:
|
||||
- Idempotent seed pipeline (server/prisma/seed-class-progression.ts) generic over D16_CLASS_NAMES — Plan 03b appends data, no script changes
|
||||
- Hand-curated spell-slot overlay (server/prisma/data/spell-slot-overlays.ts) with type definitions + Wizard L1..L19 fully populated
|
||||
- Hand-curated class-feature-options (server/prisma/data/class-feature-options.ts) with type definitions + 1 Wizard School entry
|
||||
- Pinned Foundry pf2e tag (pf2e-8.0.3) and dev README at .planning/phases/01-level-up-pf2e-regelkonform/SEED-README.md
|
||||
- 320 ClassProgression rows in PostgreSQL (16 D-16 classes × L1..L20)
|
||||
- 1 ClassFeatureOption row (wizard-school / battle-magic)
|
||||
- Wizard end-to-end worked example: choiceType=school, choiceOptionsRef=wizard-school, ARCANE slot progression L1..L19, 5 cantrips at L1
|
||||
affects: [01-03b (data-only append to overlays), 01-04 (LevelingService.commit reads ClassProgression rows), 01-05 (wizard UI uses choiceOptionsRef → ClassFeatureOption)]
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Idempotent compound-key Prisma upsert pattern via findUnique + update OR create"
|
||||
- "Prisma.JsonNull sentinel for nullable Json fields (Prisma 7 typed-input contract)"
|
||||
- "Foundry pf2e clone consumed at seed time only; runtime never reads JSON files"
|
||||
- "Foundry-driven seed + hand-curated overlay merge (Pitfall #6 mitigation: slot tables in prose, not rules)"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- .planning/phases/01-level-up-pf2e-regelkonform/SEED-README.md
|
||||
- server/prisma/data/spell-slot-overlays.ts
|
||||
- server/prisma/data/class-feature-options.ts
|
||||
- server/prisma/seed-class-progression.ts
|
||||
modified:
|
||||
- server/tsconfig.json (exclude prisma/data/foundry-pf2e from compilation — gitignored third-party content)
|
||||
|
||||
key-decisions:
|
||||
- "Pinned Foundry pf2e tag pf2e-8.0.3 — current stable Pathfinder 2e system release with Player Core + APG content for all 16 D-16 classes"
|
||||
- "Spell-slot/cantrip/repertoire data hand-curated in spell-slot-overlays.ts (Pitfall #6: Foundry encodes them in description prose, not machine-readable rules)"
|
||||
- "All 16 D-16 class names appear as keys in SPELL_SLOT_OVERLAY (even when value is []) so Plan 03b appends without missing-key bugs"
|
||||
- "ClassFeatureOption.proficiencyChanges stays optional in the entry interface; passing null at the seed call site is rewritten to Prisma.JsonNull to satisfy typed-input contract"
|
||||
- "Pipeline is generic over D16_CLASS_NAMES + SPELL_SLOT_OVERLAY[className] — Plan 03b adds data only, no script changes"
|
||||
- "Foundry pf2e v8 nests pf2e packs under packs/pf2e/classes/ (v7 had packs/classes/ directly) — Pitfall #5 mitigation documented in code comment + SEED-README"
|
||||
|
||||
patterns-established:
|
||||
- "Idempotent seed: findUnique → update OR create per row, with compound-key where clauses inferred from @@unique declarations"
|
||||
- "Hand-curated overlay file with all class keys present (empty arrays for unsupported classes) so future plans append safely"
|
||||
- "Loud-fail seed: missing Foundry clone triggers a deterministic error that points the dev to SEED-README"
|
||||
- "Third-party clone exclusion in tsconfig — gitignored data directories that ship their own TS source must be excluded from project tsc"
|
||||
|
||||
requirements-completed: [LVL-08, LVL-14]
|
||||
|
||||
# Metrics
|
||||
duration: ~14min
|
||||
completed: 2026-04-27
|
||||
---
|
||||
|
||||
# Phase 1 Plan 03: Seed Pipeline + Wizard Worked Example Summary
|
||||
|
||||
**Built the Foundry-pf2e-driven seed pipeline that populates ClassProgression (320 rows) and ClassFeatureOption (1 row) from a pinned pf2e-8.0.3 clone plus hand-curated spell-slot overlays, with Wizard fully wired end-to-end (L1..L19 ARCANE slot progression, 5 cantrips at L1, school choice routing) and idempotent re-runs reporting `0 created, 320 updated`.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** ~14 min
|
||||
- **Tasks:** 5 completed (README, spell-slot overlay, class-feature options, seed script, run+verify)
|
||||
- **Files created:** 4 (SEED-README.md, spell-slot-overlays.ts, class-feature-options.ts, seed-class-progression.ts)
|
||||
- **Files modified:** 1 (server/tsconfig.json — exclude clone dir)
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **Pinned Foundry tag chosen:** `pf2e-8.0.3` — current stable Pathfinder 2e system release on the `foundryvtt/pf2e` repo. Verified via the GitHub Releases API; tag SHA `c6aac85d186ac768d1db9ac2d379e9510a0825f8`.
|
||||
- **SEED-README.md** at `.planning/phases/01-level-up-pf2e-regelkonform/SEED-README.md` documents the clone command, the run command, failure modes, and the v8 pack-layout note. Mentions Plan 03 vs Plan 03b split explicitly.
|
||||
- **spell-slot-overlays.ts** exports `SpellTradition`, `SpellSlotOverlayEntry`, and `SPELL_SLOT_OVERLAY`. Wizard fully populated for L1..L19 (20 entries: L1 has both a cantrip-increment entry and a slot-increment entry). All 15 other D-16 class names appear as keys with empty-array values for Plan 03b to fill.
|
||||
- **class-feature-options.ts** exports `ClassFeatureOptionEntry` and `CLASS_FEATURE_OPTIONS` (1 entry: Wizard School `battle-magic` / "School of Battle Magic"). Anchor comments name 13 other optionsRef strings so Plan 03b knows where to append.
|
||||
- **seed-class-progression.ts** is generic over `D16_CLASS_NAMES` and the two overlay modules — Plan 03b appends entries to the data files and re-runs the same seed without touching the script. Uses Prisma 7's `Prisma.JsonNull` sentinel for nullable Json fields and the compound-key field names `className_level` and `optionsRef_optionKey` generated from Plan 01's `@@unique` declarations.
|
||||
- **Live DB state after seed:**
|
||||
- 320 ClassProgression rows (16 classes × L1..L20)
|
||||
- 20 Wizard rows; L1 has full structure (`grants`, `proficiencyChanges {fortitude:UNTRAINED, reflex:UNTRAINED, will:TRAINED}`, `spellSlotIncrement {tradition:ARCANE, spellLevel:1, count:2}`, `cantripIncrement: 5`, `choiceType: 'school'`, `choiceOptionsRef: 'wizard-school'`)
|
||||
- L2..L18 carry the correct ARCANE/L1..L9 slot progression (2 + 1 per spell grade per pair of levels)
|
||||
- L19 carries the L10 capstone slot
|
||||
- 1 ClassFeatureOption row (`wizard-school` / `battle-magic` / "School of Battle Magic")
|
||||
- **Idempotency proven:** First run reports `320 created, 0 updated, 0 errors` for ClassProgression and `1 created, 0 updated, 0 errors` for ClassFeatureOption; second and third runs report `0 created, 320 updated, 0 errors` and `0 created, 1 updated, 0 errors`.
|
||||
- **Type-clean:** `npx tsc --noEmit -p tsconfig.json` exits 0 across all four new files; the cloned Foundry source is excluded from compilation (it ships its own TS source that targets a different toolchain).
|
||||
- **Existing leveling tests still pass:** `npm test -- --testPathPatterns=leveling` reports 55/55 passing.
|
||||
|
||||
## Task Commits
|
||||
|
||||
| Task | Description | Commit |
|
||||
|------|-------------|--------|
|
||||
| 1 | Dev README for the Foundry pf2e clone path | `6567665` (docs) |
|
||||
| 2 | Spell-slot overlay file — types + Wizard worked example | `29fe01d` (feat) |
|
||||
| 3 | Class-feature-options file — types + Wizard School worked example | `d86cf4f` (feat) |
|
||||
| 4 | Seed script — Foundry pf2e + overlays → ClassProgression + ClassFeatureOption | `e85f790` (feat) |
|
||||
| 5 | Run the seed (Wizard end-to-end verification) — path fix + README update | `ce214ab` (fix) |
|
||||
| — | tsconfig exclude foundry-pf2e clone dir (Rule 3 deviation) | `d421aad` (chore) |
|
||||
|
||||
## Verification Output
|
||||
|
||||
```
|
||||
$ cd server && npm run db:seed:class-progression
|
||||
Seeding ClassProgression for 16 classes x 20 levels...
|
||||
ClassProgression: 0 created, 320 updated, 0 errors
|
||||
Seeding 1 ClassFeatureOption rows...
|
||||
ClassFeatureOption: 0 created, 1 updated, 0 errors
|
||||
ClassProgression + ClassFeatureOption seed complete.
|
||||
```
|
||||
|
||||
```
|
||||
$ cd server && npm test -- --testPathPatterns=leveling
|
||||
Test Suites: 5 passed, 5 total
|
||||
Tests: 55 passed, 55 total
|
||||
```
|
||||
|
||||
Wizard L1 row queried via the Prisma client (verification helper, not committed):
|
||||
|
||||
```json
|
||||
{
|
||||
"className": "Wizard",
|
||||
"level": 1,
|
||||
"grants": ["Wizard Spellcasting", "Arcane School", "Arcane Bond", "Arcane Thesis"],
|
||||
"proficiencyChanges": { "will": "TRAINED", "reflex": "UNTRAINED", "fortitude": "UNTRAINED" },
|
||||
"spellSlotIncrement": { "count": 2, "tradition": "ARCANE", "spellLevel": 1 },
|
||||
"cantripIncrement": 5,
|
||||
"repertoireIncrement": null,
|
||||
"choiceType": "school",
|
||||
"choiceOptionsRef": "wizard-school"
|
||||
}
|
||||
```
|
||||
|
||||
Wizard L1..L5 spell-slot progression (matches AoN canonical Wizard table):
|
||||
|
||||
| Level | Slot increment | Cantrip increment |
|
||||
|-------|----------------|-------------------|
|
||||
| 1 | ARCANE / L1 / count=2 | 5 |
|
||||
| 2 | ARCANE / L1 / count=1 | — |
|
||||
| 3 | ARCANE / L2 / count=2 | — |
|
||||
| 4 | ARCANE / L2 / count=1 | — |
|
||||
| 5 | ARCANE / L3 / count=2 | — |
|
||||
|
||||
(Continues through L19 with the L10 capstone — matches Player Core / APG.)
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- **Foundry tag = pf2e-8.0.3 (Pathfinder 2e v8).** The current Foundry pf2e repo bundles both PF2E and SF2E (Starfinder 2e) under one tree, so tags are now prefixed `pf2e-` or `sf2e-`. `pf2e-8.0.3` is the latest stable PF2E release with Player Core + APG content. Recorded both in SEED-README and in the seed script comment.
|
||||
- **v8 pack layout discovered at run time.** The plan's instructions referenced `packs/classes/` (Foundry pf2e v7 layout). The cloned v8 tree ships pf2e packs under `packs/pf2e/classes/`. Documented in SEED-README as a layout note and in the seed script's `FOUNDRY_CLASSES_DIR` comment.
|
||||
- **Prisma.JsonNull for nullable Json fields.** Prisma 7's `Json?` typed inputs reject JS `null` and require the sentinel `Prisma.JsonNull` to distinguish "set to JSON null" from "leave column unchanged". Both `spellSlotIncrement` (in ClassProgression) and `proficiencyChanges` (in ClassFeatureOption when undefined on the entry) use this sentinel.
|
||||
- **All 16 class keys must be present in SPELL_SLOT_OVERLAY.** Even when a class's array is empty (Plan 03b stub), the key exists so the seed's `(SPELL_SLOT_OVERLAY[className] || []).filter(...)` never accidentally elides a class because of a typo. This is the contract Plan 03b appends to.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 — Blocking] Worktree had no node_modules and no .env file**
|
||||
- **Found during:** Task 4 verification (running `npx tsc --noEmit`)
|
||||
- **Issue:** The parallel-execution worktree was created clean (no `npm install` had been run, `.env` is gitignored so it didn't transfer). Without these, `prisma`, `tsx`, `jest`, and the Prisma adapter failed to resolve.
|
||||
- **Fix:** Copied `server/.env` from the parent repo into the worktree (file is gitignored and identical) and ran `npm install` in `server/`. Same fix Plan 01-01 documented.
|
||||
- **Files modified:** `server/.env` (gitignored, not staged), `server/node_modules/` (gitignored), `server/package-lock.json` (gitignored, not modified)
|
||||
- **Commit:** N/A — environment setup, no source files changed
|
||||
|
||||
**2. [Rule 1 — Bug] Foundry pf2e v8 packs path differs from plan's documented v7 path**
|
||||
- **Found during:** Task 5 Step 2 (verifying clone has class JSONs)
|
||||
- **Issue:** Plan and SEED-README documented `packs/classes/` (correct for Foundry pf2e v7). Cloned `pf2e-8.0.3` placed pf2e content under `packs/pf2e/classes/`. The seed script's `FOUNDRY_CLASSES_DIR` constant pointed at the v7 path and would have failed loudly with "Foundry pf2e clone not found".
|
||||
- **Fix:** Updated `FOUNDRY_CLASSES_DIR` in `seed-class-progression.ts` to `packs/pf2e/classes/`, added a Pitfall #5 comment block explaining the version-specific path, and added a "Foundry pf2e v8 layout note" section to SEED-README.md plus updated the failure-mode error path string.
|
||||
- **Files modified:** `server/prisma/seed-class-progression.ts`, `.planning/phases/01-level-up-pf2e-regelkonform/SEED-README.md`
|
||||
- **Commit:** `ce214ab`
|
||||
|
||||
**3. [Rule 1 — Bug] Prisma 7 typed-input rejected JS null for nullable Json fields**
|
||||
- **Found during:** Task 4 type-check (`npx tsc --noEmit`)
|
||||
- **Issue:** `Type ... is not assignable to type 'InputJsonValue | NullableJsonNullValueInput | undefined'`. Prisma 7 differentiates "set to JSON null" (`Prisma.JsonNull`) from "leave unchanged" (omit the field) and JS `null` is not accepted in either slot for `Json?` columns.
|
||||
- **Fix:** Imported `Prisma` namespace from the generated client. Replaced `?? null` with `?? Prisma.JsonNull` for `spellSlotIncrement` (in `buildProgressionRow`) and `proficiencyChanges` (in `seedClassFeatureOptions`). Typed `buildProgressionRow`'s return as `Prisma.ClassProgressionUncheckedCreateInput` so future schema drift surfaces as a TS error at the boundary.
|
||||
- **Files modified:** `server/prisma/seed-class-progression.ts`
|
||||
- **Commit:** `e85f790` (the corrected file was the one committed; pre-fix code was never committed)
|
||||
|
||||
**4. [Rule 3 — Blocking] tsc --noEmit picked up TS errors in the cloned Foundry source**
|
||||
- **Found during:** Final verification before SUMMARY
|
||||
- **Issue:** Foundry pf2e ships its own TS source at `prisma/data/foundry-pf2e/types/...` and `prisma/data/foundry-pf2e/vite.config.ts`. These reference modules our project doesn't have (`@common/...`, `svelte`, `vite`, `peggy`) and produce ~25 `tsc --noEmit` errors that have nothing to do with our code.
|
||||
- **Fix:** Added `prisma/data/foundry-pf2e` to `tsconfig.json` `exclude` list (alongside `node_modules` and `dist`). The clone is gitignored and the seed reads it as raw JSON, so excluding it from compilation is correct and surgical.
|
||||
- **Files modified:** `server/tsconfig.json`
|
||||
- **Commit:** `d421aad`
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 4 (1 environment infra, 1 Foundry data-shape correction, 1 Prisma 7 typed-input correction, 1 third-party-source exclusion).
|
||||
**Impact on plan:** None of the deviations changed plan scope. All were correctness fixes required to make the plan run cleanly on this worktree.
|
||||
|
||||
### Authentication Gates
|
||||
|
||||
None — seed pipeline is local-only.
|
||||
|
||||
### Architectural Changes
|
||||
|
||||
None — schema was authored in Plan 01-01; this plan only seeds it.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
- **Plan referenced v7 path `packs/classes/`.** Documented in plan and research. Resolved by Rule 1 fix as above; v8 layout is now noted in SEED-README and code comments so future re-cloning to a different major version surfaces the issue immediately.
|
||||
- **No psql in shell.** The plan's verification step recommended `psql $DATABASE_URL -c '...'`. The Windows shell here doesn't have psql. Wrote a temporary `verify-plan-03-wizard.ts` helper using the existing Prisma client, ran it, captured the output for this Summary, then deleted it. Verification result documented in §Verification Output above. The Prisma-client query approach yields the same information the plan's psql commands would have.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None for this plan. The dev who maintains the project does need to clone the Foundry pf2e repo at the pinned tag for future fresh clones — the README documents the exact command.
|
||||
|
||||
## Plan 03b Readiness
|
||||
|
||||
Plan 03b can append data to `spell-slot-overlays.ts` and `class-feature-options.ts` and re-run `npm run db:seed:class-progression` without touching the seed script. The contract Plan 03b consumes:
|
||||
|
||||
1. `SpellSlotOverlayEntry` interface — extend `SPELL_SLOT_OVERLAY[className]` arrays for Cleric/Druid/Witch/Bard/Sorcerer/Oracle/Champion. Empty `[]` arrays for non-casters can stay or be removed.
|
||||
2. `ClassFeatureOptionEntry` interface — append entries below the anchor comments in `CLASS_FEATURE_OPTIONS`. The `optionsRef` strings already match what `seed-class-progression.ts L1_CHOICE_MAP` emits onto each class's L1 row.
|
||||
3. The `optionsRef`/`optionKey` compound key is `Prisma.@@unique([optionsRef, optionKey])` — Plan 03b's bulk-append must keep `optionKey` unique within an `optionsRef`.
|
||||
|
||||
Re-running the seed after Plan 03b's append will report the count of new ClassFeatureOption rows created and the existing 320 ClassProgression rows updated in place with new overlay data.
|
||||
|
||||
## Threat Flags
|
||||
|
||||
None — no new network endpoints, auth paths, file-access patterns, or schema changes at trust boundaries beyond what the plan's threat register already covered.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
**Verified files exist:**
|
||||
- `.planning/phases/01-level-up-pf2e-regelkonform/SEED-README.md` (FOUND)
|
||||
- `server/prisma/data/spell-slot-overlays.ts` (FOUND)
|
||||
- `server/prisma/data/class-feature-options.ts` (FOUND)
|
||||
- `server/prisma/seed-class-progression.ts` (FOUND)
|
||||
- `server/tsconfig.json` (FOUND, modified — exclude added)
|
||||
|
||||
**Verified commits exist on this branch:**
|
||||
- `6567665` — docs(01-03): add SEED-README with Foundry pf2e clone instructions and pinned tag
|
||||
- `29fe01d` — feat(01-03): add spell-slot overlay types with Wizard worked example
|
||||
- `d86cf4f` — feat(01-03): add class-feature-options types with Wizard School worked example
|
||||
- `e85f790` — feat(01-03): add idempotent ClassProgression + ClassFeatureOption seed script
|
||||
- `ce214ab` — fix(01-03): align Foundry path with pf2e-8.0.3 layout
|
||||
- `d421aad` — chore(01-03): exclude foundry-pf2e dev clone from tsconfig
|
||||
|
||||
**Verified runtime checks:**
|
||||
- `npx tsc --noEmit -p tsconfig.json` exits 0 (foundry-pf2e clone excluded; project code clean)
|
||||
- `npm run db:seed:class-progression` first run: 320 created / 1 created
|
||||
- `npm run db:seed:class-progression` second/third runs: 0 created / 320 updated / 0 errors (idempotent)
|
||||
- Wizard L1 row query returns full structure with ARCANE slot progression, cantrip=5, choiceType=school
|
||||
- Wizard L1..L19 carry non-null spellSlotIncrement; ClassFeatureOption has 1 row for `wizard-school`
|
||||
- Existing 55 leveling tests still pass
|
||||
|
||||
---
|
||||
*Phase: 01-level-up-pf2e-regelkonform*
|
||||
*Completed: 2026-04-27*
|
||||
Reference in New Issue
Block a user