Skip to content

fix(documents): persist offline document edits by sending relativePath - #34

Merged
Adron merged 1 commit into
mainfrom
fix/documents-sync-relative-path
Sep 2, 2026
Merged

fix(documents): persist offline document edits by sending relativePath#34
Adron merged 1 commit into
mainfrom
fix/documents-sync-relative-path

Conversation

@Adron

@Adron Adron commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

Editing an existing document (or creating one) appeared to save on-device but silently never reached the server, and the doc later reverted to its created-date state. Root cause: the offline sync path (on by default) POSTs ops to /api/documents/sync, whose backend handler drops any document create/update op lacking a required, folder-unique relativePath — while still returning 200. The client never sent relativePath, so every edit was silently discarded; the optimistic edit lived only in local cache until the next full pull merged the untouched server row back over it.

The client's SyncOpData already had a relativePath field — it was simply never populated. This PR populates it everywhere a document create/update op is built.

What's included

  • documents (fix): decode relativePath on Document from the sync response; populate it on every offline sync op — updateDocumentOffline echoes the doc's existing path (falls back to "<id>.md" for pre-field cached rows), createDocumentOffline generates a collision-safe "<id>.md", and DocumentSyncConflict.makeConflictCopy gives conflict copies a path (they'd otherwise fail to push too). (commit 9990a0d)
  • tests: new coverage that pushed create/update/conflict-copy ops carry a non-empty relativePath, that updates echo the server path, and that Document decodes relativePath. (commit 9990a0d)
  • docs: logged the backend defect as ask A7 in the-gaps.md (server should not require relativePath on update, should fall back on create like POST /api/documents, and the 200-on-silent-drop blind spot). (commit 9990a0d)

Testing

  • xcodebuild build (iPhone 16 simulator) — BUILD SUCCEEDED
  • xcodebuild test -parallel-testing-enabled NO -skip-testing:InterlinedListTests/E2EReadOnlyTests805 tests, 0 failures

Caveats / follow-ups

  • This is an iOS-only workaround; the server-side hardening (ask A7) is still worth doing for other clients (il-sync, web) and to close the silent-failure blind spot.
  • Deeper design gap left untouched: syncCycle pulls-then-pushes and never re-pulls after a push, so a future silent per-op server rejection would still go undetected.

🤖 Generated with Claude Code

Document edits and creates route through the offline sync path (default
on) and are POSTed to /api/documents/sync. The backend gates every
document create/update op on a required, folder-unique `relativePath`
(`if (!id || !relativePath) continue;`) yet always returns 200, so the
client silently believed the push succeeded: it cleared the outbox and
marked the doc synced while the server dropped the op. The edit lived
only in the optimistic cache until the next full pull merged the
untouched server row back over it — so an edited doc reverted to its
created-date state and never reached the server.

The client `SyncOpData` already had a `relativePath` field; it was just
never populated. Populate it at all three document op sites:

- Document: decode `relativePath` from the sync response so pulled docs
  carry their real server path.
- AppDataStore.updateDocumentOffline: echo the existing doc's path,
  falling back to "<id>.md" for rows cached before the field existed.
- AppDataStore.createDocumentOffline: generate a collision-safe
  "<id>.md" (UUID basename can't violate @@unique([folderId,
  relativePath])).
- DocumentSyncConflict.makeConflictCopy: give conflict copies a path
  too — they would otherwise fail to push for the same reason.

Add tests covering the pushed create/update/conflict ops and the new
model field, and log the backend defect (should not require
relativePath on update; should fall back on create; 200-on-drop blind
spot) as ask A7 in the-gaps.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATYNi3UKZpQH28AH6tCTup
@Adron
Adron merged commit 16c0d33 into main Sep 2, 2026
1 check passed
@Adron
Adron deleted the fix/documents-sync-relative-path branch September 2, 2026 16:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant