Three related lifecycle gaps found in the post-v1.0 review (none bite the MCP server today because it discards the whole DwarfClient on error, but any consumer trusting the instance gets a dead client):
connected lies after the socket dies; the instance can never reconnect. src/connection.ts:87 (close handler) and fail() at :167-172 reject pending calls but never reset shookHands or ready. After DF restarts, connected still returns true and connect() returns the stale resolved promise forever — only close() (:174-181) resets state. Fix: in fail(), also set shookHands = false, ready = null, and destroy the socket.
- Protocol desync leaves a corrupted connection alive.
:124-128: a reply frame with no pending call calls fail(new ProtocolError(...)) but leaves the socket open and the parse buffer mid-stream; subsequent calls write onto a desynced stream and can mispair replies. Fix: this.sock?.destroy(err) inside fail() so a desync is terminal.
close() does not clear the parse buffer. :174-181 resets sock/shookHands/ready but not this.buf or textFrames, so close() -> connect() reuse on one instance would corrupt the new handshake parse. Fix: clear both in close() (and in fail()).
Three related lifecycle gaps found in the post-v1.0 review (none bite the MCP server today because it discards the whole
DwarfClienton error, but any consumer trusting the instance gets a dead client):connectedlies after the socket dies; the instance can never reconnect.src/connection.ts:87(close handler) andfail()at:167-172reject pending calls but never resetshookHandsorready. After DF restarts,connectedstill returnstrueandconnect()returns the stale resolved promise forever — onlyclose()(:174-181) resets state. Fix: infail(), also setshookHands = false,ready = null, and destroy the socket.:124-128: a reply frame with no pending call callsfail(new ProtocolError(...))but leaves the socket open and the parse buffer mid-stream; subsequent calls write onto a desynced stream and can mispair replies. Fix:this.sock?.destroy(err)insidefail()so a desync is terminal.close()does not clear the parse buffer.:174-181resetssock/shookHands/readybut notthis.bufortextFrames, so close() -> connect() reuse on one instance would corrupt the new handshake parse. Fix: clear both inclose()(and infail()).