feat(feats): author the 2014 Feat catalog rows behind the edition tag - #1731
Conversation
Restores the 2014 half of the Feat catalog `6491c528` (#1154) replaced. Adds 25 new EDITION_2014 rows (24 recovered verbatim from the pre-#1154 catalog, plus Grappler and Savage Attacker freshly transcribed from SRD 5.1 / PHB'14) and stamps the 32 previously-shared rows EDITION_2024, so every Feat row is now edition-tagged and no NULL row remains — a 2014 character no longer sees PHB'24 rules text for feats it never had. PHB'14 has no Origin/Fighting Style/Epic Boon taxonomy, so every 2014 row is category "general" with no levelPrerequisite; featOfferedForAsiSlot's `?? 4` default is what encodes PHB'14's "earliest ASI is level 4" rule, so the function drops its now-unused `edition` parameter (an edition-invariant rule takes none). Fixes the fixture-lookup drift the plan flagged in advance: species-creation-choices-2024.test.ts's featByName helper resolved by bare name and would have picked a nondeterministic edition now that Savage Attacker/Magic Initiate fork — pinned to EDITION_2024. Closes #1310 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018rre9Ho8Vx8zNtzzKkpvFn
|
Claude finished @Sandersland's task in 6m 16s —— View job Code Review: PR #1731 — 2014 Feat catalog rows
OverviewSolid PR. The data model transformation is complete and correct: zero The removal of the Comment drift (CLAUDE.md: "update or delete its comment in the same edit")
// The seeded Alert fork (#1306's worked example) is unusable here: both rows
// are category "origin", rejected by featOfferedForAsiSlot BEFORE the guard
// runs — a naive test against Alert would pass for the wrong reason.After this PR, Alert 2014 is Test label imprecision (nit, not a bug)
The actual Rest of the changes — no issues found
One confirmed comment-drift finding, one test-label nit. Both are straightforward fixes. |
- cross-edition-catalog-id.test.ts: the "both rows are category origin" comment went stale when this PR moved Alert 2014's category to "general" — only the Alert 2024 row is origin-gated now. Narrowed the comment to that fact; the conclusion (use a general-category fixture trio instead) is unchanged. - feats-2014-general-take.test.ts: the level-1/no-XP case's label claimed to prove featOfferedForAsiSlot's `?? 4` default, but a level-1 character has zero ASI slots, so the 400 comes from the slot-availability check, not the category default. Renamed to match what the test actually exercises; the real `?? 4` proof is the atThree=0/atFour=26 pair in feats.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018rre9Ho8Vx8zNtzzKkpvFn
|
Claude finished @Sandersland's task in 1m 14s —— View job Code Review: PR #1731 — 2014 Feat catalog rows (re-review)
Fixes from prior review — both verified ✓
Both changes are accurate and exactly match what the prior review requested. No new issues foundThe fix commit ( Verdict: All prior findings resolved. PR is clean. |
Summary
Restores the 2014 half of the Feat catalog
6491c528(#1154) replaced. EveryFeatrow is now edition-tagged (noedition: NULLremains) — before this change a 2014 character was served 36 rows of PHB'24 rules text, including feats PHB'14 doesn't have.EDITION_2014rows: 24 recovered verbatim fromgit show 6491c528^:backend/prisma/seed/feats.ts, plus Grappler (transcribed from SRD 5.1 via the open dnd5eapi.co/2014/feats/grapplerendpoint) and Savage Attacker (PHB'14-only — not SRD content — transcribed from and cross-checked against two independent published transcriptions, dnd5e.wikidot.com and aidedd.org, since it isn't available from an SRD API).EDITION_2024(Alert's 2014 row already existed; itscategorymovesorigin→generalsince PHB'14 has no Origin taxonomy).EDITION_2014Fighting Style rows 2014 fighting styles: author the six PHB'14 styles as EDITION_2014 Feat rows #1311 already shipped = 32 totalEDITION_2014rows. 2024: 37 rows, unchanged in count. 0NULLrows.featOfferedForAsiSlot's now-unusededitionparameter (and itseslint-disable): PHB'14's "no per-feat level gate, earliest ASI at level 4" rule is already what thegeneralbranch's?? 4default encodes, so no fork is needed — an edition-invariant rule takes noeditionparameter.Content accuracy
improvements(the description already reads "of your choice", matching the 2024 sibling's description-only treatment).Live drift caught
species-creation-choices-2024.test.ts'sfeatByNamehelper resolved by bare{ name }with no edition filter — now that Savage Attacker and Magic Initiate fork, that lookup became non-deterministic and 3 tests went red. Pinned toEDITION_2024(the file is 2024-only throughout).Test plan (RED → GREEN)
prisma/seed/__tests__/seed-data.test.ts: 2 pre-existing tests went red on the seed change alone (General-feat invariant needed EDITION_2024 scoping; the Alert/Grappler illustration needed inverting since Grappler now forks) — fixed, plus a newFEATS — 2014 general/origin catalog (#1310)describe block (9 new assertions: row count, Mobile/Speedy split, Grappler/Savage Attacker/Weapon Master specifics, zero origin/epic_boon rows, zero NULL rows, all 4BACKGROUNDS.originFeatNamevalues present in both editions).src/lib/rules/__tests__/catalog-edition-constraints.test.tsandsrc/routes/catalog/__tests__/feats.test.ts: both had a "Grappler stays one shared row" assertion that inverted — fixed, plus a newGET /api/feats?asiLevel= — 2014 general/origin feats (#1310)suite proving the 32/37 row counts and the?? 4default live against the real seeded catalog for 2014 (not just fixtures).src/lib/srd/__tests__/srd-feat-offer.test.ts: updated for the 2-arg signature; the "same verdict in both editions" parity test (now meaningless — there's only one signature) is removed.src/routes/character/__tests__/feats-2014-general-take.test.ts: end-to-end creation + advancement-transaction coverage — a 2014 character takes Sentinel via an ASI slot with noabilityChoice(no PHB'14 bump), a 2014 half-feat (Athlete) requires one, a sub-level-4 2014 character is rejected, and a 2024 character is rejected on a 2014 feat id with edition wording (not a category message).feats.test.tsandfeats-asi-level-defaults.test.ts(the?? 4"unreachable from the real catalog" claim, now false),reference.test.ts(Savage Attacker no longer a shared-NULL row).feat-fork-reseed.test.ts(already existed from 2014 fighting styles: author the six PHB'14 styles as EDITION_2014 Feat rows #1311, category-agnostic mechanism) — header comment updated to note it also backs this issue; verified live viaprisma db seedrun twice (32 EDITION_2014 / 37 EDITION_2024 / 0 NULL, stable both times, confirmed via directpsqlquery).Gate:
npm run lint,npm run test(backend 4379 passed, frontend 3469 passed),npm run typecheck(all workspaces) — all green.fallow audit --base origin/stagingverdictpass(0 dead code, 0 duplication, one pre-existing/inherited complexity finding not introduced by this change). Pre-commit and pre-push hooks (lefthook: fallow, typecheck, test) passed.Frontend
No changes. The two
category === "fighting_style"filters the issue flagged as needing a no-crash check were already safe — #1311 gave 2014 six realfighting_stylerows, so the "catalog now contains zero fighting_style rows for 2014" premise in that scope item no longer holds.Closes #1310
🤖 Generated with Claude Code
https://claude.ai/code/session_018rre9Ho8Vx8zNtzzKkpvFn