CR-001: record change request and decisions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
fireshaper 2026-10-03 10:42:06 -04:00
parent a28fe5c5cb
commit b1dde3696f
4 changed files with 238 additions and 4 deletions

View File

@ -474,3 +474,10 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
- **Taps are detected in the canvas's own pointer handling:** one pointer, moved less than 8 px, then released. Hit-testing is a pure function over the same scene the canvas draws, with a 22 px reach (about a fingertip); ship markers win close calls because they are drawn on top. Tapping empty space closes the panel. - **Taps are detected in the canvas's own pointer handling:** one pointer, moved less than 8 px, then released. Hit-testing is a pure function over the same scene the canvas draws, with a 22 px reach (about a fingertip); ship markers win close calls because they are drawn on top. Tapping empty space closes the panel.
- **Panels are React components over the canvas**, so they work like the rest of the UI. The location panel's board link uses `/contracts?bay=<id>`; the Contracts screen now preselects the bay from that parameter. - **Panels are React components over the canvas**, so they work like the rest of the UI. The location panel's board link uses `/contracts?bay=<id>`; the Contracts screen now preselects the bay from that parameter.
- **Component tests "tap" through the canvas test double**, which renders one button per tappable item from the real `buildScene` and calls the same `onSelect`. Hit-testing itself is unit-tested with screen coordinates. - **Component tests "tap" through the canvas test double**, which renders one button per tappable item from the real `buildScene` and calls the same `onSelect`. Hit-testing itself is unit-tested with screen coordinates.
## CR-001 — Faster Start (Phase 10A)
- **Change request CR-001 is stored in `memory-bank/change-requests/`** and inserted as Phase 10A, before Phase 11.
- **Decision numbering:** the plan's §0.5 already used 21–28, so the CR's new decisions 0.5.21–0.5.26 are recorded as **0.5.29–0.5.34** (Loaner ship, The deal, Handling time, Loan, Scripted tutorial contracts, Onboarding goals). Decisions 0.5.4 and 0.5.16 are replaced, 0.5.3 amended, and 0.5.21 amended (see below).
- **Error code `tutorial-active`** (kebab-case like every other code) implements the CR's `tutorial_active`.
- **Tutorial boards are keyed by run number as well as period and location.** With three contracts per board and accepted contracts hidden, a period-only key would empty the board after three runs and strand the player until the next period. Keying by tutorial runs completed gives every run a fresh board (needed anyway for the run-3 scripted contract); a new period also gives a fresh board, so a contract cannot expire with no replacement.

View File

