docs: record the parentage merge landing in ADR 0049, and what is left in TODO
Build Debug APK / build (push) Successful in 3m59s
Build Debug APK / build (push) Successful in 3m59s
This commit is contained in:
@@ -554,6 +554,14 @@ How it works: `rust/lib/src/stores/lock.rs` and
|
||||
`docs/adr/0018-agent-settings-documents.md` solved for Notes.
|
||||
|
||||
### Documents
|
||||
- **Concurrent moves can still disagree about which one survived.**
|
||||
`parent_modified_at` merges a reparent last-write-wins, and both `set_parent`
|
||||
and `sync_receive_parent` refuse to *store* a cycle, so every tree walk
|
||||
terminates. What is not converged is the case where two nodes each make a
|
||||
locally legal move that is only a cycle together: each keeps its own and
|
||||
drops the other's, until someone makes a newer move. Converging it wants a
|
||||
move log with undo/redo by timestamp (Kleppmann et al., *A highly-available
|
||||
move operation for replicated trees*). See ADR 0049.
|
||||
- Look at the Papra open-source project for document UI design ideas; also PdfDing
|
||||
- Some documents get misleading titles when imported naively (e.g. comics) — importers
|
||||
should produce sensible titles, since those are what the Documents view shows
|
||||
|
||||
@@ -121,19 +121,24 @@ not have to be "parentage becomes an edge."
|
||||
structural fields are written once, on first arrival, and never updated from
|
||||
a peer. `DocumentStore::set_parent` therefore has no propagation at all:
|
||||
`NotebookManager::move_item` moves a note on the node the user is sitting
|
||||
at, and the ensemble never learns. That is a live gap, not a hypothetical
|
||||
one, and this decision inherits it. The fix is a merge rule for the field —
|
||||
a `parent_modified_at` reconciled last-write-wins is the smallest thing that
|
||||
matches how the rest of the record behaves — and it is now the outstanding
|
||||
work this ADR points at.
|
||||
- **Acyclicity is the field's invariant to enforce, and nothing enforces it.**
|
||||
`set_parent` writes whatever it is given; `NotebookManager::move_item`
|
||||
checks only that the destination is a collection, not that it is outside the
|
||||
moved subtree. Moving a collection into its own descendant produces a cycle,
|
||||
and `apply_tombstone`/`delete_recursive` recurse over children with no
|
||||
visited set. An edge model would not have fixed this either — it is
|
||||
pre-existing — but owning the field means owning the invariant, and it
|
||||
should be checked where the move is made rather than survived downstream.
|
||||
at, and the ensemble never learns. That was a live gap, not a hypothetical
|
||||
one, and this decision inherited it. **Closed on the same day this ADR was
|
||||
written**: `documents.parent_modified_at` is the field's own merge stamp and
|
||||
`DocumentStore::sync_receive_parent` reconciles a peer's move
|
||||
last-write-wins, declining when our stamp is newer, when the destination has
|
||||
not reached us yet, or when the move would close a cycle.
|
||||
- **Acyclicity is the field's invariant to enforce**, and it is now enforced
|
||||
where a single node can decide it: `set_parent` refuses a move into the
|
||||
document's own descendant. What one node cannot decide is two nodes making
|
||||
moves that are a cycle only once merged; there the merge path refuses to
|
||||
store the second and logs it, which keeps every tree walk terminating —
|
||||
`apply_tombstone`, `delete_recursive` and `collect_ancestor_collections` all
|
||||
recurse without a visited set — at the cost of the two nodes disagreeing
|
||||
about which move survived until someone makes a newer one. Converging that
|
||||
properly needs a move log with undo/redo by timestamp (Kleppmann et al., *A
|
||||
highly-available move operation for replicated trees*) and is deliberately
|
||||
not built. An edge model would not have fixed this either: it is a property
|
||||
of concurrent tree moves, not of how the link is stored.
|
||||
- **Query shape is unchanged**: `idx_documents_parent_id` serves
|
||||
`list_children`, `only_roots` stays `parent_id IS NULL`, and no tree query
|
||||
gains a join.
|
||||
|
||||
Reference in New Issue
Block a user