Repository navigation
Description
Important
Problem: The Files Changed panel currently shows git working-tree diffs — all uncommitted changes regardless of source. This is noise when the user cares about what this session's agent actually wrote.
Approach: Replace the git/branch/turn mode system entirely with a single session-scoped view. Compute a net diff (session-start snapshot vs current state) filtered to only files the agent touched (via PatchParts). Reuse the existing snapshot infrastructure and file tree UI.
Scope: Backend: revive Session.diff with snapshot-based net-diff logic. Frontend: remove VCS mode machinery, wire the panel to the session diff endpoint. No new dependencies.
Assumptions: Snapshot tree hashes survive for the session's lifetime (GC is 7 days; sessions rarely outlive that). Bash-authored files are captured via PatchParts (snapshot-based, not tool-metadata-based).
Acceptance Criteria
- The Files Changed panel shows only files written by the agent during the current session
- The diff shown is the net diff (state at session start vs current state), not per-turn patches
- A file tree is rendered on the left with A/D/M badges; selecting a file shows its diff on the right
- No git/branch/turn mode selector exists in the UI
- A session with no agent file changes shows an empty state ("No changes yet")
- Files touched via Bash (e.g.
sed,mv) are included (PatchPart captures them) - Files the agent edited then externally reverted show no diff (filtered out when
additions + deletions === 0) - Sessions older than snapshot GC (7 days) gracefully show empty state
Key Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Mode system | Remove entirely | User only wants session context; git/branch are noise |
| File set source | Union of PatchPart.files across session |
Captures all agent-authored files including Bash writes |
| Diff computation | snapshot.diffFull(firstStepStart, current) |
Gives true net diff; reuses existing infra |
| Filtering | Intersect diffFull result with PatchPart file set | Excludes external changes that happened to land in the snapshot window |
| Empty/stale session | Return [], render empty state |
Graceful degradation, no error |
Implementation Sketch
Backend (packages/opencode/src/session/)
-
Session.diff— replace the stub with:- Query all messages + parts for the session
- Extract first
step-starthash (session-start ref) - Union all
PatchPart.files(agent-touched set) snapshot.track()for current statesnapshot.diffFull(from, to)filtered to agent-touched files- Filter out zero-change entries
-
HTTP handler —
GET /session/:sessionID/diffalready wired; just returns the revivedSession.diffresult
Frontend (packages/app/src/pages/session.tsx + side panel)
- Remove
reviewModesignal andChangeModetype - Remove
vcsQuery(thesdk().api.vcs.diff(...)call) - Replace
reviewDiffs()with a single query tosdk().api.session.diff(sessionID) - Re-fetch on
file.watcher.updatedevents (same trigger as before) - File tree + diff viewer stay as-is, fed by the new data source
- Empty state when
diffs.length === 0
TUI (packages/tui/)
- Remove mode selector from diff-viewer
- Point at session diff endpoint
Constraints & Invariants
- Snapshot tree objects must exist for the session's first turn (GC = 7 days)
PatchPartis the canonical source of "which files the agent touched" — no alternative tracking needed- The file tree component (
file-tree-v2.tsx) and diff viewer are untouched — only their data source changes
Activity
Replace 'Files Changed' with session-scoped 'Modified Files' (#174)