Skip to content

refactor(remote-bridge): rewrite the Go bridge as a TypeScript port#1336

Merged
Astro-Han merged 8 commits into
devfrom
claude/i1188-ts-bridge
Jun 16, 2026
Merged

refactor(remote-bridge): rewrite the Go bridge as a TypeScript port#1336
Astro-Han merged 8 commits into
devfrom
claude/i1188-ts-bridge

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Jun 16, 2026

Copy link
Copy Markdown
Owner

What

Rewrites packages/remote-bridge from a standalone Go module (a cc-connect sidecar binary) into a faithful TypeScript port that lives natively in the Node/Electron runtime. Drops the Go toolchain, the compiled sidecar binary, and all cross-language plumbing.

This supersedes the Go implementation merged in #1275. Part of #1188 (mobile companion).

Why

The bridge talks to PawWork's opencode server over local HTTP + SSE — code we already have in TypeScript. The Go module existed only because cc-connect is Go. Vercel's Chat SDK + @larksuite/vercel-chat-adapter provide the same messaging-bridge capability as a TypeScript library (outbound WebSocket long-connection, no public IP/webhook; native cardkit streaming; interactive cards), so the bridge no longer needs to be a separate-language sidecar. It can live in-process with the engine it already shares a runtime with.

What's preserved (the hard semantics)

  • session pointers — remoteKey↔session map, parent chains, atomic file writes via a process-unique temp name + rename (the exact contention the Go fixed-.tmp writer hit on reconnect)
  • PawWork HTTP/SSE client — fatal-vs-transient error classification (401/403/404/protocol = fatal), Last-Event-ID cursor persistence, per-directory hydration that skips transient blips but surfaces fatal/malformed responses (never silently drops a pending permission)
  • event dispatch — advance the cursor before reconciling on an undecodable critical frame, so one bad event neither wedges the stream nor hides a pending confirmation
  • engine — a single active blocker per root conversation, delivered-before-answerable ordering (an unshown prompt can never intercept an ordinary message), bounded delivery retry, child→root conversation routing
  • gateway run-loop — connect → stream ready → hydrate → start platforms; retry non-fatal stream errors; audience gating (specific allow_from, or Feishu/Lark allow_chat + group_only). The chat platform sits behind a Platform seam fed by an injected factory.

Testing

  • 71 unit/contract tests ported from the Go *_test.go suites (session-pointers, client HTTP/SSE, event dispatch, engine, gateway run-loop), all green.
  • tsgo --noEmit typecheck clean.
  • No live run — exactly as feat(remote-bridge): standalone Go chat companion bridge (1/2) #1275 was validated; the live Feishu round-trip is the manual acceptance gate, performed in the follow-up.

