Skip to content

fix(cli): overhaul trajectory browser interaction ux - #379

Merged
forrestjgq merged 5 commits into
mainfrom
fix/trajectory_browser_ux
Sep 5, 2026
Merged

fix(cli): overhaul trajectory browser interaction ux#379
forrestjgq merged 5 commits into
mainfrom
fix/trajectory_browser_ux

Conversation

@forrestjgq

Copy link
Copy Markdown
Contributor

Summary

Overhaul the interactive raven trajectory browser end to end, fixing every
issue from real-terminal testing:

  • Navigation: every screen opens with a help area under its title (page
    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.
  • Tables: the session list is TITLE | ATTEMPTS | LAST ACTIVITY and the
    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.
  • Space preview: Space on an attempt row prints that attempt's per-turn
    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.
  • Merge hotkey: with two or more attempts, m/M opens the multi-select merge
    (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.
  • Theme and feel: new success (green check) and help (readable hint text)
    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

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

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 passed

  • COLORTERM=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 check and uv run ruff format --check on the four touched files -> clean

  • PTY 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

forrestjgq1982 and others added 5 commits September 2, 2026 12:25
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 gloryfromca left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 check and uv run ruff format --check on 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.

@forrestjgq
forrestjgq merged commit 3df533a into main Sep 5, 2026
11 checks passed
@forrestjgq
forrestjgq deleted the fix/trajectory_browser_ux branch September 5, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants