pmacs/docs
Levi Neuwirth e173f61a61 fix(persistence): one buffer owns a path's recovery slot (Q#AS13)
Addresses the PR #100 review round 3.

pmacs.buffer.from_file does not dedup, so two buffers can visit one path.
Ownership was tracked as a path-wide `owned: HashSet<path_hash>`, which
made the duplicate case silently corrupting: both dirty buffers queued a
write to autosave/<same hash>, the later write won on disk, and BOTH were
recorded in `written` --- so the loser skipped future sweeps while its
contents were unrecoverable. The path-wide set also let either buffer's
save/kill retire the other's recovery.

A recovery file must stay keyed by path (a later session knows only
paths, never old BufferIds), so two divergent buffers cannot both be
protected under one key. Ownership is now `owner: path_hash -> BufferId`:

- the first modified buffer to reach a free slot claims it, including
  within a single pass (the write loop updates `owner`, so the gather
  loop tracks slots queued this pass --- otherwise two duplicates both
  queue a write);
- any other buffer on that path is counted `conflicted` and reported
  ("autosave paused for N buffer(s): another buffer is visiting the same
  file"), never silently mis-protected. It records no `written` entry, so
  it re-attempts each sweep instead of believing itself saved;
- `discard_buffer` (save/kill) retires ONLY slots this buffer owns, which
  now enforces both invariants at once: an unowned slot is unclaimed
  crash data (Q#AS12), and a slot owned by another buffer is that
  buffer's recovery;
- saving or killing the owner releases the slot; the duplicate claims it
  on the next sweep;
- `recover-file` adopting into a buffer makes that buffer the owner --- the
  file's contents are now its contents, and the previous owner truthfully
  becomes conflicted.

sweep() now returns (written, blocked, conflicted). Its gather phase is
extracted into `gather()` (clippy too-many-lines).

This is honest rather than clever: pmacs cannot protect two divergent
buffers over one file, and now says so instead of pretending.

Tests (autosave_acceptance now 27):
duplicate_buffers_on_one_path_conflict_instead_of_corrupting (owner's
copy on disk; the dup never wins the slot by editing),
a_duplicate_buffers_save_does_not_retire_the_owners_recovery,
killing_the_owner_frees_the_slot_for_the_duplicate.

Gates: fmt + workspace clippy clean; lib 1499; crdt 1670; autosave 27 + 8
units; desktop 11; persistence 5; m7_8 5; GPU 58; git diff --check clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 11:36:55 -04:00
..
autosave-recovery-framing.md fix(persistence): one buffer owns a path's recovery slot (Q#AS13) 2026-07-09 11:36:55 -04:00
context-menu-framing.md docs: right-click context menu framing + as-built (Q#CM) 2026-06-27 22:19:00 -04:00
cua-typeover-undo-framing.md CUA type-over is a single undo step (Q#U1) 2026-06-15 20:00:53 -04:00
desktop-save-framing.md fix(persistence): reliable daemon gate, unarm, per-pane after-load 2026-07-08 22:27:24 -04:00
gpu-attach-robustness-framing.md fix(gpu): F-008 fail-fast must actually tear down the session, not just flag it 2026-07-03 17:14:14 -04:00
gpu-chord-forwarding-framing.md docs: GPU general chord forwarding framing + as-built (Q#GC) 2026-07-03 10:05:55 -04:00
gpu-golden-harness-framing.md docs: GPU headless render harness framing + as-built (Q#GH) 2026-07-03 12:15:19 -04:00
gpu-minibuffer-framing.md docs: GPU minibuffer framing + as-built (Q#MB) 2026-06-30 21:03:32 -04:00
in-buffer-completion-framing.md docs(completion): phase-2 as-built notes (Q#C6 narrowing, window routing, v15 riders) 2026-07-07 19:57:10 -04:00
in-buffer-search-framing.md docs: consolidate in-buffer search framing (substring + regex) 2026-06-27 15:03:31 -04:00
lsp-panels-framing.md docs(panels): correct as-built accuracy — position encoding landed, refresh drifted refs 2026-07-08 14:29:23 -04:00
lua-bindings-split-framing.md refactor(lua): re-export install_* wiring fns to preserve the public API 2026-07-06 11:57:56 -04:00
mcp-for-package-authors.md M9 ship gate 2026-05-09 15:04:23 -04:00
package-author-guide.md V0.2-prerequisite pull-forward + M10.11 clean audit round 2026-05-18 10:31:31 -04:00
package-manager-hardening-framing.md fix(packages): F-005 must also guard the frozen/lockfile plan path 2026-07-03 19:05:08 -04:00
packages.md Fix CI and Documentation issues 2026-05-04 10:19:19 -04:00
persistence-framing.md docs: persistence framing (Arc 3) 2026-07-08 17:33:25 -04:00
pmacs-gpu-design.md audit remediation: workspace clippy gate + stale metadata + cruft (F-001/F-013/F-015) 2026-07-03 10:31:17 -04:00
pmacs-gpu-diagnostic-parity-framing.md framing: pmacs-gpu diagnostic parity — quad squiggles, minimap marks 2026-06-12 10:00:06 -04:00
pmacs-gpu-mouse-deferred-framing.md framing: mouse deferred set — triple-click, Shift-click, minimap jump, edge auto-scroll 2026-06-12 12:12:18 -04:00
pmacs-gpu-mouse-framing.md session M-1 — Pointer wire + daemon byte-space mouse semantics 2026-06-10 13:10:17 -04:00
pmacs-gpu-perline-reshape-framing.md session R — per-line incremental reshape (typing latency floor) 2026-06-10 15:01:51 -04:00
pmacs-gpu-phase-a-audit.md Close pmacs-gpu phase A audit 2026-05-28 12:49:23 -04:00
pmacs-gpu-quad-backgrounds-audit.md docs: close quad-backgrounds arc — audit + framing status 2026-05-29 10:49:21 -04:00
pmacs-gpu-quad-backgrounds-framing.md docs: close quad-backgrounds arc — audit + framing status 2026-05-29 10:49:21 -04:00
pmacs-gpu-scroll-framing.md docs: viewport-scoped rendering + scroll framing (perf) 2026-05-30 10:28:59 -04:00
pmacs-gpu-status-band-framing.md framing: GPU status band — local freshness + v8 StatusFacts 2026-06-12 13:45:12 -04:00
pmacs-gpu-wavy-squiggles-framing.md pmacs-gpu: wavy diagnostic squiggles (Q#W1) 2026-06-15 18:41:56 -04:00
project.md Fix PTY final-output drain race 2026-05-04 09:44:30 -04:00
query-replace-framing.md fix(query-replace): pin the session to its origin buffer (wrong-buffer guard) 2026-07-08 17:07:08 -04:00
repository-audit-2026-07-03.md docs(features): document the Lua feature matrix; drop unreachable compile_error idea (F-002) 2026-07-03 19:54:35 -04:00
roadmap-2026-07.md docs: July 2026 roadmap — ranked arcs; Arc 1 (LSP utility surface) active 2026-07-07 11:00:09 -04:00
semantic-frontend-protocol.md M1+M2: FileStyleSummary (minimap producer, Q#2 resolved) + doc 2026-05-19 19:23:20 -04:00
ux-arc-framing.md docs(ux): as-built for sub-arc 3 + arc close (relative/hybrid, protocol v14) 2026-07-07 10:03:32 -04:00