# Empirical Adversarial Challenge Report — Cocos Kinematics & Mechanics

- **Challenger Agent**: `challenger_kinematics_stress`
- **Target Subsystem**: Cocos Creator 3.x Client Kinematics, Wall-Sliding, A* Pathfinder, Combat Evasion & Boss Gate State Machine
- **Test Harness**: `tests/unit/test_challenger_cocos_kinematics_stress.ts`
- **Execution Engine**: `npx tsx tests/unit/test_challenger_cocos_kinematics_stress.ts` (Node v24.14.0)
- **Verdict**: **REQUEST_CHANGES**

---

## 1. Observation

Direct empirical execution of `npx tsx tests/unit/test_challenger_cocos_kinematics_stress.ts` against the live TypeScript source files yielded **27 checks: 22 PASSED, 5 FAILED**:

```
TOTAL CHECKS: 27
PASSED: 22
FAILED: 5

FAILURE DETAILS:
- [MEDIUM] [S1_ZERO_MOMENTUM] Active movement velocity telemetry calculation: velX during movement = 0 (expected ~5.5). Order-of-operations bug in KinematicsEngine.ts:114-117 causes velX to be 0!
- [HIGH] [S3_ASTAR_OBSTACLES] Diagonal corner-touching wall pinch LoS check: hasLineOfSight returned true across corner-touching walls (11,10) and (10,11)! DDA Bresenham lacks diagonal corner-cutting test!
- [HIGH] [S3_ASTAR_OBSTACLES] A* diagonal pinch bypass prevention: findPath returned path length=1. Cuts corner directly=true
- [MEDIUM] [S3_ASTAR_OBSTACLES] Long distance LoS (>120 tiles) step-limit false positive: hasLineOfSight for 130 tiles with wall at tile 125 returned true! Step counter loop bails out with 'return true' on step 120!
- [MEDIUM] [S4_COMBAT_EVASION] Clean recovery to Idle and action unlock: Anim after complete dodge=dodge, actionLocked=true
```

### Observation Details:

1. **`AStarPathfinder.ts:32-71` & `85-87` (Corner Cutting Bug)**:
   Lines 32-71 implement Bresenham line raycasting without orthogonal cell checks. When moving diagonally from `(10.5, 10.5)` to `(11.5, 11.5)` with walls at `(11, 10)` and `(10, 11)`:
   - `hasLineOfSight` returns `true` (observed: `losResult === true`).
   - Line 85: `if (this.hasLineOfSight(startWx, startWy, goalWx, goalWy, grid)) { return [{ x: goalWx, y: goalWy }]; }`
   - `findPath` bypasses A* search and returns `[{ x: 11.5, y: 11.5 }]`, cutting through the zero-clearance corner seam of the touching walls.
2. **`AStarPathfinder.ts:53-70` (Step Exhaustion LoS False Positive)**:
   Lines 53-70 run a loop `for (let step = 0; step < 120; step++)`. When `step` reaches 120 without reaching the destination (e.g. distance > 120 tiles), the function executes line 70:
   - `return true;`
   - Tested: Start `(0.5, 0.5)`, Goal `(130.5, 0.5)` with an impassable wall at `(125, 0)`.
   - Observed: `hasLineOfSight` returns `true`.
3. **`KinematicsEngine.ts:114-117` (Active Velocity Zeroing Bug)**:
   ```typescript
   this.wx = resolution.nextWx;
   this.wy = resolution.nextWy;
   this.velX = (resolution.nextWx - this.wx) / dt;
   this.velY = (resolution.nextWy - this.wy) / dt;
   ```
   `this.wx` is updated to `resolution.nextWx` before calculating `(resolution.nextWx - this.wx) / dt`.
   Observed: During active full-speed movement `(1.0, 0.0)`, `ke.velX === 0` and `ke.velY === 0`.
4. **`AnimationFSM.ts:83` (Single Frame Advance Under dt Lag)**:
   Lines 83-88: `if (this.frameTimer >= frameDuration)` only increments `currentFrame` once per call.
   Observed: Calling `updateAnimation(0.5)` on a 5-frame dodge animation (frame duration 0.0625s) only advances from frame 0 to frame 1. `currentState` remains `'dodge'` and `isActionLocked` remains `true`.
5. **Passing Observations**:
   - Zero-residual momentum in frame 0 is strict: `velX === 0.000`, `velY === 0.000`, 0 displacement, 0 drift over 100 frames, snap-freeze rotation angle holds without lerping.
   - Wall-sliding in concave 90° corners, 45° diagonal walls, 1-tile corridors, and V-wedges prevents clipping (`WallSliding.isPositionBlocked` is false across all 500+ frames tested).
   - Boss Gate state machine strictly enforces lock at 0/3, 1/3, and 2/3 packs (`DungeonWorld.isBlocked` returns `true`, kinematics blocked before gate tile), and unlocks into walkable floor at exactly 3/3 packs.
   - Rapid dodge during attack combo cancels attack animation into dodge, grants 0.25s i-frame, and rejects rapid spam.

