What happened
I opened a Markdown file in the file preview panel and switched it to the readable/formatted view. An agent then modified that same file. From that point on the panel kept showing the old content — the agent's changes never appeared, and the diff showed nothing either. Reopening the file showed the pre-edit content.
The project was a local folder with no remote at the time. I pushed it to GitHub thinking that might matter, and the behaviour was unchanged — which in hindsight fits, since nothing about this looks git-related.
What I found in the code
Reading at 02f4ce56, there's a path where a file's cached content can outlive the file itself.
useProjectFileQuery returns the optimistic entry in preference to the query result:
apps/web/src/components/files/projectFilesQueryState.ts:194 — data: optimisticFile?.data ?? data
- same again in
resolveProjectFileQueryData, projectFilesQueryState.ts:107
So while an optimistic entry exists for (environmentId, cwd, relativePath), it masks whatever is actually on disk.
That entry is cleared in exactly one place — the success branch after a save is confirmed:
projectFilesQueryState.ts:93-94 — if (result._tag === "Success" && appAtomRegistry.get(atom) === confirmed) { appAtomRegistry.set(atom, null) }
If that re-read doesn't succeed, or the atom moved on in between, the entry stays. I couldn't find anything that drops it afterwards, and in particular I couldn't find any invalidation of a file's query when something outside the editor — an agent tool call — writes that file.
Two things that look related:
clearProjectFileQueryData (projectFilesQueryState.ts:110) does exactly this job, and has no callers outside its own definition and projectFilesQueryState.test.ts.
confirmedAgainst is written at :54 and :85 but never read anywhere. It reads like a staleness check that wasn't finished.
What I have not verified
I traced this from the source; I have not reproduced it from a clean checkout with a deterministic trigger, and I don't know what made the confirm step fail in my case. So treat the mechanism as a strong hypothesis rather than a diagnosis. It does explain every part of what I saw, including it persisting across reopening the file.
Happy to send a PR
I didn't open one because the fix is a decision rather than a line: the right place to drop optimistic state could be on agent tool completion, on a file-watcher event, or on any external write, and that's your call rather than mine. If you tell me which invalidation point you want, I'll keep it small and focused.
What happened
I opened a Markdown file in the file preview panel and switched it to the readable/formatted view. An agent then modified that same file. From that point on the panel kept showing the old content — the agent's changes never appeared, and the diff showed nothing either. Reopening the file showed the pre-edit content.
The project was a local folder with no remote at the time. I pushed it to GitHub thinking that might matter, and the behaviour was unchanged — which in hindsight fits, since nothing about this looks git-related.
What I found in the code
Reading at
02f4ce56, there's a path where a file's cached content can outlive the file itself.useProjectFileQueryreturns the optimistic entry in preference to the query result:apps/web/src/components/files/projectFilesQueryState.ts:194—data: optimisticFile?.data ?? dataresolveProjectFileQueryData,projectFilesQueryState.ts:107So while an optimistic entry exists for
(environmentId, cwd, relativePath), it masks whatever is actually on disk.That entry is cleared in exactly one place — the success branch after a save is confirmed:
projectFilesQueryState.ts:93-94—if (result._tag === "Success" && appAtomRegistry.get(atom) === confirmed) { appAtomRegistry.set(atom, null) }If that re-read doesn't succeed, or the atom moved on in between, the entry stays. I couldn't find anything that drops it afterwards, and in particular I couldn't find any invalidation of a file's query when something outside the editor — an agent tool call — writes that file.
Two things that look related:
clearProjectFileQueryData(projectFilesQueryState.ts:110) does exactly this job, and has no callers outside its own definition andprojectFilesQueryState.test.ts.confirmedAgainstis written at:54and:85but never read anywhere. It reads like a staleness check that wasn't finished.What I have not verified
I traced this from the source; I have not reproduced it from a clean checkout with a deterministic trigger, and I don't know what made the confirm step fail in my case. So treat the mechanism as a strong hypothesis rather than a diagnosis. It does explain every part of what I saw, including it persisting across reopening the file.
Happy to send a PR
I didn't open one because the fix is a decision rather than a line: the right place to drop optimistic state could be on agent tool completion, on a file-watcher event, or on any external write, and that's your call rather than mine. If you tell me which invalidation point you want, I'll keep it small and focused.