@ -0,0 +1,192 @@
# Change Request CR-001 — Faster Start
**Project:** Deadhead: Space Freight Tycoon
**Applies to:** a build with Phases 1–10 complete
**Do this:** as a new **Phase 10A**, before starting Phase 11
**Related docs:** `implementation-plan.md`, `game-design-document.md`, `DECISIONS.md`
> **Numbering note (added when implemented):** the plan's §0.5 already used decisions 21–28. The new decisions below numbered 0.5.21–0.5.26 are recorded in the plan as **0.5.29–0.5.34** in the same order (21→29 Loaner ship, 22→30 The deal, 23→31 Handling time, 24→32 Loan, 25→33 Scripted tutorial contracts, 26→34 Onboarding goals). The error code `tutorial_active` is implemented as `tutorial-active`, matching the existing kebab-case codes.
---
## 1. Why
Playtesting shows the opening is slow and has no hook. The current tutorial asks the player to fly the same Earth→Moon run 15–25 times (45–120 real minutes) before they own anything. There are no choices, no stakes, and the interesting systems (owning ships, discovery, hiring) are an hour or more away.
This change keeps the employee opening but cuts it to about four quick runs, gives the player a ship on credit right after, puts a real choice on the board from the first minute, shows the whole map from the start, and adds a short list of first-session goals.
## 2. Target experience
| Milestone | Target (real time, active play) |
|---|---|
| First dispatch | Under 30 seconds from "Start your company" |
| First real choice between contracts | Under 2 minutes |
| Own a ship (deal accepted) | Under 10 minutes |
| Hire first pilot | 30–45 minutes |
| First discovery | Within 90 minutes |
### New opening, in order
1. Player taps "Start your company" and lands straight on the tutorial contract board with a two-line intro.
2. The board offers **three** contracts with different mass, pay, trip time, and XP. The loaner ship is a fast company Courier, so each run takes under a minute.
3. On the third run, the board includes one scripted special contract ("Unmarked crates — double pay, no questions asked").
4. After four runs (back at Earth), the company owner offers to sell the player his old Courier **and** his Earth bay lease **on credit**.
5. The player accepts. They now own a ship and an Earth bay, owe a loan, and the tutorial is over. A cut of every payout goes to the lender until the loan is paid.
6. HQ shows a short list of goals with credit rewards to guide the first session.
7. From the first screen, the map shows every location; undiscovered ones are locked, and each one says which ship can reach it.
## 3. Rules for this change
- All rules in `implementation-plan.md` §0.1 still apply: steps in order, one commit per step, each step's test must pass, `packages/sim` stays pure, all numbers live in `packages/content`.
- **Do not write data migrations for old tutorial state.** There are no production players. Resetting the dev and test databases is fine. Schema migrations are still required.
- Dispatches already stored keep their stored arrival times. Changes to the travel-time formula only affect new dispatches.
- Update existing tests whose expectations change. Do not delete a test unless §6 says the step is replaced.
- Update `architecture.md` for every schema or API change and `progress.md` after every step.
- Stop for the user's review at the end of Phase 10A.
## 4. Decision changes
Record these in `DECISIONS.md` and in `implementation-plan.md` §0.5.
**Replaced**
- **0.5.4 (Tutorial end)** → The tutorial ends when the player accepts the deal (see 0.5.22). Buying a ship and leasing an Earth bay no longer ends it.
- **0.5.16 (Tutorial purchases)** → During the tutorial, buying ships, leasing bays, and hiring pilots are blocked with a `tutorial_active` error.
**Amended**
- **0.5.3 (Tutorial movement)** → Unchanged, except the tutorial board now has three contracts with different masses (0.5.23), and the loaner is a dedicated content ship (0.5.21).
**New**
- **0.5.21 Loaner ship.** The loaner is its own ship model, "Company Courier", marked not purchasable. It is faster and has lower overhead than the player's Courier so tutorial runs take under one real minute.
- **0.5.22 The deal.** Once the player has completed at least the configured number of tutorial runs and the loaner is at Earth with nothing in flight, a one-time deal becomes available. Accepting it removes the loaner, gives the player a Courier and an Earth bay, assigns the avatar to that Courier, sets the loan balance, and ends the tutorial. There is no decline option.
- **0.5.23 Handling time.** Travel time gains a loading/unloading term: `travel time = distance ÷ cruise speed + overhead + cargo mass × handling time per tonne`. Repositions carry zero mass. Because XP is per delivery and pay is per tonne, light contracts earn more XP per minute and heavy contracts earn more credits per minute. That is the tradeoff on every board.
- **0.5.24 Loan.** The company has a loan balance. When a dispatch resolves with positive credits (after pilot pay), the lender takes `min(balance, credits × repayment share)`. The player can also pay down any amount up to the balance at any time. No interest, no missed payments, no fail state.
- **0.5.25 Scripted tutorial contracts.** Content can define special tutorial contracts keyed to a tutorial run number. A scripted contract replaces one board slot on that run and can carry a custom name, pay multiplier, and a flavor message shown after delivery.
- **0.5.26 Onboarding goals.** A fixed, ordered list of goals in content, each with a condition evaluated from player state and a one-time credit reward the player claims. Goals appear after the tutorial ends.
## 5. Steps — Phase 10A
### Step 10A.1 — Content: new config and loaner ship
- Add a `purchasable` flag to ShipModel. Add a "Company Courier" ship model with `purchasable` false, faster than the Courier, with low overhead and enough capacity for the tutorial board's heaviest contract.
- Add GameConfig fields: tutorial runs before the deal (default 4); tutorial board size (3); tutorial mass tiers (light, medium, full); handling time per tonne (game hours); loan repayment share (default 0.3); deal loan amount (default: Courier price + Earth bay lease). Lower starting credits to a small cushion (a few fuel loads).
- Add a scripted tutorial contract entry: run 3, name "Unmarked crates", Standard cargo, pay multiplier 2, and a flavor message after delivery (for example: "Nobody asked what was inside. You got paid. Licensed hazardous-cargo pilots earn this kind of money every run.").
- Add the onboarding goals list (see Step 10A.5 for conditions) with rewards.
- **Test:** All content still validates through its schemas. The Market's list of purchasable ships does not include Company Courier. Every scripted contract and goal references valid ids. Deal loan amount is greater than zero.
### Step 10A.2 — Sim: handling time
- Add the handling term to the travel-time function (decision 0.5.23).
- Update the Step 3.5 hand-calculated test to include handling time.
- **Test:** On the same route with the same ship, a heavier contract takes longer than a lighter one. A reposition (zero mass) gives the same time as the old formula. Company Courier Earth→Moon at full tutorial load takes under 1 game hour (under 1 real minute at compression 60).
### Step 10A.3 — Sim: tutorial board v2
- The tutorial board generator takes one more input: the number of tutorial runs completed.
- It returns three Standard contracts from the loaner's current location to the other end of the Earth–Moon route, at the light, medium, and full mass tiers, each paying the tutorial share.
- If a scripted contract is keyed to the upcoming run number, it replaces the medium slot.
- Update the Step 3.10 tests to match.
- **Test:** The board has exactly three contracts with three different masses, all within Company Courier capacity. Same inputs give an identical board. With two runs completed, the "Unmarked crates" contract appears at double pay; with any other count, it does not. The light contract gives more XP per real minute than the full one, and the full one gives more credits per real minute than the light one. The sum of the four slowest possible tutorial trip times is under 6 real minutes.
### Step 10A.4 — Sim: loan repayment
- Add a pure function that splits a credit amount into repayment and net, given the loan balance and repayment share.
- Dispatch resolution now returns the repayment amount alongside the credits to add.
- **Test:** Repayment equals `min(balance, credits × share)`. Repayment is zero when the balance is zero or the credits are zero or negative. Net plus repayment equals the original credits. Repayment never exceeds the balance.
### Step 10A.5 — Sim: onboarding goal evaluation
- Add a pure function that takes a player state summary and returns which goals are complete.
- Goals, in order:
1. Deliver a contract with your own ship.
2. Spend your first skill point.
3. Buy a second ship.
4. Hire your first pilot.
5. Discover a new location.
6. Pay off your loan.
- **Test:** For each goal, a state summary just short of the condition reports it incomplete, and a summary that meets it reports it complete. Results are identical for identical inputs.
### Step 10A.6 — Schema and database
- Add to the database: company loan balance; tutorial runs completed; a deal-accepted flag; claimed goals per player; the repayment amount on each dispatch record; the `purchasable` flag reflected wherever ship models are listed.
- Add API endpoints to the route catalog: accept deal, repay loan (amount), claim goal (goal id).
- Player state gains: loan balance, tutorial runs completed, deal available (yes/no), and goals with status (incomplete, ready to claim, claimed).
- Add the `tutorial_active` error code.
- Update `architecture.md`.
- **Test:** Migrations run on a fresh database and are a no-op the second time. New schemas accept valid examples and reject invalid ones. The route catalog test (unique method and path, every endpoint has schemas) still passes.
### Step 10A.7 — Server: tutorial flow v2
- Guest creation: small starting credits, Company Courier loaner at Earth, avatar at Earth, tutorial runs completed = 0.
- The tutorial board endpoint uses the v2 generator.
- Each resolved tutorial dispatch increments tutorial runs completed.
- Deal available = tutorial is on, runs completed ≥ configured count, loaner is at Earth, and no tutorial run is in flight.
- Buying ships, leasing bays, and hiring pilots return `tutorial_active` while the tutorial is on.
- Update the Step 6.3, 6.4, 6.5, 6.6, and 6.8 tests to match.
- **Test:** After three runs, the deal is not available. After four runs ending at Earth, it is. With the configured count set to 3, the deal is not available until a fourth run returns the loaner to Earth. Buying a ship during the tutorial returns `tutorial_active` and changes nothing.
### Step 10A.8 — Server: accept the deal
- In one transaction: remove the loaner; create an owned Courier at Earth; create an Earth bay; assign the avatar to the Courier; set the loan balance to the deal loan amount; turn the tutorial off; set deal accepted.
- This replaces Step 6.7. Remove the old automatic tutorial-end rule and its tests.
- **Test:** Accepting when available produces exactly that state: one owned Courier at Earth crewed by the avatar, one Earth bay, the configured loan balance, tutorial off, no loaner. Accepting when not available fails with no changes. Accepting twice fails the second time. Two simultaneous accepts produce exactly one deal.
### Step 10A.9 — Server: loan repayment
- Resolution applies the repayment from Step 10A.4: reduce the loan balance, add only the net to credits, and store the repayment on the dispatch.
- The repay endpoint moves any amount up to the balance from credits to the loan. It uses the same row lock as Step 6.14.
- **Test:** A delivery with a loan balance increases credits by net and reduces the balance by the repayment. With no balance, the full amount goes to credits. Repaying more than the balance only takes the balance. Repaying more than available credits fails with no changes.
### Step 10A.10 — Server: goals
- Player state includes goal statuses evaluated with the Step 10A.5 function. Goals are only shown once the tutorial is over.
- The claim endpoint adds the reward once and marks the goal claimed.
- **Test:** Claiming a completed goal adds its reward. Claiming it again fails. Claiming an incomplete goal fails. During the tutorial, state contains no goals.
### Step 10A.11 — Client: first launch goes straight to work
- After "Start your company", navigate directly to the Contracts screen with a short intro card, for example: "Halvorsen Lunar Freight needs a pilot. Their ship, your hands. Get the crates to the Moon." Highlight the light contract as the suggested first pick.
- Update the Step 8.4 test.
- **Test:** Component test: after create-guest succeeds, the Contracts screen shows the intro card and a highlighted contract, and one tap on "Fly it" sends a dispatch.
### Step 10A.12 — Client: tutorial board shows the tradeoff
- Each tutorial contract shows pay, trip time, and XP side by side, so the light-versus-heavy choice is visible.
- The scripted contract has distinct styling. After it resolves, its flavor message appears in the delivery summary.
- Update the Step 9.3 tests.
- **Test:** Component test: three contracts show pay, time, and XP matching the sim. The scripted contract renders with its special style. A resolved scripted delivery shows its flavor message.
### Step 10A.13 — Client: the deal
- When the deal is available, show a modal with a short story beat (for example: "Old Halvorsen is retiring. He'll sell you his Courier and his Earth bay. Pay him back out of every job."), the ship and bay included, the loan amount, the repayment share, and a single "Shake on it" button.
- After accepting, go to HQ.
- **Test:** Component test: the modal appears only when state says the deal is available; it shows the configured loan amount and share; the button calls accept and navigates to HQ.
### Step 10A.14 — Client: loan on HQ
- Replace the old "save up for your first ship" goal on HQ (Step 9.1) with the loan: balance, a paid-off progress bar, and a "Pay down" action with an amount input.
- Delivery summaries show the repayment line when there is one.
- **Test:** Component test: the progress bar matches state; paying down calls the endpoint with the entered amount; amounts above credits or balance are blocked in the UI; a summary with a repayment shows it.
### Step 10A.15 — Client: goals on HQ
- Show the next three unclaimed goals with their rewards. Completed goals show a "Claim" button.
- **Test:** Component test: only three goals show; a ready goal shows Claim; claiming calls the endpoint and the next goal moves into view.
### Step 10A.16 — Client: whole map from the start
- The map is available from the first screen, including during the tutorial.
- Undiscovered locations are drawn dimmed with a lock. Tapping one shows "Undiscovered — deliver here to unlock" and which ship models can currently reach it from Earth (using the sim range check over purchasable models), for example "Reachable with: Freighter, Bulk Hauler".
- **Test:** Component test: undiscovered locations render as locked; the reachability list for Mars matches the sim range check for the mocked time; discovered locations render normally.
### Step 10A.17 — Client: something waiting on return
- If goals became ready while the player was away, list them in the "while you were away" modal with a link to claim.
- **Test:** Component test: with a seeded ready goal and a completed delivery, the modal shows both; the link navigates to HQ goals.
### Step 10A.18 — Plan amendments and regression
- Apply the amendments in §6 to `implementation-plan.md`.
- Run the full suite.
- **Test:** CI is green. Manual check: on a fresh guest account, the deal is accepted in under 10 real minutes of play, and the first contract after the deal shows a loan repayment in its summary.
## 6. Amendments to `implementation-plan.md`
| Step | Change |
|---|---|
| 2.5 | Replace the 15–25 runs test. New test: the total tutorial share earned over the configured tutorial runs, plus starting credits, covers fuel for at least three Courier Earth→Moon runs (no soft-lock after the deal). |
| 3.5 | Formula includes handling time (10A.2). |
| 3.10 | Superseded by 10A.3. |
| 6.3, 6.4 | Board size 3, v2 generator, runs-completed counter (10A.7). |
| 6.5, 6.6, 6.8 | Blocked during tutorial with `tutorial_active` (10A.7). |
| 6.7 | Replaced by 10A.8. |
| 8.4 | Lands on Contracts, not HQ (10A.11). |
| 9.1 | Tutorial savings goal replaced by loan and goals (10A.14, 10A.15). |
| 9.3 | Shows pay, time, and XP (10A.12). |
| 12.1 | Not built yet. Change the thresholds to: deal accepted under 10 real minutes; first pilot hired between 30 and 45 minutes; first discovery under 90 minutes; and a new check that after the deal the player always either has a ship in flight or can afford fuel for at least one contract. The simulated player accepts the deal as soon as it is available, takes the highest credits-per-minute contract, and pays down the loan only through automatic repayment. Adjust content values, not thresholds. |
| 12.2 | Replaced: use the balance script's measured numbers in the new Step 2.5 test. |
| 12.3 | Rewritten: guest → four tutorial runs → accept deal → state shows one owned Courier at Earth crewed by the avatar, one Earth bay, a loan balance, and tutorial off → one contract dispatched and resolved with a repayment applied. |
## 7. Out of scope
Interest, missed-payment penalties, or anything that can bankrupt the player. A decline option on the deal. Random events beyond the one scripted contract. Urgent/deadline contracts. Anything else in `implementation-plan.md` §0.3.

