Files
boocode/docs/features/git-diff-panel/feature-specification.md
indifferentketchup 2a05d2f9fe docs: archive shipped openspec batches; add feature/plan/research notes
Move 13 shipped openspec change docs under openspec/changes/archived/.
Add docs/features/git-diff-panel, docs/plans/post-review-backlog, and
docs/research/cross-app-contract-ssot.md (the research behind the
@boocode/contracts SSOT work). Update BOOCHAT.md, BOOCODER.md, and
boocode_roadmap.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 21:20:33 +00:00

15 KiB

Feature specification — Git diff panel

Source

No formal upstream PRD. Authored from a direct request (2026-06-02): "add a git diff panel that can be shown instead of the file browser, similar to Paseo," plus a read-only review of the Paseo reference at /opt/forks/paseo. Ground-truth conventions drawn from the project's own docs and existing surfaces.

Outcome

A person working in a session can review the uncommitted (and on-branch) changes of the project's repository in a diff view that lives in the same right-side panel slot as the file browser, switching between the two with a tab. From that view they can read each changed file's diff, stage and unstage files, commit staged work with a message, and discard a file's changes — without leaving the session or opening a terminal. The view tells them, at a glance, which files changed and by how much, and stays current as agents and the user make edits.

Actors and triggers

  • The session user (single user) opens the right-side file panel and switches its tab from Files to Git. The Git tab is the trigger; switching back to Files restores the file tree in the same slot (D1).
  • The panel is available in any session that has the file panel, because the stage / commit / discard actions are the human user's own UI actions, not assistant tools — the AI has no access to these endpoints, and the artifact sandbox prevents a rendered artifact from reaching them (D8, D12).
  • An ambient indicator on the file-panel header signals the repository is dirty, making the Git tab findable without opening it (D17).

Primary flow

  1. The user opens the right-side file panel and selects the Git tab.
  2. The panel shows the changes of the project's repository, defaulting its comparison mode by the repository state on first open: Uncommitted (working tree vs. the last commit) when the working tree is dirty, Committed (the current branch vs. its base) when it is clean. Once the user selects a mode explicitly, that choice is pinned for the session and subsequent refreshes do not override it; if a refresh would change the auto-selected mode (e.g. the tree went clean while Uncommitted was pinned), the panel briefly notes the change rather than swapping silently (D2, D3, D14).
  3. The panel presents a list of changed files, each with its path, change type (added / modified / deleted / renamed / untracked), and an added/removed line count. In Committed mode the header labels the base used ("Git — branch vs <base>") (D13).
  4. The user expands a file to read its diff inline; collapsing hides it again. A control expands or collapses all files at once.
  5. The user stages or unstages individual files. Staged and unstaged files are grouped into separate sections and distinguished by both a label/icon and grouping — not color alone — and each stage/unstage control carries an accessible name that includes the file path.
  6. The user writes a commit message and commits the staged files. The commit's author and committer identity is derived from a server-side source; the request cannot set or influence it (D12). On success the committed files leave the list, the panel refreshes to the new repository state, and a brief non-blocking confirmation is shown (D6).
  7. The user may switch the comparison mode explicitly (Uncommitted ↔ Committed) at any time; the file list and counts update to the selected mode, and the choice is pinned for the remainder of the session.

Alternate flows and states

  • Loading: while the first difference is being computed the panel shows a brief loading indicator in place of the file list.
  • Empty (no changes): when the selected mode has no changes the panel shows a mode-specific empty message ("No uncommitted changes" / "No changes vs. the base branch") instead of a file list. When the Git view is empty but the session has unapplied pending changes, the empty state hints that those live in the pending-changes panel (D17).
  • Discard a file: the user discards a single file's changes from an overflow or secondary affordance, separated from the Stage/Unstage controls (not an equal-weight sibling). Because discard is irrecoverable, the panel asks for a plain confirmation before acting, with wording that distinguishes the two cases: "Discard changes to X?" for a tracked file (which returns to its committed content) and "Delete X? It has never been committed and cannot be recovered" for an untracked file (which is permanently removed). On confirmation the file's changes are applied and the list refreshes (D7, D15).
  • Commit with an empty message or nothing staged: the commit control is unavailable until at least one file is staged and a non-empty message is present.
  • Refresh: the panel re-reads the repository state when the Git tab is opened, after any stage / unstage / commit / discard it performs, after an agent turn completes, after the user applies or discards a queued change in the pending-changes panel, and on an explicit Refresh control. Concurrent refresh triggers are coalesced — a refresh already in flight absorbs later triggers rather than spawning a second concurrent read, so the panel settles to a single final snapshot (D10).

Edge cases and failure modes

  • Not a git repository: when the project's path is not a git repository the Git tab is not offered; the file panel stays on Files only.
  • Binary files: a changed binary file appears in the list with its path and change type but its body shows a "Binary file" placeholder instead of a diff.
  • Very large diffs: a file whose diff exceeds a display threshold appears in the list with its path and counts but its body shows a "Diff too large to display" placeholder; a git read that does not complete within a deadline exits the loading state, shows an error, and offers Refresh; the overall response is bounded so a huge change set cannot stall the panel (D5).
  • A git action fails: the panel surfaces the failure as an inline error — commit-area errors appear near the commit control; per-file action errors appear in the affected file row. The panel leaves the repository as close to its pre-action state as the git layer allows; the list refreshes to reflect the repository's true state.
  • Repository busy (index locked): when a write fails because the repository's index is locked by another process (e.g. a concurrent agent turn), the panel communicates "the repository is busy, try again" rather than a raw error.
  • In-progress git operations: when the repository is mid-operation (merge, rebase, cherry-pick, or bisect), the panel disables its write affordances and shows the repository's current state, rather than allowing stage / commit / discard to fail with raw errors.
  • Concurrent edits during a read: the displayed diff is a snapshot at read time; a later edit is picked up on the next refresh rather than mutating the view mid-read.
  • The base branch cannot be resolved (Committed mode, no discoverable base): the panel falls back to showing uncommitted changes and labels the mode as a fallback, rather than silently swapping or erroring (D13).

