ekp/postmortem/20260728-nonmutating-save-serialization.md
Kinneyzhang fd1210e557 fix: close repository audit findings
Complete task002 through task016 across cache correctness, editor lifecycle, C boundaries, build and release governance, dictionary provenance, performance, interactive workflows, tests, documentation, and final cleanup.
2026-07-28 22:43:10 +08:00

57 lines
2.1 KiB
Markdown

# Serialize a Logical Copy Instead of Restoring After Save
## Context
EKP previously unformatted the display buffer in `before-save-hook` and
reformatted it in `after-save-hook`. Emacs does not run `after-save-hook`
when writing fails or is interrupted, so the screen stayed unformatted and
the marker state stayed live.
## Decision
Do not mutate the source buffer during save. A buffer-local
`write-region-annotate-functions` entry switches whole-buffer writes to a
hidden logical copy. This is the same documented buffer-switch boundary used
by Emacs format encoders, so normal backup, coding, file-mode, VC, and later
annotation behavior remains owned by Emacs.
## Alternatives Rejected
### Add another restoration hook
There is no general failure hook paired with `after-save-hook`. A
post-command fallback would miss programmatic saves and nested calls.
### Advise `save-buffer` or `write-region` globally
Global advice would make every Emacs save share EKP's lifecycle and create
ordering conflicts with other packages.
### Reimplement saving in `write-contents-functions`
Owning the actual write would duplicate backup, coding, file modes, VC, and
visited-file semantics that Emacs already implements.
## Consequences
- Success and failure paths leave the visible layout byte-identical.
- Disk and auto-save output receive logical text.
- A successful write disposes the copy immediately.
- Because Emacs does not call the post-annotation function after a failed
write, at most one hidden copy remains per source buffer; retry or teardown
replaces and clears it.
- No global advice, external dependency, or saved-file format changed.
## Verification
The old model failed real missing-directory and forced-quit tests by leaving
the logical text visible. The new model passes success, filesystem failure,
encoding failure plus retry, and interruption tests. Full ERT passes
102/102, C/Elisp fuzz passes 300/300, and warnings-as-errors byte compilation
plus checkdoc are clean.
## Rollback
Restore the before/after hooks and their marker state. Existing files need
no migration because both designs write the same logical bytes.