# UI guidelines ## Authority [`PRODUCT.md`](PRODUCT.md) defines product behavior and safety. This document resolves UI choices left open by the specification. Follow the established conventions in [POSIX utility syntax](https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap12.html), [GNU CLI standards](https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html), and [CLI Guidelines](https://clig.dev/) unless they conflict with the specification. Optimize simultaneously for excellent user experience, perceived performance, actual performance, mechanical sympathy, robustness, and simplicity. Prefer one mechanism that advances several priorities over separate features. ## Checklist and details **TJ-UI-11.** The primary screen is a unified, vertically scrolling checklist. The accepted visual direction is shown in the [primary checklist mockup](mockups/primary-checklist.html). Each finding uses a vertically composed block that shows at least: - selection state; - human-friendly name or path, wrapped with a hanging indent rather than requiring horizontal scrolling; - estimated reclaimable or primary allocated size; - a fixed-width proportional size track; - a category icon and text label. The selection control is the only per-finding state shown in the compact list. Per-finding policy labels do not appear there. The initial selection reflects recommendation policy internally; the details view explains why an item was pre-selected when applicable. Unicode size tracks use fractional block glyphs for sub-cell precision. A positive value below the track's resolution remains visibly nonzero, zero reclaim uses an empty track, and unknown size uses a distinct unknown track with the text `???B`. ASCII-only mode renders the same size values and category labels with ASCII glyphs. The default ordering assigns every finding to the first applicable tier: 1. recommended candidates; 2. manual-only findings meeting the large-file or large-directory threshold; 3. remaining findings with a known primary size; 4. findings with unknown primary size. Recommended candidates sort by descending estimated reclaimable size, with unknown estimates after known estimates and then by descending reliable allocated size. Large manual-only findings sort by descending allocated size. For remaining findings, primary size is estimated reclaimable size when known and otherwise reliable allocated size; it sorts descending. Equal size keys sort by compiled category identifier, then a tagged ownership key, then the raw path or manager package-identity bytes, all ascending. The ownership key contains known filesystem identity or package ownership domain. Unknown and conflicting ownership use explicit tags that sort after known ownership and include the configured root or adapter identifier. Findings with an identical complete key are one duplicate finding, not separately ordered rows. Selection, focus, arrival time, display text, viewport, and presentation mode never affect the ordering key. The UI supports filters for category, filesystem, age, risk, package ecosystem, and selection state. The compact screen persistently shows only the help shortcut; help exposes the complete key map. **TJ-UI-12.** Details distinguish applicability from knowledge. An applicable known field shows its value. An applicable unavailable, unsupported, invalid, or incomplete field shows `unknown` and a concise reason. A field structurally irrelevant to that finding type is omitted. Blank values and `-` never stand for both unknown and irrelevant. Every finding shows stable identity; category; risk; selection reason and evidence; availability; scan root or ownership domain; warnings; and incomplete state. A filesystem finding additionally shows a losslessly escaped full path; allocated and apparent size; modification and reported access timestamps and their resolution; filesystem, mount, device, inode, type, link count, owning package manager, and exact filesystem action. A symlink additionally shows its separate entry identity, raw target text, target-resolution namespace, target status, and target identity when established. A directory additionally shows manifest count, capacity, completeness, browse control, and non-additive state. Directory candidates can be browsed before selection through bounded read-only inspection. Browse results never become a cleanup manifest or mutation authority automatically; planning constructs the authoritative reviewed manifest separately. A package finding additionally shows canonical package identity, contributing adapter provenance, manager state, owning manager, exact executable and argument vector, effect set, indirect operations, hooks, cache and network scope, binding capability, and expected side effects. ## Interaction - Keep focus and selection attached to stable identities while results arrive or ranks change. New findings never change the focused identity. Reordering preserves its screen row, clamped only when a list boundary makes that row impossible. Metadata, eligibility, and availability changes preserve focus. If filtering hides or live discovery removes the focused identity, focus the next retained visible finding in its previous total order, then the previous visible finding if no next finding exists. Focus the empty-result state when no findings remain visible. Clearing filters does not restore an older hidden focus. Selections survive reordering and filtering; removal discards that finding's selection immediately. - Distinguish focus, eligibility, recommendation, selection, reviewed plan, and authorization in the [canonical state model](PRODUCT.md#101-canonical-state-model). Only the [final confirmation](PRODUCT.md#10-planning-dry-run-and-confirmation) authorizes mutation. - Keep help access, warning count, selection summary, and incomplete state visible. The compact screen shows only the help shortcut; help exposes contextual controls and the complete key map. The summary is `selected X · hidden Y · unavailable Z`; ASCII mode replaces `·` with `|`. `X` counts every selected retained finding, `Y` counts selections hidden by filters or search, and `Z` counts selections that cannot enter a confirmable plan. A selection may count in both `Y` and `Z`; manual-only selections count normally. Viewport position never affects counts. Saturated counters use `at least N`. - A logging failure keeps an audit-incomplete indicator visible on every screen. Review-only mode keeps scanning, details, dry-run discovery, and quit usable while disabling confirmation and every mutation control. A post-mutation logging failure offers no continue action. - Active filter dimensions combine with AND. Selected values within one dimension combine with OR. An inactive dimension imposes no restriction. Unknown metadata matches only an explicit `unknown` value, never a numeric range or known-value category. Search is a separate predicate combined with the active filters using AND. Changing filters never changes selection. - “All visible” means every retained non-manual-only candidate matching the active filters and search, not only the viewport. Bulk selection and clearing preserve hidden and manual-only selections. Removing a finding decrements every applicable count; omitted findings never acquire selection state. - An empty filtered result shows the active filters, complete selection summary, warnings, incomplete state, and help, clear-filter, cancellation, and quit controls. - Refresh is the sole full reset and is unavailable during mutation execution or post-cancellation reconciliation. During scanning or read-only planning, it requests cancellation through the shared controller and stops old-generation admission immediately. One UI transition clears findings, focus, selections, details, filters, search, plans, and confirmation input. Old-generation worker, process, completion, and audit storage remains reserved until every admitted effect is reconciled. Discard only stale presentation and progress events; reconcile stale filesystem, process, network, logging, and mutation effects normally. Start scanning only after a free initialized generation slot exists. Until then show `refresh waiting for prior work`. Never reuse old plans, buffers, handles, or identifiers. Generation-counter exhaustion is a visible typed failure and never wraps. - Quitting before execution is immediate because it cannot lose user data. - While work is active, every cancel control, including Escape and Ctrl-C, requests the same cancellation controller. Accept it on the next event-loop turn, stop admission immediately, and request cancellation of active work. Active work stops at its next bounded safe checkpoint; an indivisible kernel operation may return later, but never blocks the UI. An admitted mutation retains its completion and audit reservations and shows `cancellation requested; action outcome pending` until reconciled. Reconcile from observed state, not event order, and report every mutation completing after the request. Cancellation never offers continuation into remaining mutations. Retain partial scan results and mark them incomplete. - Network disclosure is non-modal and appears in persistent operation status before access begins. It states the adapter, purpose, and request kind without exposing credentials or payloads. Access starts no earlier than the next event-loop turn and uses the ordinary cancellation controller. Offline and per-adapter denial produce an unavailable capability without a prompt. ### Directory browser Open directory browsing from details with `l`, Right, or Enter. Its states are `loading`, `complete`, `incomplete`, `cancelled`, and `failed`. Use retained scan data when present; otherwise submit a bounded generation-tagged read-only inspection job. Partial entries and the reason remain visible in incomplete, cancelled, and failed states. Inspection never changes selection, recommendation, ordering, plan identity, or mutation authority. `j`, `k`, arrows, Page Up, Page Down, Home, and End navigate entries. `l`, Right, or Enter expands a directory or opens entry details. `h` or Left collapses the current directory, then returns to its parent when already collapsed. Escape returns one UI level unless work is active, when it retains cancellation priority. Space has no browser action. Focus uses stable entry identity and normal removal fallback. Returning to the checklist restores focus to the originating directory finding. ### Key map - `j` or Down moves to the next finding; `k` or Up moves to the previous finding. - Page Down and Page Up move by one page; Home and End move to the first and last visible finding. - `l`, Right, or Enter expands or opens; `h`, Left, or Escape collapses or returns. - Space toggles the focused finding. - `+` selects all visible non-manual-only candidates; `-` clears their selections. - `f` opens filters; `s` opens sorting; `/` starts search. - `n` and `N` move to the next and previous search match. - `r` refreshes; `L` opens the run log; `d` starts dry-run planning. - `p` opens plan review; `c` enters final confirmation; `q` quits; `?` opens help. Ctrl-C always requests cancellation while work is active. Escape requests cancellation while work is active; otherwise it closes the current overlay or returns one navigation level. `c` never mutates directly; mutation requires the final confirmation phrase ([PRODUCT §10](PRODUCT.md#10-planning-dry-run-and-confirmation)). Contextual help keeps unavailable commands visible with their reason. The compact view persistently shows only `? help`. The help overlay opens and closes with `?` without changing application state. It shows current-screen controls first, unavailable controls with concise reasons second, and the complete global key map last. `j`, `k`, arrows, Page Up, Page Down, Home, and End scroll it; `h` or Left closes it. Escape closes help only while no work is active. During active work Escape and Ctrl-C retain their global cancellation meaning, while `?` still closes help. Help never executes a described command. Selection, warnings, incomplete state, active work, and cancellation status remain visible behind or within help. Constrained layouts use bounded vertical scrolling and never require horizontal scrolling. ASCII and no-color modes preserve identical help text and availability reasons. ## Performance [`LIMITS.md`](LIMITS.md) defines queue, turn, process, terminal, cancellation, and latency bounds. - Acknowledge input before optional work, render only changed cells, and redraw only when visible state changes. - Keep terminal input and rendering independent from blocking discovery and package execution. - Report measured root count; returned directory entries; metadata successes and failures; apparent and reliable allocated bytes observed; package records; and completed jobs. Never infer a completion percentage when total work is unknown. - Avoid decorative animation, periodic redraw, and layout movement. Responsiveness is feedback plus stability, not motion. - Bound every queue and reserve capacity for cancellation, resize, failure, shutdown, and audit events. Coalesce progress by operation identity before dropping optional progress. The status region on every screen shows warning count, incomplete state, current measured progress, and active or pending cancellation. Saturated counts use `at least N`. Details identify affected roots or adapters; bounded technical examples remain in the run log. The below-baseline resize screen and every overlay retain this status. Cancellation checks occur before and after every directory enumeration batch, metadata batch, adapter read, process spawn, pipe-drain batch, network admission, log frame, plan action, manifest entry, and bounded CPU-work interval. They occur before mutation admission and after every indivisible syscall. No cancellation check may intervene between the final revalidation effect and its adjacent mutation request. Child processes use the signal and timeout bounds from `LIMITS.md`. ## Terminal contract - [`--help` and `--version`](PRODUCT.md#13-cli-surface) run without terminal initialization or scanning and write to stdout. Usage errors and diagnostics write to stderr and return nonzero. - Interactive startup and restoration follow [Initialization and restoration](#initialization-and-restoration). Redirected or absent terminals never fall back to unsafe noninteractive behavior. - Color precedence follows [Color](#color); [`--ascii`](PRODUCT.md#13-cli-surface) is independent of color. - Restoration covers every exit path per [Initialization and restoration](#initialization-and-restoration). - Input parsing follows [Input and mouse](#input-and-mouse). Confirmation input semantics, including buffered input, paste, stale events, auto-repeat, and the legacy unmarked-input disclosure, are owned by [PRODUCT §10](PRODUCT.md#10-planning-dry-run-and-confirmation). - Sanitize control bytes and invalid UTF-8 for display. Keep original path bytes separate from shortened display text and mutation authority. - Support a 40 by 12 compact baseline and operation at 80 by 24 as required by the [acceptance criteria](PRODUCT.md#15-acceptance-criteria). Test 160 by 48 as the representative large layout. Below the baseline, show a static resize message while retaining status, help, cancellation, and quit controls. Clamp dimensions above the registered cell-grid limit and show `display limited to WIDTHxHEIGHT` using its generated values. ### Layout invariants Every rendered grid exactly matches the clamped terminal dimensions, contains no partial cell, and writes no cell outside its bounds. At 40 by 12 the persistent status and help shortcut consume fixed rows; each finding retains selection, at least one name line, size text, size track, and text category. At 80 by 24 the same fields remain and details gain secondary metadata. At 160 by 48, additional columns and details may appear without changing reading order, focus, selection, or action semantics. Names wrap only at Unicode cell boundaries with a two-cell hanging indent after the first line. When vertical capacity is exhausted, truncate the final visible line using the collision-safe label from [`FILESYSTEM_CAPABILITIES.md`](FILESYSTEM_CAPABILITIES.md). Never require horizontal scrolling. A code point wider than the remaining content width renders as the replacement glyph and escaped code-point text in details. ### Text and width Display decoding uses strict UTF-8. Each maximal invalid byte sequence becomes U+FFFD in Unicode mode and `?` in ASCII mode. C0, DEL, C1, bidi-control, and noncharacter code points render as bounded `` or `` escapes. Original bytes remain separate. Cell width uses a checked-in Unicode 15.1 width table. Combining marks attach to the preceding base; at line start they follow a dotted circle, or `?` in ASCII. Wide characters occupy two initialized cells and are replaced rather than split at a boundary. Tabs render as `<09>`. Newline and carriage return never create display lines from path data. Unicode size tracks use eighth-block fractional cells. Zero is all spaces; every positive known value uses at least one eighth block; unknown uses a repeated `?` track and `???B`. ASCII tracks use `#` for filled cells, `+` for a positive sub-cell value, spaces for zero, and `?` plus `???B` for unknown. Text size values and category labels are identical across color modes. Semantic reading order is status, screen title, filter/search summary, focused-item marker, selection, name, size text, category text, warnings, details, controls, then `? help`. Focus uses an explicit text marker; warnings start with `warning:`; incomplete values include the word `incomplete`; confirmation starts with `confirmation:`. Color, icon, cursor shape, and Unicode never carry unique meaning. Broad assistive-technology compatibility remains manual best-effort evidence, not a universal claim. ### Input and mouse Version 1 supports classic terminal keys, bracketed paste, and SGR mouse mode 1006 with button-event mode 1000. Mouse support is enabled only after the terminal positively reports the required mode or matches a configured capability; otherwise it remains disabled. Kitty keyboard and graphics protocols are future work, not version 1 behavior. The decoder buffers at most the limits in `LIMITS.md`. It recognizes complete sequences incrementally, waits at most the escape ambiguity interval, and emits Escape when no valid continuation arrives. An oversized, malformed, unknown, or impossible sequence emits one ignored-input warning, resets at the next ground-state byte, and never changes keyboard mappings. Fragmented UTF-8 and escape input has the same result as contiguous input. Bracketed paste decodes as one paste event; its confirmation admission is owned by [PRODUCT §10](PRODUCT.md#10-planning-dry-run-and-confirmation). After every malformed mouse sequence, the next ordinary keyboard byte is decoded from ground state. ### Color `--no-color` has highest priority. Otherwise any nonempty `NO_COLOR` disables color. Otherwise the configuration value applies, defaulting to color enabled only when the terminal reports color support. `--ascii` affects glyphs and width tables only and is independent from color. ### Initialization and restoration `--help` and `--version` bypass configuration, terminal initialization, logging, and scanning. Interactive startup requires terminal stdin and stdout, a nonempty `TERM` other than `dumb`, and successful bounded terminal initialization. Failure writes one concise stderr diagnostic and exits nonzero without scanning or entering review-only mode. Record original termios and enabled terminal features before changing them. On normal exit, handled signals, suspension, and errors, restore mouse mode, bracketed paste, cursor visibility, alternate screen, and termios in reverse enable order. Suspension restores before stopping and re-enters only after resume and capability recheck. Disconnect attempts nonblocking restoration only while writes succeed, for at most four attempts and 100 ms total. Failure after that bound is reported to stderr when possible and never blocks exit. ### Output transport Cell-grid differences are the sole source of ordinary terminal output. An unchanged grid emits no bytes. A changed frame emits the minimum ordered runs needed by the first-party renderer without rewriting unchanged cells. Partial writes advance one bounded pending-output buffer. `EAGAIN` yields to input and events; it never waits or spins. Optional redraw is suppressed when that buffer is full, while status damage is retained and rendered when capacity returns. Input processing never depends on output progress. ## Bounded-state authority Bounded interaction follows TJ-SCAN-08 through TJ-SCAN-10, TJ-PLAN-06, TJ-PLAN-08, TJ-UI-04, and TJ-UI-05 in [`REQUIREMENTS.md`](REQUIREMENTS.md). Fixed capacities are owned by [`LIMITS.md`](LIMITS.md); filesystem capability behavior is owned by [`FILESYSTEM_CAPABILITIES.md`](FILESYSTEM_CAPABILITIES.md). This document defines their visible UI presentation only. ## Implementation constraints Runtime architecture is owned by [`CODING_STYLE.md`](CODING_STYLE.md#runtime-architecture). Verification is owned by [`TESTING.md`](TESTING.md). This document specifies only operator-visible interaction and terminal behavior.