deadhead-space-freight-tycoon/memory-bank/change-requests/CR-002-open-markets.md

128 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Change Request CR-002 — Open Markets, Bays as HQs
**Project:** Deadhead: Space Freight Tycoon
**Applies to:** a build with Phases 1–10 and CR-001 (Faster Start) complete
**Do this:** as **Phase 10B**, after Phase 10A and before Phase 11
**Related docs:** `implementation-plan.md`, `change-request-faster-start.md`, `game-design-document.md`, `DECISIONS.md`
> **Numbering note (added when implemented):** the plan's §0.5 already used 27 (pilot location storage), 28 (Git hosting) and 29–34 (CR-001). The new decisions below numbered 0.5.27 and 0.5.28 are recorded as **0.5.35 Ship delivery** and **0.5.36 Undiscovered locations**. The error code `location_undiscovered` is the existing `location-undiscovered`.
---
## 1. Why
Contracts are currently only offered at locations where the player has a bay, and repositioning is only allowed to and from bay locations. A ship that delivers to a location without a bay (for example the Moon) is stranded: there is no contract board there, and it cannot reposition home.
The fix follows the American/Euro Truck Simulator model:
- **Every discovered location has a contract board (the freight market).** Any idle ship can pick up work wherever it is, which makes backhaul work.
- **Bays are HQs (like ATS garages).** You need a bay to hire pilots there, to take delivery of newly bought ships there, and to unassign pilots there. Bays no longer control where contracts are available.
- **Discovery still gates expansion.** An undiscovered location has no board until the player delivers there.
## 2. 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 contract boards.** Resetting the dev and test databases is fine. Schema migrations are still required.
- Update existing tests whose expectations change. Do not delete a test unless §5 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 10B.
## 3. Decision changes
Record these in `DECISIONS.md` and in `implementation-plan.md` §0.4 and §0.5.
**Glossary changes (§0.4)**
- **Bay** → A docking bay the player leases at a discovered location. It is an HQ: pilots are hired there, newly bought ships are delivered there, and pilots can be unassigned there. A bay does **not** control where contracts are offered.
- **Contract board** (new) → The freight market at a discovered location. Every discovered location has one per player per board period, whether or not the player has a bay there.
- **Deadhead** (new) → A reposition: a ship flying empty to another location. Fuel only, no payout, no XP.
**Replaced**
- **0.5.5 (Contract boards are stored)** → A board exists for every **discovered location**, per player, per period. It is generated on the first request in a period and stored; later requests in that period return the stored board. Every contract expires at the end of its board period. *(Amended by CR-019:)* a stored location board also records the qualifying fleet it was generated for, and reads hide jobs the current qualifying fleet can't complete (Step 19.3).
- **Step 3.14 rule (reposition only to a bay)** → An idle ship can deadhead from **any** location to **any discovered** location other than its current one, subject to the normal range and landing checks.
**New**
- **0.5.27 Ship delivery.** Buying a ship requires choosing one of the player's bays. The new ship appears there, idle and uncrewed.
- **0.5.28 Undiscovered locations.** Requesting the board of an undiscovered location fails with a `location_undiscovered` error. Deadheading to an undiscovered location is not allowed; only a contract delivery discovers a location.
**Unchanged (confirm, don't modify)**
- **0.5.2 Pilot locations.** Hiring happens at a player bay; unassigning is allowed only at a location with a player bay.
- **CR-001 deal.** The deal still includes the Earth bay.
## 4. Steps — Phase 10B
### Step 10B.1 — Sim: boards at any location
- Change the contract board generator's input from "the bay's location" to "the board's location". No other generation rules change.
- Rename any sim types or functions that say "bay board" to "location board".
- **Test:** The Step 3.9 tests still pass with a location input. Generating a board for the Moon with no bay data supplied anywhere works and is deterministic.
### Step 10B.2 — Sim: deadhead validation
- Reposition validation allows any origin. The destination must be discovered, must differ from the origin, and must pass the range and landing checks. The bay requirement is removed.
- Update the Step 3.14 tests.
- **Test:** A Courier idle at the Moon with no Moon bay can deadhead to Earth. Deadheading to an undiscovered location fails with `location_undiscovered`. Deadheading to the ship's own location fails. Deadheading out of range fails with the out-of-range reason. A deadhead record still has zero payout and zero XP.
### Step 10B.3 — Database: boards keyed by location
- Change `contract_boards` to be keyed by player, location, and board period (the tutorial board keeps its own key).
- Update `architecture.md`.
- **Test:** Migrations run on a fresh database and are a no-op the second time. Two boards for the same player and period at different locations can coexist; a second board for the same player, location, and period is rejected by a unique constraint.
### Step 10B.4 — Server: board endpoint for any discovered location
- Replace "get contract board for a bay" with "get contract board for a location" in the route catalog and server. Any discovered location works; an undiscovered location returns `location_undiscovered`.
- Update the Step 6.9 tests.
- **Test:** The Moon's board is returned for a player with no Moon bay. Two requests in the same period return the same board. Requesting Mars's board before discovery fails with `location_undiscovered`; after a delivery discovers Mars, it succeeds.
### Step 10B.5 — Server: no more stranded ships
- Contract dispatch works from any discovered location where the ship is idle (it already validates that the ship is at the contract's origin; confirm no bay check remains).
- Deadhead dispatch uses the Step 10B.2 rules.
- Update the Step 6.10 and 6.11 tests.
- **Test (the original bug):** A ship delivers Earth→Moon with no Moon bay. After arrival, the Moon's board is available, a Moon→Earth contract can be dispatched with that ship, and it resolves normally. Separately, the same ship can instead deadhead Moon→Earth.
### Step 10B.6 — Server: ship delivery to a bay
- Buying a ship takes a bay id. The ship appears idle and uncrewed at that bay's location. Buying without a bay, or with a bay the player doesn't own, fails.
- Update the Step 6.5 tests.
- **Test:** With bays at Earth and the Moon, buying a Freighter for the Moon bay places it at the Moon. Buying with no bay id fails with a validation error. Buying with another player's bay id fails and changes nothing.
### Step 10B.7 — Client: Contracts screen by location
- The Contracts screen's selector lists all discovered locations instead of bays. Each entry shows a badge with the number of idle ships there.
- Default selection: the location of the most recently arrived idle ship; otherwise Earth.
- Fleet screen: each idle ship gets a "Find work here" action that opens its location's board.
- Update the Step 9.4 tests.
- **Test:** Component test: the selector lists every discovered location and no undiscovered ones; idle-ship badges match state; the default is the most recently arrived idle ship's location; "Find work here" opens the right board.
- **Amended (decision 0.5.164):** "Find work here" is disabled when the ship can't be dispatched now: its pilot is resting (CR-004; rest beside service, CR-023/CR-027, included), it's in the shop for an upgrade, repair or service (CR-009/CR-026/CR-027), or it has no pilot. The button says why with the countdown that ends last ("Resting · 04:12", "In the shop · 12:30", "No pilot assigned") and re-enables on its own when it ends. Every ship, Independent pilots' included; the Contracts tab stays browsable.
### Step 10B.8 — Client: deadhead action
- Rename the Fleet screen's "Reposition" action to "Deadhead (fly empty)". It is available for every idle ship. The destination picker lists discovered locations; ones out of range or not landable are shown disabled with the reason. Show fuel cost and trip time for each.
- Update the Step 9.6 tests.
- **Test:** Component test: an idle ship at the Moon with no Moon bay shows the action; the picker excludes undiscovered locations and the ship's current location; an out-of-range destination is disabled with its reason; fuel and time match the sim.
### Step 10B.9 — Client: bays as HQs
- Market screen: "Buy" opens a bay picker ("Deliver to which HQ?"). With only one bay, preselect it.
- Locations screen and map panels: every discovered location has a "View contracts" link. The "Lease bay" action explains what a bay does: "Open an HQ here: hire pilots and take delivery of new ships at this location."
- Update the Step 9.8, 9.9, and 10.3 tests.
- **Test:** Component test: Buy shows the bay picker and sends the chosen bay id; with one bay it is preselected. Every discovered location shows "View contracts", with or without a bay. The lease action shows the HQ explanation.
### Step 10B.10 — Plan amendments and regression
- Apply the amendments in §5 to `implementation-plan.md`, and update `game-design-document.md` §4 and §10 to match §1 of this document.
- Run the full suite.
- **Test:** CI is green. Manual check: on a fresh account after the CR-001 deal, deliver to the Moon, pick up a Moon→Earth contract with the same ship, and deliver it back to Earth without leasing a Moon bay.
## 5. Amendments to `implementation-plan.md`
| Step | Change |
|---|---|
| 0.4, 0.5 | Apply §3 of this document. |
| 3.9 | Input is a location, not a bay (10B.1). |
| 3.14 | Deadhead rules replace reposition-to-bay (10B.2). |
| 5.3 | `contract_boards` keyed by player, location, and period (10B.3). |
| 6.5 | Ship purchase takes a bay id (10B.6). |
| 6.9 | Boards for any discovered location (10B.4). |
| 6.10, 6.11 | No bay checks on dispatch; deadhead rules (10B.5). |
| 9.4 | Selector lists discovered locations (10B.7). |
| 9.6 | "Deadhead (fly empty)" for every idle ship (10B.8). |
| 9.8, 9.9, 10.3 | Bay picker on purchase; "View contracts" everywhere; HQ explanation (10B.9). |
| 12.1 | The simulated player picks up contracts at whatever location its ships are at, and only deadheads when no eligible contract exists there. Add a check: across the whole run, no ship is ever idle with no eligible contract **and** no reachable deadhead destination. |
| 12.5 | After Mars is discovered, also confirm Mars's contract board is available without a Mars bay. |
## 6. Out of scope
Garage-style bay capacity or slot limits, bay upgrades, recurring bay rent, pilots that pick their own jobs from their home bay (ATS-style AI drivers), and anything else in `implementation-plan.md` §0.3.