the code editor collides with itself: two panels, one model, two buffers

The "two Monaco editors sharing a model" elimination asked which OTHER
component mounts Monaco and never asked whether the code editor collides with
itself. It does — the panel is addable to a dashboard twice, and useEditorState
keeps files in a plain useState, so two panels hold two independent buffers for
one globally-keyed model.

Verified against @monaco-editor/react@4.7.0's built source rather than inferred.
Model lookup is getModel(uri) || createModel(value), so the second mount adopts
the first panel's buffer and ignores its own freshly-read value. The value-sync
effect then overwrites the shared model with executeEdits. And the flag that
suppresses onChange during that write is per-instance, so one panel's
programmatic write fires the OTHER panel's onChange — which CodeEditor answers
with setContent(path, value). The other panel's buffer becomes this one's
content and goes dirty with no keystroke: dirty tab, content nobody typed, disk
untouched, fixed by reopening. Exactly the reported shape.

Unmount also disposes the shared model (keepCurrentModel defaults false), and
key={activeFile.path} means that fires on every tab switch, not only on panel
close — so one panel can dispose a model another is still displaying.

Not yet a closed case: nothing in the app writes job output into
useEditorState, so this produces the right shape but does not explain where the
log text comes from. No fix shipped — the two candidates (per-panel model URIs
vs sharing the state to match the shared model) differ in whether Monaco's
TypeScript resolution keeps working, and that is the owner's call. Trap stays
armed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: f7a62308-0977-4cf4-aa29-c020853fd420
This commit is contained in:
2026-08-25 07:16:22 +00:00
co-authored by Claude Opus 5
parent 7a651aee77
commit f16aa87d8b
2 changed files with 55 additions and 11 deletions
@@ -1,6 +1,6 @@
---
id: known-bugs/background-job-logs-appeared-inside-an-open
title: "Background job logs appeared INSIDE an open file in the code editor"
title: 'Background job logs appeared INSIDE an open file in the code editor'
theme: known-bugs
status: open
created: 2026-08-16
@@ -60,6 +60,23 @@ One thing to rule out first, because it is mine and it is recent: `2b70a570` gav
panels open on the same file now share one model. That should be an improvement, but it is a change
in exactly the layer that turned out to be involved.
**2026-08-25 — that suspicion was right, and the "ruled out" row above was wrong.** The elimination asked
which OTHER component mounts Monaco and never asked whether the code editor collides with **itself**. It
does: the panel is addable to a dashboard twice, and `useEditorState` keeps `files` in a plain `useState`,
so two panels hold two independent buffers for one globally-keyed model. Verified in
`@monaco-editor/react@4.7.0`'s source: model lookup is `getModel(uri) || createModel(value)` so the second
mount's value is ignored, the value-sync effect then overwrites the shared model with `executeEdits`, and
**the flag that suppresses `onChange` during that write is per-instance** — so one panel's programmatic
write fires the OTHER panel's `onChange`. `CodeEditor` responds with `setContent(path, value)`: the other
panel's buffer becomes this one's content and goes dirty with no keystroke. Unmount also disposes the
shared model (`keepCurrentModel` defaults false), and `key={activeFile.path}` means that happens on every
tab switch, not just on panel close.
That reproduces the symptom's exact shape — dirty tab, content nobody typed, disk clean, fixed by
reopening. It does **not** explain how job output becomes one of those buffers, and nothing in the app
writes job output into `useEditorState`. So the trap stays armed; it is still the only thing that can name
where the text came from. Full write-up in `docs/editor-buffer-corruption.md`.
Worth noting because it is the reason to look at all: an editor that can show content the file does
not contain is a correctness problem even when it never writes — the next person's instinct will be to
save the file to "fix" it.