Skip to content

Replace sync soft-deletes with hard deletes - #184

Open
faetalize wants to merge 1 commit into
mainfrom
feat/message-deletion-cleanup
Open

Replace sync soft-deletes with hard deletes#184
faetalize wants to merge 1 commit into
mainfrom
feat/message-deletion-cleanup

Conversation

@faetalize

@faetalize faetalize commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replace sync soft-delete updates with hard deletes in Sync.service.ts.
  • Remove tombstone retention/deletion bookkeeping from the sync flow.

Related: #182

Validation

  • Not run in this session; PR opened from the existing pushed branch.

Stop tombstoning synced rows (deleted=true) and DELETE them instead, so
deletions are committed immediately rather than relying on the 24h
cleanup_sync_tombstones reaper.

- markDeletedMessages -> deleteSyncedMessagesFrom: ranged DELETE instead
  of UPDATE deleted=true (same chunked/bounded logic).
- deleteSyncedChat: single ranged DELETE of messages (messages first,
  then the chat row, so a failure stays retryable) instead of the
  maxIndex read + chunked soft-delete.
- deleteSyncedPersona: DELETE instead of UPDATE deleted=true.

Existing DELETE RLS policies already permit this; no schema changes.
Read filters on deleted=false are left in place to keep hiding any
legacy tombstones until the cron drains them.

@faetalize faetalize left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found one blocking issue in this PR.

The change switches chat/persona deletes from tombstones to hard deletes, but the pull path still relies on fetched deleted = true rows to propagate removals to other local stores. With the new behavior, a device that already has a local chat/persona copy will never see a remote tombstone after another device deletes it, so the local copy can remain stale and later be resurrected by an edit or sync. Either keep the tombstone contract until there is a replacement reconciliation path, or add explicit local-vs-remote ID reconciliation before hard deleting old rows.

// ── Delete the chat row ────────────────────────────────────────
const { error: chatError } = await supabase
.from("user_synced_chats")
.delete()

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: hard-deleting the user_synced_chats row removes the tombstone that pullChats() currently depends on. pullChats() only deletes local IndexedDB copies when it fetches a row with deleted === true; if this row is gone, another device with an existing local copy has no deletion signal and can retain or later resurrect the chat. Keep the soft-delete tombstone here, or add a replacement reconciliation path that removes local IDs absent from the remote set before hard deletes are allowed.

const { error } = await supabase
.from("user_synced_personas")
.update({ deleted: true })
.delete()

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same propagation issue for personas: pullPersonas() only removes a local persona when it receives a remote row marked deleted. Hard-deleting this row means other devices with cached/local persona data never receive that signal. Please preserve the tombstone behavior or introduce explicit local/remote reconciliation for missing persona IDs.

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