Skip to content

Refresh working tree diff after turns - #3906

Open
jakeleventhal wants to merge 1 commit into
pingdotgg:mainfrom
jakeleventhal:t3code/fix-deleted-file-diff-state
Open

Refresh working tree diff after turns#3906
jakeleventhal wants to merge 1 commit into
pingdotgg:mainfrom
jakeleventhal:t3code/fix-deleted-file-diff-state

Conversation

@jakeleventhal

@jakeleventhal jakeleventhal commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

What changed

  • make review diff-preview queries react to an explicit per-environment revision without changing query identity or RPC payloads
  • invalidate previews after deduplicated VCS status changes, including a later turn deleting an untracked file
  • extend the existing settled ref-mutation invalidation hook so pull, push, publish, stacked actions, and explicit refreshes also refresh branch previews
  • keep the initial status snapshot passive to avoid a redundant preview request on mount

Why

The Working tree diff preview could reuse a cached response after a later agent turn changed the repository. This was especially visible when a file created in one turn was deleted in the next: Git correctly omitted the deleted untracked file from a fresh preview, but the UI could continue rendering the earlier cached patch.

The earlier implementation solved this with localGeneration, a remoteRefHash scan, and unconditional local-status publication. That conflicts with the resource-conscious ref architecture now on main, which deduplicates status events and refreshes ref-backed data through explicit invalidation after mutations.

This rewrite follows that architecture: actual status changes and settled ref mutations bump a lightweight client revision, and existing diff-preview atoms refresh reactively from that signal. There is no remote-ref hash subprocess, polling loop, always-publish behavior, or client-only cache-key churn.

Impact

Working tree and branch diff views refresh after relevant repository changes while retaining fingerprint deduplication and the resource bounds introduced by #4727 and #2679.

Validation

  • vp test run packages/client-runtime/src/state/runtime.test.ts packages/client-runtime/src/state/review.test.ts packages/client-runtime/src/state/vcs.test.ts packages/client-runtime/src/state/vcsAction.test.ts packages/client-runtime/src/state/sourceControl.test.ts (47 passed)
  • targeted vp fmt --check and vp lint for the six changed files
  • vp run --filter @t3tools/client-runtime typecheck
  • integrated web verification in an isolated T3 environment: with the diff panel left mounted, adding an untracked fixture changed the preview from 224 to 225 additions; deleting it and issuing the same explicit VCS refresh returned the live preview to 224 additions

Note

Medium Risk
Touches shared query atom wiring and VCS status streaming; extra refetches on status updates could increase RPC load but behavior is scoped per environment with tests.

Overview
Fixes stale working tree diff preview by re-running the cached reviewGetDiffPreview query when Git state changes, instead of relying only on SWR staleTime.

Adds a per-environment reviewDiffPreviewRevisionAtom and invalidateReviewDiffPreviews, and wires diffPreview to a new optional refreshSignal on environment RPC query atoms so subscribing to the revision triggers a refetch without changing the RPC payload.

Invalidation runs when VCS status updates after the initial snapshot (projectVcsStatusChanges / refactored status stream) and when cached refs are cleared (invalidateCachedVcsRefs). Tests cover environment scoping, reactive dependents, status-driven bumps, and ref-invalidation paths.

Reviewed by Cursor Bugbot for commit 3ada610. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Refresh working tree diff previews after VCS status changes and ref invalidations

  • Adds a per-environment revision counter atom (diffPreviewRevisionByEnvironment) in review.ts and an invalidateReviewDiffPreviews function that increments it.
  • Wires the diff preview query atom to re-fetch when its environment's revision counter changes, via a new refreshSignal option in createEnvironmentQueryAtomFamily.
  • Calls invalidateReviewDiffPreviews from vcs.ts on every VCS status change after the initial snapshot, and from vcsRefInvalidation.ts when cached VCS refs are cleared.

Macroscope summarized 3ada610.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 12538cd2-7e14-4ba8-ad8a-860e3b1e5db7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions github-actions Bot added the vouch:unvouched PR author is not yet trusted in the VOUCHED list. label Jul 12, 2026
@jakeleventhal
jakeleventhal marked this pull request as ready for review July 12, 2026 03:13
@github-actions github-actions Bot added the size:S 10-29 changed lines (additions + deletions). label Jul 12, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Jul 12, 2026
@macroscopeapp

macroscopeapp Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a new diff preview refresh mechanism that changes runtime behavior. Multiple unresolved review comments identify potential cache invalidation gaps where diffs may not refresh correctly (e.g., HEAD changes, content changes without status changes, remote ref updates). These design concerns warrant human review.