User interactions

  • A Files / Git tab switch in the file panel header. The diff view occupies the same slot as the file tree; only one is shown at a time. The tab strip and header fit on one line without horizontal scroll or wrapping; all interactive controls meet the app's existing mobile tap-target minimum. The tab affordance and the panel work the same on mobile (where the panel is a slide-in drawer) as on desktop (D16, D18).
  • An ambient dirty indicator on the file-panel toggle/header when the repository is dirty (D17).
  • A comparison-mode selector (Uncommitted / Committed) at the top of the Git view.
  • Per-file rows showing path, change type, and an added/removed count, each expandable to reveal a syntax-highlighted unified diff, with an expand-all / collapse-all control.
  • Files grouped into staged and unstaged sections; each section is labeled and the grouping itself is the primary distinction (supplemented by a per-file label/icon), not color alone; each stage/unstage control carries an accessible name including the file path.
  • Per-file Stage / Unstage affordances and a Discard affordance in an overflow or secondary position (not an equal-weight sibling of Stage/Unstage), and a commit message field with a Commit action. Stage / Unstage / Commit / Discard are available only in Uncommitted mode; Committed mode is read-only review (D6, D15).
  • A Refresh control.
  • Diffs render in a single (unified) layout; additions and removals are visually distinguished with line numbers (D9).

Coordinations

  • The file panel hosts the view and owns the Files ↔ Git tab state.
  • The project repository is the single source of truth for the diff and the target of stage / commit / discard. The repository root is derived server-side from the session's project record; per-file arguments are validated to resolve inside the repository and rejected if they escape it; user-supplied text (commit message, file targets) is passed as discrete arguments and never interpreted as commands. The git-write actions are never registered as assistant tools; the artifact sandbox prevents a rendered artifact from invoking them (D11, D12).
  • The pending-changes panel remains the place where unapplied agent edits (held in a separate working copy) are reviewed and applied; the diff panel reflects the project repository's real state, so applying or discarding a pending change is one of the events that refreshes the diff panel. The two panels are complementary, not duplicates (D2).
  • Agent turns and the user's own edits change the repository; turn completion is a refresh trigger.

Out of scope

  • Pushing, pulling, creating pull requests, merging, or any operation that talks to a remote.
  • Per-line or per-hunk review comments and "send selected lines to an agent" — that is a separate feature (the diff-line re-prompt item), and this panel deliberately does not build a line-selection/commenting surface.
  • Side-by-side (split) diff layout.
  • Staging or discarding individual hunks/lines (stage and discard operate at whole-file granularity).
  • A live file-system watcher that streams diffs as files change on disk; refresh is event- and demand-driven, not continuous.
  • Showing the session agent's separate working-copy diff in this panel; that remains the pending-changes panel's job.
  • Renaming the existing pending-changes panel; naming and scope changes to that panel are out of scope for this feature.

Deferred (YAGNI)

  • Push / pull / pull-request / merge actions. Deferred — not requested (the request was "stage/commit"), and the assistant-level no-remote-write rule signals remotes are out of band for now. Reopening trigger: a stated need to publish commits from the panel. Evidence gate failed: no user-described need.
  • Side-by-side diff layout. Deferred — the primary surface is mobile-first and unified reads well there; a split layout is a desktop-only enhancement. Reopening trigger: a request to compare wide files side-by-side on desktop. Evidence gate: simpler unified version satisfies the stated need.
  • Per-hunk staging / discarding. Deferred — whole-file granularity covers the stated stage/commit need. Reopening trigger: a request to commit part of a file.
  • Continuous file-watch streaming of the diff. Deferred — event- and demand-driven refresh covers a single-user workflow without a watcher's cost. Reopening trigger: the diff is observed to feel stale between refresh events in practice.

Open items

  • None. Commit author/committer identity is settled: derived server-side from the host git configuration; the request cannot set or influence it (F3, D12).

Summary

A Files / Git tab in the right-side file panel that shows the project repository's diff in two modes (uncommitted vs. HEAD, and the branch vs. its upstream/default base, auto-selected by repo state on first open and then pinned to the user's choice), lets the user stage, unstage, commit (with server-derived identity), and discard whole files (with irrecoverable-discard confirmation distinguishing tracked and untracked cases), and stays current via coalesced event- and demand-driven refresh. Single actor (the session user); the panel is complementary to the existing pending-changes panel. The tab is named "Git" (Files / Git), distinct from the pending-changes panel. An ambient dirty indicator makes it findable. Write affordances are disabled during in-progress git operations. 18 decisions recorded. Four items deferred under YAGNI (remote actions, split layout, per-hunk granularity, file-watch streaming). Review team: junior-developer, user-experience-designer, adversarial-security-analyst, on-call-engineer. No load-bearing mechanics required a technical-notes file (the git mechanics are discoverable from the codebase's existing git-metadata path).