Deliberately deferred to the desktop-integration follow-up (supersedes #1334)

The chat platform sits behind a Platform interface + injected factory, so the testable core ships independently. The two pieces whose only real validation is a live Feishu run land next:

  1. Lark adapter binding — wrap @larksuite/vercel-chat-adapter as a Platform (inbound text → handler; outbound thread.post; reconstructReplyCtx via the thread-id codec; audience filter via isDM/channelIdFromThreadId).
  2. Electron utilityProcess.fork host — per a codex architecture review: the embedded opencode server runs in-process in Electron main, so the bridge's lifecycle tracks the engine, but the beta chat adapter is isolated into a main-owned utility process (crash isolation without external-Node packaging pain). Started fire-and-monitor after the health-wait; stopped bridge-then-server in killSidecar.

Summary by CodeRabbit

Release Notes

  • New Features

    • Rebuilt the remote-bridge conversation engine with /new, session management commands, ordered permission/question prompting, and delivery retries.
    • Improved SSE handling with reconnect + replay refresh.
    • Added persistent session pointer storage with cursor recovery.
  • Refactor

    • Replaced the previous remote-bridge runtime/gateway with a TypeScript implementation, including the HTTP client and event routing.
  • Chores

    • Updated CI to run dedicated Bun unit tests for remote-bridge and report JUnit results.

Replace the standalone Go module (cc-connect sidecar) with a faithful
TypeScript port that lives natively in the Node/Electron runtime, dropping
the Go toolchain and cross-language plumbing while preserving the bridge's
hard-won semantics:

- session pointers: remoteKey<->session map, parent chains, atomic file
  writes via a process-unique temp name + rename
- PawWork HTTP/SSE client: fatal-vs-transient classification, Last-Event-ID
  cursor persistence, per-directory hydration that skips transient blips but
  surfaces fatal/malformed responses
- event dispatch: advance the cursor before reconciling on an undecodable
  critical frame, so one bad event never wedges the stream or hides a prompt
- engine: a single active blocker per root, delivered-before-answerable
  ordering, bounded delivery retry, child->root conversation routing
- gateway run-loop: connect -> stream ready -> hydrate -> start platforms;
  retry non-fatal stream errors; audience gating; the chat platform sits
  behind a `Platform` seam fed by an injected factory

71 unit/contract tests ported from the Go *_test.go suites; typecheck clean.
The live Lark adapter binding (@larksuite/vercel-chat-adapter) and the
Electron utilityProcess worker wiring land in the desktop-integration
follow-up, where they are validated by a live Feishu run.
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Astro-Han, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 30 minutes and 50 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 98eb6a19-a0f2-4e5b-b665-140101932fab

📥 Commits

Reviewing files that changed from the base of the PR and between 557ae8e and 6d5bb12.

📒 Files selected for processing (4)
  • packages/remote-bridge/src/pawwork-client-hydration.test.ts
  • packages/remote-bridge/src/pawwork-client.ts
  • packages/remote-bridge/src/pawwork-events.test.ts
  • packages/remote-bridge/src/pawwork-events.ts
📝 Walkthrough

Walkthrough

The entire packages/remote-bridge package is ported from Go to TypeScript/Bun. All Go source files (main entrypoint, go.mod, bridge engine, session pointers, gateway, PawWork client/events, and platforms) are deleted and replaced with equivalent TypeScript modules. The CI workflow and its validation test are updated to use bun test ./src instead of the Go toolchain.

Changes

remote-bridge Go → TypeScript port

Layer / File(s) Summary
Go code deletion and CI switch to Bun
.github/workflows/build.yml, packages/opencode/test/github/build-workflow.test.ts
CI workflow replaces Go toolchain setup and go test steps with bun install and bun test ./src for the remote bridge; the workflow validation test is updated to match, asserting the Go race step no longer exists.
TypeScript project setup and domain type contracts
packages/remote-bridge/package.json, packages/remote-bridge/tsconfig.json, packages/remote-bridge/src/types.ts
Package manifest defines exports, scripts, and devDependencies; tsconfig extends @tsconfig/bun; types.ts introduces all domain interfaces: Session, PendingPermission, PendingQuestion, Question, Sidecar, SessionPointers, EventCursorStore, Platform, Message, and MessageHandler.
SessionPointers persistence
packages/remote-bridge/src/session-pointers.ts, packages/remote-bridge/src/session-pointers.test.ts
SessionPointers implements in-memory and file-backed stores with atomic temp-file writes, legacy JSON migration, and root-conflict/cycle detection; tests cover memory constraints, file store loading, concurrent writes, and event cursor persistence.
PawWork SSE event dispatch and parseSSE
packages/remote-bridge/src/pawwork-events.ts, packages/remote-bridge/src/pawwork-events.test.ts
EventHandler interface, RepairableEventError/ReplayRefreshError error types, dispatchEvent router, and parseSSE stream parser with strict payload validation, reconciliation semantics, and frame assembly; tests cover all routing, validation errors, and stream handling.
PawWorkClient HTTP client and SSE stream
packages/remote-bridge/src/pawwork-client.ts, packages/remote-bridge/src/pawwork-client-http.test.ts, packages/remote-bridge/src/pawwork-client-hydration.test.ts, packages/remote-bridge/src/pawwork-client.test.ts
PawWorkClient sidecar HTTP client with error types (HTTPStatusError, StreamProtocolError, isFatalStreamError), session/directory caching, permission/question mutation/hydration APIs with per-directory error skipping, doJSON with per-request timeouts, streamEvents with SSE reconnect and EventCursorStore cursor persistence, and ClientEventHandler reconciliation wrapper; three test suites cover HTTP request construction, hydration field mapping/validation, and SSE streaming with reconnect behavior.
Engine conversational state machine
packages/remote-bridge/src/engine.ts, packages/remote-bridge/src/engine.test.ts
Engine class manages per-remote-key session pointers, an ordered blocker queue (permissions/questions surfaced one at a time), delivery retry with bounded attempts/backoff, command routing (/new, /sessions, /stop, /help), blocker delivery/resolution, and rendering/parsing helpers; 788-line test suite covers session lifecycle, blocker ordering, retry bounds, restart persistence, concurrent session coalescing, and multi-select parsing.
Gateway App lifecycle and wiring
packages/remote-bridge/src/gateway.ts, packages/remote-bridge/src/gateway.test.ts
Config/PlatformConfig types, decodeConfig/loadConfig/hasRemoteAudience helpers, createApp factory, and App class orchestrate abort-controlled run: event stream connect before hydrate before platforms, stream readiness deferred, initial hydration with warning-and-skip, platform start/stop with fatal deferred, and reconnect retry loop with configurable event stream delay; 554-line test suite covers config loading, platform filtering, audience gating, hydration behavior, run lifecycle ordering, reconnect resilience, and message handler failure logging.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Astro-Han/pawwork#1275: Added the original Go implementation of packages/remote-bridge (main entrypoint, gateway, engine, session-pointers, PawWork client/events, platforms, and Go test/race CI steps) that this PR fully removes and replaces with the TypeScript/Bun equivalent.
  • Astro-Han/pawwork#125: Modified CI workflow contract around Linux unit jobs in .github/workflows/ci.yml and corresponding test assertions that now differ with the added unit-remote-bridge job definition.

Suggested labels

enhancement, platform, desktop

🐇 From Go to TypeScript the bridge now hops,
With Bun tests bouncing from the bottom to the tops!
The engine queues blockers, the client streams SSE,
Session pointers persist atomically with glee—
A full rewrite landed, TypeScript victory! 🌟

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/i1188-ts-bridge

@github-actions github-actions Bot added ci Continuous integration / GitHub Actions P2 Medium priority labels Jun 16, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested priority: P2 (includes non-doc, non-test paths outside the low-risk bucket).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request migrates the remote-bridge package from Go to TypeScript/Bun, introducing the @opencode-ai/remote-bridge workspace package along with comprehensive test suites. The code review highlights several critical areas for improvement in the new TypeScript implementation, specifically addressing potential TypeError crashes when handling empty JSON responses in the PawWork client, a potential connection leak in the SSE stream parser if errors occur during reading, and the need for proper AbortSignal propagation to support request cancellation.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/remote-bridge/src/pawwork-events.ts
Comment thread packages/remote-bridge/src/pawwork-client.ts
Comment thread packages/remote-bridge/src/pawwork-client.ts
Comment thread packages/remote-bridge/src/pawwork-client.ts
Comment thread packages/remote-bridge/src/types.ts Outdated
Comment thread packages/remote-bridge/src/gateway.ts Outdated
- ci: replace the deleted Go test steps in build.yml with bun test
- hydrate: surface an undecodable /external-result question instead of
  dropping a pending one (listQuestions throws on incomplete)
- events: treat a wrong-typed questions field as undecodable and
  reconcile, never surface an empty-question prompt
- pointers: restore the legacy bare-map fallback so an upgrade keeps its
  chat-to-session bindings
- gateway: a platform start() that resolves on its own no longer tears
  the bridge down; stay up until abort or a fatal error
- tests: cover each behavior; fix a tsgo null-narrowing typecheck break
  in the hydration test via a holder object
…ing start

- build-workflow.test.ts asserted the deleted Go test/race steps; update it
  to the bun step and assert the race step is gone (both reviewers: CI was red)
- gateway: add the missing test for a platform whose start() resolves on its
  own — run() must stay up until abort, the behavior the prior fix introduced
@github-actions github-actions Bot added the harness Model harness, prompts, tool descriptions, and session mechanics label Jun 16, 2026
@Astro-Han Astro-Han added the task Narrow execution, audit, spike, migration, tracking, or upstream follow-up work label Jun 16, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/remote-bridge/src/engine.ts`:
- Around line 328-347: In the `restoreDelivery` method, after awaiting the
`platform.reconstructReplyCtx(key)` call and checking for
`this.active.get(sessionID)`, add an additional check for the root active
target. If a root active target exists (which may have been set while the
reconstruction was pending), return that active target instead of proceeding to
create a new proactive delivery with the reconstructed context. This ensures
that if a user message established a fresh active conversation at the root level
during the await, that takes precedence over the proactively reconstructed
target.
- Around line 405-410: The ensureSession method has a race condition where
multiple concurrent calls with the same key can both observe no existing session
and both call createSession(), splitting the conversation across multiple
sessions. Add serialization using a per-key locking mechanism (such as a Map
that tracks in-flight session creation promises) so that only one thread creates
a session for each key. After acquiring the lock, check again if a session
already exists (in case another thread created it while waiting), and only
proceed to call this.sidecar.createSession() and this.setCurrent() if no session
was created by another thread.

In `@packages/remote-bridge/src/gateway.ts`:
- Around line 222-225: The handleEventRepairRefresh handler needs to be wired to
trigger hydration when parseSSE() skips an undecodable critical event and
advances the cursor. Currently, the repair refresh handler only hydrates when
server.connected is true, which delays reconciliation of state changes until a
later reconnect. Find the handleEventRepairRefresh handler definition and ensure
it calls this.hydrate(signal) immediately when invoked, similar to how
handleReplayRefresh is already doing so in the diff context, to ensure pending
state from skipped critical frames is reconciled without waiting for a
reconnection.

In `@packages/remote-bridge/src/pawwork-events.ts`:
- Around line 86-90: The validation in the pawwork-events.ts file at lines 86-90
and 205-213 only checks if patterns and questions are arrays, but does not
validate the types of their contents. Add deeper validation after the array type
check to ensure that patterns contains only strings and questions contains
properly typed objects with string header and description fields. When invalid
nested types are detected (e.g., patterns containing numbers or questions with
non-string descriptions), throw a RepairableEventError with an appropriate
message instead of allowing the malformed data to reach the Engine where it will
cause rendering failures when .trim() is called on non-string values.
- Around line 260-265: The try-catch block around the
`handler.handleEventRepairRefresh()` call swallows the error by only logging it
with console.warn, which prevents the failure from propagating up to the stream
loop for reconnection and retry. Since the cursor has already advanced past the
repairable event, suppressing this error can leave pending state hidden
indefinitely. Remove the catch block that swallows the error or rethrow it
instead so that failures in handleEventRepairRefresh propagate to the caller and
trigger stream loop reconnection and hydration retry.

In `@packages/remote-bridge/src/session-pointers.ts`:
- Around line 123-131: The writeFile call in the writeSnapshot method relies on
process umask for file permissions, which could make the session state file
readable by other local users. Add an explicit chmod call with mode 0o600 on the
tempPath after the writeFile call and before the rename call to ensure the
temporary file has private read-write-only permissions for the owner, preventing
other users from accessing sensitive session data like remote keys and cursor
state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 257e2c7b-cfb3-4626-9f99-fcb70589eb98

📥 Commits

Reviewing files that changed from the base of the PR and between a1e9de0 and b673c2b.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • packages/remote-bridge/go.sum is excluded by !**/*.sum
📒 Files selected for processing (31)
  • .github/workflows/build.yml
  • packages/opencode/test/github/build-workflow.test.ts
  • packages/remote-bridge/cmd/pawwork-remote-bridge/main.go
  • packages/remote-bridge/go.mod
  • packages/remote-bridge/internal/bridge/engine.go
  • packages/remote-bridge/internal/bridge/engine_test.go
  • packages/remote-bridge/internal/bridge/session_pointers.go
  • packages/remote-bridge/internal/bridge/session_pointers_test.go
  • packages/remote-bridge/internal/gateway/gateway.go
  • packages/remote-bridge/internal/gateway/gateway_test.go
  • packages/remote-bridge/internal/pawwork/client.go
  • packages/remote-bridge/internal/pawwork/client_test.go
  • packages/remote-bridge/internal/pawwork/events.go
  • packages/remote-bridge/internal/pawwork/events_test.go
  • packages/remote-bridge/internal/platforms/platforms.go
  • packages/remote-bridge/internal/platforms/platforms_test.go
  • packages/remote-bridge/package.json
  • packages/remote-bridge/src/engine.test.ts
  • packages/remote-bridge/src/engine.ts
  • packages/remote-bridge/src/gateway.test.ts
  • packages/remote-bridge/src/gateway.ts
  • packages/remote-bridge/src/pawwork-client-http.test.ts
  • packages/remote-bridge/src/pawwork-client-hydration.test.ts
  • packages/remote-bridge/src/pawwork-client.test.ts
  • packages/remote-bridge/src/pawwork-client.ts
  • packages/remote-bridge/src/pawwork-events.test.ts
  • packages/remote-bridge/src/pawwork-events.ts
  • packages/remote-bridge/src/session-pointers.test.ts
  • packages/remote-bridge/src/session-pointers.ts
  • packages/remote-bridge/src/types.ts
  • packages/remote-bridge/tsconfig.json
💤 Files with no reviewable changes (14)
  • packages/remote-bridge/cmd/pawwork-remote-bridge/main.go
  • packages/remote-bridge/go.mod
  • packages/remote-bridge/internal/bridge/engine.go
  • packages/remote-bridge/internal/pawwork/events.go
  • packages/remote-bridge/internal/bridge/session_pointers.go
  • packages/remote-bridge/internal/gateway/gateway.go
  • packages/remote-bridge/internal/platforms/platforms_test.go
  • packages/remote-bridge/internal/pawwork/client.go
  • packages/remote-bridge/internal/pawwork/events_test.go
  • packages/remote-bridge/internal/bridge/engine_test.go
  • packages/remote-bridge/internal/bridge/session_pointers_test.go
  • packages/remote-bridge/internal/platforms/platforms.go
  • packages/remote-bridge/internal/pawwork/client_test.go
  • packages/remote-bridge/internal/gateway/gateway_test.go

Comment thread packages/remote-bridge/src/engine.ts
Comment thread packages/remote-bridge/src/engine.ts
Comment thread packages/remote-bridge/src/gateway.ts
Comment thread packages/remote-bridge/src/pawwork-events.ts Outdated
Comment thread packages/remote-bridge/src/pawwork-events.ts
Comment thread packages/remote-bridge/src/session-pointers.ts
Validated each finding against current code and the Go original.

Fidelity/crash fixes (TS diverged from Go or could crash):
- client: default list responses to [] — doJSON returns undefined on an empty
  2xx body, where Go's nil slice ranged zero times (listSessions/Permissions/
  Questions)
- events: cancel the stream reader on any error path, matching Go's deferred
  Body.Close, so a thrown dispatch/reconcile cannot leak the connection
- events: reject wrong-typed nested permission/question fields (patterns:[123],
  non-string header/option) — Go's typed unmarshal rejected these; coercion
  would crash prompt rendering on .trim() and wedge the blocker
- pointers: write the state file 0o600 (Go's os.CreateTemp default), not umask
- thread an AbortSignal through Sidecar.listSessions and hydrate (Go took ctx)

Deliberate divergence from Go (races confirmed real by /codex, opt-in hardening):
- engine: coalesce concurrent first-session creation per key onto one in-flight
  promise — fire-and-forget dispatch let two messages split a new conversation
- engine: restoreDelivery re-checks the root active target too after reconstruct

Tests cover each behavior; the two race fixes are labeled stricter-than-Go.

Pushed back (no change): handleEventRepairRefresh is already wired via
ClientEventHandler (matches Go's clientEventHandler); a failed mid-stream repair
refresh is logged-and-continued in Go too (only reconnect propagates); and
title/parentID stay "" since Session.title is string and sessionLabel calls
.trim().
Boolean("false") is true, so a malformed multiple:"false" silently flipped a
single-select question to multi-select. Go's `Multiple bool` unmarshal rejected
it; questionHasWrongTypes now does too (reconcile), and the mapper reads
multiple as q?.multiple ?? false. Completes the strict-decode parity for
nested question fields.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/remote-bridge/src/pawwork-events.test.ts (1)

2-2: ⚡ Quick win

Tighten error assertions to validate the intended failure mode.

These cases use rejects.toThrow() without checking error type, so unrelated runtime throws can still pass. Assert the specific error classes (RepairableEventError for reconciliation cases, ReplayRefreshError for replay-refresh) to prevent false positives.

Suggested test hardening
-import { dispatchEvent, type EventHandler, parseSSE, ReplayRefreshError } from "./pawwork-events.ts"
+import { dispatchEvent, type EventHandler, parseSSE, ReplayRefreshError, RepairableEventError } from "./pawwork-events.ts"

-  ).rejects.toThrow()
+  ).rejects.toThrow(RepairableEventError)

-  ).rejects.toThrow()
+  ).rejects.toThrow(ReplayRefreshError)

Also applies to: 144-149, 157-163, 169-175, 182-188, 207-208

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/remote-bridge/src/pawwork-events.test.ts` at line 2, The test
assertions in the pawwork-events test file use generic `rejects.toThrow()` calls
without validating specific error types, which allows unrelated runtime errors
to pass tests incorrectly. Update all these error assertions to check for the
specific error classes: use `.rejects.toThrow(RepairableEventError)` for
reconciliation-related test cases and `.rejects.toThrow(ReplayRefreshError)` for
replay-refresh test cases. First, ensure that both `RepairableEventError` and
`ReplayRefreshError` are imported in the test file at the top (they may already
be exported from pawwork-events.ts); then locate each `rejects.toThrow()` call
throughout the test file and replace it with the appropriate specific error
class assertion based on the test's intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/remote-bridge/src/engine.test.ts`:
- Line 737: Replace the fixed sleep at line 737 and line 771 in
packages/remote-bridge/src/engine.test.ts with deterministic synchronization
instead of using setTimeout with an arbitrary delay. Rather than awaiting a
promise that resolves after 5ms, use explicit synchronization primitives like
"entered barrier" promises returned from createSession or reconstructReplyCtx
helper functions to ensure operations have actually completed before proceeding,
making the test ordering deterministic and preventing scheduler-dependent
flakiness.

---

Nitpick comments:
In `@packages/remote-bridge/src/pawwork-events.test.ts`:
- Line 2: The test assertions in the pawwork-events test file use generic
`rejects.toThrow()` calls without validating specific error types, which allows
unrelated runtime errors to pass tests incorrectly. Update all these error
assertions to check for the specific error classes: use
`.rejects.toThrow(RepairableEventError)` for reconciliation-related test cases
and `.rejects.toThrow(ReplayRefreshError)` for replay-refresh test cases. First,
ensure that both `RepairableEventError` and `ReplayRefreshError` are imported in
the test file at the top (they may already be exported from pawwork-events.ts);
then locate each `rejects.toThrow()` call throughout the test file and replace
it with the appropriate specific error class assertion based on the test's
intent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 416bb697-8702-4498-9129-2a4ced192221

📥 Commits

Reviewing files that changed from the base of the PR and between b673c2b and f617416.

📒 Files selected for processing (10)
  • packages/remote-bridge/src/engine.test.ts
  • packages/remote-bridge/src/engine.ts
  • packages/remote-bridge/src/gateway.ts
  • packages/remote-bridge/src/pawwork-client-hydration.test.ts
  • packages/remote-bridge/src/pawwork-client.ts
  • packages/remote-bridge/src/pawwork-events.test.ts
  • packages/remote-bridge/src/pawwork-events.ts
  • packages/remote-bridge/src/session-pointers.test.ts
  • packages/remote-bridge/src/session-pointers.ts
  • packages/remote-bridge/src/types.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/remote-bridge/src/pawwork-client-hydration.test.ts
  • packages/remote-bridge/src/types.ts
  • packages/remote-bridge/src/session-pointers.test.ts
  • packages/remote-bridge/src/gateway.ts
  • packages/remote-bridge/src/engine.ts
  • packages/remote-bridge/src/pawwork-events.ts
  • packages/remote-bridge/src/session-pointers.ts

Comment thread packages/remote-bridge/src/engine.test.ts Outdated
Add a unit-remote-bridge job to ci.yml that runs the package's 83 tests
on every pull request, mirroring unit-desktop. Previously the tests ran
only in build.yml, which is workflow_dispatch-only, so TS test
regressions could merge unseen. Typecheck was already covered by the
gating `bun turbo typecheck` job.

- package.json: add test:ci (junit reporter, scoped to ./src to bypass
  the root bunfig test root); fix test to `bun test ./src`
- turbo.json: declare @opencode-ai/remote-bridge#test:ci
- ci.yml: new job + wire into the `check` result gate
- ci-workflow.test.ts: extend the workflow contract in lockstep
…iers

The two concurrency regression tests coordinated their gates with a fixed
5ms setTimeout, which is scheduler-dependent. Replace each with an
"entered" barrier resolved from inside the gated async (createSession /
reconstructReplyCtx): the test awaits the signal that the first operation
has parked before releasing the gate, so ordering no longer relies on
timing. Behavior under test is unchanged.
Go decodes every SSE event and REST hydration row into a typed struct, so
a wrong-typed scalar is an unmarshal error, not a lenient coercion. The TS
port used `?? ""`/falsy extraction, letting a number/bool/object in a
top-level field flow into the engine — where it could crash prompt
rendering on .trim(), surface a bogus question (externalResultReady:
"false" read as true), or clear the wrong blocker (numeric status read as
resolved).

Add small strict scalar decoders (decodeString/Boolean/StringArray/
OptionalNumber) — the single shared definition of a valid scalar — and
route permission, session, assistant-text, and question decoding plus the
listSessions/listPermissions hydration rows through them. A wrong-typed
critical SSE event now reconciles (RepairableEventError); a wrong-typed
assistant-text part is skipped; a wrong-typed hydration row fails the list.
This replaces the scattered ad-hoc guards with one boundary and matches
Go's typed-unmarshal semantics. Zero new dependencies.

Adds focused malformed-field tests for permission id/sessionID/permission,
session info.id/title/parentID, assistant sessionID/text, question
status/externalResultReady/messageID/callID, and wrong-typed REST rows.
@Astro-Han Astro-Han merged commit 5f5ecd0 into dev Jun 16, 2026
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Continuous integration / GitHub Actions harness Model harness, prompts, tool descriptions, and session mechanics P2 Medium priority task Narrow execution, audit, spike, migration, tracking, or upstream follow-up work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant