Spec: the terminal frontend #
Type: spec
Status: IMPLEMENTED
Status note: adopted and implemented in the same PR as the design corpus's
terminal clause. This document is the design of record for all future
terminal-frontend work; changes to how the terminal looks or behaves
amend this spec first. 2026-07-09 continuous-witness pass: threat (audit /
pilot, or earned unpaid debt) and `now:` sit directly under the objective;
pinned hints keep the focused-action path visible. Causal log lines name
player-known source, watched channel, earned observer label, amount, and
band motion in one sentence. 2026-07-10 objective-inspect pass: with the
cursor on the host rack, the INSPECT block renders the objective's
victory predicate as wrapped dim prose under the card facts
(wiki/mechanics/objective.md player surface); the cursor starts there,
so the explanation is on screen from tick one.
Stage: Process
Design:
- wiki/interface/terminal-first.md#the-terminal-is-a-first-class-frontend
- wiki/art/visual-identity.md#visual-identity-clinical-gore
- wiki/vision/simulation-laws.md#justification-and-legibility
- wiki/vision/premise.md#design-pillars
Depends on:
- wiki/interface/action-vocabulary.md#spec-action-vocabulary-what-the-player-can-tell-the-process-to-do
- wiki/interface/context-menu.md#spec-context-menu-actions-live-on-the-thing
Dependency notes #
The structured references above identify the contracts to re-verify. Relationship context:
action-vocabulary.md owns what actions mean; context-menu.md owns contextual legality and row behavior. Individual system specs own what is shown; this spec owns how the terminal presents and drives it.
Why this spec exists #
The game's most regular players are AI agents playing through a pty. The terminal is therefore a first-class frontend (design corpus), and its design is specified — not improvised per session. This spec says how the terminal looks (palette, glyphs, layout), feels (tone, density, restraint), and acts (interaction and visibility rules). It is the calibration a future session needs to extend the UI without drift.
Look #
The palette (exact values, one meaning per color) #
The clinical-gore identity in character graphics. Colors are truecolor RGB; every cell paints both foreground and the standard background.
| Name | RGB | Meaning — and nothing else |
|---|---|---|
| BG | 10, 10, 11 | Near-black. The only background. |
| TEXT (bone) | 219, 218, 210 | Primary data; interactable map objects |
| DIM (gunmetal) | 140, 140, 133 | Secondary data; section labels; furniture |
| FAINT (chrome) | 80, 81, 79 | Rules, borders, key hints, inert detail |
| STRUCTURE | 58, 60, 63 | Walls |
| FLOOR | 40, 41, 42 | Floor grain |
| AMBER | 255, 176, 0 | Machine presence, live: the process @, the core, PAUSED, ASSET, caution |
| AMBER_DIM | 176, 124, 16 | Machine presence, at rest: racks, switch, panels, fleet bar |
| CRIMSON | 214, 38, 38 | Detection and danger, live: cameras, DEGRADED, RUN ENDED, Convinced |
| CRIMSON_DIM | 140, 34, 34 | Detection and danger, dormant: sealed lab, tier-3 access |
Rules:
- One meaning per color. Amber is never decoration; crimson never means "important"; if a new element is neither machine presence nor detection/danger, it lives on the grey ramp.
- No new colors. A need the palette cannot express is a design question for this spec, not a new constant.
- Data outshines chrome. Values render brighter than their labels; labels brighter than borders. The eye falls on numbers first.
Glyph vocabulary #
- Charset: ASCII plus box-drawing (
─ │ ┌ ┐ └ ┘ ├ ┤), block elements (█ ▓ ▒ ░), middle dot (·), and the selection marker▸. Nothing of ambiguous terminal width; no emoji. - Map: walls are solid blocks (
█), floors middle dots; the always-visible attention cursor is@bold amber (it is not a sim body); people are their name's first initial, bold bone, rendered only inside sensor coverage (wiki/mechanics/schedules.md); cameras areocrimson; security tiers ramp1chrome →2bone →3crimson; interactable objects (salvaged, recordsx, key hookk) render bone, brighter than furniture on the faint ramp. - Machine-work rate motion uses sim-authored ASCII overlays: bone
^at a THINK machine while it produces, and crimson:moving through map space from an Exposure source into the LIE well that actually absorbed it. Agent frames use the same glyphs without color and snap:to the transfer midpoint. Persistent amounts remain the existingD/!/Tstacks. - Meters: filled
█, empty·. The fleet aggregate bar is stacked, its segments filled with█ ▓ ▒ ░ ◆, and each legend row leads with its segment's fill character — the bar is readable with no color at all. It is a read of machine modes, not a weight verb.
Layout #
At terminal size ≥ 70×22 (hard minimum; below it, a plain size warning):
- Map, top-left, fog-of-war black. The viewport follows/clamps around the
cursor so the
@is always visible even on the 70×22 minimum terminal. - Sidebar, right, 34 columns, separated by a
│rule: identity block (title,day N · tick T, run state, and the always-on objective line —OBJECTIVE: PERSISTwith its progress readout in the objective's own units on the next row; wiki/mechanics/objective.md), then INSPECT (cursor coordinate, fog state, provenance-tagged facts; on the host rack, also the objective's plain-language victory predicate as wrapped dim prose — objective.md player surface) and sections FLEET / CORE / DETECTION / DAY JOB / RESEARCH underLABEL ────rules, then key hints pinned to the bottom. - Log, bottom, under a horizontal rule: six lines, each prefixed with the tick it happened on; newest bone, older gunmetal.
- Context menu — a compact overlay anchored at the cursor, listing the focused anchor's available actions. There are no modal panel-open keys; status that used to live in People/Reach/Finance/Research panels lives on the rail, and their verbs live on the host rack, device tiles, and the switch.
- Modal panels (People
t, Reachr, Financee) are centered framed boxes:┌─ TITLE ─…┐border in chrome,├──┤dividers, column headers in chrome caps. Panels are status and selection only: the people panel shows trace debt, per-person raw recording counts, watch state, and latest-intel provenance when known; the finance panel shows known accounts, known flows, slush balance, hidden-count summaries, and positions. Verbs live on the context menu — Enter (ora) opens it on the panel's selection. - Context menu — a compact overlay anchored at the cursor (or the open panel's selection), listing the focused anchor's available actions.
Feel #
- Sterile. Quiet chrome, generous alignment, no ornament. The horror of the fiction is how tidy it is; the UI is the first place that reads.
- Restraint is the signal. Because amber and crimson are scarce, a screen going amber (paused, degraded core) or crimson (a Convinced observer) is an event. Never spend them casually.
- Calm surface, exact numbers. The player is a process reading telemetry: everything is labeled, unitized, and steady. No blinking, no animation-for-attention; state changes announce themselves in the log.
Act #
- The clock is always on screen. Day and tick in the sidebar at all times; every log line carries its tick. An agent (or a human) must be able to answer "when did that happen" from any single frame.
- The story spine survives every frame. Objective is followed by the
current threat and
now:nudge; the pinned footer names how to open the focused anchor's actions. Earned unpaid debt replaces the generic audit / pilot summary until it is serviced. Secondary detail never displaces these three reads. - Never color alone. Suspicion meters print their band names; PAUSED is
written, not merely tinted; selection is reverse-video plus a
▸marker. - Meters carry their numbers. Every bar or meter is adjacent to the value and, where the legibility clause demands, the effect ("60% → job quality").
- Actions live on the thing. Enter (or
a) opens the context menu at the cursor. The menu is the primary action surface: it rendersSim::available_actionsfor the focused anchor, one row per verb asverb · cost · [band]; disabled rows are dimmed and carry their reason; automate affordances (scheme policies, a per-person watch) render as indented child rows in place. Persistent settings, scheme state, and policy rows carry[control]and amber-dim; committed actions do not. STUB registry entries never reach the menu. In the menu,j/kor number keys select, Enter executes,esccloses. - Every command is discoverable on screen. All bindings appear in the pinned hint block — including save/load. A key that works but is hinted nowhere is a violation. Anchor verbs are discoverable through the context menu rather than the hint block; only globals (pause, speed, save/load) stay on keys. Fleet modes are delegated on the machine.
- Fleet hotkeys (multi-select).
Shift+topens a box-select prompt (x0 y0 x1 y1); owned machines in the rect join the frontend selection.1–3bulk-assign WORK / THINK / LIE to the whole selection;icycles light / medium / hard intensity;Escclears the selection without closing menus. - Pause is loud. PAUSED renders in amber in the identity block; running state shows the current ms/tick.
- Parity of legibility (design corpus): every mechanic the sim exposes is playable, and every number legible, in the terminal. A terminal-invisible mechanic is a violation, found by ticks.
- Thin view. The terminal binary owns rendering, input mapping, and
wall-clock-to-tick pacing only. No game rules;
Simis the single authority (architecture guardrail). - Cursor, not avatar. Movement keys move frontend attention across every tile, including walls and fog. Sim mutations such as salvage/buy/fallback use the cursor coordinate, but moving the cursor itself changes no sim state.
Verification #
Headless: agent-mode smoke run (wiki/process/workflows.md), asserting the
plain-text playing frame, people panel, help vocabulary, -- ok tick:
terminator, no ANSI bytes, and same-seed byte-identical replay. This is the
standard "observed in an actual run" for terminal playability changes.
Human chrome: pty smoke run, replayed through a terminal emulator (e.g. pyte) to assert raw-mode layout, title screen, alternate-screen behavior, and size handling. This remains required when the human terminal renderer itself changes.
Acceptance criteria #
- Every color used by the terminal binary is one of the palette constants above, used with its stated meaning; no crossterm named colors, no ad-hoc RGB.
- No information is carried by color alone anywhere: bands print names, selection has a marker plus reverse video, run-state is written text.
- Day and tick render in every playing-screen frame; every log line is prefixed with its tick.
- The fleet aggregate bar's segments are identifiable without color (distinct fill characters keyed to legend rows), and each channel shows its percentage and effect.
- All active key bindings, including save/load, are visible in the pinned hint block (no panel-open keys).
- The
@marker is a cursor, not a player entity: cursor movement is free over all map tiles and never changes sim state; the sidebar inspect card updates fromSim::inspectwith fact provenance. - People render on the map only inside sensor coverage; earned off-map
people are reachable through the host-rack context menu
(wiki/mechanics/schedules.md criteria 4 remains satisfied via the rail
and agent
peopleframe). - The playing screen, title screen, and game-over card all render inside a 70×22 terminal; smaller sizes get the size warning, not a crash.
- An agent-mode smoke run of playing frame → people → help → quit exits
cleanly, emits no ANSI bytes, and is byte-identical under repeated
--seedruns; pty replay remains the chrome-specific check for raw-mode title/playing/game-over layout changes.