Step 18.5: plan amendments and regression
CI / check (push) Successful in 6m25s Details

CR-018 §6: plan 0.5.94, 0.5.96, 0.5.97 and 12.1, CR-015 §3, D5, Steps
15.4 and 15.6, CR-013 Step 10M.3 amended. The e2e flying helper refuels
before each trip now that ships are on Manual.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
fireshaper 2026-10-10 19:25:37 -04:00
parent 424f4583fb
commit 86e511f181
6 changed files with 27 additions and 7 deletions

View File

@ -1611,3 +1611,7 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
- **The warning names the leg** ("Ceres Station needs 928 fu"), so the player knows what the fuel is for; it stays a notice, not a block, and "Refuel here first" in the dispatch sheet is still the main way to refuel. - **The warning names the leg** ("Ceres Station needs 928 fu"), so the player knows what the fuel is for; it stays a notice, not a block, and "Refuel here first" in the dispatch sheet is still the main way to refuel.
- **No warning in flight**: the check is about leaving from where the ship is. - **No warning in flight**: the check is about leaving from where the ship is.
- **Manual comes first in the toggle** and its hint begins "The default". - **Manual comes first in the toggle** and its hint begins "The default".
## Step 18.5
- **The e2e flying helper refuels before every trip**, as a Manual player would. It had relied on Auto top-up, so e2e 12.6 failed on the Step 18.3 and 18.4 commits; the unit suites didn't cover it.

View File

