ekp/postmortem/20260728-buffer-integration-ownership.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

64 lines
2.5 KiB
Markdown

# Let Layout Spans and Auto Mode Own Buffer Integrations
> Updated by `20260728-public-filter-dispatch.md`: the same composition
> decision now delegates through Emacs's public dispatcher instead of its
> private helper, with DELETE cleanup ordered after dynamic-binding unwind.
## Context
`filter-buffer-substring-function` is a single-slot protocol. EKP replaced
that slot and later killed its local binding, losing any prior buffer-local
owner. Separately, public unjustify removed layout markers but left save,
search, change, and copy integrations installed.
## Decision
On first installation, record both the previous filter value and whether it
was buffer-local. The composed function invokes that filter first, preserving
its buffer context and DELETE semantics, then structurally inverts EKP
markers in the returned string.
Separate the structural unjustify core from the public lifecycle wrapper.
Internal reflow, isearch, serialization, and teardown call the core. Public
unjustify and external edits remove integrations when neither auto mode nor
any justified span still needs them.
## Alternatives Rejected
### Replace the prior filter
This silently breaks major-mode or user copy semantics and cannot be called
composition.
### Run the prior filter on a temporary logical buffer
Arbitrary filters may depend on the original buffer's local variables,
positions, fields, and deletion behavior. A temporary buffer cannot preserve
that contract.
### Leave hooks installed because most become no-ops
The copy slot remains occupied, hidden state survives after the visible
feature is gone, and later packages cannot recover ownership predictably.
## Consequences
- Prior local and inherited filters are restored exactly.
- Copy and kill preserve both prior-filter and EKP logical-text semantics.
- Internal temporary unjustify no longer changes integration lifecycle.
- A prior filter that intentionally removes text properties also removes
EKP marker evidence; that filter owns its returned representation.
- No new abstraction layer or external dependency was introduced.
## Verification
The old model failed both the composition and final-span cleanup controls.
The new model passes five public-path cases plus the existing isearch/save/
mode suite. Full ERT passes 107/107, fuzz passes 300/300, and
warnings-as-errors byte compilation plus checkdoc are clean.
## Rollback
Restore direct filter assignment and the monolithic public unjustify
function. No persisted data needs migration.