Skip to content

fix: review panel flickers/rerenders on every filesystem event including ignored paths #21452

@drrcool

Description

@drrcool

Description

The review panel in the desktop app (and any environment with OPENCODE_EXPERIMENTAL_FILEWATCHER=true) flickers/rerenders every few seconds during active development. Each rerender clears the current diff state momentarily before refetching, making the panel unusable.

Root cause

Two bugs compound each other:

Backend (packages/opencode/src/file/watcher.ts): The parcel/watcher callback was shared across both subscriptions (worktree dir and git dir). Events from all paths were published as file.watcher.updated without being filtered through FileIgnore.match() after receipt. This means build artifacts like out/renderer/index.js, .turbo/ lock files, etc. all fired watcher events even though they match the ignore patterns.

Frontend (packages/app/src/pages/session.tsx): The file.watcher.updated listener only skipped paths starting with the relative string .git/. In git worktrees (and on Windows), the git dir lives at an absolute path like /path/to/.git/worktrees/<branch>/HEAD, so those events slipped through and triggered spurious refreshVcs() calls.

Steps to reproduce

  1. Run electron-vite dev (or any build tool that writes output to a directory not in .gitignore at the OS level — note: FileIgnore ignores out/ but the filter was not applied on incoming events)
  2. Open a session in the desktop app
  3. Open the Review tab
  4. Observe the diff panel repeatedly clearing and reloading every few seconds

Or: use a git worktree and observe the same flicker driven by absolute .git/worktrees/…/HEAD paths.

OpenCode version

1.4.0

OS

macOS (also reproducible on Linux worktrees and Windows due to path separator issue)

Fix

  • Move the parcel/watcher callback inside subscribe() so each subscription filters independently
  • Non-git subscriptions now run incoming paths through FileIgnore.match() before publishing
  • Git-dir subscription only allows HEAD through (the only event that signals a branch/commit change)
  • Frontend listener normalizes separators and checks both path.startsWith(".git/") and path.includes("/.git/") to handle worktree absolute paths

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions