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>
66 lines
4.3 KiB
Markdown
66 lines
4.3 KiB
Markdown
---
|
|
id: known-bugs/background-job-logs-appeared-inside-an-open
|
|
title: "Background job logs appeared INSIDE an open file in the code editor"
|
|
theme: known-bugs
|
|
status: open
|
|
created: 2026-08-16
|
|
date_source: stated
|
|
order: 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.
|