Expand description
Per-cell document projection (#qcellmerge1 groundwork).
agent-doc’s shipped crdt::merge reconciles concurrent
edits with a whole-document CRDT merge. When two sides touch unrelated
cells the yrs text merge can still splice content across component / item
boundaries — the recurring corruption class where, e.g., a queue head’s text
ends up inside an exchange block.
This module is additive groundwork for the eventual per-cell cutover. It
does not change crdt::merge. Instead it:
- Projects a parsed agent-doc document into an ordered, keyed, per-component
model (
project_document): for each component occurrence an orderedVec<(NodeKey, ItemValue)>keyed by the stablecomponent:occurrence:item-id:indexnode-key scheme. - Computes minimal per-item ops between two document revisions
(
diff_document) using the lazilyreconcileLIS keyed-diff — so a structural edit becomes a handful ofInsert/Remove/Move/Updateops scoped to one component, never a whole-component (or whole-document) replace. - Drives a reactive
CellTree/CellMaprepresentation (CellDocTree) so an edit to one item invalidates only that item’s value cell — the core anti-corruption property that the eventual per-cell merge needs.
§Reuse of the existing parse + key scheme
- Component framing is parsed with
crate::component::parse(the single document-component parser). - Item segmentation reuses the crdt module’s keyed-child splitters
([
crate::crdt::split_list_children] / [crate::crdt::split_exchange_children]) and their durablelist_item_keyidentity (id:<id>/txt:<normalized>), so the same items that the live merge keys on are the items projected here. The crdt module’sPREAMBLE_KEYleading-text child is preserved.
The exposed NodeKey is the component:occurrence:item-id:index string
used throughout turn_scope / op_log (e.g. queue:0:beta:0), where
item-id is the durable child key.
Structs§
- Byte
Span - A half-open byte span
[start, end)into the full source document. - Cell
Conflict - A surfaced 3-way conflict: the same keyed item was updated to different text
on both sides at the SAME lawful lifecycle level. The merge applies a
deterministic policy (ours-wins, see
ConflictPolicy) and records the conflict here so callers never silently lose the losing side. - Cell
DocTree - A reactive, per-item representation of a document: a
CellTreeroot whose children are component-occurrence subtrees, each of whose children are item value cells. - Cell
Merge Outcome - Outcome of a 3-way per-cell merge.
- Component
Diff - Per-component diff between two document revisions: the component identity plus the minimal lazily op set for its item level.
- Component
Occurrence - One component occurrence projected into an ordered keyed item sequence.
- Item
Span - One projected item plus its full-document byte span.
node_key/valuemirrorComponentOccurrence::items;spanis the lossless source range ofvaluein the full document (so concatenating an occurrence’s spans in order reproduces its body, and the spans tile the body with no gaps).
Enums§
- Conflict
Kind - What kind of same-item-both-sides divergence produced a
CellConflict. - Conflict
Policy - How a same-item-both-sides conflict is resolved deterministically. Recorded
in the outcome regardless; the chosen value lands in
merged_text.
Constants§
- CELL_
MERGE_ CONFLICT_ MARKERS_ ENV - Environment variable that opts a recorded same-region
CellConflictinto an operator-visible in-band conflict marker in the merged document (#qcellconflictconflict-surfacing rung). - CELL_
MERGE_ ENV - Environment variable that gates the live CRDT merge per-cell
3-way path (
merge_3way, routed fromcrate::crdt::merge_by_component). Default ON with an env kill-switch: per-cell merge is the production default. Only an explicit falsy value (0/false/off/no) turns it off; absent / empty / any other value ⇒ ON. Seecell_merge_enabled. - CELL_
MERGE_ OPCAPTURE_ ENV - Environment variable that opts a same-cell both-sides-changed divergence
into the op-level
TextCrdt3-way merge (#qcellmerge1opcapture rung).
Functions§
- cell_
merge_ conflict_ markers_ enabled - Whether recorded same-region
CellConflicts are surfaced as operator-visible in-band conflict markers (#qcellconflict). Default OFF as of #provauth1/#provauth4: same-cell conflicts now resolve by component authorship (operator-owned cells keep the operator’s edit, agent-owned cells keep the binary’s — seecomponent_conflict_policy), so an in-band<<<<<<<marker would re-surface an operator action as a suspicious conflict, which is exactly what the provenance principle forbids. The marker machinery is retained behind an explicit opt-in (AGENT_DOC_CELL_MERGE_CONFLICT_MARKERStruthy) for debugging/inspection only. As a sub-feature of the per-cell merge stack it is still forced OFF whenevercell_merge_enabledis disabled. - cell_
merge_ enabled - Whether the per-cell 3-way merge routing seam is enabled. Default ON with
an env kill-switch: per-cell merge is the production default and only an
explicit falsy
AGENT_DOC_CELL_MERGE(0/false/off/no, case/space- insensitive) turns it off. Absent / empty / any non-falsy value ⇒ ON. - cell_
merge_ opcapture_ enabled - Whether the op-level
TextCrdt3-way merge for same-cell both-sides edits is enabled (#qcellmerge1opcapture). Default ON with an env kill-switch: only an explicit falsyAGENT_DOC_CELL_MERGE_OPCAPTUREturns it off. As a sub-feature of the per-cell merge stack, it is also forced OFF whenever the master switchcell_merge_enabledis explicitly disabled — disabling the master kills the whole stack regardless of the sub-gate value. - component_
conflict_ policy - Per-component authorship conflict policy (#provauth1/#provauth4 — operator provenance authority). When the same keyed cell was edited on both sides, the owner of that component wins, deterministically and cleanly — no in-band conflict marker.
- diff_
document - Diff two document revisions into per-component minimal op sets.
- merge_
3way - 3-way per-cell merge (
#qcellmerge1cutover): the lazily-reconcile-based counterpart ofcrate::crdt::merge. - merge_
3way_ with_ ops - Op-aware 3-way per-cell merge: build the
theirsside per cell by replaying the real captured ops routed to that cell (not a text-diff guess), then run the SAME per-cell 3-way join asmerge_3way. - node_
key_ identity - The reorder-stable identity of a node key: everything but the trailing
:index. Two revisions of the same item (same component/occurrence/item-id) share this even if the item moved, so the keyed diff matches them. - project_
document - Project a parsed document body into ordered keyed item sequences, one per component occurrence.
- project_
document_ spans - Project a document into span-aware items, one entry per keyed item across all
component occurrences, in document order. Back-compatible companion to
project_document(which the 26 existing callers/tests keep using). - route_
ops_ to_ cells - Route captured editor ops to the cell (item span) that contains each one,
translating each op’s absolute offset to a cell-local offset
(
op.offset - span.start).
Type Aliases§
- Item
Value - An item’s content: the exact source slice of the keyed child (lossless — concatenating an occurrence’s item values reproduces its component body).
- NodeKey
- The stable per-item identity:
component:occurrence:item-id:index, matching the node-key scheme used byturn_scope/op_log(e.g.queue:0:beta:0).