- manager: surface worker_thread errors/exit to clients and tear the sim
down (issue 001); cache last status/state and replay on attach so a
reconnecting client sees current state even when paused (issue 002)
- web socket: capped exponential reconnect backoff + bounded command queue
(issue 003); derive ws/wss URL from page origin (issue 004)
- runner: build replacement engine before freeing the old one (issue 005)
- remove dead VesselConfig message path across types/api/web (issue 006)
Tests added for each fix. Full monorepo suite green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The fan-out and router roundtrip tests waited a fixed 180ms for the 20 Hz
loop to emit a State, which was flaky under CPU contention (full turbo
run). Poll for the State event with vi.waitFor instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add @fastify/websocket (v8, Fastify 4) and a GET /ws endpoint. MessageRouter
validates inbound ClientMessages (TypeBox), dispatches to SimulationManager,
binds each connection to its sim_id on Create/Attach, and maps worker events
to ServerMessage frames. REST routes coexist unchanged.
Task: task_05. Tests: 31 passing (real Fastify + WebSocket roundtrip,
malformed-message handling, REST coexistence); branches 82.5%.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Owns every live simulation on the Fastify main thread: spawns one
worker_thread per sim (ADR-002), maps sim_id -> worker + connections,
fans events out to connected clients, enforces a max-sim limit, and
tears orphaned sims down after a configurable TTL (ADR-006). Make
SimulationRunner.dispose idempotent.
Task: task_04. Tests: 19 passing incl. a real worker_thread booting the
engine; coverage 93.6% stmts / 88% branches.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Server-side simulation runtime: SimulationRunner owns one real WASM
engine and its 20 Hz tick loop, applying commands (loadScenario,
play/pause/reset, actuators, environment) and producing snapshots.
attachRunnerToPort bridges it to a worker_thread port; the thread
entrypoint lives in simulation-worker.entry.ts. Adds vitest to apps/api.
Task: task_03. Tests: 12 passing (real engine + MessageChannel bridge),
coverage 98.6% stmts / 89.5% branches.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The legacy-row backfill spread `Record<string,number>` which tsc could not narrow
to InitialCondition, breaking `pnpm --filter @ymir/api build` (and the docker api
image). Construct each InitialCondition explicitly with defaults. Verified
`apps/api` tsc + full `docker compose build` + up (api/web healthy).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add react-router-dom with two routes: "/" → MyScenariosPage (scenario grid with
search, create, open, delete against the scenarios API) and "/editor" → AppShell.
"Sair do cenário" returns to the list. New: use-scenarios hook, ScenarioCard,
MyScenariosPage (+ tests, 235 web tests green).
API: backfill legacy initialConditions rows missing instanceId so GET /scenarios
serializes under the updated schema.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
react-leaflet v5 requires React 19 — caused "render2 is not a function"
runtime crash and white screen. Downgraded to v4.2.1.
fix(docker): separate DB volume mount from API dist directory
Volume was mounted at /app/apps/api, overwriting compiled dist/ with stale
files from previous container runs. Changed mount to /app/data for DB only.
fix(api): copy area.json and vessel1.json into API Docker image
Seed files could not resolve JSON data at container startup.
Added COPY in Dockerfile and corrected path depth from 5 to 4 levels.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces the Area entity: geographic origin (lat/lng = simulation 0,0),
GeoJSON polygon boundary, and environmental constants (gravity, water/air
density, magnetic correction). A simulation cannot run outside its area.
- areas table with origin, polygon (GeoJSON), and environment constants
- Auto-seed from area.json on startup (Baía de Guanabara)
- GET/POST/DELETE /areas endpoints (route → service → repository)
- scenarios.area_id FK — nullable for existing rows, idempotent ALTER migration
- AreaDTO + CreateAreaDTO in @ymir/types; ScenarioDTO gains optional areaId
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds 6 supplementary SQLite tables (physical_props, current, connection_points,
rudders, thrusters, waves) to hold all hydrodynamic data for the VLCC
VLCC_L350_B63_T230 (L=350m, B=63m, T=23m).
- Auto-seed at startup: reads vessel1.json into all tables when DB is empty
- GET /vessels/:id/config endpoint returns full physics config for simulation
- Refactored vessels route to use service + repository layer pattern
- Expanded @ymir/types with VesselConfigDTO and all sub-component schemas
- Simulation worker fetches vessel config on init, passes to WASM loadConfig()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add dist/, *.profraw, .wasm-stage/ to .gitignore
- Remove apps/api/dist/, apps/web/dist/, packages/types/dist/ from git
(generated on build, should not be versioned)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>