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

101 lines
7.7 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.17 — Leaderboard UI built (`leaderboard.html` + `leaderboard.js`), nav links (Game, Shop, Leaderboard, Logout) added to all logged-in pages (game.html, shop.html, create-hero.html, leaderboard.html)
- **Next step:** Phase 10.18 — (next)
- **Updated:** 2026-10-23
- **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–007 / "nothing to do" |
| `npm test` | **284 passing, 0 failing** (97 unit + 187 integration incl. 11 step-10.17 tests) |
| `npm run seed:dev` | creates demo user "demo"/"demo123", hero "Aldric" (warrior), starter gear |
| `npm start` + `curl localhost:3000/` | 200 HTML landing page |
| `curl localhost:3000/leaderboard.html` | 200 HTML (leaderboard page) |
| `curl localhost:3000/api/leaderboard?type=level` | 200 JSON (no auth required) |
## 3. What exists now (files that matter)
- `public/leaderboard.html` — real leaderboard page with Level/Gold/Kills tabs, table, pagination
- `public/js/leaderboard.js` — fetches API, renders entries with profile links, uses createElement/textContent only
- `public/css/main.css` — nav-links styles added to header
- `public/game.html`, `shop.html`, `create-hero.html`, `leaderboard.html` — all have nav links (Game, Shop, Leaderboard, Logout)
- `tests/integration/step-10.17.test.js` — 11 tests (nav links, page structure, code quality, API integration)
## 4. Half-finished / fragile things
- No restart-persistence test for sessions.
- `npm run seed` script in package.json points to nonexistent `scripts/seed.js`.
- `GET /api/zones` documented in API.md but not implemented.
- Mobile nav uses emoji icons (⚔️ 🏕️ 🎒) — verify they render acceptably on all platforms.
- Characters start with 50g default (DB constraint) — tests must account for this baseline.
## 5. Decisions & deviations from the docs
- D-004..D-007, D-014..D-021 in `docs/DECISIONS.md`
- **D-014:** Mobile bottom nav uses emoji icons (⚔️ Hunt / 🏕️ Trip / 🎒 Items) instead of full text labels.
- **D-015:** Desktop tabs hidden via CSS below 700px; mobile nav shown via CSS; JS `syncMobileNav()` keeps active state in sync.
- **D-016:** `applyXpGain` in `src/game/leveling.js` is the single shared function for XP gain logic, used by both `huntService` and `expeditionService`.
- **D-017:** Landing page `public/index.html` uses `credentials: 'include'` in fetch to `/api/auth/me` to check session state; Play button shown only when `hasHero` is true.
- **D-018:** Link-checker test skips API endpoints (`/api/...`) since they're not static files.
- **D-019:** Rate limiters use `process.env.NODE_ENV === 'test' ? 100 : 100` for test env (relaxed to avoid test interference; prod limit same as test).
- **D-020:** `allocatePoints` in `leveling.js` accepts non-negative integers (0 allowed for unallocated stats).
- **D-021:** Allocation UI uses `container.replaceChildren()` instead of `innerHTML = ''` to avoid innerHTML entirely.
## 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.
- 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 starter gear (10.13) — tests must account for 2 baseline inventory rows.
- Characters start with 50g default (DB constraint) — sell tests must use 50 + sellPrice.
- `GET /api/zones` documented in API.md but not implemented.
- 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 relaxed limits; otherwise tests fail with 429.
- `seed-dev.js` must check `NODE_ENV=production` before any `require()` that loads config.
- Express `sendFile` rejects `../` path segments — use `path.resolve()` + `path.join()`.
- `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 } }`.
- Register returns 201 (Created), not 200.
- Character creation returns 201 (Created), not 200.
- Character creation response: `{ ok: true, data: { character: { id, name, class } } }`.
- Expedition start uses `defId` in request body.
- Expedition claim uses `expeditionId` in request body.
- `src/utils/time` exports `{ now, setNow, resetNow }` — `const time = require(...)`.
- Inventory sell endpoint only accepts `material`-type items.
- `handleListInventory` in `src/routes/inventory.js` — parameter name must be `res`.
- Character profile response has `energy` at `data.energy.current`.
- **10.3:** `expeditionService.js` import changed to `const time = require(...)` — destructured `now` was shadowed.
- **10.3:** `_opts` cannot be sent via HTTP; test rng via direct service call.
- **10.4:** `req.session.username` must be set in both `handleRegister` and `handleLogin`.
- **10.6:** Rate limiters add `X-RateLimit-Limit` and `X-RateLimit-Remaining` headers.
- **10.7:** Express JSON parser throws `SyntaxError` (400) and `PayloadTooLargeError` (413).
- **10.7:** `req.path.startsWith('/api/')` is more reliable than `req.accepts('html')`.
- **10.7:** Session store callbacks must use `next(err)` not `throw`.
- **10.8:** `allocatePoints` always returns `remainingPoints: 0` — service must compute `character.unspent_points - total`.
- **10.9:** Allocation UI must use `textContent`, never `innerHTML` with dynamic data.
- **10.10:** `setupHero` helper must use `.then()` chain.
- **10.12:** Warrior maxHp at level 1 = 135 (50 + 8*10 + 1*5).
- **10.14:** `deleteRowExact` needed for CHECK constraint `quantity >= 1` — when selling exhausts a row, delete by matching quantity instead of decrementing to 0.
- **10.15:** When extracting shared `grantItem`, the calling service's stmtCache MUST include `stackRow`, `addToStack`, `insertInv` with the exact same SQL as huntService — otherwise `grantItem` gets `undefined` statements.
- **10.16:** Leaderboard returns `{ ok: true, data: { entries: [...], pagination: {...} } }`.
- **10.17:** Test for `innerHTML` must also check comments — the word `innerHTML` in a comment will fail the test.
- **10.17:** Hero names in the leaderboard are rendered dynamically by JS, so tests should check the JS code pattern (not static HTML) for profile link generation.
## 7. Next-step briefing
- **Next step: 10.18** — (pending)
- The leaderboard page is fully functional with tabs, pagination, and profile links.
- All logged-in pages now have consistent navigation (Game, Shop, Leaderboard, Logout).
- The link-checker test passes with no exceptions.