# roundel/policy

> Every export of roundel/policy, with its signature and doc comment: colorLevel, outputMode, flown, plus 7 types.

Source: https://roundel.interlace.tools/docs/api/policy

<!-- Generated by scripts/api-reference.ts from the built dist/*.d.ts. Do not edit; run `npx tsx scripts/api-reference.ts`. -->

The output policy — one answer to "where is this output going?", read from a
`Runtime`, never from `process` (R1, R2, R9 of `roundel`; U2 of the stack).

Five colour libraries disagreeing about the terminal (clack #286) is what this file
exists to end: every package in the family asks here, and nothing else in it reads
`isTTY`, `NO_COLOR`, `FORCE_COLOR`, `CI`, `CLI_ACCESSIBLE` or a `--color` flag. It also
holds the one record `fly()` writes and every token reads, because it is the only module
every subpath may import (R7).

The code here is written tight — ternaries where a reader might expect statements —
because `./chalk` reaches this file and R8 caps that whole graph at chalk 6's own
9,370 bytes. Comments are stripped from `dist`, so the prose is free; the statements
are not.

```ts
import { colorLevel, outputMode, flown } from 'roundel/policy';
```

## Functions

### colorLevel

chalk's level, obeying the user's explicit instruction in any mode (R2 revised
2026-09-08): `NO_COLOR` wins outright; then `FORCE_COLOR=0`, which supports-color settles
*before* it looks at any flag, so `FORCE_COLOR=0 --color=256` is off and not 2 — an
explicit "colour off" is never undone by a level flag; then the `--color` flags in
`argv`, an exact `--color=256` beating a numeric `FORCE_COLOR` as chalk's own suite says;
then `FORCE_COLOR`. `--json` is the one output the level never enters: structured text
carries no escapes. The mode decides redraws (U2), never the level.

Accessible mode defaults to 0, on the same footing as a pipe rather than a terminal:
`CLI_ACCESSIBLE` is itself an explicit instruction from a human, and ANSI colour is noise
to a screen reader. As with a pipe, an explicit colour ask (`FORCE_COLOR`, `--color=…`)
still wins and `NO_COLOR` still beats everything.

With no instruction the order is supports-color's own, and deliberately so — chalk's
`level.js` asserts it, and a family that disagreed with chalk about a bare pipe would be
the clack #286 bug again. A pipe is 0, because a pipe nobody asked to colour is a file or
another program's stdin; the one exception is Azure Pipelines (`TF_BUILD` *and*
`AGENT_NAME` — `TF_BUILD` alone is a build without an agent), which supports-color reads
before it gives up on a pipe. Once colour *is* being detected — a terminal, or a run that
asked — `TERM=dumb` is the floor, a `CI` run is its vendor's level (gated on `'CI' in env`
as supports-color gates it, so an empty `CI=` still selects the table), and anything else
is what `TERM`/`COLORTERM` report. So the CI user who exports `FORCE_COLOR=true` to get
coloured logs gets their runner's colours, and nobody else's pipe changes.

ponytail: supports-color also consults the platform, TEAMCITY_VERSION, TERM_PROGRAM and
the emulator allow-list; R2 refuses that detection, so those stay unread.

```ts
function colorLevel(rt: Runtime, opts?: ModeOptions): ColorLevel;
```

| Parameter | Type |
| :-- | :-- |
| `rt` | `Runtime` |
| `opts` (optional) | `ModeOptions` |

**Returns** `ColorLevel`

## Constants

### flown

What `fly()` decided, once, for the process: the colour level and each token's paint.
Every token reads it; nothing writes it but `fly()`. Until then the level is 0 and every
token is the identity, so a program that never declares its runtime prints plain text.

```ts
const flown: {
    level: ColorLevel;
    paint: Partial<Record<TokenName, Paint>>;
};
```

### outputMode

First match wins: `json` if asked; `accessible` if `CLI_ACCESSIBLE`; `ci` if `CI` and not
a TTY; `pipe` if not a TTY; else `tty`.

```ts
const outputMode: (rt: Runtime, opts?: ModeOptions) => OutputMode;
```

## Interfaces

### ModeOptions

```ts
interface ModeOptions {
    /** Whether this run was asked for `--json`: the engine's knowledge, not the process's. */
    json?: boolean;
}
```

### Runtime

The slice of a runtime the policy needs. burgee's `processRuntime` satisfies it, so does
a two-line literal in a test; nothing here imports a type from anywhere.

```ts
interface Runtime {
    env: Record<string, string | undefined>;
    isTTY: {
        stdout: boolean;
    };
    /**
     * The process arguments, when the caller owns them: `--color`, `--no-color` and
     * `--color=…` are read in `policy.ts` and nowhere else. A test literal leaves it out.
     */
    argv?: readonly string[];
}
```

## Types

### ColorLevel

chalk's levels: none, 16 colours, 256 colours, truecolor.

```ts
type ColorLevel = 0 | 1 | 2 | typeof MAX_LEVEL;
```

### Format

One `util.styleText` format name: `'bold'`, `'red'`, `'redBright'`, `'dim'`…

```ts
type Format = import('node:util').InspectColor;
```

### OutputMode

```ts
type OutputMode = 'tty' | 'pipe' | 'json' | 'accessible' | 'ci';
```

### Paint

A style as a token paints it: `styleText` format names, or the SGR parameters of one
foreground colour (`38;5;n` for 256 colours, `38;2;r;g;b` for truecolor), which
`styleText` cannot express.

```ts
type Paint = readonly Format[] | {
    readonly sgr: readonly number[];
};
```

### TokenName

```ts
type TokenName = 'error' | 'warn' | 'ok' | 'hint' | 'muted' | 'command' | 'flag' | 'value' | 'heading';
```
