Files
platform/todo/known-bugs/background-job-logs-appeared-inside-an-open.md
T
pastilhasandClaude Fable 5 6071ded175 todo: one file per item, grouped by theme, with a generated index
TODO.md had reached 1050 lines and 72 open items across 28 sections, read mostly
on a phone. It is now a directory: todo/<theme>/<item>.md, one card per file, with
frontmatter carrying id, status, created date and order, and the prose body copied
across verbatim. TODO.md becomes a generated index (bun run todo:index).

Dates: where an item's prose stated one it is used; otherwise its old section's;
otherwise 2026-08-20, floored at the day before the split rather than invented.
date_source records which.

Shaped for a two-way Kanban board, filed as todo/tooling — a card is a file, id is
the identity, frontmatter is the machine's half and the body is the owner's.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 05:42:39 +00:00

4.3 KiB

id, title, theme, status, created, date_source, order
id title theme status created date_source order
known-bugs/background-job-logs-appeared-inside-an-open Background job logs appeared INSIDE an open file in the code editor known-bugs open 2026-08-16 stated 1

Background job logs appeared INSIDE an open file in the code editor. Reported 2026-08-16 by the owner, on the production host, running as a member with a bun dev background job streaming. The editor had a file open; the runner's log output was rendered into that file's buffer. It was never saved — the file on disk was intact, and the editor was not marked dirty as far as the report goes. So this is a DISPLAY-layer mixing, not a write path: the bytes reached a buffer that only exists in the browser.

Not reproduced, not diagnosed, and deliberately not guessed at further than this. What is worth recording is where the two streams could meet, because both were added in the same week and they are the only two things writing text into a panel:

  • useTaskDetail polls GET /chat/tasks/:id every 2s while a job runs and returns {kind:'log', text}. CodeSurface renders it — and CodeSurface is shared with the editor's own surfaces.
  • The editor keys open files by PATH (useEditorState), while the task panel keys by TASK ID, and several panel-scoped stores in between are keyed by panelId through useGlobal / usePanelMemory. A key collision between those namespaces would produce exactly this: one panel's text arriving in another's buffer, with no server involvement and nothing persisted.

Screenshot 2026-08-16 answers the first question and kills two theories. The tab carries a DIRTY dot, and the log lines have Monaco line numbers continuing the file's own sequence (…180, then eight lines of dotenv/POSTGRES_URL output, then 189 </Link>). So the MODEL was mutated — this is the editor's content state, not its render, and the earlier "the file was okay" means it was simply never saved. Note the splice is mid-file, not a replacement: JSX text and log text share line 180 (Forgot?--env-file=.env --watch src/server.tsx).

Ruled out by that: CodeSurface renders with shiki, not Monaco, so the two cannot share an editor instance. And the app's only other Monaco mount (FileViewer/renderers/EditorRenderer.tsx) passes no path, so its model URI is inmemory://model/N and cannot collide with the editor's file:///….

So the pollution arrives as TEXT through the editor's own content path — openFile, setContent, or the end-of-turn refresh that re-reads every clean file. That last one is the prime suspect: it is the only thing that writes a buffer without a keystroke, it runs exactly when a background job is producing output, and it lands its result by path.

Narrowed 2026-08-16, and a trap is now set. reloadFile sets originalContent and clears isDirty, so the end-of-turn refresh CANNOT produce the dirty dot in the screenshot — the refresh path is out. Nothing in the app injects text into the page either: no execCommand('insertText'), no synthetic keyboard events, no clipboard writes near the editor. That leaves Monaco's own change event, which means it looks like a paste or a programmatic executeEdits.

docs/editor-buffer-corruption.md is the procedure — read that when someone reports this. A trap is set: reportSuspiciousEdit in CodeEditor.tsx records any insertion over 200 characters arriving while the editor is NOT focused, with the stack at that moment, into localStorage['officer:editor-buffer-trap'] (last five). It persists rather than only logging because this arrives as a report hours later, when the console is long gone — the question becomes "paste this key" instead of "what did you see?". Delete all of it once the cause is known.

One thing to rule out first, because it is mine and it is recent: 2b70a570 gave <Editor> a real path, so models are now keyed by file URI globally rather than being per-mount inmemory://. Two 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.

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.