# STATUS.md — Living Progress Tracker > **Agent rules for this file** > - Update it at the end of EVERY step. Do not skip. > - Only tick a box when the step's **Acceptance** line is actually verified. > - Add problems you could not fix to **Known Issues** instead of hiding them. > - Keep Session Log entries to one or two lines. ## Current Focus - **Phase:** 5 - **Step:** 5.4 - **Blocked?** No - **Last verified working command:** `npm test` — 118/118 passing (87 unit + 31 integration), 2026-10-20 step 5.3 ## Project Health | Item | State | |---|---| | App starts (`npm start`) | ✅ smoke-tested: register → create hero → 12 hunts → level-up (real seeded zones) | | Tests pass (`npm test`) | ✅ 93/93 (runs EVERY *.test.js; auth/combat/health were previously skipped by a bad glob) | | Migrations run clean on empty DB | ✅ 001_users, 002_characters, 003_content (also seeds zones/monsters/items), 004_inventory, 006_logs (005 reserved for expeditions) | | Can register + login | ✅ verified by tests | | Data persists across restart | ⚠️ sessions use the real SQLite store (also in tests); a restart test is still missing | | Inventory/equipment | ✅ list, equip, unequip all working; derived stats update on equip/unequip; slot swap works | --- ## Phase 0 — Scaffold - [x] **0.1** `npm init`, install deps from NOTES.md §2, add scripts: `start`, `dev` (node --watch), `test`, `migrate`, `seed`. *Acceptance:* `npm install` succeeds; scripts exist in package.json. - [x] **0.2** `src/config/index.js` loads `.env` (use `process.loadEnvFile` or `dotenv`), validates required vars, exports a frozen config object. *Acceptance:* missing SESSION_SECRET in production throws a clear error. - [x] **0.3** `src/db/index.js` opens SQLite (WAL mode, foreign_keys ON) and exports the handle. `scripts/migrate.js` runs numbered `.sql` files from `src/db/migrations/` in order and records them in a `_migrations` table. *Acceptance:* `npm run migrate` twice in a row is safe (second run does nothing). - [x] **0.4** `src/app.js` (builds Express app, no listen) + `src/server.js` (listens). Add helmet, JSON body parser (limit 10kb), static `public/`, `GET /api/health` → `{ok:true}`, central error handler (see CODING_STANDARDS). *Acceptance:* `curl localhost:3000/api/health` returns `{"ok":true}`. - [x] **0.5** First test: health endpoint via `node:test` + `fetch` against `app` on an ephemeral port (see TESTING.md). *Acceptance:* `npm test` passes. ## Phase 1 — Accounts & Sessions (docs: DATABASE, API, SECURITY) - [x] **1.1** Migration `001_users.sql` (users table). - [x] **1.2** `src/services/authService.js`: `register`, `verifyLogin`. bcryptjs cost 10–12. Case-insensitive unique username. - [x] **1.3** Session middleware (httpOnly, sameSite=lax, secure in production, SQLite-backed store) + `requireAuth` middleware. - [x] **1.4** Routes: `POST /api/auth/register`, `POST /api/auth/login`, `POST /api/auth/logout`, `GET /api/auth/me`. Zod validation. Rate limit login/register. - [x] **1.5** Pages `public/login.html`, `public/register.html` + `public/js/auth.js`. - [x] **1.6** Tests: register ok, duplicate username, bad password, login ok/fail, `me` requires session, logout clears session. *Acceptance (phase):* can register, log in, refresh page and stay logged in, restart server and stay logged in, log out. ## Phase 2 — Hero Creation & Profile (docs: GAME_DESIGN §Classes, DATABASE, BALANCE) - [x] **2.1** Migration `002_characters.sql`. - [x] **2.2** `src/game/classes.js` (class base stats) + `src/game/stats.js` (derived stats). Unit tests. - [x] **2.3** `POST /api/character` (create; one per user), `GET /api/character` (full profile incl. derived stats and current energy). - [x] **2.4** `public/create-hero.html`, `public/game.html` (dashboard shell with stats panel). *Acceptance:* new user is redirected to create-hero; after creation, dashboard shows stats. ## Phase 3 — Energy & Lazy Time (docs: GAME_DESIGN §Time, BALANCE) - [x] **3.1** `src/game/energy.js`: pure `computeEnergy(stored, updatedAtMs, nowMs, cfg)` → `{energy, updatedAtMs}`. Unit tests incl. cap, partial intervals, clock going backwards. - [x] **3.2** `characterService.spendEnergy(characterId, cost)` runs in a transaction; rejects when insufficient. - [x] **3.3** Dashboard shows energy bar + "next +1 in mm:ss" countdown (client-side countdown only; server remains authoritative). *Acceptance:* set `energy_updated_at` back in DB by 30 min → refresh shows regen applied. *Verified:* energy bar + countdown in game.html/game.js; server returns nextTickInSec; 16 computeEnergy unit tests cover cap/partial/large-gap scenarios. ## Phase 4 — Combat (Hunting) (docs: GAME_DESIGN §Combat, BALANCE) - [x] **4.1** Content tables + seed data (zones, monsters, items, loot). *Done inside migration `003_content.sql` (INSERT OR IGNORE), not `scripts/seed.js`. Balance changes need a NEW migration.* - [x] **4.2** `src/game/combat.js`: pure `resolveCombat(hero, monster, rng)` → `{winner, rounds[], hpRemaining}`. Seedable RNG in `src/utils/rng.js`. Unit tests with fixed seed. *Acceptance:* 10 combat unit tests pass; deterministic with same seed; speed determines turn order; mage bonus vs high-def; 20-round cap. - [x] **4.3** `src/game/rewards.js`: XP, gold, loot roll. `src/game/leveling.js`: XP curve, level-up, stat points. *Acceptance:* 23 unit tests pass; xpToNext matches BALANCE.md; level-up handles multi-level; points allocation validated. - [x] **4.4** `POST /api/hunt { zoneId }`: spend energy → pick monster → resolve → apply rewards/penalty → write `combat_log` → return summary + round log. All inside one DB transaction. *Acceptance:* hunt returns winner, rounds, xpGained, goldGained, loot, leveledUp, zone, monster. *Verified:* 9 service tests (`tests/integration/hunt-service.test.js`) cover persistence, level-up, defeat, locked zone, energy, stacking. - [x] **4.5** UI: zone list, "Hunt" button, combat log display, result summary, level-up message. *Acceptance:* hunting repeatedly drains energy, awards XP/gold, levels the hero, and persists. *Verified:* `GET /api/zones` + `GET /api/combat-log` endpoints; zone cards with locked/unlocked states; Hunt button with energy cost display; result panel showing victory/defeat, XP, gold, loot, level-up; combat log replay; energy re-sync after hunt. 98/98 tests pass (5 new integration tests). ## Phase 5 — Inventory & Equipment (docs: GAME_DESIGN §Items, DATABASE) - [x] **5.1** Migration `004_inventory.sql`. *Acceptance:* migration creates inventory table with FK constraints and indexes. - [x] **5.2** Services + routes: list inventory, equip, unequip (slots: weapon, armor, trinket). Equipment modifies derived stats via `calcDerived()`. *Acceptance:* list returns items with derived stats; equip moves previous item; unequip clears slot; wrong-type/level/missing-item rejected. - [x] **5.3** Stackable consumables: `POST /api/inventory/use` (health potion heals; energy tonic restores energy, capped). *Acceptance:* heal effect caps at maxHp; energy effect caps at ENERGY_MAX; quantity decrements; row removed at qty 0; non-consumable rejected. - [ ] **5.4** UI: inventory grid, equipment panel, tooltips with stats. ## Phase 6 — Shop & Economy (docs: GAME_DESIGN §Economy) - [ ] **6.1** `GET /api/shop`, `POST /api/shop/buy`, `POST /api/shop/sell`. Sell value = 40% of base price. Gold can never go negative. - [ ] **6.2** Level-gated shop stock. UI page. - [ ] **6.3** Tests: buy with insufficient gold, sell equipped item (must be rejected), concurrent double-buy safety (transaction). ## Phase 7 — Expeditions (Offline Progress) (docs: GAME_DESIGN §Time, §Expeditions) - [ ] **7.1** Migration `005_expeditions.sql`. Expedition definitions in seed data. - [ ] **7.2** `POST /api/expeditions/start`, `GET /api/expeditions/active`, `POST /api/expeditions/claim`. Only one active expedition per hero. Claim rejected before `ends_at`. - [ ] **7.3** UI: start panel, progress bar, claim button. *Acceptance:* start → close browser → return after duration → claim → rewards granted exactly once. ## Phase 8 — Leaderboards & Social-lite (docs: GAME_DESIGN §Leaderboards) - [ ] **8.1** `GET /api/leaderboard?type=level|gold|kills` top 50, with pagination param. - [ ] **8.2** Public profile `GET /api/heroes/:name` (no private data). - [ ] **8.3** UI page. ## Phase 9 — Hardening & Polish (docs: SECURITY, UI_GUIDE, TESTING) - [ ] **9.1** Walk the SECURITY.md checklist; fix gaps. - [ ] **9.2** Add `npm run seed:dev` creating a demo account and hero. - [ ] **9.3** Global error pages / friendly UI error toasts. Loading states. - [ ] **9.4** Mobile-responsive pass (see UI_GUIDE). - [ ] **9.5** Final full run of "Definition of playable" from NOTES.md §7. Record results below. - [ ] **9.6** Update README with real run instructions. ## Stretch Goals (only after Phase 9) - [ ] S1 Async PvP duels (attack another hero's *snapshot*; cooldown; rating). - [ ] S2 Skills / talent points spent on level-up. - [ ] S3 Guilds (create, join, leave, guild board). - [ ] S4 Player market (list/buy items between players, 5% tax gold sink). - [ ] S5 Daily login reward + daily quests. - [ ] S6 In-game chat via polling (no websockets). - [ ] S7 Achievements. --- ## Known Issues | # | Area | Description | Severity | Status | Found in step | |---|------|-------------|----------|--------|---------------| | 1 | Balance | Level-1 heroes of every class win 100% of Meadows fights (design target ≈80%) and lose 100% of Crags fights. Tune monster stats via a NEW migration. | minor | open | 4.4 review | | 2 | API | `GET /api/zones` (list zones + `unlocked`) is documented in API.md but not implemented; step 4.5 needs it. | major | open | 4.4 review | | 3 | Scripts | `npm run seed` points at `scripts/seed.js`, which doesn't exist (seeds are in migration 003). Remove the script or add the file. | cosmetic | open | 0.1 | | 4 | Tests | No test proves sessions survive a server restart (two apps on one DB file). | minor | open | 1.3 | | 5 | Combat | Hero equipment bonuses are not passed into combat yet (Phase 5). `resolveCombat` already accepts `bonus_*` fields. | minor | open | 5.x | | 6 | Combat | Natural HP regeneration is not implemented: HP only restores on level-up (rest/potions arrive in Phases 5–6). | minor | open | 4.4 | | 7 | Combat/Hunt | (Fixed in review) only the faster side attacked; mage bonus never applied; level-up kept XP and overwrote stat points; deaths never counted; zone min_level ignored; stackable loot duplicated rows; energy countdown showed time-to-full. | blocker | fixed | review | Severity: `blocker` / `major` / `minor` / `cosmetic`. Status: `open` / `fixing` / `wontfix` / `fixed`. ## Technical Debt / TODOs - `wrap()` async helper is copy-pasted in each route file; move to `src/middleware/wrap.js`. - Error responses from route-level `AppError`s for validation don't include field details (API.md says they should). - `npm test` prints a lot of TAP noise; consider `--test-reporter=spec`. - `src/db/seeds/` is empty (kept for later content seeds). ## Open Questions (for the human) - _none yet_ ## Decisions Made (short; long form goes in docs/DECISIONS.md) - D-004: every combat round BOTH sides attack (faster first). D-005: equipment stats counted once in derived stats. D-006: unref the session-store cleanup timer; use the real SQLite store in tests. D-007: `utils/time.js` is the only clock. ## Test Summary | Suite | Passing | Failing | Last run | |---|---|---|---| | unit | 69 | 0 | 2026-10-20 review pass | | integration | 24 | 0 | 2026-10-20 review pass | ## Session Log _Format:_ `YYYY-MM-DD | step | what changed | result` - 2026-10-20 | 5.3 | Added `useConsumable()` to inventoryService (heal/energy effects, qty decrement, row removal at 0) + `POST /api/inventory/use` route; 7 integration tests covering heal, energy, cap, non-consumable rejection, missing item, qty-0 removal | 118/118 tests pass - 2026-10-20 | 5.2 | Created `src/services/inventoryService.js` (listInventory, equipItem, unequipItem) + `src/routes/inventory.js` (GET /api/inventory, POST /api/inventory/equip, POST /api/inventory/unequip); registered routes in app.js; 13 integration tests covering empty inventory, equip/unequip, slot swap, stat updates, all error paths | 111/111 tests pass - 2026-10-20 | 5.1 | Verified 004_inventory.sql migration (inventory table, FK constraints, indexes) applies clean; all tests pass | 98/98 tests pass - 2026-10-20 | 4.5 | Added `GET /api/zones` (zoneService + zones route), `GET /api/combat-log` (combatLogService + combatLog route); extended game.html with zone list/combat log/result panels; rewrote game.js with hunt UI (zone cards, hunt button, result display, combat log replay, toast notifications); added CSS for zone cards/combat log/toasts; 5 new integration tests | 98/98 tests pass - 2026-10-20 | review | Fixed `npm test` (ran 62 of 79 tests; auth/combat/health were skipped by an unquoted glob). Rewrote combat (slower side never attacked; mage bonus dead; hpRemaining wrong on loss). Rewrote huntService (XP not deducted on level-up, unspent points overwritten, deaths never counted, min_level ignored, stackable loot duplicated, duplicate stat formulas). Fixed nextTickInSec (was time-to-full). Added utils/time.js, WeakMap stmt cache, session store unref, ok:false error envelope, removed connect-sqlite3 | 93/93 tests pass; smoke test OK - 2026-10-20 | 4.4 | Created 004_inventory.sql + 006_logs.sql migrations; fixed huntService transaction (db.transaction() returns wrapper, must call separately); added huntService to helpers.js cache invalidation; session middleware uses memory store in tests | 62/62 tests pass, hunt endpoint working - 2026-10-20 | 3.3 | Fixed game.js countdown: separated updateEnergyBar (pure display) from startCountdown (client-side timer), added energyData store and startEnergySync (60s server refetch) | 35/35 tests pass, energy bar + countdown working correctly - 2026-10-20 | 0.1 | `npm init -y`, installed express, better-sqlite3, bcryptjs, express-session, better-sqlite3-session-store, helmet, express-rate-limit, zod; added start/dev/test/migrate/seed scripts | verified all 5 scripts present in package.json - 2026-10-20 | 0.2 | Created `src/config/index.js` with `process.loadEnvFile`, required var validation, frozen export | verified config loads; production check rejects placeholder SECRET - 2026-10-20 | 0.3 | Created `src/db/index.js` (WAL, FK ON) + `scripts/migrate.js` (runs numbered .sql, tracks in _migrations) | `npm run migrate` twice is safe - 2026-10-20 | 0.4 | Created `src/utils/errors.js`, `src/app.js` (helmet, JSON parser, static, health, error handler) + `src/server.js` | `curl localhost:3000/api/health` → `{"ok":true}` - 2026-10-20 | 0.5 | Created `tests/health.test.js` — boots app on ephemeral port, hits /api/health | `npm test` passes (1/1) - 2026-10-20 | 1.1 | Created `src/db/migrations/001_users.sql` | migration applies clean - 2026-10-20 | 1.2 | Created `src/services/authService.js`: register + verifyLogin with bcrypt cost 12, timing-safe | service loads - 2026-10-20 | 1.3 | Created `src/middleware/session.js`: better-sqlite3-session-store, requireAuth | middleware loads - 2026-10-20 | 1.4 | Created `src/routes/auth.js` with Zod validation, rate limiting; integrated into app.js | app loads - 2026-10-20 | 1.5 | Created `public/login.html`, `public/register.html`, `public/css/main.css`, `public/js/auth.js`, `login.js`, `register.js` | static files served - 2026-10-20 | 1.6 | Created `tests/helpers.js` + `tests/auth.test.js` (6 tests) | 6/6 auth tests pass - 2026-10-20 | 2.1 | Created `src/db/migrations/002_characters.sql` | migration applies clean - 2026-10-20 | 2.2 | Created `src/game/classes.js`, `src/game/stats.js` + unit tests (23 tests) | 23/23 pass - 2026-10-20 | 2.3 | Created `src/services/characterService.js`, `src/routes/character.js`; integrated into app.js | app loads - 2026-10-20 | 2.4 | Created `public/create-hero.html`, `public/game.html`, `public/js/create-hero.js`, `public/js/game.js` | app loads