Every stream store exposes its attach machine as a read-only phase store
(initialized -> attaching -> attached -> detached | failed)
plus explicit attach() / detach() controls. And on the server, ctx.batch(fn) turns
publishes into an all-or-nothing batch: a throw after an await retracts publishes issued before it.
Reading as Wacky Lobster (095e5e7e)
Feed hidden - the subscription is . Attach to render it.
attach() holds an internal retain: the stream
stays subscribed with no UI subscriber and reattaches
itself across outages (watch the badge cycle attaching -> attached after a reconnect). detach() is "done" - it releases the retain and
tears the subscription down immediately, skipping the
resume-grace window a normal component unmount gets.
Both buttons run the same handler: ctx.batch(async () => { publish first; await;
maybe throw; publish second }). The failing
variant throws BETWEEN the two publishes, after a real await
boundary - and neither entry appears, because the collector
buffers publishes across awaits precisely so a later throw
can retract them. Bare ctx.publish would already
have flushed the first one at the await.