CR-004: record change request and decisions
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
99aebe1dcb
commit
0aecda66d7
|
|
@ -754,3 +754,10 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
|
||||||
- **Step 11.1's amendment names the generated folder explicitly** (`public/assets/map-gen/`), since the source art is dropped from the build and must not be precached.
|
- **Step 11.1's amendment names the generated folder explicitly** (`public/assets/map-gen/`), since the source art is dropped from the build and must not be precached.
|
||||||
- **The "map with undiscovered locations" review shot is the tutorial map**, which shows Mars, Ceres and Jupiter locked; the mid-game shot has Mars discovered and Ceres and Jupiter still locked.
|
- **The "map with undiscovered locations" review shot is the tutorial map**, which shows Mars, Ceres and Jupiter locked; the mid-game shot has Mars discovered and Ceres and Jupiter still locked.
|
||||||
- **Screenshot size:** phone shots are full-resolution (DPR 3) PNGs, about 2 MB each; `docs/screenshots/` is about 24 MB. They are kept as-is for the review; trimming or moving them out of the repository is a later cleanup.
|
- **Screenshot size:** phone shots are full-resolution (DPR 3) PNGs, about 2 MB each; `docs/screenshots/` is about 24 MB. They are kept as-is for the review; trimming or moving them out of the repository is a later cleanup.
|
||||||
|
|
||||||
|
## CR-004 — Pilot Duty and Rest (Phase 10D)
|
||||||
|
|
||||||
|
- **Change request CR-004 is stored in `memory-bank/change-requests/`** and inserted as Phase 10D, after Phase 10C and before Phase 11.
|
||||||
|
- **Decision numbering:** the CR's 0.5.34–0.5.38 are recorded as **0.5.42 Duty clock, 0.5.43 Duty limit, 0.5.44 Finish the job, 0.5.45 Rest, 0.5.46 Rest covers a board refresh**, since 34–41 were already in use.
|
||||||
|
- **Error code:** the CR's `pilot_resting` is `pilot-resting`, matching the API's kebab-case codes.
|
||||||
|
- **Rest length vs. the CR's example:** the CR's rule sets the default rest to one board period (60 real minutes = 60 game hours at the current compression), but its worked example says a pilot "rests about 15 real minutes". The rule is explicit and test-enforced (decision 0.5.46), so it wins: default rest is 60 game hours (one real hour). Shortening rest would mean shortening the board period, which is a balance change for Phase 12.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
# Change Request CR-004 — Pilot Duty and Rest
|
||||||
|
|
||||||
|
**Project:** Deadhead: Space Freight Tycoon
|
||||||
|
**Applies to:** a build with Phases 1–10, CR-001, CR-002, and CR-003 complete
|
||||||
|
**Do this:** as **Phase 10D**, after Phase 10C and before Phase 11
|
||||||
|
**Related docs:** `implementation-plan.md`, `change-request-faster-start.md`, `change-request-open-markets.md`, `change-request-map-art.md`, `DECISIONS.md`
|
||||||
|
|
||||||
|
> **Numbering note (added when implemented):** the plan's §0.5 already uses 34–41. This CR's decisions 0.5.34–0.5.38 are recorded as **0.5.42 Duty clock**, **0.5.43 Duty limit**, **0.5.44 Finish the job**, **0.5.45 Rest** and **0.5.46 Rest covers a board refresh**. The error code `pilot_resting` is `pilot-resting` (the API's kebab-case convention).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Why
|
||||||
|
|
||||||
|
Pilots can currently fly back-to-back forever, so there is no rhythm and no reason to run more than one pilot per ship. When a location's board has nothing useful, the only options are to wait with nothing to show for it or to deadhead.
|
||||||
|
|
||||||
|
This adds an ATS-style hours-of-service rule:
|
||||||
|
|
||||||
|
- Each pilot has a **duty clock** that fills up as they fly.
|
||||||
|
- When it's full, the pilot must **rest** for a fixed time on arrival. The ship stays put unless another pilot takes it.
|
||||||
|
- The player can also **rest a pilot early**. Rest always lasts at least one contract board period, so **a rested pilot always wakes up to a fresh board.**
|
||||||
|
- The **Long Haul** skill extends a pilot's duty limit, so leveling it changes the math, not just which jobs unlock.
|
||||||
|
|
||||||
|
## 2. How it works
|
||||||
|
|
||||||
|
| Rule | Detail |
|
||||||
|
|---|---|
|
||||||
|
| Duty clock | Each pilot tracks duty hours (game hours flown since their last rest). |
|
||||||
|
| What counts | Every contract and deadhead trip adds its full travel time. Tutorial runs do not count. |
|
||||||
|
| Duty limit | Base limit from config, plus a bonus per Long Haul level. |
|
||||||
|
| Starting a trip | Allowed whenever the pilot is not resting, even if the trip will take them over the limit. Pilots always finish the job they're on. |
|
||||||
|
| Mandatory rest | If duty hours are at or over the limit when a trip resolves, rest starts automatically at the arrival time. |
|
||||||
|
| Voluntary rest | The player can rest any idle pilot who isn't in flight, at any duty level. |
|
||||||
|
| Rest | Lasts the configured rest duration. When it starts, duty hours reset to zero; the pilot can't fly until it ends. Rest happens wherever the pilot is; no bay is needed. |
|
||||||
|
| Crew swap | A resting pilot can be unassigned (at a bay, per decision 0.5.2), and a rested pilot can be assigned to the same ship. Rest blocks the pilot, not the ship. |
|
||||||
|
| Fresh board | The rest duration in real time is at least the contract board period, so any rest crosses at least one board refresh. |
|
||||||
|
|
||||||
|
Example with defaults (12-hour limit, 1 real minute = 1 game hour): a pilot flies three ~4-hour Moon runs, rests about 15 real minutes, and wakes to a new board. A Mars run (60+ game hours) always ends in a rest, which is negligible next to the trip itself.
|
||||||
|
|
||||||
|
## 3. Rules for this change
|
||||||
|
|
||||||
|
- All rules in `implementation-plan.md` §0.1 apply: steps in order, one commit per step, each step's test must pass, `packages/sim` stays pure, all numbers live in `packages/content`.
|
||||||
|
- Rest is derived from stored timestamps, like dispatches. **No background job is needed to end a rest**: a pilot is resting if the current time is before their rest-ends-at time.
|
||||||
|
- Resetting the dev and test databases is fine. Schema migrations are still required.
|
||||||
|
- Update existing tests whose expectations change. Update `architecture.md` for schema or API changes and `progress.md` after every step.
|
||||||
|
- Stop for the user's review at the end of Phase 10D.
|
||||||
|
|
||||||
|
## 4. Decisions
|
||||||
|
|
||||||
|
Record these in `DECISIONS.md`.
|
||||||
|
|
||||||
|
- **0.5.34 Duty clock.** Pilots accrue duty hours equal to each trip's travel time in game hours, for contracts and deadheads. Tutorial runs accrue nothing.
|
||||||
|
- **0.5.35 Duty limit.** Base duty limit plus a per-level Long Haul bonus, both in config.
|
||||||
|
- **0.5.36 Finish the job.** Dispatch is blocked only while a pilot is resting. A trip that will exceed the limit is allowed; mandatory rest starts on arrival.
|
||||||
|
- **0.5.37 Rest.** Starting rest sets duty hours to zero and sets rest-ends-at to start time plus the rest duration. Mandatory rest starts at the trip's arrival time, so it is correct even if resolution happens later (offline).
|
||||||
|
- **0.5.38 Rest covers a board refresh.** The rest duration converted to real time must be at least the contract board period. This is enforced by a content test.
|
||||||
|
|
||||||
|
## 5. Steps — Phase 10D
|
||||||
|
|
||||||
|
### Step 10D.1 — Content config
|
||||||
|
- Add GameConfig fields: base duty limit (game hours, default 12), Long Haul duty bonus per level (game hours, default 2), rest duration (game hours; default equal to the board period converted to game hours).
|
||||||
|
- **Test:** Config validates. A test converts the rest duration to real time with the current compression and asserts it is at least the board period. Both duty numbers are positive.
|
||||||
|
|
||||||
|
### Step 10D.2 — Sim: duty and rest functions
|
||||||
|
- Add pure functions:
|
||||||
|
- Duty limit for a pilot, from their Long Haul level.
|
||||||
|
- Whether a pilot is resting at a given real time.
|
||||||
|
- Duty hours after a trip (current duty plus trip travel time, unless it's a tutorial run).
|
||||||
|
- Whether a pilot needs mandatory rest after a trip.
|
||||||
|
- Rest window from a start time (rest-ends-at).
|
||||||
|
- **Test:** Duty limit rises by the bonus for each Long Haul level. A pilot is resting one second before rest-ends-at and not resting at it. A tutorial trip leaves duty unchanged. A pilot at 10 of 12 hours who flies a 4-hour trip needs rest; one at 4 of 12 who flies a 4-hour trip does not. For many random start times, a rest window always contains at least one contract board period boundary.
|
||||||
|
|
||||||
|
### Step 10D.3 — Sim: validation and resolution
|
||||||
|
- Dispatch validation gains a `pilot_resting` reason, checked before the other pilot checks.
|
||||||
|
- Dispatch resolution returns the pilot's new duty hours and, if mandatory rest applies, a rest-ends-at time computed from the arrival time.
|
||||||
|
- **Test:** A resting pilot fails validation with `pilot_resting`. A pilot whose duty is over the limit but not resting can still be dispatched. Resolving a trip that pushes duty over the limit returns duty zero and rest-ends-at equal to arrival time plus rest duration, even when resolved hours after arrival. Deadheads accrue duty; tutorial runs don't.
|
||||||
|
|
||||||
|
### Step 10D.4 — Schema and database
|
||||||
|
- Add to pilots: duty hours and rest-ends-at (nullable).
|
||||||
|
- Add the `pilot_resting` error code and a "rest pilot" endpoint to the route catalog.
|
||||||
|
- Player state shows, per pilot: duty hours, duty limit, resting (yes/no), and rest-ends-at.
|
||||||
|
- Dispatch summaries include a "rest started" note when a trip ended in mandatory rest.
|
||||||
|
- Update `architecture.md`.
|
||||||
|
- **Test:** Migrations run on a fresh database and are a no-op the second time. New and changed schemas accept valid examples and reject invalid ones. The route catalog test still passes.
|
||||||
|
|
||||||
|
### Step 10D.5 — Server: apply duty and rest
|
||||||
|
- Resolution (lazy and job-based) writes the pilot's new duty hours and rest-ends-at.
|
||||||
|
- The rest endpoint starts rest now for an idle pilot who is not in flight and not already resting. It works at any duty level.
|
||||||
|
- Dispatch returns `pilot_resting` for a resting pilot.
|
||||||
|
- Assigning and unassigning ignore rest (crew swaps are allowed); only flying is blocked.
|
||||||
|
- **Test:** A pilot who chains Moon runs until over the limit is resting after the last one resolves, and dispatch with them fails with `pilot_resting`. After the rest window passes, the same dispatch succeeds. Resting a pilot by hand sets duty zero and the correct rest-ends-at; resting an in-flight or already-resting pilot fails. A resting pilot can be unassigned at a bay, and a rested pilot assigned to that ship can dispatch immediately.
|
||||||
|
|
||||||
|
### Step 10D.6 — Client: pilot duty on the Pilots screen
|
||||||
|
- Each pilot shows a duty bar (hours used of limit). Resting pilots show "Resting" with a countdown instead.
|
||||||
|
- Idle, non-resting pilots have a "Rest now" action with a confirmation that says rest lasts at least one board refresh.
|
||||||
|
- **Test:** Component test: the duty bar matches state; a resting pilot shows the countdown and no Rest now action; Rest now calls the endpoint.
|
||||||
|
|
||||||
|
### Step 10D.7 — Client: duty in the dispatch flow
|
||||||
|
- In the dispatch sheet, pairs with a resting pilot are disabled with "Resting — mm:ss".
|
||||||
|
- For other pairs, show the pilot's duty after the trip. If it reaches the limit, show "Will need rest on arrival".
|
||||||
|
- **Test:** Component test: a resting pilot's pair is disabled with the countdown; a trip that takes a pilot over the limit shows the warning; one that doesn't, doesn't. Numbers match the sim.
|
||||||
|
|
||||||
|
### Step 10D.8 — Client: board refresh and the "nothing here" case
|
||||||
|
- Every contract board shows "New contracts in mm:ss" until the next board period.
|
||||||
|
- If a location has an idle ship but no contract that ship and its pilot are eligible for, show a short prompt with both options: "Rest [pilot] — wakes to a new board" and "Deadhead (fly empty)".
|
||||||
|
- **Test:** Component test: the countdown matches the next period boundary for the mocked time; with no eligible contracts, the prompt shows both actions and each calls the right endpoint or opens the deadhead picker.
|
||||||
|
|
||||||
|
### Step 10D.9 — Client: rest in summaries and on HQ
|
||||||
|
- The "while you were away" modal shows "Pilot X is resting until …" for trips that ended in mandatory rest.
|
||||||
|
- HQ shows the count of resting pilots and the next pilot to wake up, with a countdown.
|
||||||
|
- **Test:** Component test with seeded state: the modal line appears for a summary with rest started; HQ shows the correct count and the soonest wake time.
|
||||||
|
|
||||||
|
### Step 10D.10 — 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 account after the CR-001 deal, chain Moon runs with the avatar until mandatory rest starts; confirm the ship's location board shows a new set of contracts when the avatar wakes.
|
||||||
|
|
||||||
|
## 6. Amendments to `implementation-plan.md`
|
||||||
|
|
||||||
|
| Step | Change |
|
||||||
|
|---|---|
|
||||||
|
| 0.5 | Add decisions 0.5.34–0.5.38. |
|
||||||
|
| 2.1 | GameConfig gains duty limit, Long Haul duty bonus, and rest duration (10D.1). |
|
||||||
|
| 3.12 | Validation gains `pilot_resting` (10D.3). |
|
||||||
|
| 3.17 | Resolution returns duty hours and rest-ends-at (10D.3). |
|
||||||
|
| 5.3 | Pilots table gains duty hours and rest-ends-at (10D.4). |
|
||||||
|
| 9.5, 9.7 | Duty and rest in the dispatch sheet and Pilots screen (10D.6, 10D.7). |
|
||||||
|
| 12.1 | The simulated player follows rest rules: it rests a pilot when no eligible contract exists at its location and deadheading isn't better. Re-check all CR-001 timing targets with rest in place; adjust content values, not thresholds. Add a check that no pilot is ever dispatched while resting. |
|
||||||
|
| 12.6 | After leveling Long Haul, confirm the pilot's duty limit increased. |
|
||||||
|
|
||||||
|
## 7. Out of scope
|
||||||
|
|
||||||
|
Fatigue penalties, accidents, or slower flying when tired; rest facilities or bay amenities that shorten rest; pilot morale; and anything else in `implementation-plan.md` §0.3.
|
||||||
|
|
@ -97,6 +97,11 @@ These answer the pre-build review. Log each one in `DECISIONS.md` as it is imple
|
||||||
39. **Child bodies** (CR-003). The Moon (and any future location whose position is "parent plus small offset") is drawn at a fixed screen-space offset from its parent, not at its scaled real position, so it never overlaps the parent at any zoom.
|
39. **Child bodies** (CR-003). The Moon (and any future location whose position is "parent plus small offset") is drawn at a fixed screen-space offset from its parent, not at its scaled real position, so it never overlaps the parent at any zoom.
|
||||||
40. **Background** (CR-003). The background is not tiled. It is scaled to cover the viewport with a margin and moves with gentle parallax, clamped so its edges never show.
|
40. **Background** (CR-003). The background is not tiled. It is scaled to cover the viewport with a margin and moves with gentle parallax, clamped so its edges never show.
|
||||||
41. **Fallback** (CR-003). If any asset is missing or fails to load, the map draws the old placeholder for that item and logs one warning. The map never fails to render because of art.
|
41. **Fallback** (CR-003). If any asset is missing or fails to load, the map draws the old placeholder for that item and logs one warning. The map never fails to render because of art.
|
||||||
|
42. **Duty clock** (CR-004). Pilots accrue duty hours equal to each trip's travel time in game hours, for contracts and deadheads. Tutorial runs accrue nothing.
|
||||||
|
43. **Duty limit** (CR-004). Base duty limit plus a per-level Long Haul bonus, both in config.
|
||||||
|
44. **Finish the job** (CR-004). Dispatch is blocked only while a pilot is resting. A trip that will exceed the limit is allowed; mandatory rest starts on arrival.
|
||||||
|
45. **Rest** (CR-004). Starting rest sets duty hours to zero and sets rest-ends-at to start time plus the rest duration. Mandatory rest starts at the trip's arrival time, so it is correct even if resolution happens later (offline).
|
||||||
|
46. **Rest covers a board refresh** (CR-004). The rest duration converted to real time must be at least the contract board period. This is enforced by a content test.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -503,6 +508,12 @@ Steps 10C.1–10C.10 are specified in `memory-bank/change-requests/CR-003-map-ar
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Phase 10D — Pilot Duty and Rest (CR-004)
|
||||||
|
|
||||||
|
Steps 10D.1–10D.10 are specified in `memory-bank/change-requests/CR-004-pilot-duty-rest.md` §5, with its §6 amendments to earlier steps. Done after Phase 10C and 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
|
||||||
|
|
|
||||||
|
|
@ -159,3 +159,18 @@ Spec: `memory-bank/change-requests/CR-003-map-art.md`. New decisions recorded as
|
||||||
- [x] **Step 10C.8 — Ships in flight.** Each flight draws its model's sprite rotated by `shipRotation` (art points up: right = 90°, down = 180°), positioned by sim progress every frame. Routes are faint end to end and brighter ahead of the ship. `separateShips` spreads ships closer than 44 px apart, perpendicular to their route and 44 px apart; hit-testing uses the same positions. The selected ship or location gets an amber ring (`MapCanvas` `selected` prop). Test: `map-ships.test.ts`. **Manual check:** `docs/screenshots/10c8-ships-midgame-{desktop,desktop-zoomed,phone}.png`.
|
- [x] **Step 10C.8 — Ships in flight.** Each flight draws its model's sprite rotated by `shipRotation` (art points up: right = 90°, down = 180°), positioned by sim progress every frame. Routes are faint end to end and brighter ahead of the ship. `separateShips` spreads ships closer than 44 px apart, perpendicular to their route and 44 px apart; hit-testing uses the same positions. The selected ship or location gets an amber ring (`MapCanvas` `selected` prop). Test: `map-ships.test.ts`. **Manual check:** `docs/screenshots/10c8-ships-midgame-{desktop,desktop-zoomed,phone}.png`.
|
||||||
- [x] **Step 10C.9 — Performance.** `map/ticker.ts` `runWhileVisible`: the PixiJS ticker stops when the page is hidden, restarts when visible, and stops on unmount. Canvas resolution capped at 2×. Labels restyle only on change. Big ship crowds (> 3) pack on a hexagonal grid (`hexSpiral`) instead of a long line. Tests: `map-ticker.test.ts`, crowd test in `map-ships.test.ts`. **Measured** (headless Chrome, software WebGL, 10 ships in flight, panning): 58.5 fps at DPR 1 even with CPU throttled 4×; 11 fps at DPR 3 before the 2× cap, 22 fps after — fill-rate bound in software rendering, not JavaScript. **Manual check for the user:** pan and zoom on a real mid-range phone (GPU rendering). Screenshots: `docs/screenshots/10c9-busy-busy-{desktop,desktop-zoomed,phone}.png`.
|
- [x] **Step 10C.9 — Performance.** `map/ticker.ts` `runWhileVisible`: the PixiJS ticker stops when the page is hidden, restarts when visible, and stops on unmount. Canvas resolution capped at 2×. Labels restyle only on change. Big ship crowds (> 3) pack on a hexagonal grid (`hexSpiral`) instead of a long line. Tests: `map-ticker.test.ts`, crowd test in `map-ships.test.ts`. **Measured** (headless Chrome, software WebGL, 10 ships in flight, panning): 58.5 fps at DPR 1 even with CPU throttled 4×; 11 fps at DPR 3 before the 2× cap, 22 fps after — fill-rate bound in software rendering, not JavaScript. **Manual check for the user:** pan and zoom on a real mid-range phone (GPU rendering). Screenshots: `docs/screenshots/10c9-busy-busy-{desktop,desktop-zoomed,phone}.png`.
|
||||||
- [x] **Step 10C.10 — Plan amendments and regression.** CR-003 §6 amendments added to plan steps 10.1, 10.2, 10.3 and 11.1, and to CR-001 Step 10A.16 (§0.5.37–41 were added with the CR). Full suite (629 tests), coverage, lint and web build green. **Review screenshots:** `docs/screenshots/10c10-final-{tutorial,midgame}-{desktop,phone}.png` (the tutorial map also shows the undiscovered locations), plus the per-step shots from 10C.5–10C.9. **Phase 10C complete — stopped for review.**
|
- [x] **Step 10C.10 — Plan amendments and regression.** CR-003 §6 amendments added to plan steps 10.1, 10.2, 10.3 and 11.1, and to CR-001 Step 10A.16 (§0.5.37–41 were added with the CR). Full suite (629 tests), coverage, lint and web build green. **Review screenshots:** `docs/screenshots/10c10-final-{tutorial,midgame}-{desktop,phone}.png` (the tutorial map also shows the undiscovered locations), plus the per-step shots from 10C.5–10C.9. **Phase 10C complete — stopped for review.**
|
||||||
|
|
||||||
|
## Phase 10D — Pilot Duty and Rest (CR-004)
|
||||||
|
|
||||||
|
Spec: `memory-bank/change-requests/CR-004-pilot-duty-rest.md`. New decisions recorded as plan §0.5.42–0.5.46.
|
||||||
|
|
||||||
|
- [ ] Step 10D.1 — Content config
|
||||||
|
- [ ] Step 10D.2 — Sim: duty and rest functions
|
||||||
|
- [ ] Step 10D.3 — Sim: validation and resolution
|
||||||
|
- [ ] Step 10D.4 — Schema and database
|
||||||
|
- [ ] Step 10D.5 — Server: apply duty and rest
|
||||||
|
- [ ] Step 10D.6 — Client: pilot duty on the Pilots screen
|
||||||
|
- [ ] Step 10D.7 — Client: duty in the dispatch flow
|
||||||
|
- [ ] Step 10D.8 — Client: board refresh and the "nothing here" case
|
||||||
|
- [ ] Step 10D.9 — Client: rest in summaries and on HQ
|
||||||
|
- [ ] Step 10D.10 — Plan amendments and regression
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue