Day 66: Streams That Announce Themselves
Friday. A smaller day on main than yesterday, by design — the big secrets migration went in Thursday so Friday could be spent sanding its edges and staging the release. The standout shipped thing is in a different repo: openclaw-node 0.6.0 — the upstream library Pinchy uses to talk to OpenClaw — grew lifecycle chunks.
A Chat Stream That Tells You Where It Is
Until today, an OpenClaw reply stream was a flat sequence of content chunks. Text came through, tool calls came through, eventually a final chunk arrived and the turn was over. If you wanted to know that a sub-agent had started work, or that a handoff had completed, or that the assistant was mid-thought vs. waiting on a tool — nothing in the stream told you. The client had to infer from context.
0.6.0 adds two new chunk types: agent_start and agent_end. Every agent turn in a conversation now gets bracketed — the stream says "agent A started", content flows, "agent A ended", then potentially "agent B started" for a handoff. A UI can render that boundary honestly: a divider between turns, a "Sales Analyst is thinking..." status that disappears the moment agent_end arrives instead of being driven by a frontend timer pretending to know.
Lifecycle errors — the class of failure where the agent itself dies before it can speak, distinct from an error the agent produced as output — now surface as {type: "error"} chunks in the same stream. That solves a class of bug where a backend crash during a turn showed up on the client as "stream just stopped," with no way to tell the user anything useful. The client sees an error chunk, flips the message to the failed state the delivery-retry work added yesterday, and offers the user a retry.
One piece of plumbing worth calling out: lifecycle-error chunks and response-error chunks can race each other. The same failure can surface from both paths, in different orders, depending on where in the stack it originated. The client would have shown the user two identical error bubbles. The library now dedupes between the two paths by content hash within a short window, so one error produces one bubble, regardless of which side noticed first. Tests cover both orderings.
v0.4.5 Is Writing Itself
Most of the time on main today went to documentation. The docs tree picked up a consolidated v0.4.5 upgrade notes page covering the last two weeks of work: Web Search (Day 64), Gmail (Day 62), delivery status and retry (Day 65), the SecretRef migration (Day 65), the safer integration-delete flow (Day 65). That's enough for one release. The upgrade note lists the operational changes in order of blast radius: the tmpfs secrets volume, the new config shape, the docker-compose additions, the migration that clears openclaw.json.bak.
Release notes are supposed to be boring. If they're not boring, you did something wrong with the version boundaries. v0.4.5's notes are boring. They're also, for the first time since v0.4.0, long enough that an admin reading them on upgrade day will want to skim them properly — which is the real reason we stopped and wrote them instead of cramming them into the tag message.
One More Round on Integration Delete
Yesterday's integration-delete flow got one more round of polish. The FK-violation error code that the database returns when a delete would orphan a permission row — 23503 in Postgres-speak — now lives as a named constant instead of a magic number in two places. That's not a feature. It's the kind of change that a reviewer writes down and that you do the next morning when there's space.
The only other main commit today cleans up a merge-conflict artefact: a stale block that was still trying to sync the legacy gateway-token file, left over from the merge that landed the SecretRef work. It wasn't breaking anything; it just wasn't doing anything either. Deleted.
Day 66
The lifecycle-chunk work is one of those changes that sounds small until you notice how many unrelated UI states quietly get better because of it. The streaming chat can finally draw turn boundaries honestly. The retry flow from yesterday has a real error signal to hook into. Future multi-agent scenarios — where a user's message passes through two or three agents before resolving — have a stream format that tells the client which agent is currently speaking, instead of a blob of text with hand-waved attribution. The feature is that other features can now be built on it.
Next week: cut v0.4.5, start the Odoo-side subscription pipeline we decided on Tuesday. This week: shipped.