← all games
action-roguelite-gpt-5-unknown — FRICTION.md
# Friction log
## 1. Scaffold installation did not finish in the command window
- Trying to do: scaffold the new game with the local `create-yage` CLI and let it install dependencies.
- What happened: the scaffold files were written, but dependency installation produced no completion output within the command window and no `node_modules` or lockfile appeared.
- Evidence: `create-yage games/action-roguelite-gpt-5-unknown --template minimal --features ui,save,effects --no-git --yes` created the starter files; a subsequent file listing showed no install artifacts.
- Workaround: keep the generated scaffold and run an explicit `npm install` after adding all required packages.
- Category: infra/test gap
## 2. Seeded scene RNG is absent from the LLM package prose
- Trying to do: map deterministic floor generation to a documented YAGE service.
- What happened: `RandomKey` is not described in `docs/llms/core-concepts.md` or the package LLM pages.
- Evidence: the public typed export in `../yage/packages/core/dist/index.d.ts` defines `RandomService`, `RandomKey`, and `createRandomService`; the local game-development skill also records this gap.
- Workaround: use the shipped public type declarations as the authoritative API and cite the exact declaration in `ENGINE-MAP.md`.
- Category: recipe gap
## 3. Complete active-device state is game-owned
- Trying to do: switch control hints based on the most recently used keyboard, pointer, or gamepad.
- What happened: `InputManager` exposes active-gamepad and hot-plug callbacks, but no one-call cross-device “last active device” value.
- Evidence: `getActivePad`, `onActivePadChanged`, `onGamepadConnected`, and `onGamepadDisconnected` are public; no equivalent all-device getter appears in the input public declarations.
- Workaround: combine active-pad callbacks with keyboard/pointer activity observed by the input router and serialize the resulting label for Inspector checks.
- Category: missing primitive
## 4. Current debug documentation is ahead of the published package
- Trying to do: step frozen browser frames while room and scene transitions resolve.
- What happened: the current debug documentation recommends `Inspector.time.stepAsync`, but `@yagejs/core@0.9.0` exposes only `freeze`, `thaw`, `step`, `setDelta`, `isFrozen`, and `getFrame` on `Inspector.time`.
- Evidence: `../yage/docs/llms/packages/debug.md` documents `stepAsync`; the browser probe enumerated the installed `Inspector.time` keys and found no async stepping method.
- Workaround: use normal browser frames for transition tests. Keep synchronous Inspector stepping inside sequences that do not destroy physics entities or cross asynchronous scene work.
- Category: infra/test gap
## 5. Removing several Rapier bodies in one frame can fail
- Trying to do: destroy every `room-local` entity at a room boundary and replace the run scene after death or extraction.
- What happened: repeated room transitions could throw `recursive use of an object detected which would lead to unsafe aliasing in rust`.
- Evidence: the browser stack ended in `PhysicsWorld.removeBody`, then `RigidBody.numColliders`, while a `WallEntity` or `EnemyEntity` was being destroyed. Three consecutive transitions reproduced the failure before the workaround.
- Workaround: `RoomDirector.retire()` teleports bodies outside the arena and destroys one queued entity per frame. Room creation and result-scene replacement wait for the retirement queue to empty. Three consecutive room cleanups, a successful run, and a failed run completed without the error after this change.
- Category: API friction
## 6. Current particle documentation is ahead of `@yagejs/particles@0.9.0`
- Trying to do: create zero-asset combat particles with a documented built-in shape.
- What happened: the current particle documentation accepts `shape` and zero-argument presets, but the installed `EmitterConfig` has no `shape` field and `ParticlePresets.sparks` requires a texture key.
- Evidence: `../yage/docs/llms/packages/particles.md` documents `shape`; `node_modules/@yagejs/particles/dist/index.d.ts` exposes the older texture-based preset contract.
- Workaround: create a five-pixel runtime texture through the renderer, register it once, and pass its key to `ParticlePresets.sparks`.
- Category: recipe gap
## 7. Steering facing is overwritten by Rapier rotation sync
- Trying to do: make enemy drawings face their travel direction through `PhysicsSteeringAgent({ faceHeading: true })`.
- What happened: the steering component wrote the heading to `Transform.rotation`, but the dynamic `RigidBodyComponent` copied its fixed Rapier rotation back to the same Transform. The enemy drawing remained at its authored angle.
- Evidence: rippers and enforcers had `faceHeading: true`, dynamic fixed-rotation bodies, and the default `syncRotation: true`. The steering documentation says `faceHeading` rotates the Transform, while the physics documentation describes rotation sync separately and does not warn about the conflict.
- Workaround: keep the Rapier root unrotated and rotate a child body drawing from a small facing component. This also keeps child status badges upright. A root visual can instead set `syncRotation: false`.
- Category: API friction
## 8. Room-door transitions need an arrival and input-rearm recipe
- Trying to do: enter a destination room at its connecting edge without immediately triggering another door.
- What happened: the first guard inferred an arrival edge from the origin exit and disabled only that arrival door. A direction held across the transition could remain active, cross a cleared destination, and trigger the next door without a new input action. A user also observed an arrival at the wrong edge.
- Evidence: the replacement resolves the destination door whose `destinationId` is the origin room, places both the Rapier body and Transform at that edge, and disables every destination door until movement returns to neutral. A browser probe held right while placing the player in consecutive east-door sensors: the run stayed in `r1-combat` until release, then entered `r2-combat` only after right was pressed again.
- Workaround: model arrival-door clearance and movement rearming as separate state. Resolve the arrival from the destination graph rather than deriving it only from the origin direction.
- Category: recipe gap
## 9. Interface requirements were not routed to the installed UI package
- Trying to do: map the HUD, menus, minimap, and world-anchored status indicators to YAGE capabilities before implementation.
- What happened: the scaffold installed `@yagejs/ui` and `@yagejs/ui-react`, but the capability matrix declared the interface game-specific and chose DOM overlays without inspecting or comparing the UI package. The implementation never registered `UIPlugin`. The missed `ScreenFollow` plus `UISurface` pattern became apparent only when later feedback asked for readable debuff icons attached to rotating enemies.
- Evidence: the scaffold command used `--features ui`; `package.json` includes both UI packages; the HUD/menu capability row in `ENGINE-MAP.md` cites renderer, input, core, and DOM rather than the UI documentation; `src/main.ts` does not register `UIPlugin`; and `src/features/ui/DomOverlay.ts` creates an HTML overlay. A source sweep of the 12 generated games found six that register `UIPlugin`, five that install `@yagejs/ui` without using it, and one that omits it and builds its interface from renderer primitives.
- Workaround: in the next generation pass, route HUD, menu, modal, health-bar, nameplate, badge, and world-indicator requirements to the `@yagejs/ui` documentation and relevant examples during capability mapping. Require `ENGINE-MAP.md` to record an explicit comparison before choosing YAGE UI, DOM, renderer primitives, or a combination. This finding is input to the prompt and recipe revision; it does not call for porting this generated game.
- Category: recipe gap