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:
@@ -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).
|
||||
- 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**
|
||||
|
||||
@@ -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**
|
||||
|
||||
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**
|
||||
|
||||
@@ -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/features/combat/combat.store.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:**
|
||||
- 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).
|
||||
|
||||
**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**
|
||||
|
||||
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`)
|
||||
Expected: PASS
|
||||
These two files build their own `Combat` fixture for unrelated tests (hunt resumption) and will fail to compile once `CombatPlayer`/`CombatMonster` gain required fields.
|
||||
|
||||
- [ ] **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
|
||||
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"
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user