v0.54.0 — long priority jobs are now always finishable (#119) #125

Merged
kamilkrzywda merged 1 commits from feat/expiry-floor into master 2026-09-22 17:31:52 +02:00
Owner

Closes #119

Fix. generatePackage (lib/game/seed.ts) drew a flat 6–72 h expiry window (priority ×0.5 → 3–36 h) independent of leg distance, so some jobs' deadlines were shorter than the flight itself and could never be completed (legs up to MAX_JOB_LEG_KM = 9000 km; fastest catalog plane ~920 km/h ≈ 10 h for a max leg). The drawn window is now clamped UP after special scaling:

deadline ≥ now + travelDurationMs(legKm, FASTEST_CATALOG_SPEED_KMH) + EXPIRY_BUFFER_MS (flat 1 h buffer), reusing the existing travelDurationMs from lib/sim/movement.ts. New data-driven constant FASTEST_CATALOG_SPEED_KMH in lib/game/catalog.ts (max of PLANE_TYPES speeds — a job must be finishable with the best available aircraft, not the starter's 510 km/h). The window is a single uniform draw, so clamping up covers both requirements: short legs are byte-identical to before, and when the raw draw falls below the floor the deadline lands exactly on it — always ≤ the scaled ceiling for any pair of airports on Earth (max leg ~20000 km → floor ≈ 22.8 h < the 36 h priority ceiling), so no separate ceiling raise is needed. Forced deadlineOffsetMs skips the floor, same as it skips special scaling.

Tests. Unit +5 in lib/game/seed.test.ts (new "expiry floor" block): constants pinned; seeded sweep over 200 unforced packages on the real dataset asserting the invariant for every one; farthest within-cap pair (~9000 km) forced with priority and null across 50 seeds each — invariant holds everywhere and at least one package per special lands exactly on the floor (deterministic, proves the clamp engages); short legs pinned byte-identical to a replay of the pre-v0.54.0 draw order (no economy regression); forced offsets still skip the floor. E2E +1 in e2e/expiry.spec.ts: existing POST /api/test/create-package hook forces EDDB→KSMF (~9000 km) with special: 'priority'; 10 packages created, every one asserted to expire ≥ travel time at max speed + buffer (and ≤ the 36 h priority ceiling), then all ignored for cleanup — no new server hooks.

Local verification: tsc --noEmit clean; vitest 364/364 in 30 files (baseline 359); playwright --list compiles, 59 tests in 15 files.

Closes #119 **Fix.** `generatePackage` (lib/game/seed.ts) drew a flat 6–72 h expiry window (priority ×0.5 → 3–36 h) independent of leg distance, so some jobs' deadlines were shorter than the flight itself and could never be completed (legs up to MAX_JOB_LEG_KM = 9000 km; fastest catalog plane ~920 km/h ≈ 10 h for a max leg). The drawn window is now clamped UP after special scaling: `deadline ≥ now + travelDurationMs(legKm, FASTEST_CATALOG_SPEED_KMH) + EXPIRY_BUFFER_MS` (flat 1 h buffer), reusing the existing `travelDurationMs` from lib/sim/movement.ts. New data-driven constant `FASTEST_CATALOG_SPEED_KMH` in lib/game/catalog.ts (max of PLANE_TYPES speeds — a job must be finishable with the best available aircraft, not the starter's 510 km/h). The window is a single uniform draw, so clamping up covers both requirements: short legs are byte-identical to before, and when the raw draw falls below the floor the deadline lands exactly on it — always ≤ the scaled ceiling for any pair of airports on Earth (max leg ~20000 km → floor ≈ 22.8 h < the 36 h priority ceiling), so no separate ceiling raise is needed. Forced `deadlineOffsetMs` skips the floor, same as it skips special scaling. **Tests.** Unit +5 in lib/game/seed.test.ts (new "expiry floor" block): constants pinned; seeded sweep over 200 unforced packages on the real dataset asserting the invariant for every one; farthest within-cap pair (~9000 km) forced with priority and null across 50 seeds each — invariant holds everywhere and at least one package per special lands exactly on the floor (deterministic, proves the clamp engages); short legs pinned byte-identical to a replay of the pre-v0.54.0 draw order (no economy regression); forced offsets still skip the floor. E2E +1 in e2e/expiry.spec.ts: existing `POST /api/test/create-package` hook forces EDDB→KSMF (~9000 km) with `special: 'priority'`; 10 packages created, every one asserted to expire ≥ travel time at max speed + buffer (and ≤ the 36 h priority ceiling), then all ignored for cleanup — no new server hooks. Local verification: `tsc --noEmit` clean; vitest 364/364 in 30 files (baseline 359); playwright --list compiles, 59 tests in 15 files.
kamilkrzywda added 1 commit 2026-09-22 17:22:07 +02:00
kamilkrzywda scheduled this pull request to auto merge when all checks succeed 2026-09-22 17:25:10 +02:00
kamilkrzywda merged commit d53ad41c09 into master 2026-09-22 17:31:52 +02:00
kamilkrzywda deleted branch feat/expiry-floor 2026-09-22 17:31:52 +02:00
Sign in to join this conversation.