Skip to content

[codex] Refactor engine architecture helpers#209

Merged
willwashburn merged 26 commits into
mainfrom
codex/engine-architecture-refactor
Jun 24, 2026
Merged

[codex] Refactor engine architecture helpers#209
willwashburn merged 26 commits into
mainfrom
codex/engine-architecture-refactor

Conversation

@willwashburn

@willwashburn willwashburn commented Jun 24, 2026

Copy link
Copy Markdown
Member

Summary

This continues the engine architecture refactor after the already-merged response-helper PR.

  • Migrates the remaining route modules to shared response/body/query helpers
  • Centralizes coded error rendering, idempotent replay response shaping, and WebSocket upgrade authentication
  • Adds zod-backed HTTP query helpers for route numeric query parsing
  • Keeps intentional protocol exceptions intact: /health raw JSON and A2A JSON-RPC request parsing

Impact

The HTTP wire shape stays consistent with the existing success/error envelopes while reducing repeated route-local parsing and rendering code. WebSocket auth and idempotent replay behavior now have single implementation points, which should make future API and auth changes easier to audit.

Validation

  • git diff --check
  • npm --workspace @relaycast/engine run typecheck
  • npm --workspace @relaycast/engine run lint
  • npm --workspace @relaycast/engine run build
  • npm --workspace @relaycast/engine test
  • Final scans:
    • raw response/body handling remains only in /health and A2A JSON-RPC protocol paths
    • numeric query preprocessing remains centralized in packages/engine/src/lib/httpQuery.ts

Notes

This branch was recreated from current origin/main because the prior local branch name belonged to merged PR #207 and was based before newer release/SDK commits. Recreating the branch avoids unrelated reversions in the PR diff.

Review in cubic

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR centralizes HTTP response formatting, request parsing, and WebSocket authentication across engine routes and middleware. It adds shared parsing/response helpers, rewires WS upgrade handling, refactors route handlers to use the helpers, and expands conformance coverage for malformed JSON and validation behavior.

Changes

HTTP Response/Auth Centralization and Route Refactoring

Layer / File(s) Summary
Core HTTP helpers
packages/engine/src/lib/httpResponse.ts, packages/engine/src/lib/httpQuery.ts, packages/engine/src/lib/httpError.ts, packages/engine/src/lib/__tests__/httpError.test.ts
Adds parsed-request value handling, optional JSON-body parsing, shared success/error helpers, query parsing helpers, and updated error message formatting.
Idempotency response helpers
packages/engine/src/middleware/idempotency.ts
Adds delivery-internal stripping and idempotent JSON response helpers.
Middleware error responses
packages/engine/src/middleware/auth.ts, packages/engine/src/middleware/fleetNodes.ts, packages/engine/src/middleware/planLimits.ts, packages/engine/src/middleware/rateLimit.ts
Replaces inline error payloads with shared jsonError responses.
WebSocket auth and upgrade wiring
packages/engine/src/engine/wsAuth.ts, packages/engine/src/engine.ts, packages/engine/src/entrypoints/node.ts, packages/engine/src/__tests__/nodeUpgradeAuth.test.ts
Adds WS auth helpers, rewires engine and entrypoint upgrade handling to use them, and updates upgrade auth coverage.
Messaging routes
packages/engine/src/routes/message.ts, packages/engine/src/routes/thread.ts, packages/engine/src/routes/dm.ts, packages/engine/src/routes/groupDm.ts
Refactors messaging routes to use shared JSON parsing, pagination, and idempotent response helpers.
Channel, reaction, and presence routes
packages/engine/src/routes/channel.ts, packages/engine/src/routes/reaction.ts, packages/engine/src/routes/presence.ts
Refactors channel, reaction, and presence handlers to use shared parsing and standardized JSON responses.
Agent, workspace, and directory routes
packages/engine/src/routes/agent.ts, packages/engine/src/routes/workspace.ts, packages/engine/src/routes/directory.ts
Refactors agent, workspace, and directory handlers to use shared parsing and response helpers, plus local not-found and config helpers.
Remaining route handlers
packages/engine/src/routes/action.ts, packages/engine/src/routes/certify.ts, packages/engine/src/routes/delivery.ts, packages/engine/src/routes/file.ts, packages/engine/src/routes/inboundWebhook.ts, packages/engine/src/routes/inbox.ts, packages/engine/src/routes/routing.ts, packages/engine/src/routes/search.ts, packages/engine/src/routes/console.ts, packages/engine/src/routes/a2a.ts
Refactors the remaining route handlers to use shared parsing and response helpers.
Conformance tests
packages/engine/src/__tests__/conformance/httpResponse.test.ts
Adds conformance coverage for malformed JSON, query validation, command success envelopes, and optional-body behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

codex

Poem

🐇 I hopped through helpers, neat and bright,
Swapped raw c.json for tidy light.
Tokens, bodies, queries too—
All now flow in helper hue.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: a broad refactor of engine helpers and related auth/response utilities.
Description check ✅ Passed The description clearly covers the route/helper refactor, query parsing, auth, exceptions, and validation work in the diff.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/engine-architecture-refactor

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed due to a network error.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@willwashburn willwashburn marked this pull request as ready for review June 24, 2026 09:50

@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 standardizes HTTP response formatting, error handling, and request/query parsing across the entire engine codebase. It introduces new helper utilities in httpResponse.ts and httpQuery.ts, extracts WebSocket authentication into wsAuth.ts, and refactors all route handlers to use these helpers, significantly reducing boilerplate. Feedback on the changes includes addressing a validation issue in positiveIntQueryParam when handling empty or whitespace-only strings, and removing a redundant mixed-case header fallback in engine.ts to comply with the repository style guide.

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 on lines +18 to +21
return z.preprocess(
(value) => (value === undefined ? undefined : Number(value)),
valueSchema,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The positiveIntQueryParam preprocess function currently converts empty or whitespace-only strings (e.g., ?limit=) to 0 via Number(value). Since 0 is not a positive integer, this causes Zod validation to fail with a 400 Bad Request instead of falling back to the parameter's default value or treating it as optional/undefined.

Updating the preprocess function to map empty or whitespace-only strings to undefined allows Zod's .optional() or .default() schemas to handle them gracefully.

Suggested change
return z.preprocess(
(value) => (value === undefined ? undefined : Number(value)),
valueSchema,
);
return z.preprocess(
(value) => (value === undefined || (typeof value === 'string' && value.trim() === '') ? undefined : Number(value)),
valueSchema,
);

Comment thread packages/engine/src/engine.ts Outdated
? authHeader.replace(/^bearer\s+/i, '').trim()
: undefined;
const token = c.req.query('token') ?? bearer;
const token = queryOrBearerToken(c.req.query('token'), c.req.header('Authorization') ?? c.req.header('authorization'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This line introduces a mixed-case field fallback: c.req.header('Authorization') ?? c.req.header('authorization').

According to the Repository Style Guide (Rule 9): "Do not introduce mixed-case field fallbacks."

Since Hono's c.req.header() is case-insensitive under the hood (conforming to the Fetch API standard), calling c.req.header('Authorization') is sufficient and will automatically retrieve the header regardless of its casing.

Suggested change
const token = queryOrBearerToken(c.req.query('token'), c.req.header('Authorization') ?? c.req.header('authorization'));
const token = queryOrBearerToken(c.req.query('token'), c.req.header('Authorization'));
References
  1. Do not introduce mixed-case field fallbacks. (link)

@coderabbitai coderabbitai 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.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/engine/src/routes/workspace.ts (1)

45-57: 🎯 Functional Correctness | 🟡 Minor

Enforce an exclusive override shape in both feature-flag routes.

/workspace/stream and /workspace/fleet-nodes still accept payloads with both enabled and mode, and mode: "inherit" takes precedence over a provided enabled. Make the Zod schemas reject mixed shapes so the API only accepts { enabled: boolean } or { mode: "inherit" }.

  • packages/engine/src/routes/workspace.ts:45-57
  • packages/engine/src/routes/workspace.ts:337-349
  • packages/engine/src/routes/workspace.ts:397-409
🤖 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/engine/src/routes/workspace.ts` around lines 45 - 57, The
updateWorkspaceStreamSchema and updateFleetNodesSchema currently allow mixed
payloads where both enabled and mode can be provided, and the route handlers
then let mode: "inherit" override enabled. Tighten the Zod validation in
packages/engine/src/routes/workspace.ts so the workspaceStream and fleetNodes
routes only accept one exclusive shape: either { enabled: boolean } or { mode:
"inherit" }, but never both together. Update the route parsing/handling in the
corresponding workspace stream and fleet-nodes endpoints to rely on these
stricter schemas and reject mixed payloads before applying any override logic.

Source: Coding guidelines

🧹 Nitpick comments (2)
packages/engine/src/routes/search.ts (1)

14-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the required q check into the Zod schema.

This adds a post-parse manual guard for q, which is the validation pattern the TS/JS guidelines ask us to avoid. Putting the trim/non-empty requirement in the schema keeps validation in one place and removes the extra branch.

Suggested fix
 const searchQuerySchema = PaginationQuerySchema.extend({
-  q: z.string().optional(),
+  q: z.string().trim().min(1),
   channel: z.string().optional(),
   from: z.string().optional(),
 });
@@
-      const parsed = parseQueryParams(c, searchQuerySchema, 'Invalid search query');
+      const parsed = parseQueryParams(c, searchQuerySchema, (failure) =>
+        failure.error.issues.some((issue) => issue.path[0] === 'q')
+          ? 'q (search query) is required'
+          : 'Invalid search query',
+      );
       if (!parsed.ok) {
         return parsed.response;
       }
       const { q, channel, from, limit, before, after } = parsed.data;
-      if (!q || !q.trim()) {
-        return jsonInvalidRequest(c, 'q (search query) is required');
-      }
As per coding guidelines, `Prefer zod schemas for validation instead of ad-hoc manual checks`.

Also applies to: 29-35

🤖 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/engine/src/routes/search.ts` around lines 14 - 18, The search route
currently validates q with a manual post-parse guard instead of the schema. Move
the trim/non-empty requirement into searchQuerySchema by making q a Zod string
refinement rather than an optional plain string, and then remove the ad-hoc q
check in the search handler logic so validation stays centralized in the schema.

Source: Coding guidelines

packages/engine/src/engine.ts (1)

167-167: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid the mixed-case header fallback.

Use one canonical header spelling here; the Authorization/authorization compatibility fallback violates the project rule against mixed-case fallbacks.

Use a single header name
-    const token = queryOrBearerToken(c.req.query('token'), c.req.header('Authorization') ?? c.req.header('authorization'));
+    const token = queryOrBearerToken(c.req.query('token'), c.req.header('authorization'));

As per coding guidelines, “Do not introduce mixed-case field fallbacks” and “Do not add mixed-case compatibility fallbacks”.

🤖 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/engine/src/engine.ts` at line 167, The token extraction in engine.ts
uses a mixed-case header fallback, which violates the header naming rule. Update
the queryOrBearerToken call in the engine request handler to read from one
canonical Authorization header spelling only, and remove the alternate
authorization fallback while keeping the existing token query support intact.

Source: Coding guidelines

🤖 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/engine/src/engine.ts`:
- Around line 260-268: In engine.ts, narrow the malformed-body handling in the
error response path so only explicit bad-body errors return
jsonMalformedBody(c); remove the broad error.message?.includes('JSON') check and
instead gate on the coded/status-based malformed-body condition already used in
this flow. Keep the fallback jsonError(...) path for all other errors in the
error handling branch around the existing jsonMalformedBody and jsonError calls.

In `@packages/engine/src/engine/wsAuth.ts`:
- Around line 44-46: The queryOrBearerToken function currently lets an empty
query token override a valid Authorization Bearer token because it uses nullish
coalescing. Update queryOrBearerToken in wsAuth.ts to treat empty or
whitespace-only queryToken values as missing and fall back to
extractBearerToken(authHeader) before returning a token, so valid header auth is
still used when the query parameter is empty.
- Around line 24-28: The success branch of NodeWsAuthResult still exposes the
nullable return type from getNodeByTokenHash, even though authenticateNodeWs
already filters out missing nodes. Update the node property in NodeWsAuthResult
to use a non-nullable form such as NonNullable<Awaited<ReturnType<typeof
getNodeByTokenHash>>> so authResult.node is guaranteed to be present in the
ok:true case.
- Around line 78-95: The `authenticateRealtimeWs` flow currently gates only the
`rk_live_` workspace-key path, so agent-scoped `at_live_` tokens can still open
`/v1/ws` when workspace streams are disabled. Update `authenticateRealtimeWs` in
`wsAuth.ts` so both realtime scopes run through `isWorkspaceStreamEnabled`
before returning success, using the authenticated workspace from
`deps.auth.authenticate` in the `at_live_` branch as well as the workspace-key
branch. Keep the existing invalid-token handling, but add the same not-found
response when the workspace-stream gate is closed for agent tokens.

In `@packages/engine/src/lib/httpError.ts`:
- Around line 42-45: The HTTP error helper is leaking backend `cause` details
into client responses through `messageWithCause()` and the `directory` route
path that opts into it. Update `messageWithCause()` in `httpError.ts` to return
only `error.message` for responses, and keep any `error.cause` handling confined
to logging/telemetry paths; also review the related response-building logic
around the referenced `directory` route and the `httpError` helpers to ensure no
caller still serializes the raw cause text.

In `@packages/engine/src/lib/httpQuery.ts`:
- Around line 14-20: The default handling in httpQuery’s value schema bypasses
validation because numberSchema.default(...) short-circuits parsing, so update
the logic around valueSchema to ensure options.defaultValue is validated by the
full schema. In the preprocess/numberSchema flow, switch to a path that runs the
default through parsing (for example, prefault) or explicitly validate
options.defaultValue before attaching it, so the .int(), .positive(), and
.max(...) constraints still apply.

In `@packages/engine/src/routes/action.ts`:
- Around line 53-64: The handler requirement is only checked in the
parseJsonBody error mapping, but registerActionSchema still allows handler_agent
and handler_node to be omitted, so the request can pass validation without a
handler. Update registerActionSchema to enforce that at least one handler field
is present (or make the required/conditional constraint explicit in the schema)
so /actions fails early with the correct validation response; keep the
parseJsonBody callback in action.ts aligned with the schema by preserving the
handler_agent/handler_node error message path.

In `@packages/engine/src/routes/directory.ts`:
- Around line 84-86: The directory route’s error handler is leaking internal
failure details by passing includeCause: true into errorResponse. Update
handleError in directory.ts to return a client-safe response without including
the underlying cause, and keep any detailed cause information out of the HTTP
body while preserving normal error handling via errorResponse.

In `@packages/engine/src/routes/message.ts`:
- Line 48: The validation message for postMessageSchema parsing is too generic
and masks which field actually failed. Update the message handling in message.ts
around parseJsonBody so schema-specific failures for fields like mode,
attachments, blocks, data, and content_type return the correct validation text
instead of always using text is required. Use the parseJsonBody call in the
message route and the postMessageSchema validation path to identify the failing
field and map it to an appropriate error message.

In `@packages/engine/src/routes/thread.ts`:
- Line 37: The validation error handling in the reply route is too specific
because `postReplySchema` validates more than just text, so the fixed message
does not match failures from `blocks` or `data`. Update the `parseJsonBody` call
in the thread reply handler to use a generic reply-body validation message
instead of `text is required`, keeping the change scoped to the route logic that
uses `postReplySchema`.

In `@packages/engine/src/routes/workspace.ts`:
- Around line 50-52: The activity feed limit in activityQuerySchema is currently
unbounded, and getActivityFeed is using the parsed limit directly, so add a
maximum cap before querying. Update the limit definition in the workspace route
to match the bounded pattern used elsewhere (for example, the session events
route), and ensure the value passed from the activity feed handler into
getActivityFeed cannot exceed that cap.

---

Outside diff comments:
In `@packages/engine/src/routes/workspace.ts`:
- Around line 45-57: The updateWorkspaceStreamSchema and updateFleetNodesSchema
currently allow mixed payloads where both enabled and mode can be provided, and
the route handlers then let mode: "inherit" override enabled. Tighten the Zod
validation in packages/engine/src/routes/workspace.ts so the workspaceStream and
fleetNodes routes only accept one exclusive shape: either { enabled: boolean }
or { mode: "inherit" }, but never both together. Update the route
parsing/handling in the corresponding workspace stream and fleet-nodes endpoints
to rely on these stricter schemas and reject mixed payloads before applying any
override logic.

---

Nitpick comments:
In `@packages/engine/src/engine.ts`:
- Line 167: The token extraction in engine.ts uses a mixed-case header fallback,
which violates the header naming rule. Update the queryOrBearerToken call in the
engine request handler to read from one canonical Authorization header spelling
only, and remove the alternate authorization fallback while keeping the existing
token query support intact.

In `@packages/engine/src/routes/search.ts`:
- Around line 14-18: The search route currently validates q with a manual
post-parse guard instead of the schema. Move the trim/non-empty requirement into
searchQuerySchema by making q a Zod string refinement rather than an optional
plain string, and then remove the ad-hoc q check in the search handler logic so
validation stays centralized in the schema.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e32ea86f-5010-4945-95fe-e78d36130baf

📥 Commits

Reviewing files that changed from the base of the PR and between 712509a and 0e73ea2.

📒 Files selected for processing (33)
  • packages/engine/src/__tests__/conformance/httpResponse.test.ts
  • packages/engine/src/engine.ts
  • packages/engine/src/engine/wsAuth.ts
  • packages/engine/src/entrypoints/node.ts
  • packages/engine/src/lib/__tests__/httpError.test.ts
  • packages/engine/src/lib/httpError.ts
  • packages/engine/src/lib/httpQuery.ts
  • packages/engine/src/lib/httpResponse.ts
  • packages/engine/src/middleware/auth.ts
  • packages/engine/src/middleware/fleetNodes.ts
  • packages/engine/src/middleware/idempotency.ts
  • packages/engine/src/middleware/planLimits.ts
  • packages/engine/src/middleware/rateLimit.ts
  • packages/engine/src/routes/a2a.ts
  • packages/engine/src/routes/action.ts
  • packages/engine/src/routes/agent.ts
  • packages/engine/src/routes/certify.ts
  • packages/engine/src/routes/channel.ts
  • packages/engine/src/routes/console.ts
  • packages/engine/src/routes/delivery.ts
  • packages/engine/src/routes/directory.ts
  • packages/engine/src/routes/dm.ts
  • packages/engine/src/routes/file.ts
  • packages/engine/src/routes/groupDm.ts
  • packages/engine/src/routes/inboundWebhook.ts
  • packages/engine/src/routes/inbox.ts
  • packages/engine/src/routes/message.ts
  • packages/engine/src/routes/presence.ts
  • packages/engine/src/routes/reaction.ts
  • packages/engine/src/routes/routing.ts
  • packages/engine/src/routes/search.ts
  • packages/engine/src/routes/thread.ts
  • packages/engine/src/routes/workspace.ts

Comment thread packages/engine/src/engine.ts Outdated
Comment thread packages/engine/src/engine/wsAuth.ts
Comment thread packages/engine/src/engine/wsAuth.ts
Comment thread packages/engine/src/engine/wsAuth.ts
Comment thread packages/engine/src/lib/httpError.ts
Comment thread packages/engine/src/routes/action.ts
Comment thread packages/engine/src/routes/directory.ts
Comment thread packages/engine/src/routes/message.ts Outdated
Comment thread packages/engine/src/routes/thread.ts Outdated
Comment thread packages/engine/src/routes/workspace.ts

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/engine/src/__tests__/nodeUpgradeAuth.test.ts (1)

47-53: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Resolve failed socket errors in tryUpgrade.

Line 52 can leave the Promise pending when the WS client emits error without unexpected-response, causing a hung test instead of a clear failure.

Proposed fix
 function tryUpgrade(headers: Record<string, string>, query = '', path = '/v1/node/ws'): Promise<number> {
   return new Promise((resolve) => {
+    let settled = false;
+    const finish = (status: number) => {
+      if (!settled) {
+        settled = true;
+        resolve(status);
+      }
+    };
     const ws = new WebSocket(`${wsBase}${path}${query}`, { headers });
-    ws.on('open', () => { ws.close(); resolve(101); });
-    ws.on('unexpected-response', (_req, res) => { resolve(res.statusCode ?? 0); });
-    ws.on('error', () => { /* handled by unexpected-response */ });
+    ws.on('open', () => { ws.close(); finish(101); });
+    ws.on('unexpected-response', (_req, res) => { finish(res.statusCode ?? 0); });
+    ws.on('error', () => { finish(0); });
   });
 }
🤖 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/engine/src/__tests__/nodeUpgradeAuth.test.ts` around lines 47 - 53,
The tryUpgrade helper can hang because it only resolves on open or
unexpected-response, so a WebSocket error path may leave the Promise pending.
Update tryUpgrade in nodeUpgradeAuth.test.ts to handle the WebSocket client's
error event by resolving or rejecting the Promise there as well, and make sure
the ws.on('error') branch does not just swallow the failure when
unexpected-response is never emitted.
🤖 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.

Outside diff comments:
In `@packages/engine/src/__tests__/nodeUpgradeAuth.test.ts`:
- Around line 47-53: The tryUpgrade helper can hang because it only resolves on
open or unexpected-response, so a WebSocket error path may leave the Promise
pending. Update tryUpgrade in nodeUpgradeAuth.test.ts to handle the WebSocket
client's error event by resolving or rejecting the Promise there as well, and
make sure the ws.on('error') branch does not just swallow the failure when
unexpected-response is never emitted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 50dec171-a372-4d82-8f60-b2a9d89a6161

📥 Commits

Reviewing files that changed from the base of the PR and between 0e73ea2 and 3401b27.

📒 Files selected for processing (14)
  • packages/engine/src/__tests__/conformance/httpResponse.test.ts
  • packages/engine/src/__tests__/nodeUpgradeAuth.test.ts
  • packages/engine/src/engine.ts
  • packages/engine/src/engine/wsAuth.ts
  • packages/engine/src/lib/__tests__/httpError.test.ts
  • packages/engine/src/lib/httpError.ts
  • packages/engine/src/lib/httpQuery.ts
  • packages/engine/src/lib/httpResponse.ts
  • packages/engine/src/routes/action.ts
  • packages/engine/src/routes/directory.ts
  • packages/engine/src/routes/message.ts
  • packages/engine/src/routes/search.ts
  • packages/engine/src/routes/thread.ts
  • packages/engine/src/routes/workspace.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • packages/engine/src/lib/httpQuery.ts
  • packages/engine/src/routes/search.ts
  • packages/engine/src/engine.ts
  • packages/engine/src/lib/httpResponse.ts
  • packages/engine/src/routes/directory.ts
  • packages/engine/src/routes/message.ts
  • packages/engine/src/routes/action.ts
  • packages/engine/src/routes/thread.ts
  • packages/engine/src/routes/workspace.ts

@willwashburn willwashburn merged commit 89fede5 into main Jun 24, 2026
5 checks passed
@willwashburn willwashburn deleted the codex/engine-architecture-refactor branch June 24, 2026 16:29
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