Skip to content

ADR 0088: the fresh setup welcome decorates only on TTY

Presentation amendment (2026-08-13; ADR 0279): The content-routing and no-colour decisions survive. An eligible colour TTY with enough room now composes the styled welcome through package Box, Section, Command, Token, and layout APIs. Narrow TTYs, --no-color, NO_COLOR, non-TTY, JSON, and later setup states retain their plain contracts. One process context decides attachment, dimensions, and colour before the pure renderer runs.

Status: accepted; narrows the TTY stance in ADR 0075 and builds on ADR 0086.

Context

§

ADR 0075 made first contact read-only and dual-addressed: a curious human can run discern safely, and a coding agent sees the verify funnel in the same output. It explicitly rejected isTerminal() for deciding who the output addresses, because the guess is unreliable: an agent shell can be a TTY, and a human can pipe output. A wrong guess there sends the wrong message to the wrong reader.

The launch-readiness setup runs showed a separate problem. The fresh welcome is the human's one owned surface before they hand the project to an agent. In a terminal, a plain block is accurate but under-designed for the product's first impression; in a pipe, that same plainness is exactly what agents need. The decision pressure is the asymmetry: sending content to the wrong reader is catastrophic, while a wrong decoration guess is cosmetic.

Decision

§

The fresh setup welcome always carries the same dual-addressed content, but its decoration branches on TTY.

The renderer is pure: it takes an explicit presentation mode such as { tty: boolean }. The command edge reads stdout.isTerminal() once, combines it with the existing --no-color / NO_COLOR plumbing, and chooses the styled TTY render only when stdout is a terminal and colour has not been disabled. Non-TTY output, --no-color, and NO_COLOR all use the plain render. JSON output is unchanged.

The branch is presentation-only:

  • No content routing. Both FOR HUMANS and FOR CODING AGENTS remain present in both renders.
  • No welcome-routing change. This does not decide when the welcome shows.
  • No ANSI on the plain path. Piped output stays clean for agents and tests.
  • No art pass for setup's later states. in_progress and done remain plain; the investment is the fresh first-contact screen.

Consequences

§

The terminal first impression can have hierarchy, a brand treatment, and one dominant action without making the agent path harder to parse. A human who runs discern sees something deliberately designed; an agent that captures stdout receives the same facts as plain text.

The code now has a small renderer seam and parity tests: the styled render is driven directly with { tty: true }, the plain render with { tty: false }, and tests assert that disabling colour resolves to the plain render even when stdout would otherwise be a TTY.

The cost is that content and decoration must stay conceptually separate. Future welcome edits should change the shared copy first, then let both renderers present it; a TTY-only fact would reintroduce the content-routing problem ADR 0075 rejected.

Alternatives considered

§
  • Keep the welcome entirely plain. Rejected: it stays easy to parse but wastes the human's first owned surface.
  • Branch the whole message on TTY. Rejected for the same reason ADR 0075 rejected it: the terminal guess is not reliable enough to decide which reader gets which content.
  • Use colour only, without structure. Rejected: colour alone does not create the hierarchy the welcome needs, and --no-color would collapse the design back to an undifferentiated block.
choose openEsc close