Fix plan defect found in SDD pre-flight scan: Task 8 fixture patch

hunt-page.component.spec.ts and resume-combat.spec.ts each build a
direct type-annotated Combat literal that the new required
CombatPlayer/CombatMonster fields would break at compile time.
Verified with an isolated tsc --strict probe, and used the same probe
to confirm the combat.service.spec.ts `as Combat` fixtures do NOT
break (structural widening through the type assertion), so no change
needed there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bastian Wagner
2026-08-20 21:04:19 +02:00
parent 67df86a818
commit 96d04d8ba3

View File

@@ -288,7 +288,7 @@ git commit -m "feat(combat): migrate combat_event_type_enum for Slice 0.6 event
- Consumes: nothing new (pure type changes). - Consumes: nothing new (pure type changes).
- Produces: `CombatIntent = 'HEAVY_ATTACK'` (exported from `combat-engine.types.ts`). `CombatEngineCombatantStats` gains optional `potionsRemaining?: number` and `pendingAction?: CombatIntent`. `CombatCombatantState` (entity) gains optional `pendingAction?: CombatIntent`; `CombatPlayerState` gains required `potionsRemaining: number`. Task 5 (engine) and Task 7 (service DTOs) both depend on these exact field names. - Produces: `CombatIntent = 'HEAVY_ATTACK'` (exported from `combat-engine.types.ts`). `CombatEngineCombatantStats` gains optional `potionsRemaining?: number` and `pendingAction?: CombatIntent`. `CombatCombatantState` (entity) gains optional `pendingAction?: CombatIntent`; `CombatPlayerState` gains required `potionsRemaining: number`. Task 5 (engine) and Task 7 (service DTOs) both depend on these exact field names.
This task is pure types with no runtime behavior, so there's no new test — its correctness is verified by the whole project still compiling and the existing suite staying green. This task is pure types with no runtime behavior, so there's no dedicated new test — its correctness is verified by the whole project still compiling and the existing suite staying green. (Pre-flight check: the pre-existing `{ ... } as Combat` fixtures in `combat.service.spec.ts` that build `playerState` without `potionsRemaining` were checked against this change with an isolated `tsc --strict` probe — a type assertion's structural-widening direction accepts them since `Combat` is assignable to the fixture's narrower inferred type, so nothing there needs touching. Direct type-annotated literals elsewhere are a different story — see Task 8's pre-flight ruling.)
- [ ] **Step 1: Extend the engine types** - [ ] **Step 1: Extend the engine types**
@@ -385,7 +385,7 @@ Leave the rest of the file (the `@Entity` class itself) unchanged.
- [ ] **Step 3: Run the full API test suite to confirm nothing broke** - [ ] **Step 3: Run the full API test suite to confirm nothing broke**
Run: `npm run test --workspace=@ashen-realms/api` Run: `npm run test --workspace=@ashen-realms/api`
Expected: PASS — these are purely additive optional fields plus one new required field (`potionsRemaining`) that nothing constructs yet outside of test fixtures using object literals typed loosely (`as Combat`), which TypeScript doesn't strictly check field-by-field. Expected: PASS — these are purely additive optional fields plus one new required field (`potionsRemaining`) that nothing in the existing suite constructs directly: the only `Combat`-typed test fixtures use `{ ... } as Combat`, and a type assertion accepts this because `Combat` structurally widens into the fixture's narrower inferred type.
- [ ] **Step 4: Commit** - [ ] **Step 4: Commit**
@@ -1115,11 +1115,15 @@ git commit -m "feat(combat): validate potions, persist telegraph state, and expo
- Modify: `apps/web/src/app/core/api/game-api.models.ts:151-188` - Modify: `apps/web/src/app/core/api/game-api.models.ts:151-188`
- Modify: `apps/web/src/app/features/combat/combat.store.ts` - Modify: `apps/web/src/app/features/combat/combat.store.ts`
- Modify: `apps/web/src/app/features/combat/combat.store.spec.ts` - Modify: `apps/web/src/app/features/combat/combat.store.spec.ts`
- Modify: `apps/web/src/app/features/hunting/hunt-page/hunt-page.component.spec.ts:50-65` (fixture only, see Step 4)
- Modify: `apps/web/src/app/core/resume-combat.spec.ts:8-23` (fixture only, see Step 4)
**Interfaces:** **Interfaces:**
- Consumes: nothing new outside the web app. - Consumes: nothing new outside the web app.
- Produces: `CombatAction` (web) = `'ATTACK' | 'HEAVY_STRIKE' | 'SHIELD_BASH' | 'DEFEND' | 'POTION'`. `CombatEventType` (web) gains `'HEAL' | 'DEFEND' | 'TELEGRAPH' | 'INTERRUPT'`. `CombatPlayer` gains `potionsRemaining: number` and `potionsMax: number`. `CombatMonster` gains `pendingIntent: 'HEAVY_ATTACK' | null`. `CombatStore.performAction(action: CombatAction): Promise<void>` replaces `CombatStore.attack(): Promise<void>` — consumed by Task 9 (combat page component). - Produces: `CombatAction` (web) = `'ATTACK' | 'HEAVY_STRIKE' | 'SHIELD_BASH' | 'DEFEND' | 'POTION'`. `CombatEventType` (web) gains `'HEAL' | 'DEFEND' | 'TELEGRAPH' | 'INTERRUPT'`. `CombatPlayer` gains `potionsRemaining: number` and `potionsMax: number`. `CombatMonster` gains `pendingIntent: 'HEAVY_ATTACK' | null`. `CombatStore.performAction(action: CombatAction): Promise<void>` replaces `CombatStore.attack(): Promise<void>` — consumed by Task 9 (combat page component).
**Pre-flight ruling:** making `potionsRemaining`/`potionsMax`/`pendingIntent` required breaks two more `const x: Combat = {...}` fixtures outside the combat feature — `hunt-page.component.spec.ts` and `resume-combat.spec.ts` both build a full `Combat` literal for their own unrelated tests (hunt resumption, not combat mechanics). A direct type-annotated object literal missing required properties is a guaranteed TypeScript error (stricter than the `as Combat` cast fixed in Task 4). Step 4 below patches both.
- [ ] **Step 1: Extend the web models** - [ ] **Step 1: Extend the web models**
In `apps/web/src/app/core/api/game-api.models.ts`, replace lines 151-188 (from `export type CombatStatus = ...` through the end of the `Combat` interface): In `apps/web/src/app/core/api/game-api.models.ts`, replace lines 151-188 (from `export type CombatStatus = ...` through the end of the `Combat` interface):
@@ -1329,15 +1333,63 @@ Also add one new test, right after the "sends only the ATTACK action..." test, t
}); });
``` ```
- [ ] **Step 4: Run to verify the store spec passes** - [ ] **Step 4: Patch the two out-of-feature fixtures**
Run: `npx ng test --watch=false --include='**/combat.store.spec.ts'` (from `apps/web`) These two files build their own `Combat` fixture for unrelated tests (hunt resumption) and will fail to compile once `CombatPlayer`/`CombatMonster` gain required fields.
Expected: PASS
- [ ] **Step 5: Commit** In `apps/web/src/app/features/hunting/hunt-page/hunt-page.component.spec.ts`, change the `startedCombat` fixture (around lines 50-65):
```ts
const startedCombat: Combat = {
id: 'combat-2',
status: 'ACTIVE',
round: 1,
player: { name: 'Aric Duskwalker', maxHp: 100, currentHp: 100, potionsRemaining: 2, potionsMax: 2 },
monster: {
key: 'road-bandit',
name: 'Straßenräuber',
level: 3,
maxHp: 75,
currentHp: 75,
artworkPath: '/images/enemies/RoadBandit.png',
pendingIntent: null,
},
events: [],
rewards: null,
};
```
In `apps/web/src/app/core/resume-combat.spec.ts`, change the `runningCombat` fixture (around lines 8-23):
```ts
const runningCombat: Combat = {
id: 'combat-running',
status: 'ACTIVE',
round: 4,
player: { name: 'Aric Duskwalker', maxHp: 100, currentHp: 62, potionsRemaining: 2, potionsMax: 2 },
monster: {
key: 'road-bandit',
name: 'Straßenräuber',
level: 3,
maxHp: 75,
currentHp: 30,
artworkPath: '/images/enemies/RoadBandit.png',
pendingIntent: null,
},
events: [],
rewards: null,
};
```
- [ ] **Step 5: Run to verify everything this task touches compiles and passes**
Run: `npx ng test --watch=false --include='**/combat.store.spec.ts' --include='**/hunt-page.component.spec.ts' --include='**/resume-combat.spec.ts'` (from `apps/web`)
Expected: PASS — this also proves the Step 4 fixture patches compile, since `ng test` type-checks the whole project before running any spec.
- [ ] **Step 6: Commit**
```bash ```bash
git add apps/web/src/app/core/api/game-api.models.ts apps/web/src/app/features/combat/combat.store.ts apps/web/src/app/features/combat/combat.store.spec.ts git add apps/web/src/app/core/api/game-api.models.ts apps/web/src/app/features/combat/combat.store.ts apps/web/src/app/features/combat/combat.store.spec.ts apps/web/src/app/features/hunting/hunt-page/hunt-page.component.spec.ts apps/web/src/app/core/resume-combat.spec.ts
git commit -m "feat(combat): generalize the web combat action and expose potions/monster intent" git commit -m "feat(combat): generalize the web combat action and expose potions/monster intent"
``` ```