Context
Who: developers running an agent in a terminal, plus the same sessions reviewed in a browser. Jobs: start a task, watch tool calls, approve or deny actions, resume work later. Constraints: the terminal emulator owns background, foreground, and the ANSI palette; the app controls only roles, weight, and layout. The TUI and the web app share one vocabulary, not one token set.
Shared (declared)
Term table — session, turn, tool call, permission, plan, checkpoint. One action keeps one word across both surfaces.
Not shared
Tokens, components, and layout. The TUI never references a web token; the drift check fails on an undeclared cross-surface reference.
Principles
| Rule | Rationale |
|---|---|
| Roles, not values | The host palette cannot be read or set; naming an RGB value would be a lie the app cannot keep. |
| Color is never the only carrier | Every colored state also carries a word or glyph, so a failed or overridden ANSI role cannot hide meaning. |
| High contrast is the default foreground plus weight | No ANSI role is legible on both light and dark hosts; “brighter color” cannot be the escape hatch. |
| The cell is the layout unit | Gaps, indents, truncation, and status collapse are counted in cells, never in pixels. |
| Surfaces stay decoupled at the token layer | A web theme choice that reached the TUI once produced dark text on a dark terminal; the boundary is now a checked rule. |
Roles and advisory contrast
The app emits roles; the numbers below are measured against one named reference palette (xterm-style defaults) and are advisory only. The guarantee is structural: a word or glyph always accompanies color, and a monochrome path always exists.
| Role | ANSI | Used for | on dark host | on light host |
|---|
Verdicts: ok ≥ 4.5 · large only ≥ 3.0 · fails. No role passes on both hosts — that is the reason the fallback is structural, not a color.
TUI scene
A whole session, not a sampler: transcript, inline permission panel, status line. Status content collapses from the outside in as width drops.
Status line collapse (40 / 26 / 18 cells)
Monochrome fallback
Switch the scene above to Monochrome HC to see the system as a user with NO_COLOR or a forced monochrome terminal sees it. Meaning survives through words, glyphs, and weight — never through a brighter color.
Left: color roles. Right: ASCII glyphs with weight as the only emphasis axis. The right column is the guaranteed path; the left is an enhancement when the host allows it.
Surface isolation
The web dashboard below is a different surface with its own tokens. Change the web theme: the dashboard must move, the TUI must not. The check asserts both directions, so a no-op cannot pass.
Web dashboard
session orbit-18 · 4 succeeded · 1 failed · 2 pending
isolation check: idle — press “Run isolation check”.
Glyphs
| Meaning | Primary | ASCII fallback | Notes |
|---|---|---|---|
| running | ⠿ | * | Braille spinner; frozen under reduced motion. |
| done | ✓ | ok | Word form survives fonts without the glyph. |
| failed | ✗ | x | Always paired with the failing check. |
| pending | ◌ | .. | Distinct from dim text alone. |
| needs attention | ◆ | ! | Used for permission and plan state. |
Emoji are excluded: width varies by host and font, which breaks cell alignment and truncation.
Typography
The host owns the font; the TUI owns weight, dim, and alignment. There is exactly one size, so hierarchy is built from emphasis and position, never from a type scale.
- Columns are counted in cells; truncation uses a visible ellipsis so “there is more” is never silent.
- Emoji are excluded from the glyph set — variable width breaks every alignment guarantee.
- Numeric columns stay right-aligned and tabular, so scanning stays vertical.
Components and states
TUI components are line types and panels. Every state below also survives the monochrome host — shape and word carry it when color cannot.
Transcript line types
Panel · focused action
Focus is inverse video: visible with color disabled.
Status line segments
Disabled and unavailable
Unavailable actions stay visible with their reason; they are not hidden.
Motion and accessibility
Motion budget
- One spinner cadence while work is running; it stops the moment the result lands.
prefers-reduced-motionand dumb terminals freeze the spinner at a static glyph.- No transitions, no scroll effects — terminal motion is expensive and distracting.
Accessibility
- Focus is inverse video, visible without color; meaning never rides on an ANSI role.
- The screen is a linear text stream — screen readers get the same order as the eye.
- Every keybinding is discoverable from a status line or help overlay.
- The monochrome high-contrast path is a shipped mode, not a fallback users must invent.
Verification
- Pairs are enumerated from rendered components, per host background — not sampled by hand.
- Advisory numbers are labeled with the reference palette they assume.
- Structural fallbacks are asserted: a no-color rendering, a monochrome rendering, and an ASCII glyph set for every role.
- The drift check rejects any TUI token that resolves to a web token unless the sharing is declared; the check itself is proven by mutating an artifact and confirming failure.