ekp/postmortem/20260728-paragraph-fast-path-identity.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

2.0 KiB

Give Both Paragraph Cache Paths One Identity Owner

Context

The paragraph hash cache used ekp--para-key, including filtered text-property intervals and every paragraph preprocessing input. The one-entry ekp--last-para path bypassed that key and compared a smaller tuple. Mutating ekp-no-break on the exact cached string object therefore returned stale boxes and break permissions.

Decision

Compute ekp--para-key before either lookup. The one-entry path may skip the hash-table operation when both string identity and that complete key match, but it does not own a second signature. Remove style-variable watchers whose only purpose was to compensate for the partial fast-path tuple.

Alternatives Rejected

Add Text Properties to the Partial Tuple

That repairs one omission while preserving two independently maintained identity rules. The next paragraph input would recreate the bug.

Clear All Paragraph Caches on Property Mutation

Standalone strings have no general mutation hook, and eager global invalidation would discard unrelated paragraphs.

Remove the One-Entry Path

That is correct but needlessly adds a hash-table lookup to repeated resolution inside one formatting call. Comparing the already-owned key keeps the narrow optimization without duplicate semantics.

Consequences

  • Same-object text-property mutations now miss both lookup paths.
  • Style, language, spacing, display context, and text properties have one paragraph identity owner.
  • Six watcher registrations and their anonymous invalidation function are removed.
  • The fast path still avoids the hash lookup, but no longer avoids computing the correctness key.

Verification

The new regression failed 0/1 before the change. It passes for both CJK and Latin-with-space strings after the change, and the related cache/style/ display-context matrix passes 6/6.

Rollback

Restore the partial tuple and watchers. No persisted data needs migration, but the same-object property regression returns.