Skip to content

fix(app): refresh review diffs on git state updates - #896

Merged
Astro-Han merged 9 commits into
devfrom
codex/branch-review-diff
May 25, 2026
Merged

fix(app): refresh review diffs on git state updates#896
Astro-Han merged 9 commits into
devfrom
codex/branch-review-diff

Conversation

@Astro-Han

@Astro-Han Astro-Han commented May 25, 2026

Copy link
Copy Markdown
Owner

Summary

Refresh the session Review panel's cached VCS diffs when Git state metadata changes.

Also align the backend Git watcher publish boundary with the frontend VCS refresh filter so staged, unstaged, and branch diff invalidation events are actually produced.

Also refresh cached project metadata when a directory becomes a Git repo after it was already opened, so Review does not stay stuck in the non-Git mode list.

A follow-up fixes two Review mode regressions found in live retesting: Last turn now requests the latest user turn instead of the whole-session aggregate, and selecting or reopening a VCS-backed Review mode forces a fresh diff load instead of reusing a prior empty cache.

Also restore workflow PR task labeling so the PR triage workflow contract matches the live labeler config.

Why

Fixes #895.

The right-side Review panel could show stale data for Branch changes because the VCS refresh path ignored Git state updates. The frontend filter originally ignored every .git/ update, and the backend watcher only published .git/HEAD from the Git directory subscription. That meant updates to .git/index, branch refs, and packed refs could fail to invalidate cached staged, unstaged, or branch diffs.

A live test also found a separate transition bug: if a directory was opened before it was a Git repo and later became one via shell commands, the cached instance context kept project.vcs empty. The Review mode selector then only showed Last turn and hid Unstaged, Staged, and Branch, even though the filesystem was now a Git repo.

A second live retest found that Last turn was still backed by the full session aggregate, and Branch changes could keep a stale empty result after the branch changed. The Review panel now fetches turn diffs with the latest user message ID, and VCS modes force-refresh when selected or when the Review panel is reopened.

