logseq / logseq/db-test

Copy/paste silently drops child blocks; originals left behind with block/page ≠ parent's block/page (undetected by graph validator)

Open
#1,111 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
No language data
Stars
28
Forks
2
PR merge metrics
No merged PRs in 30d

Description

## Copy/paste silently drops child blocks; originals left behind with mismatched `:block/page`

**Summary**

Copying a block subtree (a "Draft" outline with ~24 top-level Q&A blocks, several nested one level deep) from one page to a newly created page dropped the answer content for 5 of the 24 top-level items. The parent question blocks were copied correctly; their child (answer) blocks were not carried over, and no error or warning was surfaced during the paste.

**What we found, verified via raw DataScript query (not the app UI)**

- Source subtree: a "Draft" block on the `Publish` page (this block itself appears to be a stray leftover — see below) with 24 child blocks (questions), each with 1–3 further children (answers).
- Destination subtree: a "Draft" block correctly created on the new page (`To Those I May Serve`), also with 24 matching top-level question blocks.
- Diffing children counts per matching question block (by title, source vs destination) turned up 5 question blocks on the destination side with **zero** children, where the corresponding source block had 1–3:
- "Introduction" — source 1 child, destination 0
- "Where do I start?" — source 1 child, destination 0
- "Are you selling me something?" — source 1 child, destination 0
- "What is it like working with you?" — source 3 children, destination 0
- "How do I know if this is for me?" — source 2 children, destination 0
- The 19 other question blocks copied correctly with all children intact.
- The "lost" answer blocks were not actually deleted — they still exist in the graph, still `:block/parent`-linked to their original (source-side) question block. But their own `:block/page` reference points to a *different* page (`Purpose of Two Stewards`) than their parent's `:block/page` (`Publish`) — i.e., the child block's page-tag and its parent's page-tag disagree. This mismatch is the likely reason these children didn't get picked up by the copy operation, and also the reason they don't show up under normal tree-reads of the parent (via API tooling that reads by page) even though the raw `:block/parent` link is intact.

**Reproduction conditions (best guess, not fully isolated)**

This happened during a manual cut/paste in the desktop app of a deeply nested outline (multiple heading levels, some collapsed blocks) from one page to a brand-new page, as part of restructuring a large document into smaller constituent pages. We don't have exact repro steps for triggering the page-tag mismatch itself — flagging with what we have rather than withholding until we can isolate a minimal repro.

**Why this matters**

The paste operation gave no indication that any content had failed to transfer — the new page looked structurally complete (same 24 question headings). The only way we caught the loss was diffing child counts programmatically. A user relying on the UI alone would not notice missing content until much later, if ever.

**Logseq's own validator does not catch this**

Running Logseq's built-in graph validation over this same graph, after the bug had already occurred, reports full success:

```
Your graph is valid! {:entities 3393, :pages 398, :blocks 2969, :classes 42, :properties 169, :property-pairs 6175, :objects 365, :datoms 36391}
```

That check appears to only confirm that required attributes are *present* on each block (title, parent, order, page, uuid, timestamps) — not that a child's `:block/page` agrees with its parent's `:block/page`. A present-but-wrong page reference is schema-valid and referentially broken at the same time, so this whole class of corruption is invisible to the validator a user would actually run.

**It's broader than the one page we started with**

We built a small external tool to sweep pages for exactly this invariant (child `:block/page` must match parent's `:block/page`, walked via the non-corrupted `:block/parent` chain rather than the possibly-drifted `:block/page` field). Running it over the `Publish` page in this same graph turned up **14 blocks** with this exact mismatch — not just the 5 we'd found by hand. 9 of them belong to content we hadn't even identified as affected yet (blocks titled "The Thesis," "Fit Criteria," "The Professional Landscape," "The Anomalous Quality," "Two Stewards as Environment," "Drafts (in latest-first order)," "More random blocks," a file-path block, and an unresolved raw-UUID link block) — all still parented correctly, all mistagged with a page other than their parent's.

Sample findings (id = `:db/id`):

```
block_id 5773 "The Thesis" — page=2953 (Purpose of Two Stewards), parent 5813's page=2600 (Publish)
block_id 5855 "Fit Criteria" — page=2953, parent 5813's page=2600
block_id 5840 "The Professional Landscape" — page=2953, parent 5813's page=2600
block_id 5851 "Drafts (in latest-first order)" — page=2953, parent 5798's page=2600
block_id 5868 "More random blocks" — page=2953, parent 5798's page=2600
```

This suggests the drift isn't limited to one unlucky paste — it's accumulated across multiple operations on this graph over time, all with the same signature.

**Ask**

- Should copy/paste (and whatever else can produce this) refuse to proceed, or warn, when a to-be-moved child block's `:block/page` won't match its new parent's page context?
- Is there a known code path where a block's own `:block/page` can drift out of sync with its `:block/parent` chain — during copy/paste of collapsed or deeply nested blocks, or otherwise?
- Should the built-in graph validator check this invariant? Right now it validates attribute presence per block but not parent/child page agreement, so this entire failure mode passes silently.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the desktop app cut/paste case with nested or collapsed blocks, then inspect the built-in graph validation run and the raw DataScript queries described in the issue. Trace where child :block/page values are assigned during the operation. Done means the failure is reproducible and the affected operation or validator reports or prevents parent/child page mismatches.

Written by the indexing model from the issue text.

Assessment

Domain
database
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.