---

## 2. Logic Chain

1. **From Observation 1**: In 2D grid pathfinding with entity radius $R = 0.35$, diagonal passage between two diagonally adjacent wall tiles `(tx+1, ty)` and `(tx, ty+1)` represents zero clearance ($0.0$). A player or monster entity cannot physically pass through this corner without overlapping the wall AABB.
2. In `AStarPathfinder.ts`, line 145 correctly recognizes this invariant for the A* search graph (`// Diagonal corner-cutting check: if (grid.isBlocked(cx + n.dx, cy) || grid.isBlocked(cx, cy + n.dy)) continue;`).
3. However, `AStarPathfinder.hasLineOfSight` (line 32) lacks this check. When `e2 > -dy && e2 < dx`, both `currX += sx` and `currY += sy` happen in a single step, skipping inspection of the orthogonal diagonal corners.
4. Because `findPath` (line 85) treats `hasLineOfSight` as a fast-path shortcut, any path request that passes through diagonal wall seams bypasses graph search and directly issues a waypoint cutting through the wall.
5. **From Observation 2**: Raycasting step exhaustion (`step >= 120`) represents failure to reach the goal within the checked budget, not proof of clear line of sight. Returning `true` at line 70 creates an adversarial exploit where distant entities ignore intervening terrain.
6. **From Observation 3**: Setting `this.wx = resolution.nextWx` immediately before `this.velX = (resolution.nextWx - this.wx) / dt` reduces the numerator to $x - x = 0.0$. This zeroes out velocity telemetry for all active client movement, corrupting server prediction reconciliations and motion-matched animation blending.
7. **From Observation 4**: In animation playback, `frameTimer` accumulation without multi-frame stepping (`while` or `Math.floor`) desynchronizes animation states during frame drops or GC pauses.

---

## 3. Caveats

- We did not modify any production code in `client/cocos/assets/scripts/` (strictly adhering to review-only directives).
- Cocos Creator Web Preview (`preview_app.js`) implements an independent movement loop which computes `velX = deltaX / dt` directly (so the web canvas preview masked the `KinematicsEngine.ts:116` bug). The bug specifically resides in the canonical `KinematicsEngine.ts` class.
- The A* pathfinding bug only affects paths where the start and destination have unobstructed visual ray through diagonal corner seams; normal orthogonal mazes are solved correctly (as proven by test 3.4 with 60 valid waypoints).

---

## 4. Conclusion

**Verdict: REQUEST_CHANGES**

The kinematics and world systems successfully pass 22 critical adversarial stress checks (including 0-residual momentum frame 0, wall-sliding corner traps, 0.25s i-frame cancellation, and exact 3-pack boss gate thresholds). However, **changes are required** to fix 2 HIGH-severity pathfinding bugs and 3 MEDIUM-severity logic bugs:

1. **[HIGH] Fix `AStarPathfinder.ts:32-71` Supercover Corner Check**: In `hasLineOfSight`, when a diagonal step occurs (`e2 > -dy && e2 < dx`), check both orthogonal neighbors `grid.isBlocked(currX + sx, currY)` and `grid.isBlocked(currX, currY + sy)` before advancing diagonally.
2. **[HIGH] Fix `AStarPathfinder.ts:70` Step Exhaustion Return**: Change `return true;` at line 70 to `return false;` (if goal was not reached within 120 steps, LoS is unconfirmed).
3. **[MEDIUM] Fix `KinematicsEngine.ts:114-117` Velocity Calculation Order**:
   ```typescript
   const prevWx = this.wx;
   const prevWy = this.wy;
   this.wx = resolution.nextWx;
   this.wy = resolution.nextWy;
   this.velX = (resolution.nextWx - prevWx) / dt;
   this.velY = (resolution.nextWy - prevWy) / dt;
   ```
4. **[MEDIUM] Fix `AnimationFSM.ts:83` Frame Stepping**: Replace `if (this.frameTimer >= frameDuration)` with a `while` loop (or frame increment by `Math.floor(this.frameTimer / frameDuration)`) to preserve sync during frame-time fluctuations.

---

## 5. Verification Method

To independently execute and verify these findings:

```bash
# Run the empirical adversarial stress test suite
npx tsx tests/unit/test_challenger_cocos_kinematics_stress.ts
```

**Invalidation Conditions**:
- If `pf.hasLineOfSight(10.5, 10.5, 11.5, 11.5, pinchGrid)` returns `false` and `pf.findPath` navigates around the diagonal corner, Bug 1 is resolved.
- If `pf.hasLineOfSight(0.5, 0.5, 130.5, 0.5, longGrid)` returns `false`, Bug 2 is resolved.
- If `ke.velX` evaluates to ~`5.5` during `ke.updateKinematics({ x: 1.0, y: 0.0 }, emptyGrid, 0.016)`, Bug 3 is resolved.
