Audited every calculation (26-agent adversarial workflow + hand-verification).
Core math was sound; fixed real bugs, closed parity gaps, and overhauled the
character-build UX. 396 -> 424 tests; lint clean; build green.
Correctness fixes:
- Heavy armor no longer applies a negative DEX penalty to AC (3 paths + default)
- 5e Exhaustion 4 (HP-max halved) implemented via deriveEffectiveMaxHp + badge
- PF2e dazzled no longer makes a creature easier to hit
- PF2e immunity 'all' zeroes all damage (was misfiled as a condition immunity)
- Director schema bounds; PF2e spell-save basis from the `basic` flag; 5e
disadvantage-only save guard; remove bogus Concentrating, add Broken/Quickened
- Fix 3 lint errors (useCloud hooks naming, useless escape, explicit any)
PF2e survival subsystem (parity with 5e death saves):
- mechanics/dying.ts (maxDying/isDead/knockOut/applyRecovery/pf2eRestDecay)
- DefensesSection knock-out + recovery-check + death UI; rest-decay in applyRest;
drained HP reduction
Character-build UX:
- Persisted abilityBuild (base + per-source contributions); sheet & wizard show
every ability source; higher-level PF2e gains L5/10/15/20 boosts
- Creation surfaces granted skills (incl. new PF2e background skill parsing)
- LevelUpModal enumerates features gained + prompts every owed choice (subclass,
feats, fighting style, ...) via collectChoices/collectFeatures/subclassPrompt
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- NumberField keeps a focus-time string draft so min-bounded fields (point-buy, HP,
quantities) no longer snap to the minimum on every keystroke while retyping; the
committed value is still always a clamped integer.
- Combat tracker now logs the 5e death rules when a downed PC takes damage: instant
death from massive damage (leftover >= max HP), otherwise a reminder to mark a death
save failure (two on a crit). Surfaced, not auto-applied — the player owns their
character's death-save count (consistent with the no-auto-roll design). New tested
isMassiveDamageDeath() engine helper.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Crash fix:
- restoreBackup (file + cloud pull) now validates each row through its Zod
schema, backfilling new fields — an older backup no longer lands characters
missing feats/concentration/etc and crashing the sheet. + test.
pf2e parity (closing feature gaps vs 5e):
- +9 missing classes (Animist, Exemplar, Gunslinger, Inventor, Kineticist,
Magus, Psychic, Summoner, Thaumaturge) with data + class tips.
- the wizard now enriches pf2e classes with their caster ability, so pf2e
spellcasters get the Spells step + "Caster" tag like 5e.
- editable Perception rank and spellcasting proficiency on the pf2e sheet
(fixes wrong initiative / spell DC at higher levels); rank-10 spell slots.
- pf2e monster resistances/weaknesses/immunities now apply in combat (flat
amounts: resistance subtracts, weakness adds, 'all' matches any type). + tests.
Glyph purge (finishing the emoji→Lucide migration): replaced ~40 leftover
text-glyphs (✕ − + ✦ 🤫 ⬆ ⬇ ☑ ☐ ▶ ◀ ‹ › ★ ↻ ▸ ↑ ●) with Lucide icons across
Modal (every dialog), the sheet sections, dice, settings, player panels, world
pages, map editor, roll tray, and session UI.
Gate: 293 unit + e2e green; tsc + build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the deployable Phase 1-3 chunk: the combat tracker now runs the math.
- condition-effects engine: declarative CONDITION_EFFECTS_5E/PF2E table +
deriveState(system, speed, conditions) → effective speed / advantage state /
incapacitation / pf2e status penalty. Tracker shows effect badges
("Speed 0", "Disadv. attacks", "−2 status"); pure + 10 unit tests.
- damage types: applyDamage(c, amount, type?) consults snapshotted monster
DamageDefenses (immune→0, resist→halve, vulnerable→double), back-compatible.
Monster add snapshots damageDefenses; tracker shows a type picker only when a
creature has typed defenses, and the log notes resisted/vulnerable amounts.
- concentration + death saves are surfaced as REMINDERS, never auto-rolled:
damaging a concentrating PC logs "roll a DC N Con save…"; a downed PC's turn
logs "make a death saving throw." The player rolls and resolves via the Drop
button / death-save pips they already own. (Per user: no auto dice rolls —
it removes the player from their character.)
Build + 271 unit tests green. Verified live: grapple → Speed 0 badge;
fire-immune creature shows the immune-labelled damage picker.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Advantage/Disadvantage are now toggle modes (rollStore.mode) applied to d20
rolls everywhere (dice page, sheet skills/saves/attacks), preserving modifiers;
applyRollMode() rewrites the first d20 -> 2d20kh1/kl1
- addCombatant auto-numbers duplicate names (Goblin 1, Goblin 2, …); the first
duplicate also renames the original. Quantity add relies on this.
- Tests for applyRollMode + duplicate numbering
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Condition durations + auto-expiry: timed conditions tick down on the affected
combatant's turn and drop at 0 (engine tickConditions; UI rounds field + (Nr) chip)
- Initiative: Roll-all (d20 + per-combatant bonus, re-sort anchored) + group/quantity add
- Combat log: per-round event feed (turns, round changes, damage/heal, expiries,
removals) + in-memory Undo of the last change
- Encounter difficulty budget: 5e XP thresholds + PF2e level budget (pure lib +
tests); live difficulty/XP-award readout from monster combatants vs the party
- Combatants carry initBonus + cr/level; compendium add-to-combat passes them
18 new unit tests (durations, applyInitiatives, budget), new combat-depth e2e.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>