Skip to content

TUI: decide what lowercase and uppercase mean, then make the bindings agree - #121

Merged
MJohnson459 merged 1 commit into
mainfrom
key-case-rule
Aug 5, 2026
Merged

TUI: decide what lowercase and uppercase mean, then make the bindings agree#121
MJohnson459 merged 1 commit into
mainfrom
key-case-rule

Conversation

@MJohnson459

Copy link
Copy Markdown
Contributor

Settle what the case of a key means, and audit every binding against it.

#327 gave the key line one label format for a lowercase key and its shifted
sibling but deliberately left the convention behind it unstated. This states
it, in the foreground/background formulation triage decided: the lowercase
key acts immediately and headlessly and the operator never leaves the TUI; the
uppercase key opens an interactive surface — an agent session the terminal is
handed over to, or a picker answered before anything happens.

Two clarifications the rule needed to be usable:

  • Taking a line of text inline is not "opening a surface" — a one-line input in
    the queue is how a lowercase key takes its argument. That is what keeps r
    (and #369's a) on the lowercase side.
  • The rule binds pairs, and only pairs, which is the same line §9 already
    draws between a shifted sibling and a key that merely shares a letter. What
    it binds going forward is that a heavier, interactive variant of an existing
    action takes that action's shifted key rather than a fresh letter.

The audit

Every uppercase binding in key_normal, key_projects and key_config,
against the rule:

key verdict
D dispatch-via-picker conforms — the picker is the interactive surface
R refine in a session conforms
N plan in a session conforms
n new task in $EDITOR the one pair still out of line — both halves are foreground today; #315 fixes it
C cancel a refine outside the rule — c links documents, so there is no pair
projects A archive outside the rule — a adds a project
Config V/A defaults outside the rule — no lowercase sibling for V, and a adds a viewer; both happen to open pickers anyway
J/K, PgUp/PgDn outside the rule — navigation, no pair

No binding had to move. d/D conforms once the picker counts as the
interactive surface, and #325's finding that r "launches a backgrounded named
session anyway" is not a defect under this rule — backgrounding is precisely
what the lowercase key means. The exceptions are named in the doc rather than
rebound: the letters they share carry no kinship, and moving a key the
operator's fingers already know would buy a consistency nobody reads.

Changes

  • DESIGN.md §9 gains two paragraphs after the key-advertising one: the rule
    in its foreground/background form, and its scope with every exception named.
  • The three ? glosses (DISPATCH_KEYS, REFINE_KEYS, NEW_KEYS in
    crates/voro/src/ui.rs) are reworded to one shape the rule dictates —
    lowercase ends "headless", uppercase names the surface it opens: d dispatch
    to the resolved agent, headless / D dispatch, choosing the agent in a
    picker; r refine a proposal from a note, headless / R refine a proposal in
    an agent session; N new task, planned in an agent session. n's gloss is
    left alone — it is honest about $EDITOR until #315 changes the behaviour.
    Lengths were kept at or under the existing longest gloss so the overlay does
    not widen past 80 columns.
  • A test replaces the audit going forward
    (every_uppercase_key_is_paired_or_a_named_exception): every uppercase key in
    every screen's key map must be half of one of the three pairs or listed in
    CASE_EXCEPTIONS, which mirrors the doc's exception list screen by screen.
    A new uppercase binding cannot be added without deciding which it is, and the
    failure message points at §9.

Reconciliation with #315 and #369

#315 (n takes a quick prompt and backgrounds an agent) is the n half of this
rule and its body already cites it; it is now --blocks-ordered behind this
task so the rule lands first. Note that added a blocker and the store demoted
#315 ready → parked; it returns to ready when this task is accepted.

#369 (a sends headlessly, A attaches) is in flight and taken as given — I
did not touch a/A in either the bindings or the map. Two things for whoever
lands it: the §9 paragraph already describes a/A as an instance of the rule,
and the new test will fail on a bare A entry unless it is added as a pair
constant alongside DISPATCH_KEYS/REFINE_KEYS/NEW_KEYS — which is the
intended prompt, not an obstacle.

Verification

cargo test --workspace (614 tests) and
cargo clippy --workspace --all-targets -- -D warnings clean; cargo fmt --all --check clean. The existing overlay render test asserts the new glosses appear
whole at 80 columns, and its rendered output was inspected to confirm the
cockpit map still fits its column.

Branch: key-case-rule (worktree, not pushed).

#327 gave the key line one label format for a lowercase key and its
shifted sibling, but left the convention behind that format unstated —
so `d/D dispatch` promised a kinship nothing in the doc defined, and the
uppercase glosses in the `?` map were worded to a shape rather than to a
rule.

The rule is foreground versus background: the lowercase key acts
immediately and headlessly and the operator never leaves the TUI, and the
uppercase key opens an interactive surface — an agent session or a
picker. Taking a line of text inline is not opening a surface; it is how
a lowercase key takes its argument, which is what keeps `r` and `a` on
the lowercase side.

It binds pairs and only pairs, which is the same line §9 already draws
between a shifted sibling and a key that merely shares a letter. That
puts `C`, the projects screen's `A`, `J`/`K` and the Config screen's
`V`/`A` outside it rather than in breach of it, and none is worth
rebinding to buy a consistency nobody reads.

The audit found no binding to move: `d`/`D`, `r`/`R` and `a`/`A` already
conform (the picker is `D`'s interactive surface, and `r`'s backgrounding
is what the lowercase key means, not the defect #325 read it as), and
`n`/`N` is the one pair still out of line — both halves are foreground
today, which #315 fixes by making `n` a typed line expanded by a
background agent. #315 is now ordered behind this task so the rule lands
first.

A test replaces the audit going forward: every uppercase key in every
screen's map must be half of a pair or one of the exceptions named in
§9, so a new uppercase binding cannot be added without deciding which.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SB1A345BtsrvujQR3njwx9
@MJohnson459
MJohnson459 merged commit 8620dc7 into main Aug 5, 2026
6 checks passed
@MJohnson459
MJohnson459 deleted the key-case-rule branch August 5, 2026 18:01
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.

1 participant