Skip to main content

Module context_sync

Module context_sync 

Source
Expand description

Hosted context (annotation) sync bridge.

Local context annotations live in per-state Context attachments (ContextBlobs keyed by target). The hosted weft RepositoryService speaks an id-keyed annotation model with the SAME Annotation shape, mutated through the caller-authenticated SetContext/ReviseContext/ SupersedeContext RPCs and read back through ListContext/ GetContextHistory. This module bridges the two, mirroring crate::client::discussion_sync.

A context annotation carries a globally-unique annotation_id, and each revision a revision_id. When the server ships an annotation back on a pull, the pack carries its Annotation verbatim, so a pulled annotation’s local id (and each pulled revision’s id) IS the server id. A locally-authored annotation/revision is minted with a fresh uuid, so its id differs from the server id the RPC assigns. The mirror map records BOTH: local_annotation_id ↔ server_annotation_id and, per annotation, a set of local_revision_id ↔ server_revision_id links.

A prefix COUNT of “revisions synced” is wrong: with two clones concurrently revising one annotation, the linear server list interleaves their revisions, so a count both drops one author’s revision and duplicates the other’s on the next pull. Reconciliation is therefore by revision-id set difference in server order, and pull rebuilds the local revision list to match server order so the “current” revision agrees across clones.

§Reconciliation is author-aware, never body-alone

Where an id link is missing (a lost/rebuilt mirror, or recovering a server-minted id), an unlinked server revision is matched to an unlinked local revision only under an explicit author rule — never body equality alone, which would cross-link two different authors’ identical bodies ("lgtm") and silently drop one:

  • (i) a revision WE pushed — the local revision is self-authored (its attribution equals our local attribution) AND the server stamped it with our own hosted username ("{username} <>"); or
  • (ii) a revision we previously PULLED — the local attribution + created_at exactly equal the server revision’s. Minted-annotation-id recovery after SetContext uses the same author rule (weft returns only a count, not the id): it adopts the annotation at the target whose attribution is our hosted username, whose content matches, and whose id is not already linked.

§Idempotent, crash-safe create

SetContext mints the id server-side and does not return it. Before the RPC the mirror records a pending_create_op (write-ahead, persisted to disk) used as the client_operation_id, so a retry after a crash replays (weft dedup) instead of duplicating, and recovery re-adopts the already-created annotation by author+content among ids NOT already linked — closing the “could not recover minted id” wedge.

§Supersession chains through the mirror

supersedes_annotation_id is a LOCAL id; it is resolved to the SERVER id via the mirror before calling SupersedeContext, so superseding a previously pushed annotation marks the right server annotation superseded (rather than degrading to a plain create that leaves the old one Active on every clone).

§weft#638 limit (degrade gracefully, don’t fix here)

Each RPC advances the server head (context lives per-state); a no-HEAD repo is skipped. The mirror is saved after every annotation and on the error path, collect-and-continue, so one wedged annotation never aborts the rest or orphans a durable write. Scope: annotations only (rm is local-only).

Functions§

pull_context
Fetch hosted annotations for the head and reconcile them into the local Context attachment, rebuilding revision order to match the server.
push_context
Publish local annotations we authored to the hosted RepositoryService.