The watcher path is now narrow on both sides:

  • backend subscription keeps .git/HEAD, .git/index, .git/packed-refs, and .git/refs visible to the watcher;
  • backend publish filtering only emits HEAD, index, packed-refs, refs/heads/**, and refs/remotes/** from the Git directory;
  • frontend refresh filtering accepts those same Git metadata paths, including repo-relative, absolute POSIX, and Windows-style watcher paths;
  • object-store updates, tags, stash, and transient Git heads stay filtered as noise.

Cached non-Git instances now re-check the directory when a .git marker appears and reload once the project is confirmed as Git.

A review follow-up also covers remote branch refs, because branch diffs are computed from the default branch ref via merge-base.

The CI follow-up fixes a pre-existing workflow contract drift surfaced by unit-opencode: workflow PRs are expected to receive both ci and task labels, but .github/labeler.yml no longer had the task workflow rule.

Related Issue

Fixes #895

Human Review Status

Pending

Review Focus

Please focus on the Git watcher event boundary across packages/opencode/src/file/watcher.ts and packages/app/src/pages/session/use-session-vcs-refresh.ts, the cached project transition in packages/opencode/src/project/instance-store.ts, and the Review mode state in packages/app/src/pages/session/use-session-review-state.ts. The expected behavior is: a directory opened as non-Git can become Git later, Review should expose VCS modes, Git metadata changes should refresh staged, unstaged, or branch diffs while noisy Git internals remain filtered, Last turn should only show the latest user turn, and selecting Branch changes should not reuse a prior empty cache.

For the CI follow-up, please check that the restored task labeler rule only applies to workflow files and matches the existing contract test.

Risk Notes

No copy or layout changed. This does change visible Review panel data behavior, so I launched the dev Electron app from this branch for live retesting and verified the backend VCS branch diff against the same /Users/yuhan/PawWork repo state.

This PR touches Git metadata path filtering in the backend watcher and frontend refresh consumer. The intended path surface is narrow: POSIX native watcher events are covered locally, Windows-style watcher paths are covered in the frontend normalization test, and backend Windows behavior uses path.relative/path.sep through Node's platform path implementation.

This PR also touches cached project reload behavior for directories that transition from non-Git to Git. The reload check is limited to cached non-Git instances where a .git marker exists; already-Git projects keep the existing fast path.

This PR also touches GitHub labeler automation. The intended behavior is narrow: workflow-only PRs get task in addition to ci, while priority remains owned by the priority triage script.

No docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes are relevant.

The main residual risk is platform-specific watcher path shape variation that is not reproduced by the native macOS watcher test. The filtering is intentionally limited to VCS state paths instead of all .git/ updates.

How To Verify

Targeted frontend RED: bun test --preload ./happydom.ts src/pages/session/use-session-vcs-refresh.test.ts failed before the first fix because .git/index did not refresh.
Backend watcher RED: bun --cwd packages/opencode test test/file/watcher.test.ts failed before the backend watcher fix because .git/index and refs/heads updates were not published.
Cached project RED: bun --cwd packages/opencode test test/project/instance-store.test.ts failed before the project reload fix because a directory opened as non-Git stayed cached as non-Git after git init.
Review state RED: bun test src/pages/session/use-session-review-state.test.ts failed before the follow-up because the last-turn request and VCS force-refresh helpers did not exist.
Related app tests: bun test src/pages/session/use-session-review-state.test.ts src/pages/session/use-session-vcs-refresh.test.ts src/pages/session/review-change-mode.test.ts src/pages/session/use-session-timeline-data.test.ts src/context/global-sync/event-reducer.test.ts passed, 69 tests / 174 assertions.
App typecheck: bun run typecheck from packages/app passed.
Backend, VCS, watcher, and project tests: bun run --cwd packages/opencode test test/project/instance-store.test.ts test/project/vcs.test.ts test/file/watcher.test.ts test/server/project-init-git.test.ts passed, 34 tests / 80 assertions.
Opencode typecheck: bun run typecheck from packages/opencode passed.
Lint: bunx eslint packages/app/src/pages/session/use-session-vcs-refresh.ts packages/opencode/src/file/watcher.ts returned 0; packages/opencode/src/file/watcher.ts is ignored by repo eslint config.
Whitespace: git diff --check passed.
Runtime VCS probe: bun --cwd packages/opencode -e against /Users/yuhan/PawWork returned branch=demo-branch, defaultBranch=main, and 2 branch diff files: demo-invoices/invoice-2026-1001.txt and temp/branch-note.md.
Manual dev app: bun run dev:desktop launched Electron from this branch at http://localhost:5173/ for live Review-panel retesting.

Screenshots or Recordings

Not applicable. This change updates refresh invalidation behavior, cached project metadata, and GitHub labeler configuration; it does not change visible UI or copy.

Checklist

How to use this checklist:

  • Tick a box by replacing [ ] with [x]. Do not edit, add, or remove items.
  • The bot-applied label items can only be honestly ticked AFTER the PR is opened and the labeler / priority-triage bots have run — return to the PR description and tick them then.
  • Most items are required. The few that are conditional are explicitly marked (conditional); for those, leave unticked if they truly do not apply and explain why in Risk Notes. All other items must be ticked before requesting human review.
  • Type label — this PR carries exactly one of bug, enhancement, task, documentation. Type labels are author-added; the labeler bot does NOT assign them. Add the label in the GitHub UI, then tick this.
  • Routing labels — this PR carries at least one of app, ui, platform, harness, ci. The labeler bot assigns these on PR open based on changed paths. Confirm the bot's choice (or override if wrong), then tick this.
  • Priority label — this PR carries exactly one of P0, P1, P2, P3. The priority-triage bot suggests one on PR open. Confirm or override, then tick this.
  • Human Review Status above is set to Pending, Approved by @<reviewer>, or Not required: <reason> (default is Pending; "not required" is restricted to bot-authored low-risk PRs).
  • I linked the related issue, or stated in Summary why there is no issue.
  • I described the review focus and any meaningful risks.
  • I replaced the example block in How To Verify with the real verification steps and the key result for each.
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope.
  • (conditional) I manually checked visible UI or copy changes when needed, with screenshots or recordings. Leave unticked only if no visible UI or copy changed.
  • (conditional) I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes. Leave unticked only if no platform/packaging surface was touched.
  • (conditional) I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant. Leave unticked only if none of those surfaces was touched.
  • I reviewed the final diff for unrelated changes and suspicious dependency changes.
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English.

Summary by CodeRabbit

  • New Features

    • Auto-detect when a directory becomes a Git repository and reload the project.
    • Session review now uses turn-change based diffs and exposes a controller for turn-change handling.
  • Bug Fixes

    • Refined Git metadata filtering so only relevant .git files trigger refreshes, reducing spurious updates.
  • Tests

    • Added and updated tests covering Git metadata filtering and repository-detection reload behavior.
  • Chores

    • Added a workflow labeler rule for task-related workflow changes.

Review Change Stack

@Astro-Han Astro-Han added bug Something isn't working P2 Medium priority app Application behavior and product flows ui Design system and user interface labels May 25, 2026
@github-actions github-actions Bot removed the ui Design system and user interface label May 25, 2026
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 98899ebb-700c-4398-80de-9dab9c817834

📥 Commits

Reviewing files that changed from the base of the PR and between dc32abb and 42460b2.

📒 Files selected for processing (2)
  • packages/app/src/pages/session/use-session-review-state.test.ts
  • packages/app/src/pages/session/use-session-review-state.ts
💤 Files with no reviewable changes (1)
  • packages/app/src/pages/session/use-session-review-state.test.ts

📝 Walkthrough

Walkthrough

Normalize watcher file paths, reject missing files, and apply an explicit .git/ allowlist for refresh events; thread a publish predicate into FileWatcher and add VCS-aware helpers; detect repo init in InstanceStore to trigger reload; expand tests for allowed/disallowed .git/ paths; add a .github/workflows/** labeler rule.

Changes

VCS Refresh Event Filtering

Layer / File(s) Summary
Normalize paths and allowlist .git/ entries
packages/app/src/pages/session/use-session-vcs-refresh.ts
Adds normalizeWatcherFilePath(file) and updates isFileWatcherVcsRefreshEvent to return false for missing/invalid files, true for non-.git/ files, and true only for an explicit allowlist of .git/ metadata (index, HEAD, packed-refs, refs/heads/*, refs/remotes/*, logs/HEAD, worktrees/*).
Tests: positive and negative .git/ cases
packages/app/src/pages/session/use-session-vcs-refresh.test.ts
Broadens test name and adds positive assertions for allowed .git/ paths (including repo-root and Windows path variants) and negative assertions ensuring certain .git/* entries (notably .git/objects/...) do not trigger refresh.

FileWatcher: VCS-aware filtering

Layer / File(s) Summary
VCS constants and helper functions
packages/opencode/src/file/watcher.ts
Adds VCS-related ignore/refresh constants and exported helpers vcsWatcherIgnoreEntries and shouldPublishVcsWatcherPath to compute subscribed entries and publishable .git/* paths.
Watcher callback predicate and subscription wiring
packages/opencode/src/file/watcher.ts
Extends watcher callback to accept a shouldPublish predicate, skips publishing file.watcher.updated when predicate is false, and rewires .git subscription to use the new ignore list and publish predicate.
FileWatcher tests
packages/opencode/test/file/watcher.test.ts
Adds a git metadata filtering test suite and updates tests to assert .git/index, .git/refs/heads/*, and .git/packed-refs publish behavior (excluding unlink).

InstanceStore: detect repo init and reload

Layer / File(s) Summary
Git marker detection and reload path
packages/opencode/src/project/instance-store.ts
Adds fs/path imports, project_vcs_changed mismatch reason, hasGitMarker(directory) climbing parents for .git, and load logic to re-derive project/worktree and call reload when a repo marker appears.
Live test: git init triggers reload
packages/opencode/test/project/instance-store.test.ts
Adds a live test using Bun $ to git init a temp dir and assert a cached non-git instance is reloaded to project.vcs === "git" and the old instance is disposed.

Session UI & Review State

Layer / File(s) Summary
Wire turnChangeController into session and timeline
packages/app/src/pages/session.tsx, packages/app/src/pages/session/message-timeline.tsx, packages/app/src/pages/session/session-main-view.tsx
Introduce and pass a turnChangeController from session into SessionMainView/MessageTimeline; remove local creation in MessageTimeline and update props types.
Review-state helpers and effects
packages/app/src/pages/session/use-session-review-state.ts, packages/app/src/pages/session/use-session-review-state.test.ts
Add selectReviewChangeMode and turnChangeDisplayDiffs, extend createSessionReviewState input with latestTurnChange, route setChanges through the helper, derive turn diffs from turnChangeDisplayDiffs, and force loadVcs(..., true) when wantsReview becomes true; add tests for both helpers.

CI Labeler Update

Layer / File(s) Summary
Add task changed-files matcher
.github/labeler.yml
Introduces a new task label mapping for changes under .github/workflows/**.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Possibly related issues

  • #335: The changes address the issue where .git watcher events were being ignored, preventing VCS-backed review diffs from refreshing.

Poem

🐰
I hopped through .git/ with careful feet,
Sniffed HEAD and refs where refresh should meet.
Objects stayed sleeping, quiet and deep,
Now diffs wake fresh — no stale data to keep. 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: refreshing Review panel VCS diffs when Git state updates occur, which directly addresses the PR's core objective.
Description check ✅ Passed The PR description comprehensively covers all required template sections: Summary, Why, Related Issue, Human Review Status, Review Focus, Risk Notes, How to Verify, Screenshots/Recordings, and a fully completed Checklist.
Linked Issues check ✅ Passed The PR fully addresses issue #895's requirements: frontend refresh filtering now accepts Git metadata paths (.git/index, refs, etc.), backend watcher boundaries align with filtering, and cached projects reload when directories transition to Git repos.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the stated objectives: Git watcher filtering, Review state management, cached project reloading, and GitHub labeler workflow configuration. No unrelated refactors, dependencies, or generated files were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/branch-review-diff

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested priority: P2 (includes user-path files (packages/app/src/pages/session/use-session-vcs-refresh.test.ts, packages/app/src/pages/session/use-session-vcs-refresh.ts)).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the VCS refresh logic to allow specific Git metadata changes, such as index and branch updates, to trigger a session refresh while ensuring cross-platform path compatibility. The reviewer suggested expanding the monitored Git files to include remote branches, tags, and transient operation heads like MERGE_HEAD to ensure the UI remains synchronized during various Git operations.

Comment thread packages/app/src/pages/session/use-session-vcs-refresh.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/app/src/pages/session/use-session-vcs-refresh.test.ts (1)

5-30: ⚡ Quick win

Expand regression coverage for all newly allowlisted Git metadata paths.

This test now covers refs and object-store exclusion, but it still misses explicit assertions for the other allowlisted refresh triggers (.git/HEAD, .git/packed-refs, .git/logs/HEAD, .git/worktrees/...).

Suggested test additions
   test("refreshes for rescan, source updates, and git state updates", () => {
@@
     expect(
       isFileWatcherVcsRefreshEvent({
         type: "file.watcher.updated",
         properties: { file: ".git/refs/heads/feature/test", event: "change" },
       }),
     ).toBe(true)
+    expect(
+      isFileWatcherVcsRefreshEvent({
+        type: "file.watcher.updated",
+        properties: { file: ".git/HEAD", event: "change" },
+      }),
+    ).toBe(true)
+    expect(
+      isFileWatcherVcsRefreshEvent({
+        type: "file.watcher.updated",
+        properties: { file: ".git/packed-refs", event: "change" },
+      }),
+    ).toBe(true)
+    expect(
+      isFileWatcherVcsRefreshEvent({
+        type: "file.watcher.updated",
+        properties: { file: ".git/logs/HEAD", event: "change" },
+      }),
+    ).toBe(true)
+    expect(
+      isFileWatcherVcsRefreshEvent({
+        type: "file.watcher.updated",
+        properties: { file: ".git/worktrees/wt1/HEAD", event: "change" },
+      }),
+    ).toBe(true)
     expect(
       isFileWatcherVcsRefreshEvent({
         type: "file.watcher.updated",
         properties: { file: ".git/objects/aa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", event: "change" },
       }),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/pages/session/use-session-vcs-refresh.test.ts` around lines
5 - 30, The test for isFileWatcherVcsRefreshEvent is missing explicit assertions
for several newly allowlisted Git metadata paths; update the "refreshes for
rescan, source updates, and git state updates" test to include additional
expect(...) calls that assert true for events whose properties.file is
".git/HEAD", ".git/packed-refs", ".git/logs/HEAD", and a worktree path such as
".git/worktrees/my-worktree/gitdir", and continue to assert false for
object-store paths (e.g., ".git/objects/..."); locate the test and add these
expect(...) assertions using the same pattern as the existing
file.watcher.updated checks against isFileWatcherVcsRefreshEvent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/app/src/pages/session/use-session-vcs-refresh.test.ts`:
- Around line 5-30: The test for isFileWatcherVcsRefreshEvent is missing
explicit assertions for several newly allowlisted Git metadata paths; update the
"refreshes for rescan, source updates, and git state updates" test to include
additional expect(...) calls that assert true for events whose properties.file
is ".git/HEAD", ".git/packed-refs", ".git/logs/HEAD", and a worktree path such
as ".git/worktrees/my-worktree/gitdir", and continue to assert false for
object-store paths (e.g., ".git/objects/..."); locate the test and add these
expect(...) assertions using the same pattern as the existing
file.watcher.updated checks against isFileWatcherVcsRefreshEvent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6706cb5f-594e-48b0-9387-49d9bab74eb7

📥 Commits

Reviewing files that changed from the base of the PR and between 580fb76 and e475c7d.

📒 Files selected for processing (2)
  • packages/app/src/pages/session/use-session-vcs-refresh.test.ts
  • packages/app/src/pages/session/use-session-vcs-refresh.ts

@github-actions

github-actions Bot commented May 25, 2026

Copy link
Copy Markdown

Perf delta summary

Comparator: pass

Profile / Scenario interaction median interaction worst long task max tbt frame gap p95 frame gap max jank count cls status
default / homepage-cold 40 -> 24 (-16) 48 -> 32 (-16) 64 -> 62 (-2) 14 -> 12 (-2) 16.8 -> 33.4 (+16.6) 166.7 -> 149.9 (-16.8) 3 -> 3 (0) 0 -> 0 (0) pass
default / long-session-input-lag 48 -> 48 (0) 48 -> 48 (0) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.7 (-0.1) 16.8 -> 16.8 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-streaming-long 48 -> 40 (-8) 72 -> 72 (0) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.8 (0) 33.3 -> 33.3 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-call-expand 40 -> 40 (0) 40 -> 40 (0) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.7 (-0.1) 16.8 -> 16.7 (-0.1) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-default-open-heavy-bash 16 -> 16 (0) 16 -> 24 (+8) 103 -> 69 (-34) 72 -> 19 (-53) 66.6 -> 50 (-16.6) 116.7 -> 116.6 (-0.1) 4 -> 3 (-1) 0.004 -> 0.004 (0) pass
default / terminal-side-panel-open 40 -> 48 (+8) 48 -> 48 (0) 0 -> 0 (0) 0 -> 0 (0) 33.3 -> 33.3 (0) 33.3 -> 33.4 (+0.1) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-scroll-reading 16 -> 16 (0) 16 -> 16 (0) 0 -> 0 (0) 0 -> 0 (0) 16.7 -> 33.3 (+16.6) 16.7 -> 33.3 (+16.6) 0 -> 0 (0) 0 -> 0 (0) pass
low-end / session-scroll-reading-long 0 -> 0 (0) 0 -> 0 (0) 55 -> 51 (-4) 5 -> 1 (-4) 33.3 -> 33.3 (0) 50 -> 50 (0) 0 -> 0 (0) 0 -> 0 (0) pass
low-end / session-timeline-recompute 40 -> 40 (0) 40 -> 40 (0) 0 -> 0 (0) 0 -> 0 (0) 33.3 -> 33.3 (0) 33.4 -> 33.4 (0) 0 -> 0 (0) 1.075 -> 1.075 (0) pass
low-end / concurrent-shimmer-extreme 0 -> 0 (0) 0 -> 0 (0) 0 -> 0 (0) 0 -> 0 (0) 16.7 -> 16.8 (+0.1) 16.8 -> 16.8 (0) 0 -> 0 (0) 0 -> 0 (0) pass

@github-actions github-actions Bot added the ci Continuous integration / GitHub Actions label May 25, 2026
@github-actions github-actions Bot added the harness Model harness, prompts, tool descriptions, and session mechanics label May 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/opencode/src/project/instance-store.ts (1)

18-19: ⚡ Quick win

Prefer Effect services over synchronous Node.js APIs.

The hasGitMarker helper uses synchronous fs.existsSync, path.join, and path.dirname directly. Consider refactoring to use FileSystem.FileSystem and Path.Path services for consistency with the codebase patterns and better error handling through Effect's error channel.

♻️ Example refactor using Effect services
-import fs from "node:fs"
-import path from "node:path"
+// FileSystem and Path are already available via yield* in Effect.gen context
-function hasGitMarker(directory: string) {
-  let current = directory
-  while (true) {
-    if (fs.existsSync(path.join(current, ".git"))) return true
-    const parent = path.dirname(current)
-    if (parent === current) return false
-    current = parent
-  }
-}
+function hasGitMarker(directory: string): Effect.Effect<boolean, unknown, Filesystem> {
+  return Effect.gen(function* () {
+    const filesystem = yield* Filesystem
+    let current = directory
+    while (true) {
+      const gitPath = yield* filesystem.path.join(current, ".git")
+      const exists = yield* filesystem.exists(gitPath)
+      if (exists) return true
+      const parent = yield* filesystem.path.dirname(current)
+      if (parent === current) return false
+      current = parent
+    }
+  })
+}

Then update the call site at line 228:

-                if (exit.value.project.vcs !== "git" && hasGitMarker(directory)) {
+                const gitMarkerExists = yield* hasGitMarker(directory)
+                if (exit.value.project.vcs !== "git" && gitMarkerExists) {

As per coding guidelines: "Prefer FileSystem.FileSystem instead of raw fs/promises for effectful file I/O in Effect services" and "Prefer Path.Path, Config, Clock, and DateTime services when those concerns are already inside Effect code".

Also applies to: 76-84

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/project/instance-store.ts` around lines 18 - 19, The
helper hasGitMarker currently uses synchronous Node APIs (fs.existsSync,
path.join, path.dirname); refactor it to use the Effect services
FileSystem.FileSystem and Path.Path instead: replace direct fs/path calls inside
hasGitMarker (and related helpers referenced around lines 76–84) with effectful
operations from the FileSystem and Path services (e.g., use service-provided
exists/existsSync equivalents and path join/dirname operations via Path.Path),
return an Effect that fails on I/O errors rather than throwing, and update the
call site(s) that invoke hasGitMarker to run the resulting Effect within the
existing Effect runtime (keeping function name hasGitMarker and preserving its
semantics of detecting .git markers).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/file/watcher.ts`:
- Around line 189-191: The current createCallback
(ParcelWatcher.SubscribeCallback) restores the instance context on every (err,
evts) call; instead define the handler function once inside createCallback and
bind the instance context once using Instance.bind (e.g., bind(ctx)) when
returning the callback, then remove any per-invocation context restoration
inside the handler; this change should be made in the createCallback function so
the returned ParcelWatcher.SubscribeCallback is already bound to the correct
ctx.

---

Nitpick comments:
In `@packages/opencode/src/project/instance-store.ts`:
- Around line 18-19: The helper hasGitMarker currently uses synchronous Node
APIs (fs.existsSync, path.join, path.dirname); refactor it to use the Effect
services FileSystem.FileSystem and Path.Path instead: replace direct fs/path
calls inside hasGitMarker (and related helpers referenced around lines 76–84)
with effectful operations from the FileSystem and Path services (e.g., use
service-provided exists/existsSync equivalents and path join/dirname operations
via Path.Path), return an Effect that fails on I/O errors rather than throwing,
and update the call site(s) that invoke hasGitMarker to run the resulting Effect
within the existing Effect runtime (keeping function name hasGitMarker and
preserving its semantics of detecting .git markers).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 72f60678-68f0-4ae6-8728-4e2454d7aefd

📥 Commits

Reviewing files that changed from the base of the PR and between 9dd05cb and e969d3f.

📒 Files selected for processing (4)
  • packages/opencode/src/file/watcher.ts
  • packages/opencode/src/project/instance-store.ts
  • packages/opencode/test/file/watcher.test.ts
  • packages/opencode/test/project/instance-store.test.ts

Comment thread packages/opencode/src/file/watcher.ts
@github-actions github-actions Bot added the ui Design system and user interface label May 25, 2026
@Astro-Han
Astro-Han merged commit 0d32b48 into dev May 25, 2026
29 checks passed
@Astro-Han
Astro-Han deleted the codex/branch-review-diff branch May 25, 2026 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Application behavior and product flows bug Something isn't working ci Continuous integration / GitHub Actions harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority ui Design system and user interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Review Branch changes can show stale VCS diffs

1 participant