dd23ca67cdddf33ee207bc87f523de410390e0c6
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
Languages
Kotlin
66.8%
Python
18.7%
Shell
14.5%