fix(combat): honor participant side field as alias for isEnemy#56
fix(combat): honor participant side field as alias for isEnemy#56
side field as alias for isEnemy#56Conversation
📝 WalkthroughWalkthroughThe Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 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 |
The combat_manage create schema only accepted isEnemy:boolean. Callers following the consolidated tool's runtime hint sent side:"enemy" instead, which Zod silently dropped. Enemies came back with isEnemy=undefined → false, so they rendered with the PC glyph and the engine prompted "PLAYER TURN" on enemy turns. Adds a `side` enum (party/enemy/hostile/ally/friendly/neutral) on the participant schema and a deriveIsEnemy helper. Convention: - explicit isEnemy wins - otherwise side="enemy"|"hostile" → isEnemy=true - otherwise → false The transform happens at the consolidated-tool boundary; the underlying handleCreateEncounter contract is unchanged. Closes #46 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/server/consolidated/combat-manage.ts (1)
65-69: Consider centralizing side literals to prevent drift.
sidevalues are currently repeated in schema and mapping logic; extracting shared constants improves maintainability.Refactor sketch
+const PARTICIPANT_SIDES = ['party', 'enemy', 'hostile', 'ally', 'friendly', 'neutral'] as const; +type ParticipantSide = (typeof PARTICIPANT_SIDES)[number]; +const ENEMY_SIDES: ReadonlySet<ParticipantSide> = new Set(['enemy', 'hostile']); const ParticipantSchema = z.object({ @@ - side: z.enum(['party', 'enemy', 'hostile', 'ally', 'friendly', 'neutral']).optional(), + side: z.enum(PARTICIPANT_SIDES).optional(), @@ -function deriveIsEnemy(p: { isEnemy?: boolean; side?: string }): boolean | undefined { +function deriveIsEnemy(p: { isEnemy?: boolean; side?: ParticipantSide }): boolean | undefined { if (typeof p.isEnemy === 'boolean') return p.isEnemy; if (!p.side) return undefined; - return p.side === 'enemy' || p.side === 'hostile'; + return ENEMY_SIDES.has(p.side); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/consolidated/combat-manage.ts` around lines 65 - 69, The deriveIsEnemy function duplicates literal side strings; centralize these values by introducing a shared enum or constant (e.g., SIDES or SideEnum) and replace the string checks in deriveIsEnemy with references to those constants, and update any related schema/mapping code that uses 'enemy' or 'hostile' so all code (including deriveIsEnemy) reads from the same symbol(s) to avoid drift.tests/server/consolidated/combat-manage.test.ts (1)
124-140: Optionally extend matrix to includefriendlyandneutral.This would fully exercise all enum values introduced in the schema.
Suggested test extension
participants: [ { id: 'pc-vela', name: 'Vela', initiativeBonus: 0, hp: 38, maxHp: 38, side: 'party', position: { x: 1, y: 1 } }, { id: 'pc-tobin', name: 'Tobin', initiativeBonus: 4, hp: 28, maxHp: 28, side: 'ally', position: { x: 1, y: 2 } }, + { id: 'pc-lyra', name: 'Lyra', initiativeBonus: 1, hp: 24, maxHp: 24, side: 'friendly', position: { x: 2, y: 2 } }, + { id: 'npc-warden', name: 'Warden', initiativeBonus: 0, hp: 30, maxHp: 30, side: 'neutral', position: { x: 3, y: 2 } }, { id: 'enemy-rurk', name: 'Rurk', initiativeBonus: 1, hp: 22, maxHp: 22, side: 'enemy', position: { x: 5, y: 5 } }, { id: 'enemy-mira', name: 'Mira', initiativeBonus: 2, hp: 16, maxHp: 16, side: 'hostile', position: { x: 6, y: 5 } } ] }, ctx); const data = parseResult(result); expect(data.success).toBe(true); const byId = Object.fromEntries( (data.participants as Array<{ id: string; isEnemy: boolean }>).map((p) => [p.id, p.isEnemy]) ); expect(byId['pc-vela']).toBe(false); expect(byId['pc-tobin']).toBe(false); + expect(byId['pc-lyra']).toBe(false); + expect(byId['npc-warden']).toBe(false); expect(byId['enemy-rurk']).toBe(true); expect(byId['enemy-mira']).toBe(true);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/server/consolidated/combat-manage.test.ts` around lines 124 - 140, Extend the test participant matrix to include entries using the new enum sides (e.g., side: 'friendly' and side: 'neutral') so the participants parsing logic is exercised for all enum values; update the array passed into the tested call (the list containing pc-vela, pc-tobin, enemy-rurk, enemy-mira) to add two new objects with unique ids (e.g., 'pc-ally-friendly' and 'npc-neutral'), appropriate names/hp/position, and verify their resulting flags in the byId map (checking (data.participants).map(...).map key lookup)—ensuring assertions validate the expected isEnemy values for these new sides as well.
🤖 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/combat-manage.ts`:
- Around line 65-69: The deriveIsEnemy function duplicates literal side strings;
centralize these values by introducing a shared enum or constant (e.g., SIDES or
SideEnum) and replace the string checks in deriveIsEnemy with references to
those constants, and update any related schema/mapping code that uses 'enemy' or
'hostile' so all code (including deriveIsEnemy) reads from the same symbol(s) to
avoid drift.
In `@tests/server/consolidated/combat-manage.test.ts`:
- Around line 124-140: Extend the test participant matrix to include entries
using the new enum sides (e.g., side: 'friendly' and side: 'neutral') so the
participants parsing logic is exercised for all enum values; update the array
passed into the tested call (the list containing pc-vela, pc-tobin, enemy-rurk,
enemy-mira) to add two new objects with unique ids (e.g., 'pc-ally-friendly' and
'npc-neutral'), appropriate names/hp/position, and verify their resulting flags
in the byId map (checking (data.participants).map(...).map key lookup)—ensuring
assertions validate the expected isEnemy values for these new sides as well.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a2613275-7223-48be-a259-c789bda248eb
📒 Files selected for processing (2)
src/server/consolidated/combat-manage.tstests/server/consolidated/combat-manage.test.ts
Summary
The combat_manage create schema only accepted
isEnemy: boolean. Callers sendingside: "enemy"(the natural shape based on the runtime hint) were silently ignored — Zod dropped the unknown field — and enemies came back withisEnemy: false, rendering as PCs with player turn prompts.Fix
Add a
sideenum (party | enemy | hostile | ally | friendly | neutral) to the participant schema. AderiveIsEnemyhelper coercessidetoisEnemyat the consolidated-tool boundary; explicitisEnemystill wins when both are provided. The underlyinghandleCreateEncountercontract is unchanged.Test plan
sidevalues (party/ally/enemy/hostile) map to correctisEnemyflagsisEnemywins over conflictingsideCloses #46
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Tests