RLViz interaction specification (v1)

Binding spec for the viewer's structure and behavior. Supersedes ui-information-architecture.md and the layout assumptions in trajectory-workspace-v2.md (both retained as historical evidence). Working reference implementations of the interaction model live in web/concepts/*.html — treat them as executable spec for feel and keys, not as production code or visual design.

The two tests for any screen: (1) it must be describable in this spec's vocabulary; (2) it must survive being rendered as a TUI without losing its structure.

1. Modes

Three modes, one overlay class. No other top-level surfaces.

Browse is the hub; every descent remembers the Browse state (filter, projection, queue position) it came from.

2. Read mode at t=0

Deterministic layout per source type (a hanging protocol, never user-rearranged panes). Reading order:

No transcript wall at t=0; prose is descended into.

3. Selection model

4. Depth, fidelity, zoom (three orthogonal controls)

5. Browse / triage

6. Compare

7. Replay

Exists for hindsight control only: truncates everything after the playhead so the reader judges the next action against exactly what the model could see, then reveals it ( step, optional auto-advance). All lanes truncate together. Sequence-only sources replay by reveal-in-order; no wall clock implied. Exit restores the record, playhead becomes selection.

8. Derived-information tiers

9. Command grammar

Stable command IDs in the existing registry (web/src/commands.ts); bindings are remaps; IDs never change meaning. Letters are commands only while a reading surface has focus. Every pointer action has a keyboard route. ? shows active-scope bindings.

Keep existing bindings. Additions (conflict-checked per scope):

| Command ID | Default | Scope |
|---|---|---|
| `trajectory.nextRollout` / `previousRollout` | `n` / `p` | trajectory |
| `trajectory.ascend` | `Escape` | trajectory |
| `trajectory.markIn` / `markOut` | `i` / `Shift+O` | trajectory |
| `trajectory.goto` | `:` | trajectory |
| `trajectory.replay` | `Shift+R` | trajectory |
| `trajectory.pivotAggregate` | `.` | trajectory |
| `trajectory.dropMarker` / `cycleMarkers` | `m` / `Shift+M` | trajectory |
| `view.fidelityUp` / `fidelityDown` | `]` / `[` | all |
| `view.zoomIn` / `zoomOut` / `zoomFit` | `+` / `-` / `0` | trajectory, comparison |
| `group.tagVerdict*` | `1..4` | group |
| `comparison.toggleDivergenceCurve` | `Shift+D` | comparison |

10. Visual and semantic invariants

Colors

The grayscale chrome uses --page (#f9f9f7 light / #0d0d0d dark), --surface (#fcfcfb / #1a1a19), --ink (#0b0b0b / #ffffff), --ink-secondary (#52514e / #c3c2b7), --muted (#898781 in both modes), and --hairline (#e1e0d9 / #2c2c2a). Selection is achromatic: an ink playhead, a 1.5px ink outline, and a low-alpha ink wash. Focus uses a 2px --ctx outline.

Exactly four semantic hue tokens are available:

Mild-attention states such as stale, truncated, or partial data use muted ink with dashed or hollow shapes and no hue. Inferred and Tier-A data use muted ink with a hatched texture and no hue.

The TUI maps --ctx, --fail-policy, --fail-infra, and --good to the ANSI-16 blue, red, yellow, and green codes respectively. If NO_COLOR is set, it emits no color escape codes.

11. TUI

A second renderer of the same spec, in the same binary (--tui): same modes, same command grammar, glyph column instead of SVG. v1 TUI scope: Browse (caterpillar + table projections), Read (strip + event list, depth/ fidelity/zoom/landmarks), pair compare, search. Artifacts open via $EDITOR/browser handoff.