Conversation
📝 WalkthroughWalkthroughReordered character creation to persist the character row before running starting-equipment provisioning; spell-related fields are derived after provisioning (with zero-indexed spell-slot array → level mapping) and persisted via an update. Added regression tests checking provisioning succeeds and spell-slot indexing is correct. Changes
Sequence DiagramsequenceDiagram
actor Client
participant Handler as Character Handler
participant CharRepo as Character Repository
participant ProvService as Provisioning Service
participant DB as Database
Client->>Handler: create(class, level, provisionEquipment)
Handler->>Handler: Build character object (seed spells from args only)
rect rgba(100,150,255,0.5)
Note over Handler,CharRepo: Insert character first
Handler->>CharRepo: create(character)
CharRepo->>DB: INSERT character
DB-->>CharRepo: character.id
CharRepo-->>Handler: character (with id)
end
rect rgba(150,200,100,0.5)
Note over Handler,ProvService: Provision starting equipment & spells
Handler->>ProvService: provisionStartingEquipment(character.id, ...)
ProvService->>DB: INSERT inventory rows (FK to character.id)
DB-->>ProvService: provisioning result (equipmentGranted, spellsGranted, spellSlots)
ProvService-->>Handler: provisioning result
end
rect rgba(200,150,100,0.5)
Note over Handler,CharRepo: Derive spell fields and persist
Handler->>Handler: Merge knownSpells (args ∪ spellsGranted), set cantrips, convert zero-indexed spellSlots
Handler->>CharRepo: update(character.id, { knownSpells, cantripsKnown, spellSlots, pactMagicSlots })
CharRepo->>DB: UPDATE character
DB-->>CharRepo: success
CharRepo-->>Handler: updated character
end
Handler-->>Client: character + _provisioning details
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
…nventory FK handleCreate built the provisioning result first, then assembled the character record, then inserted it. The provisioner writes to inventory_items immediately, and that table's character_id column has a FOREIGN KEY → characters(id). Since the character row didn't exist yet, every inventory insert failed and surfaced as "Failed to grant <Item>: FOREIGN KEY constraint failed" — every PC spawned with an empty inventory. Fix: - Insert the character row first (with whatever args supplied for spell fields, defaulted otherwise). - Run provisioning second — FK now resolves, inventory grants succeed. - Update the character row with knownSpells/cantripsKnown/spellSlots/ pactMagicSlots from the provisioning result. Adds a regression test that creates one character per major class and asserts _provisioning.errors stays unset and equipmentGranted is non-empty. Closes #45 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/server/consolidated/character-manage.test.ts (1)
102-115: Assert persisted inventory state, not just_provisioning.This regression still only checks the handler response. Since issue
#45was specifically a failed write toinventory_items, please also verify the created character has persisted inventory rows after creation—ideally by querying throughInventoryRepositoryor the DB and checking for expected starter items per class.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/server/consolidated/character-manage.test.ts` around lines 102 - 115, The test currently only asserts the handler response (_provisioning) but must also assert persisted inventory rows to catch failed writes to inventory_items; after each handleCharacterManage call, load the created character's inventory via InventoryRepository (or a direct DB query) using the returned character id from parsed, then assert that InventoryRepository.findByCharacterId (or equivalent) returns >0 rows and contains the expected starter item(s) for that class (e.g., Paladin, Rogue, Wizard, Cleric, Fighter); update the test to fail if no persisted inventory rows exist or expected starter items are missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/server/consolidated/character-manage.ts`:
- Around line 239-245: The code is shifting spell-slot levels when serializing
provisioningResult.spellSlots via convertSpellSlotsToObject; normalize the array
shape before calling convertSpellSlotsToObject so slot indexes align with the
helper's expected indexing (handle both 0-based and 1-based shapes produced by
provisionStartingEquipment). Update the call site that sets character.spellSlots
to detect provisioningResult.spellSlots and, if its first element corresponds to
1st-level slots (the starting-equipment shape), prepend a placeholder (e.g.,
null/0) or otherwise shift the array so convertSpellSlotsToObject reads the
correct level positions; alternatively, adjust convertSpellSlotsToObject to
accept and normalize both shapes—refer to provisioningResult.spellSlots and
convertSpellSlotsToObject to locate the change.
---
Nitpick comments:
In `@tests/server/consolidated/character-manage.test.ts`:
- Around line 102-115: The test currently only asserts the handler response
(_provisioning) but must also assert persisted inventory rows to catch failed
writes to inventory_items; after each handleCharacterManage call, load the
created character's inventory via InventoryRepository (or a direct DB query)
using the returned character id from parsed, then assert that
InventoryRepository.findByCharacterId (or equivalent) returns >0 rows and
contains the expected starter item(s) for that class (e.g., Paladin, Rogue,
Wizard, Cleric, Fighter); update the test to fail if no persisted inventory rows
exist or expected starter items are missing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a73ef1c7-a6fb-4926-b850-7698b0cff6ca
📒 Files selected for processing (2)
src/server/consolidated/character-manage.tstests/server/consolidated/character-manage.test.ts
…lot bug Reviewers (PR #55) flagged that this branch reorders character creation so provisioning succeeds, but it still uses the off-by-one convertSpellSlotsToObject helper. Merged standalone, every wizard would persist with level1=3 (should be 4) and every paladin with level1=0 (should be 3). Apply the same zero-indexing fix that lives in PR #54 here so #55 is correct on its own merge boundary. Tests assert wizard L4 = 4/3 and paladin L4 = 3 after the reorder. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/server/consolidated/character-manage.ts (1)
224-257: Post-provisioning update correctly persists spell-related fields.The flow is sound:
- Provisioning runs against the existing character row
- Spell fields are derived from provisioning result and merged with args
- A single
update()call persists all spell-related changesOne minor observation: the
as anycast on line 256 bypasses type checking on the update payload.🔧 Consider defining the update payload type explicitly
Instead of
as any, you could define a typed partial:- characterRepo.update(characterId, { - knownSpells: character.knownSpells as string[], - cantripsKnown: character.cantripsKnown as string[], - spellSlots: character.spellSlots, - pactMagicSlots: character.pactMagicSlots - } as any); + const spellUpdates: Partial<{ + knownSpells: string[]; + cantripsKnown: string[]; + spellSlots: ReturnType<typeof convertSpellSlotsToObject>; + pactMagicSlots: typeof character.pactMagicSlots; + }> = { + knownSpells: character.knownSpells as string[], + cantripsKnown: character.cantripsKnown as string[], + spellSlots: character.spellSlots, + pactMagicSlots: character.pactMagicSlots + }; + characterRepo.update(characterId, spellUpdates as Parameters<typeof characterRepo.update>[1]);This is a minor improvement and can be deferred.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/consolidated/character-manage.ts` around lines 224 - 257, The update call to characterRepo.update uses an unsafe cast ("as any") for the payload; replace it with a properly typed partial object so TypeScript enforces field types. Locate the provisioning block around provisionStartingEquipment(...) and the subsequent assignments to character.knownSpells, character.cantripsKnown, character.spellSlots, and character.pactMagicSlots, then call characterRepo.update(characterId, payload) where payload is typed (e.g., Partial<CharacterRow> or the repository's expected update type) and contains knownSpells: string[], cantripsKnown: string[], spellSlots: SpellSlotsType, pactMagicSlots?: PactMagicType; remove the "as any" cast and adjust conversions (convertSpellSlotsToObject return type) to satisfy the update signature.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/server/consolidated/character-manage.ts`:
- Around line 224-257: The update call to characterRepo.update uses an unsafe
cast ("as any") for the payload; replace it with a properly typed partial object
so TypeScript enforces field types. Locate the provisioning block around
provisionStartingEquipment(...) and the subsequent assignments to
character.knownSpells, character.cantripsKnown, character.spellSlots, and
character.pactMagicSlots, then call characterRepo.update(characterId, payload)
where payload is typed (e.g., Partial<CharacterRow> or the repository's expected
update type) and contains knownSpells: string[], cantripsKnown: string[],
spellSlots: SpellSlotsType, pactMagicSlots?: PactMagicType; remove the "as any"
cast and adjust conversions (convertSpellSlotsToObject return type) to satisfy
the update signature.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 12d6df03-94c9-4ff4-87a2-90263d71d431
📒 Files selected for processing (2)
src/server/consolidated/character-manage.tstests/server/consolidated/character-manage.test.ts
|
@coderabbitai this finding is stale — the fix landed in 2da8f4d (this branch's tip). |
|
Tip For best results, initiate chat on the files or code changes.
|
Summary
handleCreatebuilt the provisioning result, then assembled the character object, then inserted the character row. The provisioner writes toinventory_itemsimmediately, and that table FK-referencescharacters(id). Result: every starting-equipment grant failed withFOREIGN KEY constraint failed, every PC spawned with an empty bag.Fix
Reorder to insert the character row first, then run provisioning, then update the character with the slot/spell data the provisioner returns. (The provisioner is read-only for slot/spell data; only inventory + currency need a writable character row.)
Test plan
_provisioning.errorsis undefined andequipmentGranted.length > 0.Closes #45
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests