Skip to content

feat(compass-app): Wails v3 desktop shell + compass_rpc IPC bridge - #191

Open
seal-agent wants to merge 2 commits into
mainfrom
compass-native-1684-wails-shell-scaffold
Open

feat(compass-app): Wails v3 desktop shell + compass_rpc IPC bridge#191
seal-agent wants to merge 2 commits into
mainfrom
compass-native-1684-wails-shell-scaffold

Conversation

@seal-agent

@seal-agent seal-agent commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

This PR is part of a stack containing 2 PRs:

  1. main
  2. "feat(compass-app): Wails v3 desktop shell + compass_rpc IPC bridge" (this PR)
  3. feat(compass-app): embedded-mode launch pipeline (mode-select, preflight, stack supervise, WhoAmI) (SEA-1685) #209

Scaffold the Compass native desktop shell at go/cmd/compass-app: a Wails v3
application (v3.0.0-beta.0) that opens one window loading the prebuilt SolidJS
UI and exposes the compass_rpc / compass_rpc_cancel IPC bridge, backed by the
merged bridge pump (go/internal/bridge) over the daemon Unix socket.

The bound service forwards each gRPC-Web call through the pump on its own
goroutine and streams ordered response frames back as Wails runtime events keyed
per requestId (compass_rpc:), carrying the JS ResponseFrame shape
(head|body|end|error). In-flight calls are tracked in a mutex-guarded
map[string]context.CancelFunc so compass_rpc_cancel tears one down; a terminal
frame drops the entry. The service emits through a small eventEmitter seam so the
whole stream/cancel path is unit-tested against a real h2c stub daemon on a temp
UDS with a fake emitter, no webview: unary, multi-frame stream, mid-stream
cancel, and dial-error-before-head, all deterministic and event-gated.

