Skip to main content

Module cell_doc

Module cell_doc 

Source
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:

  1. Projects a parsed agent-doc document into an ordered, keyed, per-component model (project_document): for each component occurrence an ordered Vec<(NodeKey, ItemValue)> keyed by the stable component:occurrence:item-id:index node-key scheme.
  2. Computes minimal per-item ops between two document revisions (diff_document) using the lazily reconcile LIS keyed-diff — so a structural edit becomes a handful of Insert/Remove/Move/Update ops scoped to one component, never a whole-component (or whole-document) replace.
  3. Drives a reactive CellTree/CellMap representation (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 durable list_item_key identity (id:<id> / txt:<normalized>), so the same items that the live merge keys on are the items projected here. The crdt module’s PREAMBLE_KEY leading-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§

ByteSpan
A half-open byte span [start, end) into the full source document.
CellConflict
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.
CellDocTree
A reactive, per-item representation of a document: a CellTree root whose children are component-occurrence subtrees, each of whose children are item value cells.
CellMergeOutcome
Outcome of a 3-way per-cell merge.
ComponentDiff
Per-component diff between two document revisions: the component identity plus the minimal lazily op set for its item level.
ComponentOccurrence
One component occurrence projected into an ordered keyed item sequence.
ItemSpan
One projected item plus its full-document byte span. node_key / value mirror ComponentOccurrence::items; span is the lossless source range of value in the full document (so concatenating an occurrence’s spans in order reproduces its body, and the spans tile the body with no gaps).

Enums§

ConflictKind
What kind of same-item-both-sides divergence produced a CellConflict.
ConflictPolicy
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 CellConflict into an operator-visible in-band conflict marker in the merged document (#qcellconflict conflict-surfacing rung).
CELL_MERGE_ENV
Environment variable that gates the live CRDT merge per-cell 3-way path (merge_3way, routed from crate::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. See cell_merge_enabled.
CELL_MERGE_OPCAPTURE_ENV
Environment variable that opts a same-cell both-sides-changed divergence into the op-level TextCrdt 3-way merge (#qcellmerge1 opcapture 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 — see component_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_MARKERS truthy) for debugging/inspection only. As a sub-feature of the per-cell merge stack it is still forced OFF whenever cell_merge_enabled is 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 TextCrdt 3-way merge for same-cell both-sides edits is enabled (#qcellmerge1 opcapture). Default ON with an env kill-switch: only an explicit falsy AGENT_DOC_CELL_MERGE_OPCAPTURE turns it off. As a sub-feature of the per-cell merge stack, it is also forced OFF whenever the master switch cell_merge_enabled is 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 (#qcellmerge1 cutover): the lazily-reconcile-based counterpart of crate::crdt::merge.
merge_3way_with_ops
Op-aware 3-way per-cell merge: build the theirs side 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 as merge_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§

ItemValue
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 by turn_scope/op_log (e.g. queue:0:beta:0).