@ -101,7 +101,13 @@ export async function playToDeal(page: Page) {
await expect(skills.getByLabel("Unspent skill points")).toContainText(/Rosa: \d+ points?/); await expect(skills.getByLabel("Unspent skill points")).toContainText(/Rosa: \d+ points?/);
await skills.getByRole("button", { name: "Later" }).click(); await skills.getByRole("button", { name: "Later" }).click();
await expect(skills).toBeHidden(); await expect(skills).toBeHidden();
return state(page); const owner = await state(page);
// Halvorsen's ships come full, on Manual refueling (CR-018).
for (const ship of owner.ships) {
expect(ship.fuelFu, ship.name).toBeCloseTo(ship.stats.tankCapacityFu);
expect(ship.refuelPolicy, ship.name).toBe("manual");
}
return owner;
} }
/** Tries to dispatch; returns the flight, or null if the engine refuses (not eligible). */ /** Tries to dispatch; returns the flight, or null if the engine refuses (not eligible). */
@ -144,6 +150,10 @@ export async function flyUntil(
const pilot = current.pilots.find((p) => p.id === pilotId)!; const pilot = current.pilots.find((p) => p.id === pilotId)!;
if (pilot.restEndsAt !== null) await advanceTo(page, pilot.restEndsAt); if (pilot.restEndsAt !== null) await advanceTo(page, pilot.restEndsAt);
const ship = current.ships.find((s) => s.id === shipId)!; const ship = current.ships.find((s) => s.id === shipId)!;
// Ships are on Manual refueling (CR-018): fill up first, as the player would.
if (ship.fuelFu < ship.stats.tankCapacityFu) {
await run(page, "refuelShip", { shipId }).catch(() => {});
}
let flight: Dispatch | null = null; let flight: Dispatch | null = null;
for (const contract of (await board(page, ship.locationId!)).contracts) { for (const contract of (await board(page, ship.locationId!)).contracts) {
flight = await tryDispatch(page, contract.id, shipId, pilotId); flight = await tryDispatch(page, contract.id, shipId, pilotId);

View File

@ -63,6 +63,7 @@ Record these in `DECISIONS.md` and `implementation-plan.md` §0.5.
### Step 10J.3 — Server: deal creates the Clipper ### Step 10J.3 — Server: deal creates the Clipper
- In the accept-deal transaction (CR-001 Step 10A.8, CR-007 Step 10G.4), also create an owned Clipper at Earth and assign the veteran to it. The veteran is no longer left unassigned. - In the accept-deal transaction (CR-001 Step 10A.8, CR-007 Step 10G.4), also create an owned Clipper at Earth and assign the veteran to it. The veteran is no longer left unassigned.
- **Test:** After accepting the deal, state has exactly two owned ships at Earth (Courier crewed by the avatar, Clipper crewed by the veteran), one Earth bay, the new loan amount, and the veteran at level 2 with Expensive Cargo 1. Both ships can be dispatched immediately. A failed or repeated accept creates no extra ships. - **Test:** After accepting the deal, state has exactly two owned ships at Earth (Courier crewed by the avatar, Clipper crewed by the veteran), one Earth bay, the new loan amount, and the veteran at level 2 with Expensive Cargo 1. Both ships can be dispatched immediately. A failed or repeated accept creates no extra ships.
- **Amended by CR-018:** Both ships are created with full tanks, at Earth's fuel price, on Manual refueling (decisions 0.5.111, 0.5.112; Step 18.3).
### Step 10J.4 — Client: deal modal ### Step 10J.4 — Client: deal modal
- The deal modal lists both ships and the veteran, for example: "Old Halvorsen is retiring. He'll sell you his Courier, his Clipper, and his Earth bay — and Rosa Kell, who's flown that Clipper for twelve years. Pay him back out of every job." Names come from content. Show the new loan amount. - The deal modal lists both ships and the veteran, for example: "Old Halvorsen is retiring. He'll sell you his Courier, his Clipper, and his Earth bay — and Rosa Kell, who's flown that Clipper for twelve years. Pay him back out of every job." Names come from content. Show the new loan amount.

View File

@ -73,7 +73,7 @@ All five locations sell fuel. A `sellsFuel` flag exists in content for future lo
### Refueling ### Refueling
- **Instant**, like ATS. No time cost. - **Instant**, like ATS. No time cost.
- **Refuel policy per ship:** *Auto top-up* (default) fills the tank on every landing at a location that sells fuel; *Manual* never refuels on its own. - **Refuel policy per ship:** *Auto top-up* (default) fills the tank on every landing at a location that sells fuel; *Manual* never refuels on its own. *(Amended by CR-018: Manual is the default; the base fuel price is lowered to 5 cr/fu, decisions 0.5.110, 0.5.112.)*
- If credits can't cover a full top-up, auto top-up buys what the player can afford. - If credits can't cover a full top-up, auto top-up buys what the player can afford.
- **Manual refuel command:** fill to full, or buy a chosen amount, at the ship's current location. - **Manual refuel command:** fill to full, or buy a chosen amount, at the ship's current location.
- The dispatch flow offers **"Refuel here first"** when the ship doesn't have enough fuel for the leg. - The dispatch flow offers **"Refuel here first"** when the ship doesn't have enough fuel for the leg.
@ -113,7 +113,7 @@ Record these in `DECISIONS.md` and `implementation-plan.md` §0.5. New decisions
- **CR-014.D2 Fuel tanks.** Ships carry fuel per §3. Fuel has no mass. - **CR-014.D2 Fuel tanks.** Ships carry fuel per §3. Fuel has no mass.
- **CR-014.D3 Burn and reach.** Fuel for a leg = distance × burn rate ÷ origin range factor. Replaces "effective range from origin" (0.5.60) as the range check; full-tank reach equals the old effective range. - **CR-014.D3 Burn and reach.** Fuel for a leg = distance × burn rate ÷ origin range factor. Replaces "effective range from origin" (0.5.60) as the range check; full-tank reach equals the old effective range.
- **CR-014.D4 Fuel prices.** Fixed per location (§3). Replaces the launch fuel-cost multiplier: launching from Earth is no longer charged extra, because Earth's fuel is the most expensive. - **CR-014.D4 Fuel prices.** Fixed per location (§3). Replaces the launch fuel-cost multiplier: launching from Earth is no longer charged extra, because Earth's fuel is the most expensive.
- **CR-014.D5 Refueling.** Instant. Per-ship policy: Auto top-up (default) or Manual. Auto buys what the player can afford. - **CR-014.D5 Refueling.** Instant. Per-ship policy: Auto top-up (default) or Manual. Auto buys what the player can afford. *(Amended by CR-018: Manual is the default, decision 0.5.112.)*
- **CR-014.D6 Fuel cost of a leg.** Fuel burned × the tank's average purchase price. Pilot profit share (CR-007 0.5.12) uses this instead of the old per-trip fuel cost. - **CR-014.D6 Fuel cost of a leg.** Fuel burned × the tank's average purchase price. Pilot profit share (CR-007 0.5.12) uses this instead of the old per-trip fuel cost.
- **CR-014.D7 Emergency fuel.** Per §3: enough for the cheapest reachable leg, at 2.5× local price, added to the loan. - **CR-014.D7 Emergency fuel.** Per §3: enough for the cheapest reachable leg, at 2.5× local price, added to the loan.
- **0.5.61 (amended) No-strand rule.** Uses fuel available at the destination per §3. - **0.5.61 (amended) No-strand rule.** Uses fuel available at the destination per §3.
@ -143,6 +143,7 @@ Record these in `DECISIONS.md` and `implementation-plan.md` §0.5. New decisions
- Resolution: on arrival, if the ship's policy is Auto top-up and the location sells fuel, apply the affordable refuel. - Resolution: on arrival, if the ship's policy is Auto top-up and the location sells fuel, apply the affordable refuel.
- Pilot pay (CR-007) uses leg fuel cost. - Pilot pay (CR-007) uses leg fuel cost.
- **Test:** Fuel needed from the Moon is the Earth amount ÷ 1.3. Refueling 50 fu at price 1.0 into a tank holding 50 fu bought at 2.0 leaves an average of 1.5. A ship with too little fuel fails with `not_enough_fuel`; after refueling, it passes. Dispatch no longer changes credits. On arrival with Auto top-up, the tank is full and credits drop by the right amount; with Manual, nothing changes. With credits for only half a tank, Auto buys exactly what's affordable. Pilot pay for a leg uses burned fuel × average price. Tutorial dispatches don't touch fuel. - **Test:** Fuel needed from the Moon is the Earth amount ÷ 1.3. Refueling 50 fu at price 1.0 into a tank holding 50 fu bought at 2.0 leaves an average of 1.5. A ship with too little fuel fails with `not_enough_fuel`; after refueling, it passes. Dispatch no longer changes credits. On arrival with Auto top-up, the tank is full and credits drop by the right amount; with Manual, nothing changes. With credits for only half a tank, Auto buys exactly what's affordable. Pilot pay for a leg uses burned fuel × average price. Tutorial dispatches don't touch fuel.
- **Amended by CR-018:** New ships default to Manual; the Auto top-up tests switch the ship to Auto first (Step 18.3).
### Step 14.5 — Sim: stranding and emergency fuel ### Step 14.5 — Sim: stranding and emergency fuel
- Update the `would_strand` check per §3. - Update the `would_strand` check per §3.
@ -155,6 +156,7 @@ Record these in `DECISIONS.md` and `implementation-plan.md` §0.5. New decisions
- Save migration per §5. - Save migration per §5.
- Buying a ship delivers it with a full tank at no extra cost. - Buying a ship delivers it with a full tank at no extra cost.
- **Test:** An older save migrates with every ship full, on Auto top-up. Refuel deducts credits and fills correctly; refuel with no credits fails. Ordering emergency fuel when not available fails; when available, it adds fuel and increases the loan by exactly the cost. A new ship arrives full. - **Test:** An older save migrates with every ship full, on Auto top-up. Refuel deducts credits and fills correctly; refuel with no credits fails. Ordering emergency fuel when not available fails; when available, it adds fuel and increases the loan by exactly the cost. A new ship arrives full.
- **Amended by CR-018:** The migration's ships, and every new ship, are on Manual (save version 3 moves existing ships to Manual); the deal's ships arrive full too (Step 18.3).
### Step 14.7 — Client: fuel in the UI ### Step 14.7 — Client: fuel in the UI
- **Fleet:** each ship card shows a fuel gauge (fu and %) and its refuel policy. Ship detail has Refuel (Fill / Amount), a policy toggle, and, when available, "Emergency fuel (added to loan)". - **Fleet:** each ship card shows a fuel gauge (fu and %) and its refuel policy. Ship detail has Refuel (Fill / Amount), a policy toggle, and, when available, "Emergency fuel (added to loan)".

View File

@ -149,10 +149,10 @@ These answer the pre-build review. Log each one in `DECISIONS.md` as it is imple
91. **Local notifications** (CR-014). On iOS and Android the app schedules a local notification for each arrival, each pilot's rest ending and each upgrade finishing, cancelled if the event changes; several in the same minute become one. Permission is asked right after the deal. The web build has none. 91. **Local notifications** (CR-014). On iOS and Android the app schedules a local notification for each arrival, each pilot's rest ending and each upgrade finishing, cancelled if the event changes; several in the same minute become one. Permission is asked right after the deal. The web build has none.
92. **Save export/import** (CR-014). Export gives one text code (the save, compressed and encoded) to copy or share as a file; import validates and migrates it and replaces the current company after confirmation. 92. **Save export/import** (CR-014). Export gives one text code (the save, compressed and encoded) to copy or share as a file; import validates and migrates it and replaces the current company after confirmation.
93. **Fixed map** (CR-015). Locations have fixed positions from content (polar coordinates around the Sun; the Moon is an offset from Earth); distances never change. `astronomy-engine` is removed. Replaces the ephemeris part of 0.5.9. 93. **Fixed map** (CR-015). Locations have fixed positions from content (polar coordinates around the Sun; the Moon is an offset from Earth); distances never change. `astronomy-engine` is removed. Replaces the ephemeris part of 0.5.9.
94. **Fuel tanks** (CR-015). Each ship carries fuel in a tank (generic units, "fu"), with a capacity and a burn rate per model. Fuel has no mass. New ships come with a full tank. Tutorial runs use no fuel. 94. **Fuel tanks** (CR-015). Each ship carries fuel in a tank (generic units, "fu"), with a capacity and a burn rate per model. Fuel has no mass. New ships come with a full tank. Tutorial runs use no fuel. *(Amended by CR-018, decision 0.5.111:)* the deal's ships too.
95. **Burn and reach** (CR-015, user decision on the curve). Fuel for a leg = burn rate × economic distance (0.5.84) of (distance ÷ origin range factor). A leg can be flown only with at least that much fuel on board; it is removed at departure. Full-tank reach from an origin in million km equals the old effective range (0.5.78), which this replaces as the range check. 95. **Burn and reach** (CR-015, user decision on the curve). Fuel for a leg = burn rate × economic distance (0.5.84) of (distance ÷ origin range factor). A leg can be flown only with at least that much fuel on board; it is removed at departure. Full-tank reach from an origin in million km equals the old effective range (0.5.78), which this replaces as the range check.
96. **Fuel prices** (CR-015). Fixed per location (base price × Jupiter 0.6, Ceres 0.9, Moon 1.1, Mars 1.4, Earth 1.6); a `sellsFuel` flag allows dry locations later. Replaces the launch fuel-cost multiplier. 96. **Fuel prices** (CR-015). Fixed per location (base price × Jupiter 0.6, Ceres 0.9, Moon 1.1, Mars 1.4, Earth 1.6); a `sellsFuel` flag allows dry locations later. Replaces the launch fuel-cost multiplier. *(Amended by CR-018, decision 0.5.110:)* the base price is 5 cr/fu (was 10).
97. **Refueling** (CR-015). Instant. Per ship: Auto top-up (default; fills on landing where fuel is sold, buying what the player can afford) or Manual. A refuel command fills or buys an amount at the ship's location. 97. **Refueling** (CR-015). Instant. Per ship: Auto top-up (default; fills on landing where fuel is sold, buying what the player can afford) or Manual. A refuel command fills or buys an amount at the ship's location. *(Amended by CR-018, decision 0.5.112:)* Manual is the default for new ships, and existing saves move every ship to Manual; Auto is unchanged.
98. **Fuel cost of a leg** (CR-015). Fuel burned × the tank's weighted average purchase price. Pilot profit share (0.5.12 as replaced by CR-010) uses it instead of the per-trip fuel charge. 98. **Fuel cost of a leg** (CR-015). Fuel burned × the tank's weighted average purchase price. Pilot profit share (0.5.12 as replaced by CR-010) uses it instead of the per-trip fuel charge.
99. **Emergency fuel** (CR-015). When an idle ship can't reach any other discovered location and the player can't afford the fuel, they can order enough for the cheapest reachable leg at 2.5× the local price, added to the loan balance (repaid as in 0.5.32). 99. **Emergency fuel** (CR-015). When an idle ship can't reach any other discovered location and the player can't afford the fuel, they can order enough for the cheapest reachable leg at 2.5× the local price, added to the loan balance (repaid as in 0.5.32).
100. **New locations** (CR-016). Venus Cloud City (cloud platform, atmospheric landing, 108M km at 110°, range factor 1.0, fuel × 1.3), Io Sulfur Works (orbital station, any landing, 0.42M km out from Jupiter, factor 1.2, fuel × 1.0) and Europa Ice Station (surface base, any landing, 0.67M km out from Jupiter, factor 1.3, fuel × 0.7). All start undiscovered and are discovered by delivery. 100. **New locations** (CR-016). Venus Cloud City (cloud platform, atmospheric landing, 108M km at 110°, range factor 1.0, fuel × 1.3), Io Sulfur Works (orbital station, any landing, 0.42M km out from Jupiter, factor 1.2, fuel × 1.0) and Europa Ice Station (surface base, any landing, 0.67M km out from Jupiter, factor 1.3, fuel × 0.7). All start undiscovered and are discovered by delivery.
@ -752,6 +752,7 @@ Steps 10M.1–10M.5 are specified in `memory-bank/change-requests/CR-013-veteran
- **Amended by CR-010:** Replace "first pilot hired between 30 and 45 minutes" with "second ship bought between 30 and 45 minutes". Add a check that the veteran flies at least one contract in the first session. Re-check all targets with profit-share pay; adjust content values, not thresholds. - **Amended by CR-010:** Replace "first pilot hired between 30 and 45 minutes" with "second ship bought between 30 and 45 minutes". Add a check that the veteran flies at least one contract in the first session. Re-check all targets with profit-share pay; adjust content values, not thresholds.
- **Amended by CR-012:** The script runs two second-ship policies, Lighter-first and Clipper-first; both must reach "own three ships" within 25 % of each other's time. *(Replaced by CR-013, below.)* - **Amended by CR-012:** The script runs two second-ship policies, Lighter-first and Clipper-first; both must reach "own three ships" within 25 % of each other's time. *(Replaced by CR-013, below.)*
- **Amended by CR-013:** The player starts with two ships (Courier and Clipper) and a 66,600 cr loan; re-check every timing target with them. Targets: first Market purchase 45–75 minutes, first discovery under 90 minutes, loan paid off in under 4 hours of active play. Run two first-purchase policies, Lighter-first and Freighter-first; both must reach "own four ships" within 25 % of each other's time. - **Amended by CR-013:** The player starts with two ships (Courier and Clipper) and a 66,600 cr loan; re-check every timing target with them. Targets: first Market purchase 45–75 minutes, first discovery under 90 minutes, loan paid off in under 4 hours of active play. Run two first-purchase policies, Lighter-first and Freighter-first; both must reach "own four ships" within 25 % of each other's time.
- **Amended by CR-018:** Ships are on Manual refueling: the simulated player taps "Refuel here first" before a leg only when the best job needs more fuel than is aboard (or to go looking for work), instead of relying on Auto top-up. The deal's ships start full. The first Market purchase target is 30–95 minutes (user decision). The balance script also prints each ship's fuel share of the median payout per route (Step 18.1).
- **As built:** `packages/sim/balance/simulate.ts` (run with `pnpm --filter @deadhead/sim balance [seed] [hours]`), tested in `packages/sim/test/balance.test.ts` over seven world seeds: timing targets on the median of the Lighter-first runs (the default player buys the cheaper ship first), the four-ship comparison on both policies' medians, and every invariant in every run. Meeting the targets needed decisions 0.5.84 and 0.5.85 and the tuning in `DECISIONS.md` (Step 12.1). - **As built:** `packages/sim/balance/simulate.ts` (run with `pnpm --filter @deadhead/sim balance [seed] [hours]`), tested in `packages/sim/test/balance.test.ts` over seven world seeds: timing targets on the median of the Lighter-first runs (the default player buys the cheaper ship first), the four-ship comparison on both policies' medians, and every invariant in every run. Meeting the targets needed decisions 0.5.84 and 0.5.85 and the tuning in `DECISIONS.md` (Step 12.1).
### Step 12.2 — Revisit the tutorial economy test ### Step 12.2 — Revisit the tutorial economy test

View File

@ -432,4 +432,6 @@ Spec: `memory-bank/change-requests/CR-018-fuel-tuning.md` (received as "CR-017
- [x] **Step 18.2 — Content: lower the base fuel price.** `baseFuelPricePerFu` 10 → 5 (Earth 8 cr/fu, Jupiter 3); location multiples unchanged. Every ship and route is now at most 25% of the median fitting payout except the Clipper's Mars → Earth (38.8%); the Courier's Earth → Moon is 10.5%. The first Market purchase target is now 30–95 min (user decision): cheaper fuel brings the Clipper's first long landing, and so the first purchase, forward. Balance medians: deal 3.7 min, first purchase 41.6, discovery 39.3, loan 128.1, no violations. Tests: `packages/sim/test/fuel-share.test.ts` (only the Clipper's Mars → Earth is above 25%); `balance.test.ts` (30–95 window); fuel prices updated in `tank.test.ts`, the save 1 → 2 migration test and the Locations fuel test. - [x] **Step 18.2 — Content: lower the base fuel price.** `baseFuelPricePerFu` 10 → 5 (Earth 8 cr/fu, Jupiter 3); location multiples unchanged. Every ship and route is now at most 25% of the median fitting payout except the Clipper's Mars → Earth (38.8%); the Courier's Earth → Moon is 10.5%. The first Market purchase target is now 30–95 min (user decision): cheaper fuel brings the Clipper's first long landing, and so the first purchase, forward. Balance medians: deal 3.7 min, first purchase 41.6, discovery 39.3, loan 128.1, no violations. Tests: `packages/sim/test/fuel-share.test.ts` (only the Clipper's Mars → Earth is above 25%); `balance.test.ts` (30–95 window); fuel prices updated in `tank.test.ts`, the save 1 → 2 migration test and the Locations fuel test.
- [x] **Step 18.3 — Engine: deal ships start full; Manual by default.** `deliveredTank` gives every new ship (deal or Market) a full tank at the bay's price on Manual; `config.deal.tankShare` is gone. Save version 3: the 2 → 3 migration puts every ship on Manual, leaving fuel, prices and credits as they were. The balance simulator gives the deal's ships full tanks, flies on Manual and taps "Refuel here first" only when the best job needs more fuel than is aboard, or to go looking for work. Balance medians (7 worlds): deal 3.7 min, first purchase 36.7 (30–95), discovery 36.6, loan 123.9, four ships 122.7 vs 122.9, no violations. Tests: `packages/engine/test/fuel.test.ts` (the deal's Courier and Clipper full at Earth's price on Manual; a Market ship full on Manual; landing on Manual buys nothing and changes the tank only by the leg; switched to Auto it still tops up on landing; an old save opens with every ship full on Manual); `packages/schema/test/save.test.ts` (2 → 3: every ship Manual, fuel and credits unchanged; 1 → 3 end to end); the deadhead and About tests updated. - [x] **Step 18.3 — Engine: deal ships start full; Manual by default.** `deliveredTank` gives every new ship (deal or Market) a full tank at the bay's price on Manual; `config.deal.tankShare` is gone. Save version 3: the 2 → 3 migration puts every ship on Manual, leaving fuel, prices and credits as they were. The balance simulator gives the deal's ships full tanks, flies on Manual and taps "Refuel here first" only when the best job needs more fuel than is aboard, or to go looking for work. Balance medians (7 worlds): deal 3.7 min, first purchase 36.7 (30–95), discovery 36.6, loan 123.9, four ships 122.7 vs 122.9, no violations. Tests: `packages/engine/test/fuel.test.ts` (the deal's Courier and Clipper full at Earth's price on Manual; a Market ship full on Manual; landing on Manual buys nothing and changes the tank only by the leg; switched to Auto it still tops up on landing; an old save opens with every ship full on Manual); `packages/schema/test/save.test.ts` (2 → 3: every ship Manual, fuel and credits unchanged; 1 → 3 end to end); the deadhead and About tests updated.
- [x] **Step 18.4 — Client: low-fuel warning and policy display.** `longestLeg`/`lowFuel` in `apps/web/src/game.ts`: an idle ship's longest leg on a full tank (its effective stats) to a discovered place, and whether the tank holds less than that leg burns. A Fleet card then shows "Low fuel · {place} needs {n} fu" under the gauge. The fuel sheet's policy toggle lists Manual first and calls it the default. Screenshots: `docs/screenshots/18-4-low-fuel-desktop.png`, `18-4-low-fuel-phone.png` (the deal's Clipper after flying empty to Mars: 160 fu, Ceres needs 928), `18-4-fuel-sheet-phone.png`. Tests (`apps/web/test/low-fuel.test.tsx`): a Clipper at Earth with 1 fu less than its Earth → Mars leg shows Low fuel naming Mars, and Fill clears it; with enough fuel, or with Mars undiscovered, no warning; the fuel sheet shows Manual first and checked, marked as the default. - [x] **Step 18.4 — Client: low-fuel warning and policy display.** `longestLeg`/`lowFuel` in `apps/web/src/game.ts`: an idle ship's longest leg on a full tank (its effective stats) to a discovered place, and whether the tank holds less than that leg burns. A Fleet card then shows "Low fuel · {place} needs {n} fu" under the gauge. The fuel sheet's policy toggle lists Manual first and calls it the default. Screenshots: `docs/screenshots/18-4-low-fuel-desktop.png`, `18-4-low-fuel-phone.png` (the deal's Clipper after flying empty to Mars: 160 fu, Ceres needs 928), `18-4-fuel-sheet-phone.png`. Tests (`apps/web/test/low-fuel.test.tsx`): a Clipper at Earth with 1 fu less than its Earth → Mars leg shows Low fuel naming Mars, and Fill clears it; with enough fuel, or with Mars undiscovered, no warning; the fuel sheet shows Manual first and checked, marked as the default.
- [ ] Step 18.5 — Plan amendments and regression - [x] **Step 18.5 — Plan amendments and regression.** CR-018 §6 applied: plan 0.5.94, 0.5.96, 0.5.97 and Step 12.1; CR-015 §3 (refueling), its CR-014.D5, Steps 15.4 and 15.6; CR-013 Step 10M.3 (deal ships full). The e2e `flyUntil` helper now fills the tank before each trip (it had relied on Auto top-up, so e2e 12.6 failed from Step 18.3 until this fix), and `playToDeal` checks that both deal ships arrive full on Manual. Full suite: typecheck, lint, 1300 unit tests and 9 e2e tests pass. Balance (7 worlds): deal 3.7 min (under 10), first Market purchase 36.7 (30–95), discovery 36.6, loan paid off 123.9, four ships within 25%, no violations.
**Phase 18 is complete; stopped for review.** Manual check for the user: on a fresh company, accept the deal, check both ships are full and on Manual (Fleet → Fuel), fly several Earth–Moon jobs, and check credits grow; a ship short of fuel for its longest leg shows Low fuel on its Fleet card.