deadhead-space-freight-tycoon/DECISIONS.md

7.8 KiB
Raw Blame History

Decisions

Implementation decisions not spelled out in memory-bank/implementation-plan.md. Design decisions from plan §0.5 are logged here as they are implemented.

Step 1.1

  • Repo lives on an NTFS drive (C:\Projects\...). The original location (G:) is exFAT, which cannot hold symlinks, so pnpm could not link workspace packages. Do not move the repo back to an exFAT drive.
  • TypeScript is pinned to ~6.0. TypeScript 7 is the latest release, but typescript-eslint (needed in Step 1.2) only supports <6.1. Upgrade once typescript-eslint supports 7.
  • Packages export TypeScript source directly (main/types point at src/index.ts). There is no build step for internal packages; Vite, Vitest and the server's TS runner consume the source. The base config uses moduleResolution: "Bundler" and noEmit.
  • Node 24 is the required runtime (engines + .nvmrc).
  • Line endings are LF everywhere (.gitattributes), overriding the machine's global core.autocrlf, to match Prettier and the Linux CI runner.

Step 1.2

  • ESLint 10 flat config with typescript-eslint recommended (not type-aware, to keep lint fast) and eslint-config-prettier. pnpm lint runs ESLint then prettier --check.
  • Purity rule for packages/sim and packages/content: no-restricted-properties bans Math.random and Date.now; no-restricted-syntax bans new Date() with no arguments (new Date(x) is allowed).
  • Prettier skips memory-bank/ so tooling never rewrites the design docs.

