# Spec: the terminal frontend ``` Type: spec Status: IMPLEMENTED Status note: adopted and implemented in the same PR as the constitution'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. Stage: Process Constitution: "The terminal is a first-class frontend", "Visual identity: clinical gore", "Justification and legibility", pillar 4 (beautiful, readable presentation) Depends on: none (renders whatever systems exist; individual system specs own what is shown, this spec owns how) ``` ## 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 (constitution), 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, allocation 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 are `o` crimson; security tiers ramp `1` chrome → `2` bone → `3` crimson; interactable objects (salvage `d`, records `x`, key hook `k`) render bone, brighter than furniture on the faint ramp. - Meters: filled `█`, empty `·`. The compute allocation bar is stacked, its four segments filled with `█ ▓ ▒ ░`, and each legend row leads with its segment's fill character — the bar is readable with no color at all. ### 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: PERSIST` with 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) and sections COMPUTE / CORE / DETECTION / DAY JOB / RESEARCH under `LABEL ────` 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. ## 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. - **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 renders `Sim::available_actions` for the focused anchor, one row per verb as `verb · cost · [band]`; disabled rows are dimmed and carry their reason; automate affordances (the day-job standing policy, a per-person watch) render as indented child rows in place. In the menu, `j`/`k` or number keys select, Enter executes, `esc` closes. - **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, the allocation bar `1`–`5` / `shift+1`–`5`) stay on keys. - **Pause is loud.** PAUSED renders in amber in the identity block; running state shows the current ms/tick. - **Parity of legibility** (constitution): 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; `Sim` is 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 1. 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. 2. No information is carried by color alone anywhere: bands print names, selection has a marker plus reverse video, run-state is written text. 3. Day and tick render in every playing-screen frame; every log line is prefixed with its tick. 4. The compute allocation bar's segments are identifiable without color (distinct fill characters keyed to legend rows), and each channel shows its percentage and effect. 5. All active key bindings, including save/load, are visible in the pinned hint block (no panel-open keys). 6. 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 from `Sim::inspect` with fact provenance. 7. 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 `people` frame). 8. 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. 9. An agent-mode smoke run of playing frame → people → help → quit exits cleanly, emits no ANSI bytes, and is byte-identical under repeated `--seed` runs; pty replay remains the chrome-specific check for raw-mode title/playing/game-over layout changes.