Day 80: v0.5.2 and the Drain That Doesn't Stop
Friday. v0.5.2 ships at the end of the day, rolling three threads that landed across the week into a single release. The visible part of today's work is the streaming fix that finally closes the disconnect-mid-stream class of bug — issue #199 has been open since the chat first started using WebSocket streaming, and Layer B of its eventual fix lands today. The release also picks up Wednesday's Ollama-local rewrite and Thursday's personal-agent boundary fix, both of which had been waiting on a release window.
The Stream That Stopped When Nobody Was Listening
The bug. Pinchy's chat works through a three-hop path: the browser opens a WebSocket to the Pinchy server, the server's client-router proxies the request to OpenClaw, and OpenClaw streams the assistant's response back through the same path. The client-router holds a session-keyed cache of the in-flight response chunks, so a page reload mid-stream finds the cache and resumes from where the previous WebSocket left off.
The bug was that when the browser disconnected — a navigation away, a tab close, a network blip — the client-router was correctly noticing and tearing down its end of the WebSocket. It was also, less correctly, stopping its consumption of the OpenClaw stream. OpenClaw didn't know the consumer had vanished and kept producing tokens; those tokens piled up in a buffer with no reader, OpenClaw eventually noticed the back-pressure and decided the consumer had crashed, and the whole turn got marked as failed. The assistant message that actually finished generating never made it into the session-keyed cache. The next page load saw a half-finished response that ended in a generic error bubble.
The fix is in two parts that landed in the same PR. The first part is to keep draining the OpenClaw stream after the browser disconnect — the consumer is still alive on the Pinchy side; the WebSocket is dead but the cache write is the thing that matters. The drain runs until OpenClaw closes its end naturally, which is when the assistant's turn completes; only then does the client-router clean up its side of the proxy. The cache now contains the completed response by the time anyone reloads. The second part is a regression guard against the matching failure mode in the other direction: the cleanup path was, in some race conditions, trying to send a final frame back to the now-closed browser WebSocket, which threw a WS not open error and short-circuited the rest of the cleanup. The test asserts that no frames are sent to a closed WS, and the cleanup path is now guarded against the case.
One smaller fix in the same neighbourhood: the client-router's heartbeat-cleanup timer, which fires every 30 seconds to evict cache entries whose WebSocket has been gone too long, was not being cancelled cleanly on session disconnect. The leak was small (a single timer per session, ~50 bytes), but cumulative over a long-running deployment — and the matching regression guard asserts that a clean disconnect leaves no timer behind.
Errors Stop Disappearing on Disconnect
A subtler fix shipped alongside the drain. Until today, an OpenClaw error chunk that arrived after the browser disconnected was being dropped on the floor — the proxy was returning early on the disconnect, before the error chunk's log line was emitted. That meant the post-mortem trail for a failed turn was missing exactly the error chunks that mattered most: the ones that arrived after the user had given up and navigated away. The fix is small — the log emission moves to a position in the read loop that runs before any early-return check — and is the kind of thing that matters every time someone files a support ticket for a turn that looked like it should have worked.
The TDD Red Test
The PR's first commit is the test that pins down the bug: pipeStream must keep draining after browser disconnect (#199 Layer B). The test sets up a fake-OpenClaw fixture that produces five chunks at 50ms intervals, opens the client-router pipe, closes the consumer WebSocket after the second chunk, and asserts that all five chunks land in the session-keyed cache by the time the OpenClaw side closes naturally. The test fails red against the pre-fix code (chunks three through five never arrive). The actual fix commit makes the test pass green. Two more regression guards landed alongside — heartbeat cleanup, no-frames-to-closed-WS — to pin down the failure modes the fix could re-introduce.
The test got one piece of polish during review: a variable rename from cache to freshCache in the disconnect-test setup, so the spec reads as open a fresh cache for this test, run the pipe, assert the cache contains the full message instead of the ambiguous cache name that's also used as a verb in the surrounding test file.
Two Smaller Threads
The screenshots workflow got a chat-ready timeout bump from 60 to 90 seconds. The previous timeout had been chosen against the team's laptops; on the CI runner that pulls the screenshots, the cold-start path takes longer because Docker layer caching isn't always warm. The bump is the difference between a workflow that succeeds 95% of the time and one that succeeds 99% — small per-run but big over a hundred runs.
The Ollama setup docs got a small clarification: options B and C in the local-Ollama guide had been telling users to enter a bare service name (ollama) as the host, which fails OpenClaw's URL-allowlist check. The fix is to spell out the full http://ollama:11434 form and to note that the bare name is what you'd use inside a different Docker network setup that we don't support out of the box. The kind of docs fix that costs an evening of confused-user support if it's wrong and 90 seconds of editing if you catch it early.
v0.5.2 Goes Out
The release commit at the end of the day pulls in three things from the last 72 hours. The disconnect-drain from today's PR. The personal-agent boundary fix from yesterday — admins no longer have a fast-path past isPersonal. And the Ollama-local rewrite from Wednesday — host.docker.internal gets rewritten to ollama.local, with the matching host-gateway alias in the compose file. The upgrade notes describe all three plainly: no breaking changes, no compose-file changes, just bump PINCHY_VERSION and pull.
Day 80
The pattern of the week is now visible. Each release this week has had one obviously-visible fix and two or three quieter ones underneath. The disconnect-drain is visible — it's the difference between a chat that survives a tab close and one that loses what it was doing. The personal-agent boundary is invisible until someone tries to exploit it. The Ollama rewrite is invisible to anyone whose first install actually worked. The release notes have to be honest about all three because the customer reading them is the customer running into one of them. The next release — v0.5.3 — is probably the one where the next class of visible bug shows up, the kind whose fix is a feature in its own right.