Three bugs preventing any SSE events from reaching clients:
1. SubscriptionRegistry::clone() deep-cloned the DashMaps instead of sharing
them. The sse_handler spawned a task with a disconnected clone, so
subscriptions.update() wrote into a throwaway copy — the fanout always saw
an empty registry and found zero connections to push to. Fixed by wrapping
both DashMaps in Arc so clone() is a cheap pointer copy sharing live state.
2. The SSE event data payload used .join("\ndata: ") to pre-embed the `data:`
prefix into the string, then handed it to axum's Event::data() which does
its own \n-splitting to add `data:` prefixes. This produced double-prefixed
lines (`data: data: elements ...`) that DataStar couldn't parse. Fixed by
using .join("\n") and letting axum format the data lines correctly.
3. The fanout skipped connections with None signals (new connections that had
not yet received a client action). This broke clock-style server-push events
on fresh connections. Fixed by falling back to "{}" instead of continue-ing.
https://claude.ai/code/session_0168cLRd1wr6LK9FjBsjA37W
Two bugs that caused signals to not work on the frontend:
1. data-on-signal-change → data-on-signal-patch
The data-on-signal-change attribute was removed in RC8.
The replacement is data-on-signal-patch.
2. data-bind-newTodo="" → data-bind="newTodo"
HTML normalises attribute names to lowercase at parse time, so
data-bind-newTodo becomes data-bind-newtodo in the DOM. DataStar
would then bind to signal $newtodo instead of $newTodo, breaking
the two-way binding. The value syntax data-bind="newTodo" preserves
the camelCase name correctly.
https://claude.ai/code/session_01JThiQbp3Bn9J1VhBpRuz4u