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
WsTransportconstructor 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. Whenwindow.location.portis an empty string (the browser default for standard ports), the resulting string is formatted asws://hostname:. Passing a URL ending in a colon to thenew WebSocket(...)constructor throws aSyntaxError(e.g., "The URL '...' is invalid") in modern browsers. This exception is thrown synchronously inside theWsTransportconstructor (viathis.connect()), causingcreateWsNativeApiand its callers to crash. [ Already posted ]
apps/renderer/src/wsTransport.ts
- line 164: The
sendmethod creates an interval that will execute forever if the WebSocket never opens anddisposedremains false, because thewaitForOpenlogic creates a closure overcheckbut thesetTimeoutintended to stop it (line 176) only clears the interval ifsetTimeoutfires. However, there is no direct link between the interval checkingreadyStateand the timeout. More critically, the interval logic at lines 163-177 is flawed: ifwsis null (reconnecting),this.ws?.readyStateis undefined (not OPEN). The interval runs every 50ms. ThesetTimeoutat line 176 clears the interval afterREQUEST_TIMEOUT_MS. Ifrequestis called, it sets up its own timeout (line 41). Ifsendfails to send because the socket is closed,waitForOpenstarts. If the socket never opens, the interval runs untilREQUEST_TIMEOUT_MS. However,request's promise rejection logic (line 43) does NOT clear the interval insidesend. This creates a race condition whererequestmight reject due to timeout, butsend(viawaitForOpen->setInterval) is still trying to send the message for the sameREQUEST_TIMEOUT_MSduration. If the socket connects right at the deadline,ws.sendmight be called for a request ID that has already been rejected and deleted frompending. While this specific race is mostly benign (server receives a message for a dead ID), the memory leak is real: every call torequestwhile 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
sendmethod uses a hardcoded50mspolling 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 untilREQUEST_TIMEOUT_MS(60s). If many requests are queued while offline, this results in a storm ofsetIntervaltimers firing rapidly (20 times per second per request), potentially degrading main thread performance. [ Already posted ] - line 171: The
requestmethod (line 36) relies onthis.send(message)(line 52) to transmit data. If the WebSocket is not open,sendqueues the message usingwaitForOpen(line 163). However,requestimmediately returns a Promise that sets up a strict timeout (line 41). Ifsendcannot transmit immediately,requestreturns the Promise, butsendcontinues trying to send in the background. Ifdispose()is called (line 72), it rejects allpendingrequests (line 80) and clears thependingmap. Butdisposedoes NOT clear the intervals created insidesendclosure scope (line 164). AlthoughwaitForOpenchecksthis.disposed(line 165), it only checks it inside the interval callback. Ifdisposeis called, the interval will fire one last time, seedisposedis true, and clear itself. This is handled correctly. However, a different race exists: ifrequesttimes out (line 41), it rejects the promise and deletes the ID frompending. It does NOT cancel thewaitForOpenoperation insend. If the socket connects 10ms after the timeout,waitForOpenwill 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
requestmethod creates a Promise that rejects on timeout butpending.rejectis also called indispose. ThewaitForOpenlogic insendhas its own timeout logic (setTimeout(() => clearInterval(check), REQUEST_TIMEOUT_MS)). However, ifrequesttimes out via line 41, thependingentry is deleted, but thesendmethod'swaitForOpenloop (line 164) continues running until its own timeout (line 176). Sincesenddoes not check if the request ID is still valid/pending before sending, it maysenda message for a request that has already timed out locally. [ Already posted ]
apps/server/src/index.ts
- line 20: The
findAvailablePortfunction introduces a Time-of-Check Time-of-Use (TOCTOU) race condition. It confirms port availability by binding a server and then immediately closing it viaserver.close()(line 20) to release the port. Between this release and the subsequent re-binding of the port inmain(viacreateServerat line 79 andserver.start()at line 80), another process on the system can acquire the port. If this occurs,server.start()will fail with anEADDRINUSEerror, causing the application to crash instead of handling the busy port gracefully. [ Already posted ]
apps/server/src/wsServer.ts
- line 20: The
MIME_TYPESobject defines file extensions using only lowercase keys (e.g.,".js",".css"), but the consuming code usespath.extname(line 129), which preserves the case of the file extension (e.g., returning".JS"or".CSS"for files with uppercase extensions). BecauseMIME_TYPESlacks uppercase keys and the lookup is not case-normalized, requests for such files yieldundefined, 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 ]
Loading