The web test suite fire-and-forgets `handle.close()` because awaiting it deadlocks (test runner kills the worker at 5s/15s). The fix is in tests/web-ws.test.ts:
```ts
// Bun:test exits after the test completes; handle.close() can hang on
// upgraded sockets so we skip awaiting it here.
void handle.close();
```
Production isn't affected — `rig serve --web` calls `process.exit(0)` on SIGINT — but we should track down the root cause so:
- Tests can use the real close path
- Long-running embedders can hot-swap the web server without leaking sockets
Suspect: `WebSocketServer.close()` waits indefinitely for an upgraded socket that the test's `ws.terminate()` already destroyed, OR Fastify's `app.close()` won't return because the `http.Server` still has a registered upgrade listener.
Possible fixes:
- Close in different order (Fastify first, ws second)
- Call `server.closeAllConnections()` before `app.close()` (Node 18.2+ API)
- Use a close timeout that escalates to `server.destroy()`
Repro:
```ts
const handle = await serveWeb({ projectRoot: '/tmp/x', port: 7490 });
const ws = new WebSocket('ws://127.0.0.1:7490/ws');
await new Promise(res => ws.on('open', res));
ws.terminate();
await handle.close(); // hangs
```
The web test suite fire-and-forgets `handle.close()` because awaiting it deadlocks (test runner kills the worker at 5s/15s). The fix is in tests/web-ws.test.ts:
```ts
// Bun:test exits after the test completes; handle.close() can hang on
// upgraded sockets so we skip awaiting it here.
void handle.close();
```
Production isn't affected — `rig serve --web` calls `process.exit(0)` on SIGINT — but we should track down the root cause so:
Suspect: `WebSocketServer.close()` waits indefinitely for an upgraded socket that the test's `ws.terminate()` already destroyed, OR Fastify's `app.close()` won't return because the `http.Server` still has a registered upgrade listener.
Possible fixes:
Repro:
```ts
const handle = await serveWeb({ projectRoot: '/tmp/x', port: 7490 });
const ws = new WebSocket('ws://127.0.0.1:7490/ws');
await new Promise(res => ws.on('open', res));
ws.terminate();
await handle.close(); // hangs
```