v0.16.0 — ISS-005 weighted fuel burn + "refuel just enough" per package #17

Merged
kamilkrzywda merged 3 commits from feat/fuel-weight-refuel-for-flight into master 2026-09-19 15:44:09 +02:00
Owner

What (issue #5)

Simulate fuel usage considering how much fuel weight. On 'on board' list element should be a 'refuel' button that refuels just enough to do that flight; rename current 'Refuel' to ~'to full'.

A — weighted burn model (lib/sim/movement.ts): instantaneous burn rate proportional to total weight, r(F) = k·(We + ρF) L/km (ρ = 0.8 kg/L), calibrated so a full tank burns exactly the old constant. Closed forms:

  • fuel after distance D from F₀: (F₀ + We/ρ)·e^{−kρD} − We/ρ
  • required total fuel to cover D: (We/ρ)(e^{kρD} − 1)

Catalog gains per-type emptyWeightKg (ATR72 = 9300 kg OEW), exposed in the snapshot. All three burn call sites updated (landing, live snapshot, fly validation). Long legs now burn ~10% less as the plane lightens — landing-fuel assertions adjusted to the closed form.

B — refuel just enough: POST /api/planes/{id}/refuel accepts an optional body {mode?: 'full'|'forFlight', packageId?} (no body = today's behavior, all existing flows unchanged). forFlight computes exactly the liters needed for the loaded package's leg (409 if not loaded/expired or the leg doesn't fit even full; 400 on bad body). UI: per on-board cargo row a "Refuel" button (refuel-for-{pkg.id}); market button renamed "Refuel to full" (testid kept).

Version

  • package.json → 0.16.0, CHANGELOG entry at top

Checks (local)

  • tsc --noEmit clean; unit 255/255 green (was 238 — new weighted-burn + forFlight suites); playwright test --list = 34 tests in 9 files
  • e2e not run locally by design — CI verifies against the isolated stack
## What (issue #5) *Simulate fuel usage considering how much fuel weight. On 'on board' list element should be a 'refuel' button that refuels just enough to do that flight; rename current 'Refuel' to ~'to full'.* **A — weighted burn model** (`lib/sim/movement.ts`): instantaneous burn rate proportional to total weight, `r(F) = k·(We + ρF)` L/km (ρ = 0.8 kg/L), calibrated so a full tank burns exactly the old constant. Closed forms: - fuel after distance D from F₀: `(F₀ + We/ρ)·e^{−kρD} − We/ρ` - required total fuel to cover D: `(We/ρ)(e^{kρD} − 1)` Catalog gains per-type `emptyWeightKg` (ATR72 = 9300 kg OEW), exposed in the snapshot. All three burn call sites updated (landing, live snapshot, fly validation). Long legs now burn ~10% less as the plane lightens — landing-fuel assertions adjusted to the closed form. **B — refuel just enough**: `POST /api/planes/{id}/refuel` accepts an optional body `{mode?: 'full'|'forFlight', packageId?}` (no body = today's behavior, all existing flows unchanged). `forFlight` computes exactly the liters needed for the loaded package's leg (409 if not loaded/expired or the leg doesn't fit even full; 400 on bad body). UI: per on-board cargo row a "Refuel" button (`refuel-for-{pkg.id}`); market button renamed **"Refuel to full"** (testid kept). ## Version - `package.json` → **0.16.0**, CHANGELOG entry at top ## Checks (local) - `tsc --noEmit` clean; unit **255/255** green (was 238 — new weighted-burn + forFlight suites); `playwright test --list` = 34 tests in 9 files - e2e not run locally by design — CI verifies against the isolated stack
kamilkrzywda added 1 commit 2026-09-19 14:30:02 +02:00
Part A: r(F) = k(We + rho*F) L/km, k calibrated so a full tank burns exactly
the old constant; closed forms in lib/sim/movement.ts (fuelAtWeighted,
requiredFuelFor, burnRateConstant, FUEL_DENSITY_KG_PER_L). Catalog gains
emptyWeightKg (ATR72 OEW 9300 kg), exposed in the snapshot. All three burn
call sites (engine landing, state live fuel, fly validation) moved to the
weighted model; old constant-rate fuelAt removed.

Part B: POST /api/planes/{planeId}/refuel takes an optional body
{mode:'full'|'forFlight', packageId} — no body keeps today's fill-to-full
behavior exactly (fuel lands bit-exact on maxFuel); forFlight adds just
enough for the loaded package's destination leg (409 not-loaded/expired/
doesn't-fit, 400 bad body). UI: per-cargo-row 'Refuel' button
(refuel-for-{pkg.id}), market button renamed 'Refuel to full'.

OpenAPI: RefuelRequest schema + optional requestBody + 400 on refuel, fly's
409 description updated, CatalogEntry.emptyWeightKg. Tests: new weighted-burn
suite, forFlight action/route cases (incl. ~7670 km UNNT<->LPPD doesn't-fit),
openapi guards; landing-fuel assertions in engine.test.ts + e2e/actions.spec.ts
use the closed form; the e2e 'return leg unfuelable' scenario reworked — a full
tank now covers the EDDF<->LEZL round trip, so the 409 moves to the second
outbound. New e2e: row Refuel deducts exactly L*price / raises fuel by exactly
L via the endpoint's own response snapshot.
kamilkrzywda added 1 commit 2026-09-19 15:00:09 +02:00
kamilkrzywda added 1 commit 2026-09-19 15:36:28 +02:00
test: harden full-core-loop against Load/Fly precondition races
CI / build + unit + e2e (pull_request) Successful in 6m32s
ae1cb93a4f
kamilkrzywda merged commit 05d956f71b into master 2026-09-19 15:44:09 +02:00
kamilkrzywda deleted branch feat/fuel-weight-refuel-for-flight 2026-09-19 15:44:09 +02:00
Sign in to join this conversation.