The dist is served at runtime via BundledAssetFileServer over os.DirFS
(//go:embed cannot reach apps/ui/dist across the module boundary). main.go is
gated unix && gtk3 (the only Linux Wails stack in the frozen toolchain is
GTK3 + WebKit2GTK 4.1); a cgo-free !gtk3 entrypoint stub keeps the untagged
go build ./... module lane green.

This is T3 slice 2 (SEA-1684); stack supervision, host preflight, and mode
selection are T4 (SEA-1685). The apps/ui daemon-transport.ts Wails binding is a
separate cross-lane follow-up.

Refs SEA-1684

Co-authored-by: Matt Wilkinson matt@sealedsecurity.com

@linear-code

linear-code Bot commented Aug 6, 2026

Copy link
Copy Markdown

SEA-1684

seal-agent added a commit that referenced this pull request Aug 6, 2026
…ntity-guard cancel map

Review round 1 on PR #191 (SEA-1684). Addresses the review findings:

- HIGH: response head-frame headers marshalled as {name,value} objects but
  the frozen JS ResponseFrame contract (apps/ui/src/daemon-transport.ts) feeds
  frame.headers into new Headers(...), which requires [name,value] tuples and
  throws TypeError on objects — breaking every RPC's head frame in the webview.
  responseFrame.Headers is now [][2]string (marshals as tuples); headerObjects
  deleted; frameToResponse passes the pump's [][2]string through directly. The
  request direction stays object-shaped (rpcRequest.Headers, headerSlice) —
  the asymmetry is intentional: JS sends request headers as objects.

- MEDIUM: finish() deleted the in-flight entry purely by key, so a re-registered
  requestId (same key, new call) had its live entry mis-deleted and cancelled by
  a prior call's deferred finish. In-flight map now stores *inflightCall and
  finish/CompassRPCCancel guard by pointer identity (cur == call), so a call only
  ever tears down its own entry.

- MEDIUM: added TestResponseFrameWireContract — marshals each frame kind with
  encoding/json and asserts the exact wire shape (tuple headers, base64 chunk,
  camelCase), the missing test that let the header-shape defect ship green.

- MEDIUM: added TestRPCRequestBodyDecodesNumberArray — locks the JS number[] ->
  Go []byte element-wise decode contract.

- LOW: added TestCompassRPCConcurrentDistinctIDs (per-requestId event keying +
  call independence under cancel); softened the mid-stream-cancel comment to
  state the invariant the pump actually provides.

Co-authored-by: Matt Wilkinson <matt@sealedsecurity.com>
@seal-agent seal-agent closed this Aug 6, 2026
@seal-agent seal-agent reopened this Aug 6, 2026
seal-agent and others added 2 commits August 7, 2026 00:23
Scaffold the Compass native desktop shell at go/cmd/compass-app: a Wails v3
application (v3.0.0-beta.0) that opens one window loading the prebuilt SolidJS
UI and exposes the compass_rpc / compass_rpc_cancel IPC bridge, backed by the
merged bridge pump (go/internal/bridge) over the daemon Unix socket.

The bound service forwards each gRPC-Web call through the pump on its own
goroutine and streams ordered response frames back as Wails runtime events keyed
per requestId (compass_rpc:<id>), carrying the JS ResponseFrame shape
(head|body|end|error). In-flight calls are tracked in a mutex-guarded
map[string]context.CancelFunc so compass_rpc_cancel tears one down; a terminal
frame drops the entry. The service emits through a small eventEmitter seam so the
whole stream/cancel path is unit-tested against a real h2c stub daemon on a temp
UDS with a fake emitter, no webview: unary, multi-frame stream, mid-stream
cancel, and dial-error-before-head, all deterministic and event-gated.

The dist is served at runtime via BundledAssetFileServer over os.DirFS
(//go:embed cannot reach apps/ui/dist across the module boundary). main.go is
gated unix && gtk3 (the only Linux Wails stack in the frozen toolchain is
GTK3 + WebKit2GTK 4.1); a cgo-free !gtk3 entrypoint stub keeps the untagged
go build ./... module lane green.

This is T3 slice 2 (SEA-1684); stack supervision, host preflight, and mode
selection are T4 (SEA-1685). The apps/ui daemon-transport.ts Wails binding is a
separate cross-lane follow-up.

Refs SEA-1684

Co-authored-by: Matt Wilkinson <matt@sealedsecurity.com>
…ntity-guard cancel map

Review round 1 on PR #191 (SEA-1684). Addresses the review findings:

- HIGH: response head-frame headers marshalled as {name,value} objects but
  the frozen JS ResponseFrame contract (apps/ui/src/daemon-transport.ts) feeds
  frame.headers into new Headers(...), which requires [name,value] tuples and
  throws TypeError on objects — breaking every RPC's head frame in the webview.
  responseFrame.Headers is now [][2]string (marshals as tuples); headerObjects
  deleted; frameToResponse passes the pump's [][2]string through directly. The
  request direction stays object-shaped (rpcRequest.Headers, headerSlice) —
  the asymmetry is intentional: JS sends request headers as objects.

- MEDIUM: finish() deleted the in-flight entry purely by key, so a re-registered
  requestId (same key, new call) had its live entry mis-deleted and cancelled by
  a prior call's deferred finish. In-flight map now stores *inflightCall and
  finish/CompassRPCCancel guard by pointer identity (cur == call), so a call only
  ever tears down its own entry.

- MEDIUM: added TestResponseFrameWireContract — marshals each frame kind with
  encoding/json and asserts the exact wire shape (tuple headers, base64 chunk,
  camelCase), the missing test that let the header-shape defect ship green.

- MEDIUM: added TestRPCRequestBodyDecodesNumberArray — locks the JS number[] ->
  Go []byte element-wise decode contract.

- LOW: added TestCompassRPCConcurrentDistinctIDs (per-requestId event keying +
  call independence under cancel); softened the mid-stream-cancel comment to
  state the invariant the pump actually provides.

Co-authored-by: Matt Wilkinson <matt@sealedsecurity.com>
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