Repository navigation
fix: gate canvas event stream on the webview ready handshake - #300
Open
Sanjay Singh (san360) wants to merge 1 commit into
Open
Sanjay Singh (san360) wants to merge 1 commit into
Sanjay Singh (san360) wants to merge 1 commit into
Conversation
The canvas host replays the latest progress and dataReady on /events as soon as a client connects. The bridge shim subscribed on DOMContentLoaded, which only preceded app.js while the bundle stayed a parser-blocking script; an async or code-split bundle could still miss the replay, leaving the dashboard stuck on its loading screen with no way to recover by navigating. The shim now opens the stream only when the webview posts `ready`, which initMessageListener sends after its listener is attached - the same handshake the VS Code panel uses. A jsdom regression test wires the served shim to the real listener and fails against both earlier subscription strategies. Resolves #269 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sanjay Singh (san360)
requested review from
Aymen (aymenfurter)
and
a balanced review from Copilot
October 21, 2026 19:49
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The focused change prevents the replay race, has targeted regression coverage, and has no identified blocking issues.
No open issues 🎉
What changed in this PR
Fixes #269 by delaying canvas event subscription until the dashboard signals readiness, preventing startup events from being lost.
Changes:
- Gates event subscription on the existing
readyhandshake. - Tests delayed listener registration and duplicate readiness messages.
- Documents the startup fix.
| File | Description |
|---|---|
src/canvas/host.ts |
Opens one event stream after ready. |
src/canvas/host.test.ts |
Verifies handshake gating and idempotency. |
src/canvas/bridge-shim.test.ts |
Tests replay delivery with the real dashboard listener. |
CHANGELOG.md |
Records the canvas startup fix. |
.github/extensions/ai-engineer-coach/README.md |
Explains handshake-based event subscription. |
Review effort: Balanced
Aymen (aymenfurter)
approved these changes
Oct 23, 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.
Description
Fixes a race in canvas mode where the dashboard could load and never leave its loading screen. It was originally reported as an empty content pane; on current
mainit shows as a loading screen stuck at "Discovering log directories 0%"./eventsreplays the latestprogressanddataReadyas soon as a client connects. The canvas bridge shim re-dispatches SSE messages straight towindow. If it subscribes beforeapp.jshas attached itsmessagelistener, the replay is dropped andonDataReadynever runs. Clicking a nav item can't recover the page, becauserenderPagebails untildataReadyarrives.68ceef6moved the subscription intoDOMContentLoaded. That only works whileapp.jsstays a parser-blocking script. Anasyncor code-split bundle, or a listener attached after anawait, brings the race back.Change (
src/canvas/host.ts): the shim now opens/eventsonly when the webview posts{ type: 'ready' }.initMessageListeneralready sends that message, in a microtask, right after attaching its listener, and the VS Code panel already uses it to re-senddataReady. Subscribing is idempotent. Webview code, the RPC protocol and the server replay are unchanged. Reusing the existing handshake also avoids the newwindow.__coachReadyglobal suggested in the issue.Tests
src/canvas/host.test.ts: the shim test now asserts:readymessages;readyopens exactly one/eventsstream, and a repeatedreadyis a no-op;dataReadyis dispatched.src/canvas/bridge-shim.test.ts(jsdom): connects the served shim to the realinitMessageListener, firesDOMContentLoadedbefore the dashboard listens, and replaysdataReadyas soon as the stream opens. It fails against both theDOMContentLoadedshim and the original eager shim, and passes with the fix.Manual validation (browser, real canvas host, warm cache,
app.jsthrottled to 2.5 s):main)app.jsapp.jsloadedasync/eventsopens afterapp.jsloads and the listener is attachedapp.jsAlso checked: the
?page=curiositystart page, every sidebar page, the workspace Current/All toggle, the harness filter, and no app console errors.Related Issues
Fixes #269
Checklist
npm run checkpasses (typecheck + lint + spellcheck + knip + tests): every step passes locally except 7 tests (see below)CHANGELOG.mdand the canvas extension READMEValidation, run on this branch after rebasing onto
main(d7a9446):npm run check: typecheck ✅, lint ✅ (0 errors), spellcheck ✅, knip ✅, lockfile-lint ✅. Tests: 1653 passed, 5 skipped, 7 failed. All 7 failures are insrc/core/github-app-analytics.test.ts, which shells out to thesqlite3CLI. That CLI isn't installed on my machine, the same 7 tests fail on unmodifiedmainlocally, and they are unrelated to this change.npx vitest run src/canvas/ src/webview/shared-progress.test.ts: ✅ 13 passednpm run build: ✅npm run check-size: ✅ within the 2 MB budget