Handoff Artifact · Read-only codebase review

Scrap — Unbiased Game Overview

A neutral, factual description of the scrap codebase, produced so that a separate model — with no access to this repository — can propose game formats and gameplay styles without being primed toward the current shape of the experience.

Source LOC32,959excl. deps & assets
Client game modules145,546 lines
Tests passing17814 files · verified
Art assets95approved · webp
Produced by Claude Code · model claude-opus-5 Run 2026-09-14 · 47 turns · 297s Session ba3472ad Subject agent-lab/workspaces/scrap — scrapline3 v2.0.0
How to read this

Every factual claim is marked Observed (read directly in the code, data, or docs) or Inferred (a reading drawn from the code). Gaps are stated as gaps rather than guessed. Per the governing brief this document deliberately omits navigation, interface structure, layout, input, and interaction flow, plus mechanics internals — so the reader is not anchored to the current design. Section 09 is the evidence appendix: claim → file.

01 · What this artefact is

scrap is a TypeScript monorepo containing a browser-delivered game client, a separate HTTP backend service, an image-asset pipeline, and a documentation set. Observed

The client is a Vite 6 + React 19 single-page application, styled with Tailwind 4 and plain CSS, using lucide-react for icons and motion for animation. Type-checking is tsc --noEmit; tests run under Vitest. The client source is 12,235 lines of .ts/.tsx plus 1,174 lines of CSS. Its largest area is src/game/: 14 non-test modules totalling 5,543 lines, accompanied by 12 test files totalling 2,906 lines. Game content lives in src/data/gameContent.ts (904 lines) with a separate referential-integrity checker in src/data/contentValidation.ts (139 lines). src/types.ts (645 lines) holds the shared type surface and a literal initial-state object. Observed

The backend (api/) is an independent Node 20 + Fastify 5 service using better-sqlite3, Zod, Argon2id, JWT, Nodemailer, and Pino: 7,327 lines of TypeScript across routes/, services/, db/, middleware/, and config.ts, plus 6 SQL migrations (637 lines) and 5 transactional email templates (each in html/plain/subject form). Its own package.json describes it as "Self-hosted cloud saves & accounts backend for Scrapline". Observed

