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).
|
- 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"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user