Skip to content

feat: soft-delete mappings to .trash/ - #141

Draft
kyuchia wants to merge 5 commits into
feat/github-storagefrom
feat/wire-delete-to-dashboard
Draft

feat: soft-delete mappings to .trash/#141
kyuchia wants to merge 5 commits into
feat/github-storagefrom
feat/wire-delete-to-dashboard

Conversation

@kyuchia

@kyuchia kyuchia commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Deleting a mapping from Trash now also removes it from the user's cress-mappings repo when logged in — previously it only deleted the local PouchDB copy, leaving the GitHub file orphaned (found 2026-06-18). Part of #139.

Changes

  • MappingStorage.ts: add deleteRemoteOnly (remote-only delete). It fetches the blob sha from the backend on cache miss, so cross-session deletes work even when the file wasn't opened this session.
  • Dashboard.ts: deleteFileEntry now calls deleteRemoteOnly, then removes the local doc by id. All three permanent-delete paths (Delete, Empty Trash, 30-day cleanup) route through it. Move-to-Trash / Put Back deliberately untouched — files stay recoverable while in Trash.
  • MappingStorage.ts: moveRemote now retries on 409 with backoff (re-probes the dest sha) to handle GitHub's eventual consistency on rapid consecutive trash moves. Mitigates but doesn't fully eliminate the concurrent-move race; full fix (serializing moves) tracked as follow-up.
  • Minor: removed stale issue/meeting refs from githubStorage comments.

Testing (manual, PAT in localStorage)

  • Logged in, remote file exists → deleted on GitHub (verified via commit; cross-session, so the sha fallback ran)
  • Logged in, remote file missing → 404, no delete, no error
  • Logged out → no GitHub request

Notes

@kyuchia kyuchia changed the title feat: wire trash delete to GitHub remote (#139) feat: wire trash delete to GitHub remote Jun 18, 2026
@kyuchia
kyuchia changed the base branch from main to feat/github-storage June 18, 2026 23:54
kyuchia added 2 commits June 22, 2026 16:31
Trashing a mapping now moves it to a .trash/ prefix on the remote rather
than deleting it, so a mapping is never permanently lost; permanent deletes
(Empty Trash / 30-day cleanup) only clear local state, leaving the .trash/
copy recoverable.

- trashRemote / restoreRemote move files to/from .trash/ via copy-then-delete
  (the GitHub contents API has no rename).
- Sync is centralized in moveToFolder so both the button and drag-and-drop
  paths are covered uniformly.
- contentsUrl encodes path segments individually so .trash/ stays a real
  subdir rather than a percent-escaped filename.
- moveRemote retries as an update on conflict to tolerate GitHub eventual
  consistency when re-trashing a just-restored file.

Known follow-ups: folder trash does not recurse into contained files;
same-name files can collide on the flat remote; trash should move to a
server-side primitive once the Worker backend lands.
@kyuchia kyuchia changed the title feat: wire trash delete to GitHub remote feat: soft-delete mappings to .trash/ Jun 22, 2026
@kyuchia

kyuchia commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Update (approach changed): Following the 6/18 meeting decision that the GitHub remote should never be permanently deleted, this PR no longer deletes the remote file. It now soft-deletes by moving the mapping to a .trash/ prefix, and restores by moving it back. Permanent deletes (Empty Trash / 30-day cleanup) only clear local state. The .trash/ copy stays recoverable.

The original description below describes the earlier delete-outright approach, which has been reversed.

What's in the latest commits:

  • trashRemote / restoreRemote move files to/from .trash/ via copy-then-delete (the contents API has no rename).
  • Sync is centralized in moveToFolder, so both the button and drag-and-drop paths are handled.
  • contentsUrl encodes path segments individually so .trash/foo.csv stays a real subdir.
  • moveRemote retries on conflict to tolerate eventual consistency when re-trashing a just-restored file.

Verified end-to-end: save → trash → .trash/; restore → back to top level; permanent delete → local cleared, .trash/ copy retained.

@kyuchia

kyuchia commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator Author

Added 409-retry in moveRemote for GitHub's eventual consistency on rapid consecutive trash moves, where a write can hit a stale sha.

This mitigates but doesn't fully eliminate the race. The root cause is concurrent fire-and-forget moves from moveToFolder; a full fix (serializing moves) is tracked as follow-up.

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