Skip to content

Use a native graph list with scoped local copy deletion - #79

Merged
RCmerci merged 9 commits into
mainfrom
codex/switch-graph-local-copy-20261015
Oct 16, 2026
Merged

RCmerci merged 9 commits into
mainfrom
codex/switch-graph-local-copy-20261015

Conversation

@RCmerci

@RCmerci RCmerci commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Switch graph now keeps the active graph attached and presents a native List across the whole screen, with Timeline-style floating close, refresh and existing account controls. The current graph remains in its catalog position; names switch graphs and each row's menu offers Delete local copy. Long names wrap and the native list supports accessibility Dynamic Type.

Deletion names the graph and explains that unsynced changes, drafts and attachments on this device will be lost while the cloud copy remains downloadable. The user explicitly authorized discarding unsynced content. The current graph must be switched away from first; persisted account ownership, catalog authorization and generation checks fence every request.

The worker acquires mirror ownership before preparing attachment retirement, drains target operations/leases, captures exact upload checkpoints and persists a recovery journal. Mirror commit has durable, retirement-specific evidence; pending collection retries without touching replacement files. Before commit, cancellation restores only the captured namespace through an atomic no-replace rename. Late completions and duplicate/replayed requests retain exact account/graph/retirement identity. Journal I/O failures return a retryable deletion error while preserving the current Worker; catalog refresh loading/failure is visible, including after an earlier local-copy error.

The old Delete_graph_assets, Database.delete_mirror and graph-wide upload-checkpoint delete APIs and callers are removed without compatibility wrappers. Delete_account_assets remains. The two protected deletion specs were explicitly approved; Dune, lockfiles and shared opam/pins are unchanged.

Validation:

  • Full repository compilation and native-object prerequisites pass. All 21 test suites / 834 cases and standalone source-boundary checks pass. New regressions were observed RED before the owner fixes and GREEN afterward.
  • Real iPhone 13 / iOS 26.1 Simulator, isolated synthetic data only: native XCUITest checks headerless list/current marker, name switching, close/session retention, long names/accessibility XXXL scrolling and menu reachability, current-copy protection, cancellation, real SQLite ownership-busy failure/retry, retained Not downloaded row and real-worker snapshot download.
  • Real UI text and Photos captures produced 4 durable outbox records and 1 scoped upload intent. Actual menu confirmation removed the target directory and intent while retaining the other graph. Real-worker redownload restored the fixture baseline with zero outbox records and no discarded content. The two primary XCUITest cases passed together; the Photos case passed separately after correcting its test-only native-image locator.
  • Download uses the real worker against a controlled loopback TLS fixture; this does not claim live remote-service validation. The repository Apple build entry point and pinned read-only LUI source are used. Host ARM64 OCaml restamping is experimental, with cross_abi_validated=false.
  • Independent session applied the repository journal-code-review skill to exact head daf9073. Reported issues were reproduced and fixed; final review found no new evidenced reportable findings.

All generated hosts, screenshots, reports and build artifacts remain outside Git. No personal graph, phone, real credentials or remote graph deletion was used.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T10:28:48.350948Z 0e3075c PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0e3075c614

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread logseq_overlay_db/lib/database.ml Outdated
Comment on lines +738 to +740
Unix.rename graph_directory retired;
ignore (Ownership.release ownership);
remove_mirror_tree retired;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore the mirror when tombstone removal fails

If remove_mirror_tree retired raises after the rename—for example because of an I/O or permission error—the function reports deletion failure but leaves the mirror under the .deleted-* name. The original UUID path is then absent, so a retry treats the copy as already deleted and never removes that tombstone; the graph is unavailable despite the reported failure and its storage is leaked. Restore the directory on cleanup failure or persist and retry tombstone cleanup.

Useful? React with 👍 / 👎.

Comment on lines +116 to +117
if waiter.request <> None && snapshot.local_deletion = None
then finish (match snapshot.last_error with None -> Ok () | Some message -> Error message)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve only the matching deletion completion

When a concurrent catalog refresh removes the currently selected graph while another graph's deletion is in flight, clear_selected_graph_for_catalog clears local_deletion and last_error without changing the account generation. This condition therefore resolves the deletion promise as Ok () before the matching Mirror_deleted terminal arrives; the worker then rejects the now-unauthorized cleanup, while the application marks the target as not downloaded. Resolve the waiter only from the terminal event for its captured deletion request.

Useful? React with 👍 / 👎.

Comment thread app/application.ml
Comment on lines +4457 to +4459
let owned = match state.manager with
| Some manager -> manager.startup.account_generation = request.Graph_service.account_generation
&& (if deleting then state.local_copy_deleting = Some request else (not confirm || state.local_copy_check = Some request))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fence background inspections superseded by deletion

If a visibility-triggered inspection is still in flight when the user performs the explicit check and confirms deletion, the requests can overlap because the automatic request is not represented by local_copy_check. A late automatic response is always considered owned through not confirm, so a pre-deletion Downloaded result can arrive after successful deletion and overwrite the Not_downloaded state, leaving the row incorrect and re-enabling deletion. Ignore inspection results once that graph has entered or completed a newer deletion, or fence them with a per-graph generation.

Useful? React with 👍 / 👎.

Comment on lines +1609 to +1612
let* () = asset_unit t
~scope:{ Sync.account = request.account; graph_id = request.graph_id; graph_generation = 0 }
Sync.Delete_graph_assets |> Result.map_error (fun _ ->
"The local copy is still in use. Close attachment previews and try again.") in

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve graph assets until mirror deletion is committed

When mirror removal fails after this call—for example because another process acquires the mirror ownership lock, authorization changes at the following current check, or the filesystem rejects the rename—the function returns an error while Delete_graph_assets has already permanently removed cached attachments and staged upload sources. The retained mirror is therefore no longer a retryable intact copy, and pending uploads can lose their source files despite the UI reporting that deletion failed. Defer destructive asset cleanup until the mirror has been safely retired, or restore it as part of a transactional rollback.

Useful? React with 👍 / 👎.

@RCmerci
RCmerci merged commit 1c4ce39 into main Oct 16, 2026
2 checks passed
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