Skip to content

feat(compass-app): close-time cancel of a window's in-flight calls (SEA-2035 M3b) - #360

Open
rigel-mintaka wants to merge 1 commit into
mainfrom
seal-2035-mw-m3b-close-cancel
Open

feat(compass-app): close-time cancel of a window's in-flight calls (SEA-2035 M3b)#360
rigel-mintaka wants to merge 1 commit into
mainfrom
seal-2035-mw-m3b-close-cancel

Conversation

@rigel-mintaka

@rigel-mintaka rigel-mintaka commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

This PR is part of a stack containing 2 PRs:

  1. main
  2. "feat(compass-app): close-time cancel of a window's in-flight calls (SEA-2035 M3b)" (this PR)
  3. feat(compass-app): multi-window gtk3 e2e smoke gate (SEA-2035 M4) #369

When a webview window closes, cancel every in-flight bridge call
registered to it so no call's pump goroutine or server-side subscription
leaks for the app's lifetime. Without this, closing a window left its two
app-lifetime streams live forever, and the user can open and close
arbitrarily many windows (A4, F-close).

The bridge service gains CancelWindow(windowDispatcher): under the same
lock discipline as compass_rpc_cancel it collects and drops every
inflightCall whose stored per-call window handle equals the closing
window, then cancels each outside the lock. The window handle is the
comparable windowDispatcher value M3 already captures on the
inflightCall, so matching is a plain interface compare and no compass.v1
knowledge enters the shell (thin-shell safe); the cancel drives the same
id-keyed teardown sink, so a canceled pump stops silently and the
server-side subscription terminates. A nil window matches nothing, so
windowless/fallback calls are never swept by a close.

The close handler is attached inside the newAppWindow factory
(gtk3-tagged main.go) via OnWindowEvent(WindowClosing, ...), so every
window — the New Window menu and the restore loop alike — gets the leak
gate and it cannot be forgotten at a call site. The factory's return
value (the M1 seam) is now consumed here, so its return type and the
unparam suppression are removed. bridge_service.go stays //go:build unix
and application-free; only the handler wiring imports the Wails events
package.

Tests (unix-tagged, no webview): one service holds three long-lived
calls — two on distinct windows and one windowless — kept in-flight by a
blocking stub; CancelWindow(closing) sweeps only that window's call while
the other window's and the windowless call stay live, and CancelWindow(nil)
sweeps nothing. Event-gated on channels, no sleeps, race-clean.

Spec-impact: implements docs/designs/product/compass-multi-window/design.md
§M3b (close-time cancel of a window's in-flight calls); no spec change.
Ledger-impact: none.

Co-authored-by: Matt Wilkinson matt@rigel.build

@linear-code

linear-code Bot commented Aug 16, 2026

Copy link
Copy Markdown

SEA-2035

Base automatically changed from seal-2035-mw-m3-frame-routing to main August 16, 2026 04:53
@mattwilkinsonn
mattwilkinsonn force-pushed the seal-2035-mw-m3b-close-cancel branch from 10d57b4 to fd3dc1d Compare August 16, 2026 04:53
@rigel-mintaka
rigel-mintaka force-pushed the seal-2035-mw-m3b-close-cancel branch 2 times, most recently from 4182620 to e183165 Compare August 16, 2026 15:50
…EA-2035 M3b)

When a webview window closes, cancel every in-flight bridge call
registered to it so no call's pump goroutine or server-side subscription
leaks for the app's lifetime. Without this, closing a window left its two
app-lifetime streams live forever, and the user can open and close
arbitrarily many windows (A4, F-close).

The bridge service gains CancelWindow(windowDispatcher): under the same
lock discipline as compass_rpc_cancel it collects and drops every
inflightCall whose stored per-call window handle equals the closing
window, then cancels each outside the lock. The window handle is the
comparable windowDispatcher value M3 already captures on the
inflightCall, so matching is a plain interface compare and no compass.v1
knowledge enters the shell (thin-shell safe); the cancel drives the same
id-keyed teardown sink, so a canceled pump stops silently and the
server-side subscription terminates. A nil window matches nothing, so
windowless/fallback calls are never swept by a close.

The close handler is attached inside the newAppWindow factory
(gtk3-tagged main.go) via OnWindowEvent(WindowClosing, ...), so every
window — the New Window menu and the restore loop alike — gets the leak
gate and it cannot be forgotten at a call site. The factory's return
value (the M1 seam) is now consumed here, so its return type and the
unparam suppression are removed. bridge_service.go stays //go:build unix
and application-free; only the handler wiring imports the Wails events
package.

Tests (unix-tagged, no webview): one service holds three long-lived
calls — two on distinct windows and one windowless — kept in-flight by a
blocking stub; CancelWindow(closing) sweeps only that window's call while
the other window's and the windowless call stay live, and CancelWindow(nil)
sweeps nothing. Event-gated on channels, no sleeps, race-clean.

Spec-impact: implements docs/designs/product/compass-multi-window/design.md
§M3b (close-time cancel of a window's in-flight calls); no spec change.
Ledger-impact: none.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka
rigel-mintaka force-pushed the seal-2035-mw-m3b-close-cancel branch from e183165 to 224e9b2 Compare August 17, 2026 18:47
@github-actions

Copy link
Copy Markdown

Compass engineering docs preview: https://seal-2035-mw-m3b-close-cance.compass-eng-docs.pages.dev

Deployed from seal-2035-mw-m3b-close-cancel at 224e9b2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant