Paste is routed through on_paste, which inserts into the Playbook buffer and returns:
self.insert_playbook_text(&text);
…
return;
(crates/cli/src/app.rs ~line 6786 @ ad47152, reached from CtEvent::Paste(text) => self.on_paste(text).await ~line 10454)
Unlike handle_playbook_key, which snapshots the buffer and calls flush_playbook_live_edit afterwards (crates/cli/src/app/editor.rs ~lines 652 / 822), this path never publishes. The pasted text exists only in the client.
It then poisons everything after it: the next keystroke's anchored diff is computed against a before the daemon never saw, playbook.edit fails with old_string not found, and that failure is swallowed (#1089). So after a paste, nothing the user types reaches the document either — silently.
Repro
- Open a Playbook in the TUI on a session whose document is
seed\n (daemon v27).
- Put the caret at the end and paste
- one\n- two\n - nested\n.
Observed:
=== TUI === === daemon ===
seed- one v 27
• two 'seed\n'
• nested
- Keep typing — the daemon stays at v27.
C-x C-s does publish, so the text is recoverable if the user knows to press it. But when the daemon has meanwhile changed, that save is a 3-way merge, and an unreconcilable one writes git conflict markers into the shared document:
'<<<<<<< ours\nse*ed- one\n- two\n - nested\n\n||||||| original\nseed\n=======\n\tTABbed\n>>>>>>> theirs\n'
The status line does say playbook merged with conflicts to resolve, but the markers now live in a document the owning agent will read and Run.
The web UI has no equivalent problem — its paste handler goes through document.execCommand("insertText", …), which fires input and syncs normally (crates/daemon/assets/index.html ~line 9284).
Expected
Paste is an edit; it publishes like every other edit.
Acceptance criteria
Related: #1088 (the same missing-flush shape on undo), #1089 (why the failure is invisible).
Found during a hands-on UX audit of the Playbook in the TUI and web UI.
Paste is routed through
on_paste, which inserts into the Playbook buffer and returns:(
crates/cli/src/app.rs~line 6786 @ ad47152, reached fromCtEvent::Paste(text) => self.on_paste(text).await~line 10454)Unlike
handle_playbook_key, which snapshots the buffer and callsflush_playbook_live_editafterwards (crates/cli/src/app/editor.rs~lines 652 / 822), this path never publishes. The pasted text exists only in the client.It then poisons everything after it: the next keystroke's anchored diff is computed against a
beforethe daemon never saw,playbook.editfails withold_string not found, and that failure is swallowed (#1089). So after a paste, nothing the user types reaches the document either — silently.Repro
seed\n(daemon v27).- one\n- two\n - nested\n.Observed:
C-x C-sdoes publish, so the text is recoverable if the user knows to press it. But when the daemon has meanwhile changed, that save is a 3-way merge, and an unreconcilable one writes git conflict markers into the shared document:The status line does say
playbook merged with conflicts to resolve, but the markers now live in a document the owning agent will read and Run.The web UI has no equivalent problem — its paste handler goes through
document.execCommand("insertText", …), which firesinputand syncs normally (crates/daemon/assets/index.html~line 9284).Expected
Paste is an edit; it publishes like every other edit.
Acceptance criteria
Related: #1088 (the same missing-flush shape on undo), #1089 (why the failure is invisible).
Found during a hands-on UX audit of the Playbook in the TUI and web UI.