Files
Emberfall/HANDOFF.md
T
2026-10-22 22:55:24 +11:00

110 lines
9.2 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:** 10.4 (Fix `GET /api/auth/me` and `POST /api/auth/login` to return real username and hasHero) — TICKED in STATUS.md
- **Next step:** Phase 10.5 — Add a landing page `public/index.html` (title, one-line tagline, Login/Register buttons; "Play" button if already logged in with a hero). Fix the "Return to Emberfall" links on the 404/500 pages. Add a link-checker test.
- **Updated:** 2026-10-22
- **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–006 / "nothing to do" |
| `npm test` | **187 passing, 0 failing** (85 unit + 99 integration incl. 3 new 10.4 tests) |
| `npm run seed:dev` | creates demo user "demo"/"demo123", hero "Aldric" (warrior), starter gear |
| `npm start` + `curl localhost:3000/api/health` | `{"ok":true}` |
## 3. What exists now (files that matter)
- `public/game.html` — Dashboard with 3-col grid (stats | action | combat), mobile bottom nav (⚔️ Hunt / 🏕️ Trip / 🎒 Items), tabbed action area.
- `public/js/game.js` — hunt UI + inventory UI + expedition UI + mobile nav init (DOMContentLoaded); uses shared toast.js.
- `public/css/main.css` — All styles including 3-col desktop grid, mobile bottom nav (< 700px), stacked panels on mobile, spinner, toast, button loading states.
- `public/404.html` — Custom 404 error page.
- `public/500.html` — Custom 500 error page.
- `public/js/toast.js` — Shared toast notification system.
- `public/shop.html` / `public/js/shop.js` — Shop page (buy/sell).
- `public/login.html`, `public/register.html`, `public/create-hero.html` — Auth & hero creation.
- `public/profile.html` / `public/js/profile.js` — Public hero profile page.
- `public/js/auth.js` — Shared API wrapper + showError.
- `public/js/login.js` — Login form submits to `/api/auth/login`; on success uses `data.hasHero` to redirect to `/game.html` or `/create-hero.html`.
- `public/js/create-hero.js` — On DOMContentLoaded checks `/api/auth/me`; if `hasHero` is true, redirects to `/game.html`. Otherwise shows hero creation form.
- `scripts/seed-dev.js` — Creates demo account + hero + starter gear.
- `src/app.js` — Express app with helmet, rate limiters, catch-all 404, 500 HTML serving.
- `src/services/expeditionService.js` — Fixed in 10.1 (energy tx params) + 10.2 (uses `applyXpGain`) + 10.3 (injectable `opts.now`/`opts.rng`).
- `src/services/huntService.js` — Refactored in 10.2 to use `applyXpGain` instead of inline `checkLevelUp`.
- `src/game/leveling.js` — Added `applyXpGain` shared function (used by huntService + expeditionService).
- `src/routes/expeditions.js` — Route handler passes `req.body._opts` to `claimExpedition`.
- `src/utils/time.js` — `{ now, setNow, resetNow }` — `const time = require(...)`, not destructured.
- `src/utils/rng.js` — `{ mulberry32, rngInt, rngFloat, rngRange }` — Mulberry32 PRNG.
- `src/routes/auth.js` — `handleRegister` and `handleLogin` now set `req.session.username` in addition to `req.session.userId`. `handleMe` reads `req.session.username`.
- `tests/integration/expeditions.test.js` — 22 expedition tests (incl. 2 new 10.3 tests).
- `tests/integration/*.test.js` — 102 integration tests (incl. 3 new 10.4 tests).
- `tests/unit/*.test.js` — 85 unit tests.
- `README.md` — Updated with real run instructions, play-through steps, project structure, known issues.
## 4. Half-finished / fragile things
- No leaderboard UI yet (Phase 8 API done but no UI).
- 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).
- Starter gear documented but not auto-created on character creation.
- `GET /api/zones` documented in API.md but not implemented (Issue #2).
- Mobile nav uses emoji icons (⚔️ 🏕️ 🎒) — verify they render acceptably on all platforms.
- **10.5 next:** Need a landing page at `public/index.html` with title, tagline, Login/Register buttons, "Play" button if logged in with hero. Fix "Return to Emberfall" links on 404/500 pages. Add link-checker test.
## 5. Decisions & deviations from the docs
- D-004..D-007 in `docs/DECISIONS.md` (combat rounds, equipment counted once, session store, clock).
- **D-014:** Mobile bottom nav uses emoji icons (⚔️ Hunt / 🏕️ Trip / 🎒 Items) instead of full text labels to save horizontal space on narrow screens.
- **D-015:** Desktop tabs hidden via CSS (`display: none`) below 700px; mobile nav shown via CSS; JS `syncMobileNav()` keeps active state in sync.
- 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.
- **D-016:** `applyXpGain` in `src/game/leveling.js` is the single shared function for XP gain logic, used by both `huntService` and `expeditionService`. Encapsulates `checkLevelUp` + stat point accumulation.
## 6. Gotchas learned
- `npm test` must be `node --test` with NO path/glob; files must be named `*.test.js`.
- better-sqlite3-session-store ignores `expired.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.
- SQL `SELECT id` from inventory returns the auto-increment row id; rename with `AS inventory_id` when you also need `item_id`.
- Transaction callback args must match the call order exactly. (This is the 10.1 bug — double-check param/call order in every new transaction.)
- Error responses from route-level `AppError`s do NOT include `data` field.
- `setNow` affects ALL `now()` calls including those in service logic.
- Shop API returns `{ ok: true, data: { stock: [...], character: {...} } }` — field is `stock`, not `stocks`.
- Shop stock items have flat fields (`itemId`, `buyPrice`), not nested (`item.id`).
- New characters have NO inventory items (starter gear not auto-created yet).
- Hero profile endpoint is PUBLIC — no `requireAuth` middleware.
- Profile page is public: no auth.js guard needed; uses ?name= query param for lookup.
- Rate limiters in tests: set `NODE_ENV=test` to get 1000 req/window limit; otherwise tests fail with 429.
- `seed-dev.js` must check `NODE_ENV=production` before any `require()` that loads config (config throws on placeholder SECRET).
- Express `sendFile` rejects `../` path segments — use `path.resolve()` + `path.join()` for absolute paths.
- `express.static` sends its own 404 responses; catch-all must be placed BEFORE static middleware.
- Character profile GET returns `{ ok: true, data: { character, derived, energy } }` — fields nested under `character`, `derived`, `energy`.
- Register returns 201 (Created), not 200.
- Character creation returns 201 (Created), not 200.
- Expedition start uses `defId` in request body (not `expeditionId`).
- Expedition claim uses `expeditionId` in request body.
- Expedition start response returns `data.expedition.id` — capture this for claim.
- `src/utils/time` exports `{ now, setNow, resetNow }` directly — `const time = require(...)`, not `const { time }`.
- Inventory sell endpoint (`POST /api/shop/sell`) only accepts `material`-type items.
- `handleListInventory` in `src/routes/inventory.js` — parameter name must be `res`, not `_res`.
- Character profile response has `energy` at `data.energy.current`, NOT `data.character.energy`.
- **10.3:** `expeditionService.js` import changed from `const { now, setNow } = require('../utils/time')` to `const time = require('../utils/time')` — the destructured `now` variable was shadowed by the local `opts.now ?? time.now()` pattern.
- **10.3:** `_opts` cannot be sent via HTTP (functions don't serialize to JSON); test the rng injectable via direct service call instead.
- **10.4:** `req.session.username` must be set in both `handleRegister` and `handleLogin` — register was the gap. `/me` reads it, so without it username is always empty string.
## 7. Next-step briefing
- **Next step: 10.5** — Create `public/index.html` (landing page):
- Title + tagline + Login/Register buttons (when not logged in)
- "Play" button linking to `/game.html` (when logged in with a hero)
- Can use `GET /api/auth/me` to detect logged-in state
- Fix "Return to Emberfall" links on 404.html and 500.html (ensure they point to `/` or `/index.html`)
- Add a link-checker test: read all `public/*.html`, extract local `href`/`src` values, assert each target file exists
- Note: this will also catch `/leaderboard.html` until 10.17 is done — either create a stub page or allow that one known gap