Files
Emberfall/HANDOFF.md
T
2026-10-21 01:10:23 +11:00

86 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HANDOFF.md — Session Handoff (OVERWRITE at the end of every phase or when context is getting long)
> **Read order for a new session:** `NOTES.md` → `STATUS.md` → this file → then work.
> If this file disagrees with the code, trust the code and fix this file.
## 1. Where we are
- **Last completed step:** 6.3 (Shop tests: sell equipped item rejection + concurrent double-buy safety) — TICKED in STATUS.md
- **Next step:** 7.1 — Migration `005_expeditions.sql`. Expedition definitions in seed data.
- **Updated:** 2026-10-21
- **Was the last session cut off mid-task?** No
## 2. Verified state (run these first)
| Command | Expected |
|---|---|
| `npm install` | succeeds |
| `npm run migrate` | applies 001–004 and 006 / "nothing to do" |
| `npm test` | **139 passing, 0 failing** (85 unit + 54 integration) |
## 3. What exists now (files that matter)
- `src/game/stats.js` — `calcDerived()` is the ONLY derived-stat formula. Also re-exports xpToNext/checkLevelUp.
- `src/game/combat.js` — `resolveCombat(hero, monster, rng)`; both sides attack each round, faster first.
- `src/services/huntService.js` — `hunt(characterId, zoneId, {rng, now})`; ONE transaction; test-injectable.
- `src/services/zoneService.js` — `listZones(characterLevel)` → zones with `unlocked` flag.
- `src/services/combatLogService.js` — `getCombatLog(characterId, limit)` → recent fights.
- `src/services/characterService.js` — `createCharacter`, `getCharacterProfile`, `getCharacterIdForUser`.
- `src/services/inventoryService.js` — `listInventory`, `equipItem`, `unequipItem`, `useConsumable`. Uses WeakMap stmt cache.
- `src/services/shopService.js` — `getStock`, `buyItem`, `sellItem`. Uses WeakMap stmt cache. Sell value = 40% of buy_price (materials use rarity default). **Equipped items rejected in sellItem (check moved before material type check).**
- `src/routes/shop.js` — `GET /api/shop`, `POST /api/shop/buy`, `POST /api/shop/sell`.
- `src/routes/inventory.js` — `GET /api/inventory`, `POST /api/inventory/equip`, `POST /api/inventory/unequip`, `POST /api/inventory/use`.
- `src/utils/time.js` — `now()/setNow()/resetNow()`; never call `Date.now()` in services.
- `src/routes/zones.js` — `GET /api/zones`.
- `src/routes/combatLog.js` — `GET /api/combat-log`.
- `src/db/migrations/003_content.sql` — also SEEDS zones/monsters/items/loot (INSERT OR IGNORE).
- `src/db/migrations/004_inventory.sql` — inventory table, FK constraints, partial unique index for equipped slots.
- `tests/integration/shop.test.js` — 16 integration tests (stock, level gating, buy, sell, equipped rejection, concurrent double-buy, round-trip).
- `tests/integration/shop-page.test.js` — 5 integration tests (stock listing, levelOk, sell pricing, buy/sell round-trip).
- `tests/integration/inventory.test.js` — 20 integration tests (13 equip/unequip + 7 consumable).
- `public/shop.html` — dedicated shop page with buy/sell tabs.
- `public/js/shop.js` — shop page JS: render stock, render materials, buy/sell handlers, tab switching, auth guard.
- `public/game.html` — dashboard with stats panel, tabbed action panel (Hunt/Inventory), equipment panel, inventory grid.
- `public/js/game.js` — full hunt UI + inventory UI.
- `public/css/main.css` — zone cards, combat log, result panel, toast styles, tabs, equipment panel, inventory grid, tooltips, rarity colors.
## 4. Half-finished / fragile things
- No shop link in game.html top-bar navigation (users must navigate manually to /shop.html).
- No expedition UI yet (Phase 7).
- No leaderboard UI yet (Phase 8).
- HP never regenerates naturally; only level-up restores it.
- No restart-persistence test for sessions.
- `npm run seed` script in package.json points to nonexistent `scripts/seed.js`.
- Equipment bonuses not yet passed into combat (Issue #5 in STATUS.md).
- Step 7.1: need migration `005_expeditions.sql` with expedition table + seed data.
## 5. Decisions & deviations from the docs
- D-004..D-007 in `docs/DECISIONS.md` (combat rounds, equipment counted once, session store, clock).
- Zod query param parsing uses `zod.coerce.number()` because Express query params are strings.
- Inventory response uses `itemId` (item.id) and `inventoryId` (inventory row id) to avoid confusion.
- Consumable effect parsing: regex `^(heal|energy):(\d+)$` on items.effect field.
- Qty decrement avoids CHECK constraint violation: delete directly when qty==1, decrement when qty>1.
- UI uses emoji icons (⚔️🛡️💍🧪📦) for item types per UI_GUIDE recommendation.
- Rarity colors match UI_GUIDE spec (common #b8b0a3, uncommon #5fb36b, rare #4a8fe0, epic #a45ee0, legendary #f2a23a).
- **D-008:** Shop materials with buy_price=0 sell at `floor(default_rate * character_level / item_min_level)` where default_rate is common=2, uncommon=4, rare=8, epic=16, legendary=32 (min 1).
- **D-009:** `sellItem` checks equipped_slot before material type, so equipped items are always rejected regardless of item type.
## 6. Gotchas learned
- `npm test` must be `node --test` with NO path/glob; files must be named `*.test.js`. A shell glob once skipped 17 tests.
- better-sqlite3-session-store ignores `clear:false`; that's why the timer is unref'd.
- Statement caches are per-DB-handle (WeakMap) in huntService; copy that pattern for new services.
- Verify test COUNT, not just "all passing".
- Node.js `fetch` doesn't persist cookies across calls — extract `Set-Cookie` header and pass manually in integration tests.
- Express query params are strings, not numbers — use `zod.coerce.number()` for numeric query params.
- SQL `SELECT id` from inventory returns the auto-increment row id; rename with `AS inventory_id` when you also need `item_id`.
- `itemInSlot` query must alias `id AS inventory_id` so `existing.inventory_id` works in unequip logic.
- Transaction callback args must match the call order exactly — passing `qty` as the second arg when the callback expects `etype` silently misapplies the effect.
- Error responses from route-level `AppError`s do NOT include `data` field — only `{ ok: false, error: { code, message } }`.
## 7. Next-step briefing (7.1)
- **Goal:** Migration `005_expeditions.sql` — create expeditions table and seed expedition definitions.
- **Docs:** GAME_DESIGN.md §Expeditions, DATABASE.md.
- **Files to create:** `src/db/migrations/005_expeditions.sql`.
- **Key schema:** expeditions table (id, character_id, expedition_id, started_at, ends_at, claimed boolean FK constraints). Only one active expedition per hero at a time.
- **Seed data:** expedition definitions in the same migration (INSERT OR IGNORE).
## 8. Open Known Issues
STATUS.md issues 1–6 (balance, dangling `seed` script, restart test, equipment-in-combat, HP regen, zone min_level balance).