Give the agent a cursor while it is working
The marks say what changed and wait to be read; nothing said where the agent was working right now. A cursor does, in the visual language the room already has for a peer — Lexical draws it, `labels.ts` flashes its name and fades it, and the wire already permitted a server-sent `plan:awareness`, so none of that is new. It rides in the presence mirror's own local slot. Everything else there is a reflection of some socket, which is what lets a disconnect clear it; the agent has no socket, so it goes where nothing can attribute it to one, and it is taken down deliberately when the turn ends. That slot is also in the join snapshot, so somebody arriving mid-turn sees it. Its position comes from the same anchoring pass as the marks. Two passes could disagree, and then the cursor would point at one block while the marks described another. Peers drop a state they have not heard about for thirty seconds, and an update whose clock has not advanced is ignored without even refreshing the timer — so the renewal sets the state again rather than re-encoding it. `presence.test.ts` pins that, because the failure is a cursor that disappears mid-turn with nothing anywhere to say why. The left margin rule goes with this: it was answering "where", badly, and the cursor answers it properly. What is left is a wash for prose that was written, the structural colour for what moved, and a hole for what went. A move needs the wash now that it has no rule of its own, or it would be visible only where it left and read as a deletion.
Parentf419b4b
7 files changed+448-58
StackedSplit