Files
Emberfall/docs/DECISIONS.md
T
2026-10-20 20:33:35 +11:00

2.5 KiB

DECISIONS.md — Architecture Decision Log

Record any choice that deviates from or fills a gap in the docs. Newest at the bottom.

Format:

## D-NNN: Title
- Date:
- Context: (what problem / ambiguity)
- Decision: (what you chose)
- Alternatives considered:
- Consequences:

D-001: SQLite + better-sqlite3 as the datastore

  • Date: (project start)
  • Context: Needs persistent accounts and game state with zero setup for a local build.
  • Decision: SQLite file in data/, synchronous driver, WAL mode.
  • Alternatives considered: Postgres (heavier setup), JSON files (no integrity/transactions).
  • Consequences: Single-process deployment; synchronous transactions give simple double-spend protection. Migrate to Postgres only if scaling beyond one server.

D-002: Lazy time evaluation instead of tick loops

  • Date: (project start)
  • Context: Game must progress while players are offline.
  • Decision: Store timestamps; compute energy/timers on demand.
  • Consequences: No background workers, trivially testable with injected clocks.

D-003: Vanilla frontend, no build step

  • Date: (project start)
  • Context: Reduce tooling surface area for a local-model build.
  • Decision: Static HTML/CSS/JS served by Express.
  • Consequences: Slightly more DOM boilerplate; far fewer moving parts.

D-004: Both combatants attack every round

  • Date: 2026-10-20 (review)
  • Context: Combat treated "higher speed acts first" as "only the faster side acts", so the slower side never attacked.
  • Decision: Each round, both attack; faster first; hero wins speed ties; a kill ends the round.
  • Consequences: Round log has up to 2 entries per round (max 40). Win rates need re-tuning (see STATUS issue 1).

D-005: Equipment stats are counted once in derived stats

  • Context: maxHp added equipment VIT twice (once in totalVit, once as a separate term).
  • Decision: calcDerived in src/game/stats.js is the ONLY derived-stat formula; combat and services call it.

D-006: Real SQLite session store everywhere; unref its cleanup timer

  • Context: better-sqlite3-session-store ignores expired.clear:false (clear || true), so its setInterval kept tests alive; the project had used a memory store in tests, so the real store was never exercised.
  • Decision: Wrap store creation so its interval is unref'd. Tests now use the real store.

D-007: src/utils/time.js is the only clock

  • Decision: Services call time.now(); tests use time.setNow(). Game functions take nowMs/rng as parameters.