View File

@ -56,8 +56,8 @@ These answer the pre-build review. Log each one in `DECISIONS.md` as it is imple
1. **All three base ships are atmospheric-rated.** In the base game, ships are gated by range, capacity and speed. The landing-class check stays in the sim (it matters once Hardened ships arrive) and is tested with a test-only vacuum ship. 1. **All three base ships are atmospheric-rated.** In the base game, ships are gated by range, capacity and speed. The landing-class check stays in the sim (it matters once Hardened ships arrive) and is tested with a test-only vacuum ship.
2. **Pilot locations.** An assigned pilot is wherever their ship is. An unassigned pilot stays where they were unassigned. Pilots travel only by flying a ship. Unassigning is allowed only at a location with a player bay. New hires appear at a player bay the player chooses. 2. **Pilot locations.** An assigned pilot is wherever their ship is. An unassigned pilot stays where they were unassigned. Pilots travel only by flying a ship. Unassigning is allowed only at a location with a player bay. New hires appear at a player bay the player chooses.
3. **Tutorial movement.** The avatar and loaner move with each tutorial run. The tutorial board starts at the loaner's current location (Earth→Moon or Moon→Earth). The company pays fuel and pilot pay; the player receives only the tutorial share. 3. **Tutorial movement.** The avatar and loaner move with each tutorial run. The tutorial board starts at the loaner's current location (Earth→Moon or Moon→Earth). The company pays fuel and pilot pay; the player receives only the tutorial share. *(Amended by CR-001: the board has three contracts of different masses (0.5.31), and the loaner is a dedicated content ship (0.5.29).)*
4. **Tutorial end.** The tutorial ends when the player owns a ship, has an Earth bay, and has no tutorial run in flight. The loaner is removed and the avatar moves to Earth, unassigned. 4. **Tutorial end.** *(Replaced by CR-001.)* The tutorial ends when the player accepts the deal (0.5.30). Buying a ship and leasing an Earth bay no longer end it.
5. **Contract boards are stored.** A bay's board is generated on the first request in a period and stored. Later requests in that period return the stored board, so a discovery mid-period does not change it. Every contract expires at the end of its board period; nothing carries over. 5. **Contract boards are stored.** A bay's board is generated on the first request in a period and stored. Later requests in that period return the stored board, so a discovery mid-period does not change it. Every contract expires at the end of its board period; nothing carries over.
6. **Cargo tier.** Standard is tier 0. A non-Standard contract has a tier from 1 to 5, and that tier is the required level in the cargo's skill. 6. **Cargo tier.** Standard is tier 0. A non-Standard contract has a tier from 1 to 5, and that tier is the required level in the cargo's skill.
7. **Long Haul.** A config list of distance cut-offs, one per Long Haul level. The required Long Haul level is the number of cut-offs the route distance exceeds. 7. **Long Haul.** A config list of distance cut-offs, one per Long Haul level. The required Long Haul level is the number of cut-offs the route distance exceeds.
@ -69,12 +69,12 @@ These answer the pre-build review. Log each one in `DECISIONS.md` as it is imple
13. **Hull rating** is stored but has no effect in the base game. 13. **Hull rating** is stored but has no effect in the base game.
14. **Ship names** are generated from the world seed. No renaming. 14. **Ship names** are generated from the world seed. No renaming.
15. **Bays** have no ship limit. 15. **Bays** have no ship limit.
16. **Tutorial purchases.** The player may buy or lease anything they can afford during the tutorial. 16. **Tutorial purchases.** *(Replaced by CR-001.)* During the tutorial, buying ships, leasing bays and hiring pilots are blocked with a `tutorial-active` error.
17. **Cargo fallback.** If no cargo type is both produced at the origin and demanded at the destination, use Standard. 17. **Cargo fallback.** If no cargo type is both produced at the origin and demanded at the destination, use Standard.
18. **Testable clock.** The server reads time through an injectable clock. Tests call job handlers directly; E2E tests use a test-only clock-offset endpoint that does not exist in production. 18. **Testable clock.** The server reads time through an injectable clock. Tests call job handlers directly; E2E tests use a test-only clock-offset endpoint that does not exist in production.
19. **Summaries.** Dispatches have an acknowledged-at column; resolved dispatches without one form the "while you were away" summary. 19. **Summaries.** Dispatches have an acknowledged-at column; resolved dispatches without one form the "while you were away" summary.
20. **Same domain.** In production, the client and API share one domain, with the API under `/api`, so the session cookie is first-party (iOS Safari blocks cross-site cookies). 20. **Same domain.** In production, the client and API share one domain, with the API under `/api`, so the session cookie is first-party (iOS Safari blocks cross-site cookies).
21. **Tutorial boards per location.** The tutorial board is stored per period *and* per loaner location (one Earth→Moon board, one Moon→Earth board). The tutorial board size is set large enough that a player cannot exhaust it within one period. 21. **Tutorial boards per location.** The tutorial board is stored per period *and* per loaner location (one Earth→Moon board, one Moon→Earth board). *(Amended by CR-001: boards are also keyed by the number of tutorial runs completed, so each run gets a fresh three-contract board; the "large enough" size rule no longer applies.)*
22. **Pilot endpoints.** The route catalog includes "unassign pilot", and "hire pilot" takes the bay location to hire at. 22. **Pilot endpoints.** The route catalog includes "unassign pilot", and "hire pilot" takes the bay location to hire at.
23. **Avatar during tutorial.** The avatar is locked to the loaner and cannot be assigned to an owned ship until the tutorial ends. 23. **Avatar during tutorial.** The avatar is locked to the loaner and cannot be assigned to an owned ship until the tutorial ends.
24. **Repositions need no skills.** A reposition checks range and landing only; no Long Haul or cargo skill is required. 24. **Repositions need no skills.** A reposition checks range and landing only; no Long Haul or cargo skill is required.
@ -82,6 +82,12 @@ These answer the pre-build review. Log each one in `DECISIONS.md` as it is imple
26. **Loaner in player state.** Player state has a `tutorial` object with the loaner's location and whether a run is in flight. The loaner is not in the ships list. 26. **Loaner in player state.** Player state has a `tutorial` object with the loaner's location and whether a run is in flight. The loaner is not in the ships list.
27. **Pilot location storage.** Only unassigned pilots store a location; an assigned pilot's location is derived from their ship. 27. **Pilot location storage.** Only unassigned pilots store a location; an assigned pilot's location is derived from their ship.
28. **Git hosting.** The repo is on a self-hosted Gitea instance: `http://192.168.1.107:3000/rory/deadhead-space-freight-tycoon`. CI uses Gitea Actions. 28. **Git hosting.** The repo is on a self-hosted Gitea instance: `http://192.168.1.107:3000/rory/deadhead-space-freight-tycoon`. CI uses Gitea Actions.
29. **Loaner ship** (CR-001). The loaner is its own ship model, "Company Courier", marked not purchasable. It is faster and has lower overhead than the player's Courier so tutorial runs take under one real minute.
30. **The deal** (CR-001). Once the player has completed at least the configured number of tutorial runs and the loaner is at Earth with nothing in flight, a one-time deal becomes available. Accepting it removes the loaner, gives the player a Courier and an Earth bay, assigns the avatar to that Courier, sets the loan balance, and ends the tutorial. There is no decline option.
31. **Handling time** (CR-001). Travel time = distance ÷ cruise speed + overhead + cargo mass × handling time per tonne. Repositions carry zero mass. Light contracts earn more XP per minute; heavy contracts earn more credits per minute.
32. **Loan** (CR-001). The company has a loan balance. When a dispatch resolves with positive credits (after pilot pay), the lender takes min(balance, credits × repayment share). The player can pay down any amount up to the balance at any time. No interest, no missed payments, no fail state.
33. **Scripted tutorial contracts** (CR-001). Content can define special tutorial contracts keyed to a tutorial run number. A scripted contract replaces one board slot on that run and can carry a custom name, pay multiplier, and a flavor message shown after delivery.
34. **Onboarding goals** (CR-001). A fixed, ordered list of goals in content, each with a condition evaluated from player state and a one-time credit reward the player claims. Goals appear after the tutorial ends.
--- ---
@ -442,6 +448,12 @@ These answer the pre-build review. Log each one in `DECISIONS.md` as it is imple
--- ---
## Phase 10A — Faster Start (CR-001)
Steps 10A.1–10A.18 are specified in `memory-bank/change-requests/CR-001-faster-start.md` §5, with its §6 amendments to earlier steps. Done before Phase 11; stop for review at the end.
---
## Phase 11 — PWA ## Phase 11 — PWA
### Step 11.1 — Installable PWA ### Step 11.1 — Installable PWA

