githubnext/chopinPublic

Let's compose rich plans together

Show what the agent changed, and where it is working #2

MergedKrzysztof-Cieslakmerged 6 commits intomainfromrefs/pull/2/head
  1. KC

    Two things the plan could not say: what the agent just did to it, and where it is working right now. They are different questions with different lifetimes, and this answers them separately.

    ## What the agent did

    `edit.apply` already knew and threw it away. A batch is reconciled by object identity, so a block the agent wrote is one the reconciler did not carry through — free to report, and impossible for the agent to misstate. What *moved or went* cannot be had that way: a node missing from the result may have been deleted or replaced, and a node at a new index may have moved or merely been pushed down by an insert above it. Those come from the operations, which say exactly.

    Three kinds reach the browser as anchors, not indices, because somebody may type between the frame being sent and it being painted:

    | | drawn as | |---|---| | written | a wash in the prose colour | | moved | a wash in the structural colour — the words are not new | | removed | a hairline on the edge of the block still beside it |

    A rewrite is one mark, not a disappearance and an arrival. A move is one entry with two ends, so a reader is not left to work out that the block that vanished here is the one that turned up there.

    ## Marks wait to be seen

    The agent writes wherever it likes, including several screens from whoever is reading, so a mark that spent its few seconds off screen would have told nobody anything. Nothing counts down until it has been in the viewport, which makes it an unread marker rather than a flash — and unread markers do not expire on a clock, so an unseen one waits indefinitely and the set is bounded by a cap instead. Once seen the clock runs whether or not the mark is still in view: two states and one timer, so nothing can go dark and light up again on a later scroll.

    Chips at each edge count what is still unread in that direction and go to the nearest one. The list behind them is the only place a removal can be read after the fact, because the block itself is gone.

    ## Where it is working

    A cursor, in the language the room already has for a peer — Lexical draws it, `labels.ts` flashes its name. It rides in the presence mirror's own local slot: everything else there reflects a socket, which is what lets a disconnect clear it, and the agent has no socket. That slot is also in the join snapshot, so somebody arriving mid-turn sees it.

    It sits at the end of what the agent wrote. An anchor names a block from its start, which is the stable end of one to point at; a cursor is where an edit *finished*, and a caret at the top of a new paragraph looks like something about to be typed.

    The cursor and the marks disagree about time deliberately. The cursor is presence and goes shortly after the turn; a mark is a record and may sit unread. Somebody returning after a long turn sees marks and no cursor, which reads as an inconsistency until you notice they answer different questions.

    ## Nothing takes space

    No mark may add an element or occupy layout: an agent editing below the fold must not shift the sentence somebody is mid-way through typing, and a mark expiring must not shift it back. Blocks are washed; holes are drawn in `box-shadow` on a neighbour's edge.

    ## Tests

    412 pass, up from 375. The new ones cover the parts that fail invisibly:

    - the derivation — an insert at the top reports no moves, a rewrite leaves no hole, a delete and a move in one batch does not anchor the hole to the block that left - index → anchor → block, mutation-checked: an off-by-one fails it - `endOf` resolved through Lexical's own resolver, so what is asserted is where a browser would draw the caret - awareness renewal advancing the clock — re-encoding an unchanged state is silently discarded, and the cursor would vanish mid-turn - the mark lifecycle, kept free of the DOM so it can be tested at all

    ## Worth knowing before merging

    **Not yet run in a browser.** Types, lint and tests are green and the logic is covered, but the visual judgements — the wash percentages, the hairline geometry, the caret against a heading or list item — have not been looked at. Expect to want to tune them.

    **Two corrections to `agents.md` came out of this.** The highlight registry is `plan-related`, not `plan-comment*`. And five test files blamed happy-dom for returning zero measurements; happy-dom is not a dependency and there is no DOM at all — the conclusion was right for a reason that would have sent the next person to install it.

    **One latent CSS bug fixed on the way.** A block can carry a hole above and below at once — delete the second and fourth of four blocks — and two rules setting `box-shadow` at equal specificity meant the cascade kept one and the other silently never drew. The sides are custom properties now, which compose.

    **Known gaps, by choice.** `replace_root` reports nothing, because a document marked end to end says nothing. A hole is anchored to a neighbour, so rewriting that neighbour orphans it. A reconnect discards the trail.

  2. Krzysztof-Cieslakmerged c829883463763 months ago on GitHub

  3. Krzysztof-Cieslakclosed3 months ago on GitHub

  4. Krzysztof-Cieslakhead-ref-deleted3 months ago on GitHub

  5. MaggieAppletonreferenced this in Let Tab leave the editor outside lists #292last month on GitHub

  6. MaggieAppletonreferenced this in Fix touch ergonomics on phones #299last month on GitHub

  7. MaggieAppletonreferenced this in Replace the native link prompt with an inline link popover #298last month on GitHub

  8. MaggieAppletonreferenced this in Undo and redo each person's own document edits #352last month on GitHub