Pin the wide layout to the viewport (height:100vh + overflow:hidden on the
root), add min-height:0 to the BODY wrapper and 3-column grid so the flex
chain actually constrains, and make the OPERATIONS panel a flex column whose
PanelContent is the scroll area (header stays pinned; the Panel root's default
overflow:hidden would otherwise clip). Columns' existing overflow-y:auto now
scrolls inside their tracks. Narrow mode keeps natural body flow.
Remove the bottom JUMP ROUTES button list; systems adjacent to the current
one become clickable galactic-chart nodes (same POST /api/jump flow, same
state refreshes). Current + unreachable systems are dimmed and not
clickable. NodeGraph gains optional onNodeClick + per-node disabled
(backward-compatible); two smoke tests updated to click chart fields.
CARGO HOLD // INVENTORY moves out of the LOCAL SYSTEM tab into a persistent
right-column panel; UPCOMING ENCOUNTERS removed; SHIP DATA folded into VESSEL
METRICS (SECTOR + TURN kept with real values, filler dropped). Grid goes
260px/1fr/240px -> 320px/1fr/360px. Cargo state uses the lifted-state pattern:
MissionControl owns /api/system and LocalSystemPanel reports each scan's
response up via onChanged (ShipSystemsPanel style) — no polling.
CI run 361: v0.9.0 replaced the placeholder feed with EventLogPanel, but this
pre-existing assertion still expected 'SHIP LOG // PLACEHOLDER FEED'. Assert a
real T<turn> // <MESSAGE> line instead — every save has the turn-0 new_game
event, so it holds on a fresh stack and across retries.
smoke: jump (shared helper) + scan, then assert both events are in
GET /api/events with the newer one first, and that the EVENT LOG terminal
renders both T<turn> // <MESSAGE> lines. Idempotent across retries — asserts
on this run's own entries by turn/message, never absolute counts. state: the
latest five log lines come back identical (ids included) after the mid-run
container restart.
Self-fetching EventLogPanel (MissionPanel pattern — refetches on each tab
open). One terminal line per event: T<turn> // <MESSAGE>, newest first, with
the event's local time as the timestamp; new_game lines render as system
lines. Placeholder feed + now() removed from mission-control.
expand had no event assertion at all; build and mission-complete asserted
type/message but not the event's turn. Issue #11 requires every action type
to be covered with correct turn/type/message.
lib/game/events.ts: the latest N events of the save, newest first (ORDER BY
id DESC), with parseEventLimit for the optional ?limit= param (default 50,
cap 200, 400 otherwise). Unit tests cover default/accept/reject and
newest-first ordering + slicing.
CI run 358: the ACTIVE assertion used exact text but badge.tsx renders a
'●' dot prefix for ACTIVE variants (text is '●ACTIVE'), so it could never
match. And attempt 1's accept persisted an ACTIVE mission, leaving every
ACCEPT button disabled on retry. Drop exact matching and, when a leftover
active mission exists, assert the UI state and complete that one instead of
re-accepting.
smoke: pick an AVAILABLE contract (non-DELIVER preferred), accept it through
the UI (card flips ACTIVE, button disables), complete via API — scan loop for
EXPLORE/FIND, BFS jump path for DELIVER — assert DONE and the reward landed in
the hold. state: mission board deep-equal across the mid-run container restart.
Self-fetching MissionPanel (world-map pattern — refetches on each tab open,
accept responses applied directly). One card per contract: type title + state
badge, CONTRACT // #id line, objective, reward line, EXPLORE progress line.
ACCEPT enabled only when AVAILABLE and nothing else is ACTIVE. Placeholder
encounter content removed.
Routes call ensureSave first (mission functions assume an initialized save),
map MissionError to 404/409 and non-integer path ids to 400. OpenAPI spec in
code gains both paths + the Mission schema (12 -> 14 paths); openapi.test.ts
covers the new shapes.
New missions table (schema + DDL). Pure model in lib/game/missions.ts:
DELIVER / EXPLORE / FIND contracts generated from missionRng(seed, turn),
board always topped up to >=3 AVAILABLE after every action. save.ts wires
SCHEMA_VERSION 5 -> 6 with a v6 backfill step and completion hooks inside
jumpToSystem/explorePlanet (one atomic persist). Unit tests cover generation
determinism, top-up idempotency, backfill, accept validation and per-type
completion incl. reward accumulation.