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).
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.
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.
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:
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.
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.
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.
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.