Four blocking, two high, four cleanups. Round 2's real finding: rev 2
widened the rename fix into a resource transaction, and four of the
consumers it named were not actually reachable by it. All six
substantive claims verified against c8ec8f3.
G1 — acceptance 29 was unimplementable. apply_workspace_edit captures
origin as a STRING (active_buffer_path is pmacs.editor.file_path,
lsp.lua:471-473), so no transaction reaches it and the phantom survives.
The applier itself changes: capture the buffer handle, restore with
switch_buffer, and no path fallback — restoring nothing beats inventing
a file that does not exist.
G2 — the dired subscriber could not rename its own buffer. dired.lua's
module doc says there is no pmacs.buffer.set_name, which is exactly why
Stage 1 chose buffer-per-directory. Rev 3 adds the setter (Q#DR21):
Buffer::set_name already exists and already documents itself as for
"rename operations", §5 needs it anyway for the Buffer.name half, and the
alternative — kill/recreate plus window replacement — loses placement,
cursor, intercept, round-trip input, and mode.
G3 — rec.uri was not the last LSP owner. DiagnosticView captures its URI
at construction and its own field doc anticipates this ("M5 may add
re-rooting if a buffer is renamed", diag.rs:455-457); five more stores
are URI-keyed. §5 now carries the ordered contract: flush pending
didChange, didClose, drop all five stores, re-run ensure_server, didOpen,
re-root the view per window.
G4 — Q#DR18 had no seam and was racy across the prompt. apply_resource_op
kills via find_by_path: raw path, first match, no descendants, no
modified check — it destroys unsaved work today. Rev 3 defines one shared
reconcile_delete called by both paths, harvests remove in the drain like
rename (so fire-and-forget reconciles too), and rechecks modified state
immediately before each syscall, since another frontend can edit while
the prompt is open. The policy stays asymmetric on purpose: dired refuses
the entry, an LSP-authored delete still removes the file but no longer
destroys the buffer.
G5 — w had no surface and the wrong semantics. push_entry is local and
copy() requires a region. Adds pmacs.killring.push (Q#DR22) with copy()'s
own semantics including breaking the kill chain, and makes w SET-BASED:
the parent approved the binding and Emacs copies marked filenames, so
rev 2's point-only narrowing was an unapproved change of its own. R is
now the only point-based operation.
G6 — R's no-clobber was only a preflight. rename_blocking calls plain
std::fs::rename, which silently replaces. The claim is narrowed to a
TOCTOU-bounded preflight refusal, acceptance 12 reworded to promise only
that, and a no-replace primitive named as deferred.
G7 — lsp_multi_root added to the gates, the §13/§7 slips fixed, and the
"2a's only Rust is the rename rebind" line corrected: it is now a rename
and delete reconciliation, two hooks, two new public surfaces, an LSP
teardown contract, and an applier change. §10 says so, and names the
further cut if that is now too large for one PR.
Acceptance renumbered flat (46 items) and the bite obligations are now a
table of eleven item/mutation pairs, three of them round-2 additions
where rev 2's design would have passed a weaker test.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126d2sikA6jZpFin3rtLCSK