View File

@ -101,3 +101,26 @@
- [x] **Step 10.1 — PixiJS map component.** Map tab (`/map`, lazy-loaded) with a PixiJS v8 canvas (`src/map/MapCanvas.tsx`): Sun, orbit rings, five locations (dimmed if undiscovered, ring if a bay), drag to pan, pinch or wheel to zoom, "Reset view". Square-root radial scale and screen projection in `src/map/projection.ts`, camera in `src/map/camera.ts`. Tests: scale monotonic, locations fit a 375 px map for 12 years, zoom keeps the anchor fixed. Manual phone check pending. - [x] **Step 10.1 — PixiJS map component.** Map tab (`/map`, lazy-loaded) with a PixiJS v8 canvas (`src/map/MapCanvas.tsx`): Sun, orbit rings, five locations (dimmed if undiscovered, ring if a bay), drag to pan, pinch or wheel to zoom, "Reset view". Square-root radial scale and screen projection in `src/map/projection.ts`, camera in `src/map/camera.ts`. Tests: scale monotonic, locations fit a 375 px map for 12 years, zoom keeps the anchor fixed. Manual phone check pending.
- [x] **Step 10.2 — Ships on the map.** `flightMarker` / `buildScene` (`src/map/scene.ts`): each in-flight dispatch draws a route line (origin at departure → destination at arrival) with a marker placed by sim progress every frame; idle ships (tutorial loaner included) show as a count badge at their location. Test: marker at 50% lies halfway along the route line on screen. Manual smoothness check pending. - [x] **Step 10.2 — Ships on the map.** `flightMarker` / `buildScene` (`src/map/scene.ts`): each in-flight dispatch draws a route line (origin at departure → destination at arrival) with a marker placed by sim progress every frame; idle ships (tutorial loaner included) show as a count badge at their location. Test: marker at 50% lies halfway along the route line on screen. Manual smoothness check pending.
- [x] **Step 10.3 — Map interactions.** Taps (single pointer, under 8 px of movement) are hit-tested (`src/map/hitTest.ts`, 22 px reach, ships win ties). A location panel shows details (or "Undiscovered") and, with a bay, a link to `/contracts?bay=…` which preselects that board; a ship panel shows route, pilot and ETA. Component tests tap through a canvas test double that uses the real scene. - [x] **Step 10.3 — Map interactions.** Taps (single pointer, under 8 px of movement) are hit-tested (`src/map/hitTest.ts`, 22 px reach, ships win ties). A location panel shows details (or "Undiscovered") and, with a bay, a link to `/contracts?bay=…` which preselects that board; a ship panel shows route, pilot and ETA. Component tests tap through a canvas test double that uses the real scene.
## Phase 10A — Faster Start (CR-001)
Spec: `memory-bank/change-requests/CR-001-faster-start.md`. Decisions recorded as plan §0.5.29–0.5.34.
- [ ] Step 10A.1 — Content: new config and loaner ship
- [ ] Step 10A.2 — Sim: handling time
- [ ] Step 10A.3 — Sim: tutorial board v2
- [ ] Step 10A.4 — Sim: loan repayment
- [ ] Step 10A.5 — Sim: onboarding goal evaluation
- [ ] Step 10A.6 — Schema and database
- [ ] Step 10A.7 — Server: tutorial flow v2
- [ ] Step 10A.8 — Server: accept the deal
- [ ] Step 10A.9 — Server: loan repayment
- [ ] Step 10A.10 — Server: goals
- [ ] Step 10A.11 — Client: first launch goes straight to work
- [ ] Step 10A.12 — Client: tutorial board shows the tradeoff
- [ ] Step 10A.13 — Client: the deal
- [ ] Step 10A.14 — Client: loan on HQ
- [ ] Step 10A.15 — Client: goals on HQ
- [ ] Step 10A.16 — Client: whole map from the start
- [ ] Step 10A.17 — Client: something waiting on return
- [ ] Step 10A.18 — Plan amendments and regression