fix(browse): declare lastConsoleFlushed to restore console-log persistence#1309
Open
yashkot007 wants to merge 1 commit intogarrytan:mainfrom
Open
fix(browse): declare lastConsoleFlushed to restore console-log persistence#1309yashkot007 wants to merge 1 commit intogarrytan:mainfrom
yashkot007 wants to merge 1 commit intogarrytan:mainfrom
Conversation
…tence
flushBuffers() references a `lastConsoleFlushed` cursor at server.ts:337
and assigns it at :344, but the `let lastConsoleFlushed = 0;`
declaration is missing — only the network and dialog siblings are
declared at lines 327-328.
Result: every 1-second flushBuffers tick (line 376) throws
`ReferenceError: lastConsoleFlushed is not defined`, gets swallowed by
the catch at line 369 ("[browse] Buffer flush failed: ..."), and the
console branch's append never runs. browse-console.log is never
written in any production deployment since this regressed.
Discovered by stress-testing the daemon with 15 concurrent CLIs against
cold state — the race surfaced the buffer-flush error spam in one
spawned daemon's stderr. Verified by running the daemon against a real
file:// page with console.log events: in-memory `browse console`
returns the entries, but `.gstack/browse-console.log` is never created
on disk.
Regression introduced by 1a100a2 "fix: eliminate duplicate command
sets in chain, improve flush perf and type safety" — the flush refactor
switched from `Bun.write` to `fs.appendFileSync` and added the
`lastConsoleFlushed` cursor pattern alongside its network/dialog
siblings, but missed the matching `let` declaration. Tests don't
currently exercise flushBuffers, so the regression shipped silently.
Fix:
- Declare `let lastConsoleFlushed = 0;` next to `lastNetworkFlushed`
and `lastDialogFlushed` (browse/src/server.ts:327)
- Add a source-level guard test
(browse/test/server-flush-trackers.test.ts) that fails any future
refactor that adds a fourth `last*Flushed` cursor without the
matching declaration. Same pattern as terminal-agent.test.ts and
dual-listener.test.ts — read source as text, assert invariant, no
daemon required.
Test plan:
- [x] New regression test fails on current main, passes with the fix
- [x] `bun run build` clean
- [x] Manual smoke: spawn daemon -> goto file:// page with
console.log -> wait 4s -> .gstack/browse-console.log now
exists with the expected entries (163 bytes vs zero before)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This was referenced May 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
flushBuffers()(server.ts:331) maintains a per-buffer cursor for console / network / dialog so it only appends new entries to each on-disk log on every interval tick. The console cursor is referenced + assigned in the function body — but the matchinglet lastConsoleFlushed = 0;declaration is missing. Only the network and dialog siblings are declared at lines 327-328.Result: every 1-second
flushBufferstick (line 376) throwsReferenceError: lastConsoleFlushed is not defined, gets swallowed by the catch at line 369 ([browse] Buffer flush failed: …), and the console branch's append never runs.browse-console.logis never written in any production deployment since this regressed.Root cause
Regression introduced by
1a100a2a"fix: eliminate duplicate command sets in chain, improve flush perf and type safety" — the flush refactor switched fromBun.writetofs.appendFileSyncand added thelastConsoleFlushedcursor pattern alongside its network/dialog siblings, but missed the matchingletdeclaration. Tests don't currently exerciseflushBuffers, so the regression shipped silently.How it surfaced
Found while stress-testing the daemon with 15 concurrent CLIs against cold state. The race surfaced this in one spawned daemon's stderr:
Verified by running the daemon against a real
file://page withconsole.logevents:./browse consolecorrectly returns the captured entries.gstack/browse-console.logis never created on diskAfter the fix, the same flow produces a 163-byte log file with the expected entries.
Fix
+let lastConsoleFlushed = 0; let lastNetworkFlushed = 0; let lastDialogFlushed = 0; let flushInProgress = false;One line. Same pattern as the existing siblings.
Regression test
Added
browse/test/server-flush-trackers.test.ts— a source-level guard following theterminal-agent.test.tsanddual-listener.test.tspattern (read source as text, assert invariant, no daemon required).The test extracts every
last*Flushedidentifier referenced insideflushBuffers()and asserts each one has a matchinglet X = 0;declaration at module scope. Catches the exact regression class — any future flush-perf refactor that adds a fourth buffer cursor (or copy-pastes the pattern without the matching declaration) will fail this before it ships.The failure message is intentionally specific so a future contributor sees the fix path without reading the test:
Test plan
main(verified before the fix), passes with the fixbun test browse/test/server-flush-trackers.test.ts— 1 pass / 0 failbun run build— clean rebuild of all binariesgoto file://page withconsole.log→ wait 4s for flush interval →.gstack/browse-console.lognow exists with the expected entries (163 bytes vs zero file before)🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmithwith what you need.