You can customize Macroscope's approvability policy. Learn more.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 68f23c90ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/src/components/DiffPanel.tsx Outdated
Comment on lines +273 to +276
const diffPreviewCacheKey = JSON.stringify({
latestTurnId: latestTurn?.turnId ?? null,
workingTree: gitStatusQuery.data?.workingTree ?? null,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include HEAD state in the diff preview cache key

When the current ref changes while the working tree summary is unchanged, this cache key does not change even though getReviewDiffPreview recomputes the branch-range diff from the current HEAD/branch. For example, switching between two clean branches leaves workingTree as an empty summary and latestTurnId unchanged, so the branch diff panel can keep reusing the previous ${baseRef}...HEAD preview. Include ref/HEAD-relevant status (or a VCS generation) in the cache key, not just the working-tree summary.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Comment thread apps/web/src/components/DiffPanel.tsx Outdated
Comment on lines +273 to +276
const diffPreviewCacheKey = JSON.stringify({
latestTurnId: latestTurn?.turnId ?? null,
workingTree: gitStatusQuery.data?.workingTree ?? null,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Track content changes hidden by the status summary

When an already-listed changed file is edited without changing its path or numstat summary, this key stays identical while the patch returned by getReviewDiffPreview changes. This is especially easy for untracked files because the status summary records them as the same path with 0 insertions/deletions, so editing the file contents after the preview is cached leaves the Working tree panel rendering the old patch until something else changes the key. Use a content/diff generation or refreshed diff hash rather than only the lossy workingTree summary.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@macroscopeapp
macroscopeapp Bot dismissed their stale review July 12, 2026 03:29

Dismissing prior approval to re-evaluate 601d479

@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:S 10-29 changed lines (additions + deletions). labels Jul 12, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 601d479ebe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/web/src/components/DiffPanel.tsx Outdated
const latestTurn = orderedTurnDiffSummaries[0];
const diffPreviewCacheKey = JSON.stringify({
latestTurnId: latestTurn?.turnId ?? null,
localGeneration: gitStatusQuery.data?.localGeneration ?? null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include remote ref changes in diff preview keys

When the branch preview is based on a remote ref, a fetch can move that base ref while the working tree and HEAD stay unchanged; VcsStatusBroadcaster.updateCachedRemoteStatus publishes only remoteUpdated, and applyGitStatusStreamEvent preserves the old localGeneration, so this cache key stays identical even though getReviewDiffPreview recomputes ${baseRef}...HEAD against the new ref. Include a remote generation/fingerprint in the key for branch-range previews so the panel refreshes after remote status updates.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Jul 12, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d83415d7d3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/server/src/vcs/GitVcsDriverCore.ts Outdated
Comment on lines +1222 to +1227
const remoteRefs = yield* runGitStdout("GitVcsDriver.statusDetailsRemote.remoteRefs", cwd, [
"for-each-ref",
"--sort=refname",
"--format=%(refname)%00%(objectname)",
"refs/remotes",
]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid failing status on large remote-ref sets

In repositories with many remote-tracking refs, this new for-each-ref refs/remotes output can exceed the default 1 MB stdout limit enforced by runGitStdout/executeGit; when that happens collectOutput fails with a GitCommandError, so every remote status refresh fails and the Git UI loses status even though the repository is otherwise healthy. Hash the ref stream/bounded chunks, or otherwise avoid materializing all remote refs under the default cap, before putting this on the status path.

Useful? React with 👍 / 👎.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 87b2fd2. Configure here.

Comment thread apps/server/src/vcs/VcsStatusBroadcaster.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d525d2feee

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/server/src/vcs/GitVcsDriverCore.ts Outdated
Comment on lines +1236 to +1241
const remoteRefs = yield* runGitStdout("GitVcsDriver.statusDetailsRemote.remoteRefs", cwd, [
"for-each-ref",
"--sort=refname",
"--format=%(refname)%00%(objectname)",
"refs/remotes",
]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid failing remote status on large ref sets

On repositories with many remote-tracking refs, this new for-each-ref refs/remotes output is read through runGitStdout, which uses the default 1,000,000-byte output cap and turns overflow into a GitCommandError rather than truncating. A large branch namespace can therefore make statusDetailsRemote fail just from opening or refreshing VCS status, whereas the previous path did not enumerate every remote ref; hash the refs while streaming or otherwise handle large output so remote status still loads.

Useful? React with 👍 / 👎.

@jakeleventhal
jakeleventhal force-pushed the t3code/fix-deleted-file-diff-state branch from b7f47e9 to 3ada610 Compare July 29, 2026 16:15

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3ada6109f6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +248 to +250
if (current !== null) {
const registry = yield* AtomRegistry.AtomRegistry;
invalidateReviewDiffPreviews(registry, { environmentId });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refresh previews when local status summaries stay unchanged

When a later turn edits an already-dirty file without changing its path or insertion/deletion counts, this invalidation never runs: CheckpointReactor refreshes local status after the turn, but VcsStatusBroadcaster.updateCachedLocalStatus publishes localUpdated only when the JSON fingerprint of the summary changes. The patch returned by review.getDiffPreview can therefore change while the mounted query keeps its old result indefinitely. Fresh evidence in this revision is that preview freshness now depends exclusively on receiving these status events, so invalidate after every checkpoint refresh or propagate a content generation even when the summary is identical.

Useful? React with 👍 / 👎.

Comment on lines +249 to +250
const registry = yield* AtomRegistry.AtomRegistry;
invalidateReviewDiffPreviews(registry, { environmentId });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Track fetched remote refs when invalidating previews

When the automatic status fetch moves a selected remote base such as origin/release without changing the current branch's divergence or PR fields, no remoteUpdated event is published and this invalidation is skipped, although ${baseRef}...HEAD now produces a different preview. The server invokes git fetch ... <remote> without a refspec (git fetch -h documents the repository-only form as git fetch [<repository> [<refspec>...]]), while VcsStatusBroadcaster fingerprints only VcsStatusRemoteResult, which contains no remote-ref generation. Fresh evidence in this revision is that the new event-driven cache signal has no other path for automatic remote fetches; propagate a ref generation/hash or invalidate after the fetch.

Useful? React with 👍 / 👎.

readonly environmentId: EnvironmentId;
}

const diffPreviewRevisionByEnvironment = Atom.family((environmentId: EnvironmentId) =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Scope preview invalidation to the changed repository

When one environment contains multiple projects or worktrees, every post-snapshot status event increments this single environment-wide revision, so an event from any visible sidebar thread forces the active diff preview for an unrelated cwd to execute another potentially large Git diff and send it over the WebSocket. Web and mobile both mount additional status streams outside their review panels, making parallel agent activity in one environment a practical source of repeated unrelated preview requests; key the revision by normalized repository/CWD (and invalidate only matching preview inputs) instead.

AGENTS.md reference: AGENTS.md:L15-L17

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant