Skip to content

Codex web app architecture - #8

Closed
t3dotgg wants to merge 15 commits into
mainfrom
cursor/codex-web-app-architecture-f06f
Closed

t3dotgg wants to merge 15 commits into
mainfrom
cursor/codex-web-app-architecture-f06f

turborepo and better logging

8cc7135
Select commit
Loading
Failed to load commit list.
MacroscopeApp / Macroscope - Correctness Check completed Feb 10, 2026 in 4m 5s

2 issues identified (76 code objects reviewed).

• Merge Base: e7c4292
• Head: 8cc7135

Details

✅ File Path Comments Posted
✅ .github/workflows/ci.yml 0
✅ .gitignore 0
✅ AGENTS.md 0
✅ README.md 0
✅ apps/renderer/index.html 0
✅ apps/renderer/src/App.tsx 0
✅ apps/renderer/src/components/ChatView.tsx 0
✅ apps/renderer/src/components/DiffPanel.tsx 0
✅ apps/renderer/src/components/Sidebar.tsx 0
✅ apps/renderer/src/env.ts 0
✅ apps/renderer/src/session-logic.ts 0
✅ apps/renderer/src/wsNativeApi.ts 0
✅ apps/renderer/src/wsTransport.ts 0
✅ apps/renderer/vite.config.ts 0
✅ apps/server/scripts/bundle-client.mjs 0
❌ apps/server/src/index.ts 1
✅ apps/server/src/logger.ts 0
❌ apps/server/src/wsServer.ts 1
✅ apps/server/tsconfig.json 0
✅ apps/server/tsup.config.ts 0
✅ packages/contracts/src/ws.ts 0
✅ turbo.json 0

Filtered Issues Details

apps/renderer/src/wsNativeApi.ts
  • line 32: The WsTransport constructor generates an invalid WebSocket URL when the application is served on standard ports (80 or 443), causing the application to crash immediately upon initialization. The code logic `ws://${window.location.hostname}:${window.location.port}` blindly appends a colon and the port. When window.location.port is an empty string (the browser default for standard ports), the resulting string is formatted as ws://hostname:. Passing a URL ending in a colon to the new WebSocket(...) constructor throws a SyntaxError (e.g., "The URL '...' is invalid") in modern browsers. This exception is thrown synchronously inside the WsTransport constructor (via this.connect()), causing createWsNativeApi and its callers to crash. [ Already posted ]
apps/renderer/src/wsTransport.ts
  • line 164: The send method creates an interval that will execute forever if the WebSocket never opens and disposed remains false, because the waitForOpen logic creates a closure over check but the setTimeout intended to stop it (line 176) only clears the interval if setTimeout fires. However, there is no direct link between the interval checking readyState and the timeout. More critically, the interval logic at lines 163-177 is flawed: if ws is null (reconnecting), this.ws?.readyState is undefined (not OPEN). The interval runs every 50ms. The setTimeout at line 176 clears the interval after REQUEST_TIMEOUT_MS. If request is called, it sets up its own timeout (line 41). If send fails to send because the socket is closed, waitForOpen starts. If the socket never opens, the interval runs until REQUEST_TIMEOUT_MS. However, request's promise rejection logic (line 43) does NOT clear the interval inside send. This creates a race condition where request might reject due to timeout, but send (via waitForOpen -> setInterval) is still trying to send the message for the same REQUEST_TIMEOUT_MS duration. If the socket connects right at the deadline, ws.send might be called for a request ID that has already been rejected and deleted from pending. While this specific race is mostly benign (server receives a message for a dead ID), the memory leak is real: every call to request while the socket is down spawns an interval timer that runs for 60 seconds (or until connection), piling up timers if many requests are made offline. [ Already posted ]
  • line 164: The send method uses a hardcoded 50ms polling loop to check for connection availability. If the network is flaky or the server is down, this creates a busy-wait loop for every pending request until REQUEST_TIMEOUT_MS (60s). If many requests are queued while offline, this results in a storm of setInterval timers firing rapidly (20 times per second per request), potentially degrading main thread performance. [ Already posted ]
  • line 171: The request method (line 36) relies on this.send(message) (line 52) to transmit data. If the WebSocket is not open, send queues the message using waitForOpen (line 163). However, request immediately returns a Promise that sets up a strict timeout (line 41). If send cannot transmit immediately, request returns the Promise, but send continues trying to send in the background. If dispose() is called (line 72), it rejects all pending requests (line 80) and clears the pending map. But dispose does NOT clear the intervals created inside send closure scope (line 164). Although waitForOpen checks this.disposed (line 165), it only checks it inside the interval callback. If dispose is called, the interval will fire one last time, see disposed is true, and clear itself. This is handled correctly. However, a different race exists: if request times out (line 41), it rejects the promise and deletes the ID from pending. It does NOT cancel the waitForOpen operation in send. If the socket connects 10ms after the timeout, waitForOpen will inadvertently send the stale request to the server (line 171). The server will process it and send a response. handleMessage (line 142) will try to look up the ID, fail (since it was deleted on timeout), and silently ignore it (line 143). This is a waste of bandwidth and server processing for a timed-out request. [ Already posted ]
  • line 171: The request method creates a Promise that rejects on timeout but pending.reject is also called in dispose. The waitForOpen logic in send has its own timeout logic (setTimeout(() => clearInterval(check), REQUEST_TIMEOUT_MS)). However, if request times out via line 41, the pending entry is deleted, but the send method's waitForOpen loop (line 164) continues running until its own timeout (line 176). Since send does not check if the request ID is still valid/pending before sending, it may send a message for a request that has already timed out locally. [ Already posted ]
apps/server/src/index.ts
  • line 20: The findAvailablePort function introduces a Time-of-Check Time-of-Use (TOCTOU) race condition. It confirms port availability by binding a server and then immediately closing it via server.close() (line 20) to release the port. Between this release and the subsequent re-binding of the port in main (via createServer at line 79 and server.start() at line 80), another process on the system can acquire the port. If this occurs, server.start() will fail with an EADDRINUSE error, causing the application to crash instead of handling the busy port gracefully. [ Already posted ]
apps/server/src/wsServer.ts
  • line 20: The MIME_TYPES object defines file extensions using only lowercase keys (e.g., ".js", ".css"), but the consuming code uses path.extname (line 129), which preserves the case of the file extension (e.g., returning ".JS" or ".CSS" for files with uppercase extensions). Because MIME_TYPES lacks uppercase keys and the lookup is not case-normalized, requests for such files yield undefined, falling back to "application/octet-stream" (line 130). This causes runtime failures in browsers for strict resource types like ES Modules (<script type="module">) or stylesheets, which are blocked or ignored when served with an incorrect MIME type. [ Already posted ]