# GUI → PROTO requests (M1) Filed by lane GUI while building `gui/tests/dod/` (gui/docs/pkg-qa-requests-m1.md R3's harness). Touches `tools/mockd/` — PROTO-owned (CLAUDE.md §1) — so GUI is not making the edit. Apply-ready below. --- ## `mockd --drop-connection` is a no-op over the Unix socket transport `--drop-connection ` is documented as "terminate every connection every N seconds, to exercise reconnect logic" and is exactly what `gui/tests/dod/run.sh unhappy-path` needs for its drop-connection phase. It works — but only over WebSocket. **Repro:** `tools/mockd/src/index.ts`'s `startUds()` call passes `args.slow` and stops there: ```ts startUds(args.uds, dispatcher, connections, log, args.slow); ``` `startWs()`, two lines below, gets the full options object including `dropEverySec`. `startUds()`'s own signature (`tools/mockd/src/transport/uds.ts`) has no `dropEverySec` parameter at all, and nothing in it ever calls `socket.destroy()` — the periodic-drop `setInterval` that `startWs` has (its last ~6 lines) simply does not exist on the UDS side. **Verified live**, not inferred from reading: ran `mockd --no-ws --drop-connection 5`, connected `gui/tests/dod/dod_harness unhappy-path --phase drop-connection` against it (UDS, the GUI's only transport) with a 45 s observation window, and `stateChanged` never fired — the connection sat in `Connected` the entire time. Same command with `--flaky 0.3` correctly leaves the connection state alone (that flag only fails individual call replies, which is right), so this is specific to `--drop-connection` and the UDS transport, not a harness-side detection problem. **Effect:** every GUI/CLI/nmhost consumer of mockd — the only transport they actually use — cannot be tested against a dropped connection at all today. `gui/tests/dod/run.sh` ships its `unhappy-path` drop-connection phase anyway (log intentionally records `sawDisruption` in its JSON so this is visible, not silently green), but it is currently only proving the client survives 45 quiet seconds, not a real drop. ### Fix — mirror `ws.ts`'s existing pattern onto `uds.ts` **`tools/mockd/src/transport/uds.ts`:** ```diff export function startUds( path: string, dispatcher: Dispatcher, connections: Set, log: (msg: string) => void, delayMs: number, + dropEverySec: number = 0, ): Server { mkdirSync(dirname(path), { recursive: true }); rmSync(path, { force: true }); + const sockets = new Set(); const server = createServer((socket: Socket) => { + sockets.add(socket); const session: Session = { transport: 'uds', paired: true, subscribed: new Set(), sessionId: randomUUID() }; const conn: Connection = { session, send: (frame) => { if (!socket.destroyed) socket.write(JSON.stringify(frame) + '\n'); }, }; connections.add(conn); log(`uds: client connected (${connections.size} open)`); ... socket.on('error', (err) => log(`uds: socket error: ${err.message}`)); socket.on('close', () => { connections.delete(conn); + sockets.delete(socket); log(`uds: client disconnected (${connections.size} open)`); }); }); server.listen(path, () => log(`uds: listening on ${path}`)); + + if (dropEverySec > 0) { + setInterval(() => { + log(`uds: dropping ${sockets.size} connection(s) (--drop-connection)`); + for (const s of sockets) s.destroy(); + }, dropEverySec * 1000).unref(); + } + return server; } ``` **`tools/mockd/src/index.ts`** (~line 205): ```diff - startUds(args.uds, dispatcher, connections, log, args.slow); + startUds(args.uds, dispatcher, connections, log, args.slow, args.dropEverySec); ``` Both use `.unref()`/existing shutdown handling already in `index.ts`, so no change needed there. `socket.destroy()` (vs. `.end()`) matches `ws.ts`'s `.terminate()` — an abrupt drop, which is the point of the flag. Not urgent for M0/M1 GUI work — `gui/tests/dod/run.sh`'s other two unhappy-path phases (`--slow`, `--flaky`) both work correctly over UDS today, and the drop-connection phase still exercises 45 s of otherwise-idle connection handling. But the flag's whole purpose is unmet on the transport every real consumer uses, and the fix is a direct port of code that already exists two files over.