Ovvi

Changes panel

What the agents changed: a drawer with git diffs, a git chip on cards, and the last turn

Not built, and proposed: the user has not agreed to it yet. This page is the design of a changes panel: a drawer beside the tab page that shows what changed in the keyboard card's worktree, against one of three bases (the state the diff compares with). The idea and its git commands come from Kero (research). The checkpoints, the loading, the viewed marks and the comment delivery come from herdr-gpui (research). The Git plugin is built: a pane, not a drawer, with a repository's uncommitted changes or one commit (Diff).

The drawer

  • Where it lives: in the app only, as window chrome: a drawer at the right of the tab page, outside herdr's split tree. It is not a card and not a herdr pane, like Archived Sessions. Dropping a card on it does nothing.
  • What it shows: the worktree of the keyboard card: the pane's cwd from the snapshot, else the space's root. herdr's process info seems to carry no cwd, so an agent that changes folder without a shell cd is not followed.
  • Bases: Uncommitted (HEAD against now, untracked files included), Branch (git merge-base HEAD <base> against now) and Last turn (below). Branch's <base> is the base of the branch's pull request when gh on the machine names one, else origin/HEAD, else main or master. Nothing is fetched.
  • Saved: open or closed and its width per window, in @SceneStorage next to sidebarWidth. The base and the folded files live in memory. Nothing is in herdr.
  • Data: git runs on the machine through Exec, as the git state of space rows does (Git status), with --no-optional-locks --no-ext-diff --no-textconv. The result is cached by worktree root in the session's HostStore, so two windows on one session share it. "Now" is a snapshot tree too, so untracked files show in every base.
  • Two-phase load: first the file list, cheap at any size: git diff -M -z --name-status <rev> <now-tree> and --numstat. Then the patches in batches of 64 files or 20,000 lines, the visible files first. A file over 20,000 lines or 4 MiB shows Load diff instead.
  • Viewed marks: the user marks a file viewed. The mark keeps the file's + and − counts, per worktree root and branch, per device in app storage. A file whose counts change comes back unviewed.
  • Drawing: from git's patch (git diff -M <base>), not from two whole texts: a short parser of unified diff hunks, and git does the diff algorithm. Lines draw in the terminal font in a lazy stack (or one text view per file for long files), with the green, red and yellow of the terminal palette. Word highlights can come later from git diff --word-diff=porcelain. Expanding hidden context lines needs the whole file, so it is left out at first.
  • Read only first. No writes to the working tree in the first version.
  • Each device runs its own git calls on the machine. Nothing syncs. On an iPhone the panel is a sheet opened from the card's git chip; on an iPad, the same drawer.

Git chip

A small chip in the card header shows the branch and +N −M from git diff --numstat -z HEAD, one run per worktree root. A click opens the drawer. Space rows still show no changed-file count (D67); the chip is on cards. The chip needs the user's agreement before it is built.

Last turn

  • Trigger: when an agent's status goes from idle or done to working, or from working to idle or done (not from blocked to working), the app saves a checkpoint of the working tree on the machine, without touching the user's index. It runs with -c core.fsmonitor=false:
    cp .git/index $tmp/index
    GIT_INDEX_FILE=$tmp/index git add --all -- .
    tree=$(GIT_INDEX_FILE=$tmp/index git write-tree)
    git commit-tree -p HEAD -m "Ovvi-Branch: <branch>" $tree
    git update-ref refs/ovvi/checkpoints/<sha1 of branch>/<stamp> <commit>
    A tree equal to the last checkpoint's is skipped. The newest 50 per branch are kept. The base "Last turn" is the newest checkpoint against now.
  • A commit under a hidden ref: every device and window sees the same checkpoints, they survive an app restart, git gc cannot prune them, and no state file holds them. Branches, git log and the user's index do not show them.
  • Gap: the app sees a turn only while a window has that session open. A turn that starts with no Ovvi running has no snapshot, and the drawer then offers only Uncommitted and Branch. A real fix needs herdr to take the snapshot or send a hook on a status change (an upstream ask).
  • Two devices both see the change. The second one finds the same tree and skips it.

Later

  • Revert a hunk: writes files on the machine through Exec. It refuses while an agent in that worktree works, and when the file's hash changed since it was shown.
  • Restore a checkpoint, diff first: the drawer shows the checkpoint against now before the user restores. Restore saves a checkpoint of the current state first, refuses during a merge or rebase or on another branch, then runs git read-tree -m -u <tree> and git reset.
  • Comments to the agent on lines, kept in memory per window until sent. They follow one delivery rule: the app pastes them with pane.send_input (bracketed paste, quoted code marked as data) when herdr says the agent is idle or done, or after 120 s, and sends Enter 150 ms later. It never pastes into a shell or a blocked agent. Withdraw takes back a queued batch, and Send sends only new or edited comments.

Order

Build the drawer, the git chip and the last turn as one feature, in that order. A Git card's diff (File Explorer and Git) stays for a diff the user wants to pin in a tab; the drawer covers the common case with no placeholder pane.

Not copied from Kero

  • A diff card as the first diff surface: it needs a placeholder pane, a label, a state file and a card on every device. The drawer needs none and follows the keyboard card.
  • Agent control of web pages: it opens a path from any process on the machine into the user's logged-in web views.
  • A second, screen-based status detector: herdr's agents[] is the one source (D55).
  • A daemon of Ovvi's own that holds panes, layout or scrollback: herdr owns them (D1, D57).
  • Writes in the first version, silent snapshots, and whole-text diffs with a diff algorithm of our own.

On this page