ISS-043 (part 1): flight queue backend — data model, auto-chain on landing, add/cancel endpoints #50

Merged
kamilkrzywda merged 2 commits from feat/flight-queue-backend into master 2026-09-20 13:04:17 +02:00
Owner

Part 1 of #43 "Add ability to chain flights in queue" (backend only; UI + map rendering land in part 2).

What changed

  • Data model: new queue TEXT NOT NULL DEFAULT '[]' column on planes — an ordered JSON array of ICAOs = the not-yet-started legs. The active leg stays as today (destIcao + departureSimTime). Guarded migration in migrateColumns() for existing DBs; seed sets [].
  • Auto-chain: in deliverPackages(), on arrival a plane with a non-empty queue auto-starts the next queued leg (stays 'flying') if fuel covers it; otherwise grounds safely with the remaining queue intact. Cargo delivery still runs first, unchanged. Composes correctly with v0.24.0 pause-on-landing — a chained plane counts as airborne in the fresh status='flying' select, so no premature pause mid-chain (new regression test).
  • Endpoints: POST /api/planes/{planeId}/queue {destIcao} (append a not-yet-started leg; works grounded or flying) and POST /api/planes/{planeId}/queue/cancel {index} (remove one queued leg). Both run through performAction (tick-first + lock), mirror the /fly validation, return the full snapshot.
  • OpenAPI: PlaneState.queue, two new request schemas, both paths — spec updated per AGENTS.md.

Tests

+12 unit tests: queueLeg/cancelQueuedLeg (order, works-while-flying, 404/400, index removal, OOB/negative), auto-chain (chain w/ fuel, ground-on-short-fuel with queue intact, empty-queue unchanged, chained plane does NOT trigger last-plane pause). vitest 303 passing. No e2e in this part (UI lands next).

Version: 0.24.0 → 0.25.0

Part 1 of #43 "Add ability to chain flights in queue" (backend only; UI + map rendering land in part 2). ## What changed - **Data model:** new `queue TEXT NOT NULL DEFAULT '[]'` column on `planes` — an ordered JSON array of ICAOs = the not-yet-started legs. The active leg stays as today (`destIcao` + `departureSimTime`). Guarded migration in `migrateColumns()` for existing DBs; seed sets `[]`. - **Auto-chain:** in `deliverPackages()`, on arrival a plane with a non-empty queue auto-starts the next queued leg (stays `'flying'`) if fuel covers it; otherwise grounds safely with the remaining queue intact. Cargo delivery still runs first, unchanged. Composes correctly with v0.24.0 pause-on-landing — a chained plane counts as airborne in the fresh `status='flying'` select, so no premature pause mid-chain (new regression test). - **Endpoints:** `POST /api/planes/{planeId}/queue {destIcao}` (append a not-yet-started leg; works grounded or flying) and `POST /api/planes/{planeId}/queue/cancel {index}` (remove one queued leg). Both run through `performAction` (tick-first + lock), mirror the `/fly` validation, return the full snapshot. - **OpenAPI:** `PlaneState.queue`, two new request schemas, both paths — spec updated per AGENTS.md. ## Tests +12 unit tests: queueLeg/cancelQueuedLeg (order, works-while-flying, 404/400, index removal, OOB/negative), auto-chain (chain w/ fuel, ground-on-short-fuel with queue intact, empty-queue unchanged, chained plane does NOT trigger last-plane pause). vitest 303 passing. No e2e in this part (UI lands next). Version: 0.24.0 → **0.25.0**
kamilkrzywda added 1 commit 2026-09-20 12:40:51 +02:00
- planes.queue TEXT NOT NULL DEFAULT '[]' — ordered JSON array of not-yet-started
  leg ICACs; active leg stays destIcao + departureSimTime. DDL + guarded ALTER
  migration for existing DBs; seed sets queue: [] explicitly.
- deliverPackages() auto-chains on arrival: with a non-empty queue and fuel
  covering the first queued leg (same requiredFuelFor rule as /fly), the plane
  immediately starts that leg from the arrival airport (stays 'flying',
  departureSimTime = arrival instant, queue shortened). Short tank → grounds
  safely with the queue kept intact; empty queue → unchanged grounding. Cargo
  delivery for the reached airport runs first and is unaffected. Ordering kept:
  delivery still runs before tickOnce's pause check, so a chained plane counts
  as still flying — no premature last-plane pause (ISS-041 preserved).
- New endpoints via performAction(): POST /api/planes/{planeId}/queue {destIcao}
  (append; works grounded or flying; 400 bad body/unknown ICAO, 404 unknown
  plane) and POST /api/planes/{planeId}/queue/cancel {index} (remove queued leg
  by index; active in-flight leg never affected; 400 non-integer/out-of-range,
  404 unknown plane). Both return the full state snapshot.
- PlaneState.queue: string[] in /api/state (null/malformed-safe parse helper
  shared with the engine); OpenAPI updated (PlaneState.queue, QueueLegRequest,
  CancelQueuedLegRequest, both paths) and openapi.test.ts expectations extended.
- Tests: auto-chain block in engine.test.ts (chain / short-fuel ground / empty
  queue) + chained-plane-no-pause regression guard; queueLeg/cancelQueuedLeg
  blocks in actions.test.ts; type-compat queue: [] fixtures in deriveEvents,
  right-pane, mapData and seed tests.

v0.25.0 — part 1 (backend) of ISS-043; UI/map rendering lands in a follow-up.
kamilkrzywda scheduled this pull request to auto merge when all checks succeed 2026-09-20 12:41:08 +02:00
kamilkrzywda added 1 commit 2026-09-20 12:57:58 +02:00
ISS-043: add queue endpoints to e2e OpenAPI path list
CI / build + unit + e2e (pull_request) Successful in 6m17s
537c494daa
kamilkrzywda merged commit 3395fb4f90 into master 2026-09-20 13:04:17 +02:00
kamilkrzywda deleted branch feat/flight-queue-backend 2026-09-20 13:04:17 +02:00
Sign in to join this conversation.