Clients, API, engine¶
designed · Phase 1 (#39, #40, #41) · PLAN §13.1–§13.4
browser ─── http://localhost:3000 ─── Vite dev server (clients/app) ── /api proxy ──▶ ASP.NET Core API (src/NextIteration.Api, :5000)
│ │
├── / Universe Viewer (the chronicle — players) ├── SimulationHost: one thread ticks; reader-writer lock vs. API reads
└── /dev Developer Studio (the instrument panel — developers) ├── Engine: graph, pipeline, resolver, clock, bus, trace, snapshots
├── PlayerActionQueue ◀── POST /worlds/{id}/player-actions
◀── SignalR /worlds/{id}/events, /worlds/{id}/interest ─────────────────────┤
└── saves/{universeId}/tick_N.json, trace/*.jsonl
No orchestrator and no containers in Phase 1 (PLAN §13.1). The harness and the tests run the engine in-process and never go through the API.
Who reads and writes what¶
| Surface | Reads | Writes | Notes |
|---|---|---|---|
Viewer / |
graph (/graph, /entities), events stream, interest stream, Explain, History, snapshots |
player actions (Tinkerer only), speed, rewind | Speaks world time and prose only; never a tick number or an attribute key (CLAUDE.md → PLAYER-FACING LEGIBILITY). |
Studio /dev |
everything above plus /systems (timing), WhyChanged, raw events, saves |
system toggles, player actions, save/load, step | May show raw data, paired with a readable summary. |
| API | engine state under the read lock | the player-action queue; toggles at tick boundaries | All mutation is a player action → intent; no endpoint writes the graph directly. |
| Engine | its own graph and trace | graph (resolver), trace (recorder), saves | Ticks on one thread; API reads take the read lock so they never observe a half-applied batch. |
The two routes are one app¶
clients/app is a single React + TypeScript app with two routes sharing src/api/ (typed REST client, SignalR hooks) and src/types/. The Studio embeds a small instance of the Viewer in its preview slot, so any improvement to the Viewer shows up in the Studio.
Observability¶
OpenTelemetry instrumentation only — ActivitySource spans tick → phase → system, Meter metrics, ILogger scopes — with no exporter configured by default. Per-system timing is returned by GET /universes/{id}/systems for the Studio's panel; an OTLP viewer (the standalone Aspire dashboard, Jaeger, Seq) can attach by setting OTEL_EXPORTER_OTLP_ENDPOINT (PLAN §13.2).
See also¶
API · Player actions · Persistence · Player: Ways to play