Files
Connor JohnstoneandClaude Opus 5 dd23ca67cd Add phase 5: local cache, ETag tracking, three way merge, conflict siblings
The editor now always reads and writes a local working copy, so the app
opens instantly and works with no signal. Beside it sits the pristine text
from the last agreement with the server and its ETag, which exists so a
later divergence can be merged rather than guessed at.

The merge is line based and three way, and is kept free of Android types
so it runs under JVM tests: a merge that is wrong loses writing rather
than merely looking wrong. 16 tests cover it, including the case the whole
thing exists for, a box ticked on the phone while a different box was
ticked on the laptop, and the harder ones, an insertion on one side that
must not desynchronise a later edit on the other, and repeated identical
lines that must not confuse the alignment.

Alignment is by longest common subsequence rather than by line number, so
an inserted or deleted line shifts what follows instead of mismatching
everything after it.

On a real conflict the server's copy stays as the note and the local one
is written beside it as a .sync-conflict- sibling, which is the convention
already in that directory. Nothing is adopted until that copy is known to
have been written, so local text is never replaced by something that
cannot be recovered.

Since SFTPGo ignores If-Match, the precondition is enforced by comparing
the stored ETag against the one a GET returns. The ETag a PUT reports is
not the file's own, so it is read back with a HEAD rather than believed.

Sync happens on foreground and on the button, never on a timer. The date
is recomputed on each sync, so an app left open across midnight moves to
the new day's note by itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PU5ZFfQFtDTFqqhvMTWdGH
2026-09-09 20:42:32 -04:00
..