specs/014-file-diff-review/spec.md
Feature specification: Per-file diff review
Feature: 014-file-diff-review
Created: 2026-10-03
Status: Implemented and verified
Input: User approved adding a readable per-file diff to the existing Working Tree Changes GUI.
User scenarios & testing
User story 1: Read a changed file (P1)
As a developer supervising agents, I open Working Tree Changes and select a file to read what changed without leaving signaltty.
Why this priority: Counts alone cannot explain an agent's change.
Independent test: Modify a tracked text file, open Changes, activate its row, and read the added, removed, and context lines.
Acceptance scenarios:
- Given staged and unstaged edits, selecting a file shows their combined changes against HEAD with three unchanged context lines where available.
- Given several hunks, the reader shows their locations and old/new line numbers, with additions and removals distinguished by signs as well as color.
- Given a deleted text file, the reader shows its removed content.
- Given a clean workspace, the dialog reports no working tree changes.
User story 2: Review files using the keyboard (P2)
As a developer, I navigate the file list and reader using the keyboard, refresh changes, and return to the list without losing my place.
Why this priority: Reviewing several files should not require repeated mouse navigation.
Independent test: Activate a selected row, scroll and select/copy text in its reader, return to the list, and refresh after another edit.
Acceptance scenarios:
- Keyboard focus reaches file rows; Enter opens a diff and a keyboard-accessible control returns to the list.
- Selecting another file or refreshing while a read is pending cannot display the earlier request as the new selection's diff.
- Closing the dialog while a read is pending cannot reopen it or update a subsequent dialog.
- At 360 pixels wide, the list and reader remain usable without clipped navigation controls.
User story 3: Understand files that need special treatment (P2)
As a developer, I can identify new and binary files and understand when a preview is incomplete or unavailable.
Independent test: Review a new text file, binary file, oversized file, and a filename containing punctuation.
Acceptance scenarios:
- A new text file shows its content as additions and remains labeled untracked.
- Binary content produces an explicit binary notice instead of unreadable text.
- A preview exceeding the size limit is visibly incomplete and cannot freeze the application.
- A path containing spaces, tabs, newlines, markup, brackets, or Git pathspec syntax selects exactly that file and appears as literal text.
- A repository without its first commit supports staged and untracked files; an unavailable or no-longer-changed file has a clear notice.
Requirements
- FR-001: Working Tree Changes MUST let the user activate a file and read its unified diff against HEAD, including staged and unstaged edits.
- FR-002: The reader MUST show hunk locations, old/new line numbers, unchanged context, and explicit addition/removal signs.
- FR-003: Text MUST be selectable and copyable, displayed literally with monospace alignment, and readable in light and dark themes.
- FR-004: The dialog MUST provide keyboard navigation and preserve the selected file when returning to the list.
- FR-005: Reads MUST be asynchronous, bounded in size and duration, and ignore responses superseded by a newer selection, refresh, or dialog closure.
- FR-006: New text files MUST be readable as additions; binary files MUST have an explicit notice; truncation MUST be visible.
- FR-007: File reads MUST use repository-relative literal paths and MUST NOT follow a symlink to read content outside the repository.
- FR-008: Refresh MUST refresh the summary and invalidate the displayed patch; missing repositories/files and read errors MUST produce recoverable notices.
- FR-009: The feature MUST remain read-only: signaltty MUST NOT stage or edit files, mutate server workspace state, or operate on agents or terminal widgets. Tracked comparisons retain Git's trusted configured clean/process conversions, as the existing HEAD summary does.
Key entities
- Changed file: Repository-relative path, tracked/untracked and binary status, existing addition/removal counts.
- File diff: Selected path, comparison base, text/binary status, completeness, hunks and numbered lines.
- Review selection: Selected file and current read generation, confined to the open dialog.
Success criteria
- SC-001: A tracked-file fixture with two hunks displays every expected line and old/new line number correctly.
- SC-002: A keyboard-only user can open a file, read it, return, and select another file.
- SC-003: The dialog remains responsive while a read is pending; every read finishes or reports an error within ten seconds.
- SC-004: New, deleted, binary, unusual-name, and oversized-file fixtures each produce the expected preview or explicit notice.
- SC-005: Light, dark, and 360-pixel-wide captures show readable content and reachable navigation controls.
Assumptions and scope
- This increment extends the current explicit-open/refresh Changes workflow. No background patch polling.
- The comparison remains HEAD, or an empty tree before the first commit. Per-turn snapshots remain future work under ADR-0011.
- Unified text is the default. Side-by-side views, syntax highlighting, staging, editing, blame, and rename inference are outside this increment.
- Existing file counts retain their current meaning; untracked previews do not silently change summary totals.
- Local Git and its configured canonical conversions are trusted workspace tools. They may have their own side effects; signaltty does not install or configure them. External diff and textconv presentation commands are disabled.
- Requirements are clarified from the user's accepted scope and existing product documents. No unresolved clarification markers remain.