Step 1.3

  • Vitest projects: the root vitest.config.ts treats each apps/* and packages/* folder as a project, so pnpm test runs every package's tests. Packages can add their own vitest.config.ts when they need a different environment.
  • Tests live in each package's test/ folder and are included in that package's tsconfig, so pnpm typecheck covers them.

Step 1.4

  • CI runs on Gitea Actions (.gitea/workflows/ci.yml): install with a frozen lockfile, then typecheck, lint and test.
  • .pnpm-store/ is ignored by Prettier, ESLint and git. On the Gitea runner, pnpm puts its store inside the workspace, and prettier --check failed on the store's files (CI run #1).

Step 1.5

  • One Postgres container, two databases. docker-compose.yml runs a single postgres:18 server on port 5432 (user/password deadhead). The default database deadhead is for development; docker/postgres/init/01-create-test-db.sql creates deadhead_test the first time the data volume is created. To recreate both from scratch, run docker compose down -v.
  • Env var names in .env.example: DATABASE_URL, TEST_DATABASE_URL, BETTER_AUTH_SECRET and BETTER_AUTH_URL (Better Auth's own names), PORT, and GAME_EPOCH_REAL_START plus GAME_EPOCH_EPHEMERIS_DATE (decision 0.5.9). The game-clock speed is not an env var; it belongs in GameConfig (rule 0.1.5).
  • Epoch values in .env.example are placeholders. The design docs do not fix an ephemeris date yet.

Step 2.1

  • Ids are lowercase kebab-case strings (earth, bulk-hauler, long-haul).
  • One landing-class enum (vacuum, atmospheric, hardened) serves both a location's landing requirement and a ship's landing class. hardened is not used in the base game.
  • Location body is an enum of the five astronomy bodies (earth, moon, mars, ceres, jupiter) so the sim can map each to a position source in Step 3.3.
  • Epoch fields are ISO 8601 UTC strings (epoch.realStart, epoch.ephemerisDate), not Date objects, so content stays plain data.
  • tutorialBoardSize is a GameConfig field in addition to contractBoardSize, because decision 0.5.21 sets the tutorial board's size separately.
  • XP per delivery inputs are base, perMillionKm and perCargoTier. Step 3.16 decides how they combine.
  • pilotLevelXpThresholds lists the total XP needed for level 1, 2, 3, ...; it and the Long Haul cut-offs must be strictly increasing.
  • tierPayMultiplier is one number per cargo type. Step 3.8 decides how it scales with tier.
  • Imports omit the .ts extension (moduleResolution: "Bundler").

Step 2.2

  • Location ids: earth, moon, mars, ceres-station, jupiter-cloud-platform.
  • Cargo flows follow GDD §9: Earth and Mars produce standard and high-value goods and demand ore (heavy) and fuel (dangerous); the Moon produces standard and heavy; Ceres produces heavy; Jupiter produces dangerous (fuel). The outposts import high-value equipment.
  • Launch multipliers: Earth 3, Jupiter Cloud Platform 2, Mars 1.5, Moon 0.5, Ceres 0.3.
  • Bay lease prices are first guesses (Earth 5,000 up to Jupiter 15,000). Step 2.5 tunes the Earth price against the tutorial.
  • The cargo-id check uses a placeholder list of the four cargo ids planned in Step 2.4 until the cargo data exists.

Step 2.3

  • Ranges (million km): Courier 5 (Moon only; Mars is ~55 at its closest), Freighter 650 (covers Ceres at its farthest, ~600, and Jupiter near its closest, ~590, per decision 0.5.25), Bulk Hauler 1,000 (Jupiter at its farthest, ~970). Step 3.7 checks these against real positions.
  • Speeds and overheads aim at GDD §6 durations at compression 60: Courier 3M km/h with 2 h overhead (Earth→Moon ≈ 2 real minutes, mostly overhead), Freighter 2M km/h with 4 h (Earth→Mars ≈ 1–2 real hours), Bulk Hauler 1.2M km/h with 8 h.
  • Prices (30,000 / 150,000 / 600,000), capacities (20 / 100 / 400 t), fuel costs (20 / 40 / 80 per million km) and hull ratings (1 / 2 / 3) are first guesses; Step 2.5 tunes the Courier price.

Step 2.4

  • Ids: cargo standard, heavy, high-value, dangerous; skills long-haul, heavy-cargo, expensive-cargo, dangerous-cargo.
  • Base pay (credits per tonne per million km): Standard 10, Heavy 14, High-Value 18, Dangerous 20. Tier multipliers: 1, 1.2, 1.25, 1.3. First guesses, to be tuned in Phase 12.
  • Cargo and skills share one data file (src/data/cargo.ts), since every cargo skill exists to gate a cargo type.

Step 2.5

  • The game epoch lives in GameConfig, not in env vars. Step 2.1 puts it in GameConfig, and rule 0.1.5 keeps game numbers in packages/content. The GAME_EPOCH_* variables added to .env.example in Step 1.5 are removed; the placeholder values (real start 2026-01-01, in-game 2100-01-01) are unchanged.
  • Money values scaled up. With the Step 2.2–2.4 rates a tutorial run paid ~24 credits. Pay rates and fuel costs are ×10, and prices are set so a Courier (4,000) plus an Earth bay (1,000) takes ~21 runs at ~240 credits each. Freighter 100,000, Bulk Hauler 400,000; leases Moon 800, Mars 20,000, Ceres 40,000, Jupiter 80,000.
  • Placeholder tutorial payout in the test: Standard pay × 0.384M km × average mass (midpoint of the contract minimum and Courier capacity, 12.5 t) × tutorial share (0.5).
  • Starting credits are 0. The company pays fuel during the tutorial, and the ~21-run target already assumes the player starts with nothing.
  • Other defaults: board size 8, tutorial board 40 (more than the ~30 runs that fit in a 60-minute period), first contact 10% at ×2 pay, contract mass 5–400 t, Long Haul cut-offs 100/250/400/600/800M km, ten pilot levels at 2 skill points each (enough to max all four skills), hire cost 2,000, pilot pay 100 per delivery.
  • Known balance issue for Phase 12: pay is linear in distance, but every trip pays a fixed overhead, so short Earth–Moon runs earn far less per real hour than long runs (a Freighter on Mars earns roughly 50× a Courier on the Moon).

Step 2.6

  • Lookups return undefined for an unknown id ("not found" without throwing). Callers decide whether a missing id is an error.
  • The package also exports each data list and schema individually, alongside the content bundle, so tests and the server can validate data with the schemas.