Per-call mutate() callbacks fire only for an observer's latest call, so
with overlapping saves the in-flight count could stay above zero and the
review would ignore every later server value, including a Restore.
Saves now settle through useEntryPatch's hook-level onSettled (fires for
every mutation), in FIFO order (the entry-patch scope is serial). The
draft counts pending saves, remembers the latest incoming value, skips our
own optimistic/rollback echoes, and applies an outside change once the
saves settle.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Review draft sync is a pure helper (format.ts): incoming values are
ignored while a save is in flight, and a failed save restores the
previous synced value, so the typed text is kept whichever of the
rollback and the error callback lands first. Tests cover both orders.
- A failed background refetch keeps showing the cached entry instead of
the error screen (it unmounted the review box mid-outage).
- History restore joins the entry-patch mutation scope.
- updatePreferences: read-modify-write in a transaction, row locked on
Postgres.
- Invalidate auth-status after register; out-of-range rating errors speak
in the user's scale.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Tags and custom fields update the cached entry optimistically and build
from the latest cache; one entry's PATCHes run in order (mutation scope).
- The review textarea follows server changes (Restore) unless edited.
- friendlyError() maps validation reasons and 413 to plain copy everywhere.
- Rating scale from me/preferences; /login and /register use auth/status;
Attachments lists from the API.
- Gelasio vendored (official OFL variable TTFs); minors from the review.
- Docs: DESIGN (AA on orange, faint labels), DECISIONS, TODO, ARCHITECTURE.
Co-Authored-By: Claude Opus 5.5 <[email protected]>