;; Phase 4 — federated documents: trust-gated peer ops + concurrent-external- ;; edit conflict resolution via the CRDT. (st-bootstrap-classes!) (content-bootstrap-blocks!) (content-bootstrap-doc!) (content-bootstrap-render!) (define same? (fn (a b) (= (get a :elements) (get b :elements)))) ;; base shared document, then a local edit (define base (crdt-insert (crdt-insert (crdt-empty) "h" "heading" (crdt-pos 1 0) (list (list "level" 1) (list "text" "T")) 1 0) "p" "text" (crdt-pos 2 0) (list (list "text" "Body")) 1 0)) (define local (crdt-update base "p" "text" "local" 5 1)) ;; ── provenance ── (content-test "authored tags author" (get (content/authored (crdt-op-delete "h") "ed") :author) "ed") (content-test "signed tags sig" (get (content/signed (crdt-op-delete "h") "ed" "sig1") :sig) "sig1") (content-test "trusted? yes" (content/trusted? (list "ed" "al") "ed") true) (content-test "trusted? no" (content/trusted? (list "ed") "mal") false) ;; peer ops: ed is trusted, mal is not (define peer-ops (list (content/authored (crdt-op-update "p" "text" "peer-ed" 7 2) "ed") (content/authored (crdt-op-insert "x" "text" (crdt-pos 3 0) (list (list "text" "X")) 8 2) "ed") (content/authored (crdt-op-delete "h") "mal"))) (define res (content/merge-peer local (list "ed") peer-ops)) ;; ── trust gate: only ed's ops applied ── (content-test "accepted count" (len (content/accepted res)) 2) (content-test "rejected count" (len (content/rejected res)) 1) (content-test "rejected is mal's" (get (first (content/rejected res)) :author) "mal") ;; ── resulting document ── (define rdoc (crdt-materialize "d" (content/peer-state res))) (content-test "untrusted delete blocked: h survives" (doc-has? rdoc "h") true) (content-test "trusted insert applied: x present" (doc-has? rdoc "x") true) (content-test "result order" (doc-ids rdoc) (list "h" "p" "x")) (content-test "trusted edit wins (ts7 > ts5)" (str (blk-send (doc-find rdoc "p") "text")) "peer-ed") ;; ── order-independence of accepted peer ops ── (define res-rev (content/merge-peer local (list "ed") (reverse peer-ops))) (content-test "peer merge order-independent" (same? (content/peer-state res) (content/peer-state res-rev)) true) ;; ── trust = nobody → nothing applied, state unchanged ── (define res0 (content/merge-peer local (list) peer-ops)) (content-test "no trust accepts none" (len (content/accepted res0)) 0) (content-test "no trust rejects all" (len (content/rejected res0)) 3) (content-test "no trust state unchanged" (same? (content/peer-state res0) local) true) ;; ── pluggable predicate gate (acl-on-sx hook) ── (define res-pred (content/merge-peer-with local (fn (op) (= (get op :author) "ed")) peer-ops)) (content-test "predicate gate == list gate" (same? (content/peer-state res-pred) (content/peer-state res)) true) ;; ── conflict on concurrent external edit: local vs external, same field ── ;; external (peer) state edits p concurrently with a later ts; CRDT reconciles. (define external (crdt-update base "p" "text" "external" 9 2)) (content-test "conflict LWW deterministic" (str (blk-send (doc-find (crdt-materialize "d" (crdt-merge local external)) "p") "text")) "external") (content-test "conflict merge commutes" (same? (crdt-merge local external) (crdt-merge external local)) true) (content-test "conflict merge idempotent" (same? (crdt-merge (crdt-merge local external) external) (crdt-merge local external)) true) ;; concurrent external edit with LOWER ts loses to local (define external-old (crdt-update base "p" "text" "stale" 3 2)) (content-test "older external loses to local" (str (blk-send (doc-find (crdt-materialize "d" (crdt-merge local external-old)) "p") "text")) "local")