fix(cli): overhaul trajectory browser interaction ux - #379
Conversation
Step 1 of the trajectory browser UX rework: the interaction kernel. - Bind Esc on every prompt by merging a fresh key-binding registry (text/confirm expose a read-only _MergedKeyBindings, so .add is not an option). List screens return a back sentinel and navigate one level up (the top level exits); prompts inside a running action raise _ActionCancelledError, caught only at the action boundary, so the sentinel never reaches bool()/list() or the data layer. A cancelled report keeps its pre-confirm bundle and pin, matching the declined-report contract. - Drop the Back/Exit menu entries; every list screen opens with help separator lines under the title (page purpose plus hotkeys) and suppresses questionary's default arrow-keys hint. - Erase prompts once answered and echo navigation as breadcrumbs: dim static label, untrusted text collapsed to one plain line, markup escaped, auto-highlighter off. - Restore pointed-row highlighting for formatted-text titles by appending a bold fragment without a foreground color so semantic cell colors survive; add a success green to both theme palettes (WCAG AA on their backgrounds) for the upcoming boolean columns. - Tests: real questionary pipe-input coverage for the Esc binding on all four prompt kinds, extra-key hits, the erase flag, and final style attrs; Esc cancellation for all seven action prompts with no side effects; breadcrumb escaping on a forced-terminal console. Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
Step 2 of the trajectory browser UX rework: tabulation. - Lay both lists out as fixed-width table rows under a column-header separator: sessions as TITLE | ATTEMPTS | LAST ACTIVITY, attempts as # | STARTED | TURNS | SPANS | VERDICT | PIN | MERGED | PREVIEW, with PIN/MERGED rendered as a green check via the success style. - Budget the layout per screen build from the live terminal width: the option-row indent, a 1-cell safety margin, and inter-column gaps are all part of the math. PREVIEW is dropped as a whole column when it no longer fits (gaps recomputed for the new column count) and TITLE flexes between its bounds. Below the dynamic minimum width the tightest layout is kept and the renderer clips overlong lines at the terminal edge (option rows never wrap). - Measure and truncate cells by terminal display width (get_cwidth), not code points, and collapse every dynamic cell to one plain line first: verdict sidecars only guarantee a non-empty string. Fixed column widths always fit their headers (VERDICT is 7). - Time cells keep the year and live in labeled columns; fallback session titles no longer embed a timestamp. - Tests: whole-line budget checks at 80/60 columns with gap-aware column alignment, dynamic floor and floor-1 boundaries plus digit growth of the '#' column, CJK cells, corrupt verdict/timestamp corpora, green-check attrs on pointed and plain rows, and a real render below the floor pinning clip-not-wrap on the raw stream. Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
Step 3 of the trajectory browser UX rework: the Space preview. - Collect per-turn input/output previews during the snapshot scan (no extra store reads): every sort-key component is collapsed to a plain string at ingestion, so ordering never raises on None and never follows log source order. The table PREVIEW cell now derives from the same sorted collection instead of a first-seen pick. - Space on an attempt row prints that attempt's turns and waits for one key; the waiter replaces press_any_key_to_continue's bindings: any key or Esc returns a non-None sentinel, Ctrl+C alone keeps the browser-wide cancel. All bindings are eager so default exact bindings (Up, Backspace) cannot leave the waiter stuck, and a specific CPR-response binding mirrors prompt_toolkit's default so a real terminal's cursor report is not mistaken for a keypress. - Dispatch injected keys by key and row: only Space on an attempt row previews; the merge entry or a foreign key is a cursor-keeping no-op. The reopened menu keeps the cursor via the select default; a preview never rescans. Turns whose previews are all empty render an explicit placeholder instead of a blank page. - Tests: sorted collection and derived table preview under shuffled logs, checkpoint/final dedup, missing span ids, type/length sanitization, waiter key paths on real pipe input (named keys and CPR pass-through included), a real Space keypress binding, and fake-driven flows for cursor keeping and the merge-row no-op. Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
Step 4 of the trajectory browser UX rework: the merge hotkey. - Bind m/M on the attempt screen when two or more attempts exist (a single attempt neither binds nor advertises it) and dispatch the key hit straight into the multi-select merge; the "Merge attempts" menu entry and its sentinel are gone. - The attempt help line advertises "M merge" only when it applies. - The merge checkbox opens with its own help separator (Space toggle, Enter confirm, Esc cancel) and suppresses questionary's default instruction hint; Esc keeps the step-1 cancel protocol. - Tests: fake flows migrated from the menu entry to key hits, m/M parametrization on real pipe input, per-count binding and help assertions, checkbox help/instruction checks, and the foreign-key no-op regression now uses an unbound key. Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
Real-terminal feedback round for the trajectory browser. - Add a help color to both theme palettes (WCAG AA on their backgrounds) and restyle separator lines (help text, table headers, checkbox hints) to it at render time: the separator class is nearly invisible as prose. - Unify every hotkey hint as "[Key] function" (e.g. "[Enter] open"). - Flush a lone ESC after 50ms instead of prompt_toolkit's 0.5s default: the escape-sequence disambiguation wait reads as lag on a human keypress; every prompt and the preview waiter set it. - Esc on the top-level session screen now stays put instead of quitting (a reflexive Esc must not drop the browser); quitting is Ctrl+C only, advertised as "[Ctrl+C] quit". _ask switches to unsafe_ask with its own KeyboardInterrupt handling so the exit prints one notice instead of questionary's "Cancelled by user" plus the browser's own line. - Tests: trailing quits in fake scripts migrated to Ctrl+C, a top-level stay regression, a final-attrs assertion for the help restyle, ttimeoutlen assertions on every prompt kind and the waiter, and an _ask regression pinning the unsafe_ask path. Co-authored-by: Claude (claude-fable-5) <noreply@anthropic.com>
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
I reviewed the full github/main...HEAD diff and found no actionable defects. I covered the repository rules in AGENTS.md/CLAUDE.md and the canonical trajectory terms routed by CONTEXT-MAP.md; the changed browser and theme code; the bare-command caller and trajectory data-layer contracts; the relevant file and commit history; backward compatibility of the id-taking subcommands and public row helpers; test changes for weakening; and the Runtime/TUI boundary. I traced the Esc/Ctrl+C cancellation scopes, report side effects, Space preview ordering and sanitization, m/M merge dispatch, formatted-row styling, terminal-width calculations, refresh behavior, and id/markup boundaries.
Verification:
env -u NO_COLOR COLORTERM=truecolor uv run pytest tests/test_cli_theme.py tests/test_cli_trajectory_browse.py tests/test_cli_trajectory_commands.py -q -k 'not replay'-> 213 passed, 7 deselected, exit 0.- The complete three-file run reached 220 passed, then this workspace segfaulted during native-extension interpreter teardown. Isolation showed the shutdown crash occurs only after the seven unchanged replay tests; those seven assertions pass, and GitHub's exact-head Python unit job passes.
uv run ruff checkanduv run ruff format --checkon all four touched files -> clean.git diff --check-> clean; an adversarial width/count/unicode table-layout sweep passed.- All current GitHub checks, including unit, lint, commit-message, title, repository-file, TUI, bridge, and Windows self-upgrade jobs, pass.
Summary
Overhaul the interactive
raven trajectorybrowser end to end, fixing everyissue from real-terminal testing:
purpose plus "[Key] function" hotkey hints, rendered in a new readable
help color). Esc walks one level up (action -> attempt -> session); on the
top-level session screen Esc stays put and quitting is Ctrl+C only, which
prints a single exit notice. Esc inside a running action cancels just that
action through a dedicated exception at the action boundary, so the back
sentinel can never reach a conversion or the data layer; a cancelled
report keeps its pre-confirm bundle-and-pin side effects (the existing
declined-report contract). Back/Exit menu entries are gone; answered
prompts erase themselves and navigation is echoed as breadcrumb lines
with markup-escaped untrusted text.
attempt list is # | STARTED | TURNS | SPANS | VERDICT | PIN | MERGED |
PREVIEW with green check cells. Time cells keep the year. The layout is
budgeted per screen build from the live terminal width (row indent, a
safety margin, and inter-column gaps are all part of the math); PREVIEW
drops as a whole column when it no longer fits, and below the dynamic
minimum width the tightest layout is kept while the renderer clips
overlong lines at the terminal edge. Every dynamic cell passes a
single-line sanitization gate before width math (verdict sidecars only
guarantee a non-empty string), cells are measured and truncated by
terminal display width (CJK is 2 cells), and fixed column widths always
fit their headers.
input/output previews, collected in the same snapshot scan and sorted by
a fully-stringified key so ordering never follows log source order (the
table PREVIEW cell derives from the same sorted collection). Any key,
arrow keys included, returns with the cursor kept on the row; the waiter
passes the terminal's CPR reply through to the renderer instead of
treating it as a keypress, and Ctrl+C keeps the browser-wide cancel.
(advertised in the help line; the old "Merge attempts" menu entry is
gone). The checkbox screen carries its own help line for toggle, confirm,
and cancel. A single attempt neither binds nor advertises the key.
colors in both palettes, all WCAG AA on their backgrounds; a lone ESC is
flushed after 50ms instead of prompt_toolkit's 0.5s default, removing the
laggy Esc feel.
The data layer is untouched: changes are confined to
raven/cli/trajectory_browse.py, raven/cli/_theme.py, and their tests. The
id-taking subcommands (raven trajectory save/list/...) behave as before.
Type
Verification
COLORTERM=truecolor uv run pytest tests/test_cli_theme.py tests/test_cli_trajectory_browse.py tests/test_cli_trajectory_commands.py -q-> 220 passedCOLORTERM=truecolor uv run pytest tests/ --ignore=tests/integration -q-> 6917 passed; the 10 failed + 20 errors are the pre-existing environment-dependent set, identical file-by-file on a clean main checkout (cron, everos server, config loader, tracing viewer)uv run ruff checkanduv run ruff format --checkon the four touched files -> cleanPTY smoke on a CPR-answering pseudo terminal at 100 and 58 columns: full flow (preview, merge cancel, action screen, staged Esc backout, Ctrl+C quit) and the narrow-width degradation contract
New coverage includes real pipe-input key-binding tests (Esc on all four prompt kinds, Space/m/M hits, waiter key paths with CPR pass-through), table budget/clipping boundaries at the computed minimum width, and corrupt verdict/timestamp corpora
Relevant tests pass locally
Relevant lint / type checks pass locally
User-facing docs or screenshots are updated when needed
Risk
The interactive browser's look and key protocol change wholesale (that is
the point of this PR): Esc no longer quits from the top level (Ctrl+C
does), Back/Exit entries are gone, and lists render as tables. The
machine-facing subcommands and the trajectory data layer are untouched.
Rollback: revert the squash commit; the browser returns to the previous
interaction wholesale, no data migration involved.
Security impact considered
Backward compatibility considered
Rollback path is clear for risky changes
Related Issues
N/A