The art pipeline is 7 Node scripts (scripts/*.mjs, 669 lines) driven by artwork/art-direction.json (a style/palette/negative-prompt/role/dimension spec) and a runtime manifest src/assets/assetRegistry.ts. Deployment is Docker: an nginx container serving the static build, and a second additive Compose file for the API. docs/ holds 13 markdown files (3,478 lines); a further 307-line art checklist sits at the repo root. Git history is a single squashed commit (chore: initialize repository (scrapline3 v2.0.0)), so change history is not available. Observed

02 · Subject matter and fiction

The setting is a post-industrial freight network of ten named urban districts — Rustport, East Grid, Glassmarket, Lowtown, Iron Mile, North Foundry, South Reef, Ashgate, Relay Nine, Morrow Yard — each carrying a type label (Salvage Dock, Industrial Market, Tech Bazaar, Residential Sprawl, Freight Corridor, Reactor Yard, Flood Quarter, Contraband Crossing, Signal Station, Salvage Depot), a prose description, a stability number, and a population number. Observed

The tradeable goods (25) are industrial and survival materials: scrap metal, copper wire, battery cells, water filters, circuit boards, fuel canisters, salvage parts, medical supplies, iron plating, fiber cable, solar cells, coolant packs, ration packs, protein supplements, stim packs, trauma kits, signal arrays, void crystals, collector artifacts, modified parts, coolant rods, rope & cable, sensor modules, hazmat kits, grain alcohol — sorted into seven categories (scrap, electronics, utility, power, medical, food, luxury). Observed

Four factions are defined: Ironclad Logistics Corp. (described in data as the licensing and debt-ledger authority), Rustport Dockhands, Glassmarket Brokers, Lowtown Civic Ring. Observed

Sixteen named speaker characters exist, each with a role, an archetype (contractor, lender, collector, official, neutral), an optional district affiliation and specialty, and three lines of dialogue (48 lines total). Roles include Community Organizer, Credit Broker, Debt Recovery Agent, Contract Dispatcher, Market Analyst, Dockmaster, Grid Foreman, Bazaar Runner, Clinic Quartermaster, Convoy Marshal, Reactor Steward, Canal Pilot, Checkpoint Broker, Signal Keeper, Yard Archivist, Field Mechanic. Observed

Three vehicles (Damaged Cart, Flatbed Hauler, Shielded Rig), six rank titles (Unlicensed Runner → Scrapline Magnate), and three license tiers (Provisional, Standard Salvage, Guild Certified) supply the remaining named fiction. Environmental fiction includes five disaster types (storm front, flood surge, grid blackout, industrial fire, dust squall). Observed

The art direction document states the visual register as "cinematic industrial salvage realism … near-black and slate-steel … oxidized metal, wet concrete, teal instrument light, sodium-amber practicals", and explicitly excludes "clean utopia, glossy showroom sci-fi, neon-cyberpunk excess, fantasy magic". Observed — artwork/art-direction.json

docs/STEAM_STORE_COPY.md (a draft, per its own heading) describes the product as "a focused single-player strategy game about making difficult decisions with limited time and imperfect information"; docs/RELEASE_READINESS.md records the product position as "single-player freight-trading strategy game". These are the project's own characterisations, reported here as such and not adopted. Observed

03 · Core activity, abstracted

Categories of thing the player does, named at the highest level. No procedure, no steps.

04 · Systems present

Each entry names existence and, at most, inputs and outputs. No internals.

Server-side systems: email/password and magic-link authentication, email verification, password reset, session listing and revocation, save slots with server-allocated revisions and conflict responses, save snapshots, idempotency keys, share links with public/unlisted/private visibility and abuse reporting, data export, scheduled account deletion, and an audit event log. Observed

05 · Content inventory

Game content (src/data/gameContent.ts, 904 lines, 242 top-level data objects): 25 goods · 10 districts · 118 baseline market price rows · 1 hand-authored contract instance · 2 scripted day-advance fixtures · 3 vehicles · 11 upgrade definitions · 4 route types · 9 route-event types · 9 contract templates · 4 factions · 16 speakers (48 dialogue lines) · 3 loan tiers · 6 rank tiers · 3 license tiers · 18 route definitions. Observed

Additional typed content sets: 5 disaster types · 5 broadcast kinds · 7 market conditions · 6 failure reasons · 19 route-event choice definitions in src/game/travel.ts. Observed

Art: 95 registry entries, all approved with a non-null source. 95 source PNGs in artwork/source/, 95 optimized .webp files in public/artwork/approved/, 9 review renders. By kind: 25 good, 16 portrait, 11 upgrade, 10 district, 9 contract, 9 route-event, 6 rank, 3 foundation, 3 outcome, 3 vehicle. Also present: 7 favicon files and 8 marketing assets including one .mp4. No audio files of any format exist in the repository. Observed

Tests: 14 test files in src/ containing 169 it/test declarations, one of which is an it.each over 9 route-event ids. docs/RELEASE_READINESS.md records a pass at "178/178"; the reviewer did not execute the suite. api/ has 2 hand-run test scripts (202 lines) rather than a test-runner suite. Observed

Backend: 44 registered HTTP routes (20 auth, 9 shares, 6 saves, 4 account-deletion, 1 data-export, 1 test-mail, 3 service-health/version), 15 SQL tables across 6 migrations. Observed

Docs: DEV-CHECKLIST.md 673 · NEXT-ACTIONS.md 743 · DECISIONS.md 458 (numbered, dated, signed decision entries) · RUNLOG.md 372 · SECURITY-THREAT-MODEL.md 259 · SAVE-VERSIONING.md 237 · PRIVACY-POLICY.md 183 · CONFIG.md 168 · SECRETS.md 121 · TERMS.md 112 · TRAILER_VIDEO_PROMPTS.md 64 · RELEASE_READINESS.md 61 · STEAM_STORE_COPY.md 27 lines. Observed

06 · Structural facts relevant to format

Strictly bounded to what constrains what formats are possible — stated without describing the current experience.

07 · Explicit non-scope

Per the brief governing this document, the reviewer deliberately omitted all description of navigation, interface structure, layout, input, and interaction flow, and all mechanics internals — formulas, balance constants, per-action procedures, state-machine descriptions, and cost or timing tables. Where a system's internal procedure would have been the only way to convey a fact, the system was named and the description stopped. For the same reason the reviewer excluded client files whose content is onboarding or interface material rather than game data or simulation logic, and did not describe the composition of the marketing page component. This omission is deliberate so that the reader is not primed toward the current shape of the experience; it is not a statement that those parts are absent, small, or unimportant.

08 · Unknowns and unverifiable items

09 · Evidence appendix

ClaimEvidence
Stack, scripts, dependencies (client)package.json; vite.config.ts; tsconfig.json
Client LOC totalswc -l over src/**/*.{ts,tsx,css} — 12,235 TS/TSX, 1,174 CSS
14 gameplay modules, 5,543 LOC; 12 game test files, 2,906 LOCsrc/game/*.ts (non-test / *.test.ts split)
242 content objects across 16 arrayssrc/data/gameContent.ts lines 20–885; per-array line ranges below
25 goods; 7 categoriesgameContent.ts:20–47; types.ts:270–277
10 districts with type/stability/populationgameContent.ts:49–60
118 baseline market price rowsgameContent.ts:62–204
1 authored contract; 2 day-advance fixturesgameContent.ts:206–224, 226–250
3 vehicles; 11 upgrades (6 effect kinds)gameContent.ts:252–277, 279–398; types.ts:332
4 route types; 9 route-event types; 18 routesgameContent.ts:400–405, 407–417, 863–885
9 contract templates, tiers 1–4gameContent.ts:419–547
4 factions; 16 speakers; 48 dialogue linesgameContent.ts:549–577, 579–820
3 loan tiers; 6 ranks; 3 license tiersgameContent.ts:822–846, 848–855, 857–861
Content bundled and validated as datagameContent.ts:887+ (GAME_CONTENT); contentValidation.ts:41 (validateGameContent)
5 disaster types; category mappingdisasters.ts:6–14; types.ts:223
5 broadcast kinds, 4 statusestypes.ts:235–250
7 market conditions; price state shapetypes.ts:296–314; market.ts:5–21
6 failure reasonstypes.ts:84
19 route-event choice definitionstravel.ts:554–799 (buildEventChoices)
Day counter, maxDays 30, run statustypes.ts:26–65, 520–535 (INITIAL_STATE)
Deterministic seeding, no Math.randombroadcasts.ts:6 (hashSeed); characters.ts:33; contracts.ts:81; actions.ts:708; grep for Math.random in src/ → 0 hits
Only two client timersApp.tsx:248 (setTimeout), App.tsx:1248 (setInterval, 5,000 ms clock)
Local persistence: 3 slots, schema v2, migration, career recordpersistence.ts:5–7, :127 (migrateSavedGameSnapshot), :287–331 (career)
Cloud sync targets implemented endpointscloudSync.ts:107–187 (/api/auth/*, /api/saves/*)
serverProfile targets unimplemented endpointsserverProfile.ts:105,115,122,139,147,154,165; no matching handler in api/src/routes/*
44 API routesapi/src/routes/{auth,saves,shares,test,data-export,account-deletions}.ts; api/src/server.ts:297–304
Share visibility + abuse reportingapi/src/routes/shares.ts:1–23, :30–36
15 SQL tables, 6 migrations, 637 LOCapi/migrations/0001–0006*.sql
Server-authoritative save revisions and conflictsdocs/SAVE-VERSIONING.md §1–3
Backend stack, LOC, purposeapi/package.json; wc -l over api/src/**/*.ts = 7,327
Backend design decisions (self-hosted, repository abstraction, Argon2id, token rotation)docs/DECISIONS.md D-001, D-002, D-007, D-010, D-011
95 art registry entries, all approved; kind breakdownsrc/assets/assetRegistry.ts; ls public/artwork/approved = 95; ls artwork/source/*.png = 95
Art direction spec (style, palette, negative prompt, 10 roles, 4 aspect ratios)artwork/art-direction.json
Art pipeline scripts, 669 LOCscripts/*.mjs; package.json art:* scripts
No audio anywheregrep for Audio/howler/.mp3/.ogg/.wav in src/, api/src/, both package.json → 0 hits; find for audio files → 0
No WebSocket / real-time channelgrep for websocket/socket.io in src/, api/src/ → 0 hits
Deployment: nginx static container, loopback port, Cloudflare Tunneldocker/STACK.md; docker/docker-compose.yml; docker/docker-compose.api.yml
Client entry selects game or marketing componentsrc/main.tsx:8–12
Product position, platform scope, out-of-scope list, "178/178"docs/RELEASE_READINESS.md:5–25
Store-copy self-description (attributed, not adopted)docs/STEAM_STORE_COPY.md:5,13
Game-design docs live outside this repodocs/DECISIONS.md:3–5
Single squashed commitgit log --oneline → 3c4f3c8 chore: initialize repository (scrapline3 v2.0.0)
14 test files, 169 it/test declarations, one it.each over 9 idsgrep over src/**/*.test.ts; travel.test.ts:38
13 docs files, 3,478 lineswc -l docs/*.md