content: tree-wide revision diff (772/772)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m9s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m9s
content/diff + diff-versions enumerated ids top-level only (doc-ids/ doc-find), so diffs of documents with sections missed every nested add/ remove/change. Now via doc-tree-ids + doc-deep-find; sections excluded from :changed (no own content), still reported in :added/:removed. Flat-doc diffs unchanged. +9 store tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,7 @@
|
|||||||
"data": {"pass": 25, "fail": 0},
|
"data": {"pass": 25, "fail": 0},
|
||||||
"wire": {"pass": 11, "fail": 0},
|
"wire": {"pass": 11, "fail": 0},
|
||||||
"validate": {"pass": 23, "fail": 0},
|
"validate": {"pass": 23, "fail": 0},
|
||||||
"store": {"pass": 37, "fail": 0},
|
"store": {"pass": 46, "fail": 0},
|
||||||
"snapshot": {"pass": 20, "fail": 0},
|
"snapshot": {"pass": 20, "fail": 0},
|
||||||
"crdt": {"pass": 34, "fail": 0},
|
"crdt": {"pass": 34, "fail": 0},
|
||||||
"crdt-tree": {"pass": 21, "fail": 0},
|
"crdt-tree": {"pass": 21, "fail": 0},
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
"md-doc": {"pass": 12, "fail": 0},
|
"md-doc": {"pass": 12, "fail": 0},
|
||||||
"fed": {"pass": 20, "fail": 0}
|
"fed": {"pass": 20, "fail": 0}
|
||||||
},
|
},
|
||||||
"total_pass": 763,
|
"total_pass": 772,
|
||||||
"total_fail": 0,
|
"total_fail": 0,
|
||||||
"total": 763
|
"total": 772
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ _Generated by `lib/content/conformance.sh`_
|
|||||||
| data | 25 | 0 | 25 |
|
| data | 25 | 0 | 25 |
|
||||||
| wire | 11 | 0 | 11 |
|
| wire | 11 | 0 | 11 |
|
||||||
| validate | 23 | 0 | 23 |
|
| validate | 23 | 0 | 23 |
|
||||||
| store | 37 | 0 | 37 |
|
| store | 46 | 0 | 46 |
|
||||||
| snapshot | 20 | 0 | 20 |
|
| snapshot | 20 | 0 | 20 |
|
||||||
| crdt | 34 | 0 | 34 |
|
| crdt | 34 | 0 | 34 |
|
||||||
| crdt-tree | 21 | 0 | 21 |
|
| crdt-tree | 21 | 0 | 21 |
|
||||||
@@ -45,4 +45,4 @@ _Generated by `lib/content/conformance.sh`_
|
|||||||
| md-import | 38 | 0 | 38 |
|
| md-import | 38 | 0 | 38 |
|
||||||
| md-doc | 12 | 0 | 12 |
|
| md-doc | 12 | 0 | 12 |
|
||||||
| fed | 20 | 0 | 20 |
|
| fed | 20 | 0 | 20 |
|
||||||
| **Total** | **763** | **0** | **763** |
|
| **Total** | **772** | **0** | **772** |
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
;; replay of its op stream up to a sequence number; the materialised doc is a
|
;; replay of its op stream up to a sequence number; the materialised doc is a
|
||||||
;; cache, never primary state.
|
;; cache, never primary state.
|
||||||
;;
|
;;
|
||||||
;; Requires (loaded by the harness): block.sx, doc.sx, and persist
|
;; Requires (loaded by the harness): block.sx, doc.sx, section.sx (doc-deep-find
|
||||||
;; (event/backend/log/kv/api). The persist backend `b` is opened by the caller
|
;; + doc-tree-ids, for the tree-wide diff), plus persist (event/backend/log/kv/
|
||||||
;; via (persist/open) and injected — content knows nothing about which backend.
|
;; api). The persist backend `b` is opened by the caller via (persist/open) and
|
||||||
|
;; injected — content knows nothing about which backend.
|
||||||
|
|
||||||
(define content/-stream (fn (doc-id) (str "content:" doc-id)))
|
(define content/-stream (fn (doc-id) (str "content:" doc-id)))
|
||||||
|
|
||||||
@@ -69,11 +70,18 @@
|
|||||||
(fn (b doc-id) (map (fn (ev) {:type (persist/event-type ev) :at (persist/event-at ev) :seq (persist/event-seq ev)}) (content/log b doc-id))))
|
(fn (b doc-id) (map (fn (ev) {:type (persist/event-type ev) :at (persist/event-at ev) :seq (persist/event-seq ev)}) (content/log b doc-id))))
|
||||||
|
|
||||||
;; ── diff between two materialised document versions ──
|
;; ── diff between two materialised document versions ──
|
||||||
;; Returns {:added (ids) :removed (ids) :changed (ids)} where changed = ids
|
;; Tree-wide: ids are enumerated across the whole block tree (descending into
|
||||||
;; present in both whose block content differs.
|
;; sections), so nested-block adds/removes/changes are detected, not just
|
||||||
(define
|
;; top-level ones. Returns {:added :removed :changed} (lists of ids):
|
||||||
content/-missing?
|
;; :added — ids present (anywhere) in `new` but not in `old`
|
||||||
(fn (doc id) (= (ct-index-of (doc-blocks doc) id) -1)))
|
;; :removed — ids present (anywhere) in `old` but not in `new`
|
||||||
|
;; :changed — content blocks present in both whose block value differs
|
||||||
|
;; Section containers never appear in :changed (they hold no own content — a
|
||||||
|
;; child change surfaces as that child's own entry); a whole section appearing
|
||||||
|
;; or disappearing shows up in :added / :removed by its id.
|
||||||
|
(define content/-all-ids (fn (doc) (doc-tree-ids doc)))
|
||||||
|
|
||||||
|
(define content/-missing? (fn (doc id) (= (doc-deep-find doc id) nil)))
|
||||||
|
|
||||||
(define
|
(define
|
||||||
content/-changed
|
content/-changed
|
||||||
@@ -83,15 +91,16 @@
|
|||||||
(fn
|
(fn
|
||||||
(id)
|
(id)
|
||||||
(let
|
(let
|
||||||
((bo (doc-find old id)) (bn (doc-find new id)))
|
((bo (doc-deep-find old id)) (bn (doc-deep-find new id)))
|
||||||
(cond
|
(cond
|
||||||
((= bo nil) false)
|
((= bo nil) false)
|
||||||
((= bn nil) false)
|
((= bn nil) false)
|
||||||
|
((= (blk-type bo) "section") false)
|
||||||
((= bo bn) false)
|
((= bo bn) false)
|
||||||
(else true))))
|
(else true))))
|
||||||
(doc-ids old))))
|
(content/-all-ids old))))
|
||||||
|
|
||||||
(define content/diff (fn (old new) {:changed (content/-changed old new) :removed (filter (fn (id) (content/-missing? new id)) (doc-ids old)) :added (filter (fn (id) (content/-missing? old id)) (doc-ids new))}))
|
(define content/diff (fn (old new) {:changed (content/-changed old new) :removed (filter (fn (id) (content/-missing? new id)) (content/-all-ids old)) :added (filter (fn (id) (content/-missing? old id)) (content/-all-ids new))}))
|
||||||
|
|
||||||
;; convenience: diff two persisted versions by seq.
|
;; convenience: diff two persisted versions by seq.
|
||||||
(define
|
(define
|
||||||
|
|||||||
@@ -178,3 +178,31 @@
|
|||||||
"op-log nested delete via content/at seq2"
|
"op-log nested delete via content/at seq2"
|
||||||
(doc-tree-ids (content/at B4 "nest" 2))
|
(doc-tree-ids (content/at B4 "nest" 2))
|
||||||
(list "sec" "n"))
|
(list "sec" "n"))
|
||||||
|
|
||||||
|
;; ── diff is TREE-WIDE: nested-block add/change/remove are detected, and
|
||||||
|
;; section containers never appear in :changed (a top-level-only diff would miss
|
||||||
|
;; "n" entirely and instead flag the section). ──
|
||||||
|
(define dn01 (content/diff-versions B4 "nest" 0 1))
|
||||||
|
(content-test
|
||||||
|
"diff nested added (section + child)"
|
||||||
|
(get dn01 :added)
|
||||||
|
(list "sec" "n"))
|
||||||
|
(content-test "diff nested added removed empty" (get dn01 :removed) (list))
|
||||||
|
(content-test "diff nested added changed empty" (get dn01 :changed) (list))
|
||||||
|
|
||||||
|
(define dn12 (content/diff-versions B4 "nest" 1 2))
|
||||||
|
(content-test
|
||||||
|
"diff nested changed child only"
|
||||||
|
(get dn12 :changed)
|
||||||
|
(list "n"))
|
||||||
|
(content-test "diff nested changed no add" (get dn12 :added) (list))
|
||||||
|
(content-test "diff nested changed no remove" (get dn12 :removed) (list))
|
||||||
|
|
||||||
|
(define dn23 (content/diff-versions B4 "nest" 2 3))
|
||||||
|
(content-test "diff nested removed child" (get dn23 :removed) (list "n"))
|
||||||
|
(content-test "diff nested removed no change" (get dn23 :changed) (list))
|
||||||
|
|
||||||
|
(content-test
|
||||||
|
"diff nested no-op"
|
||||||
|
(get (content/diff-versions B4 "nest" 1 1) :changed)
|
||||||
|
(list))
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ injected adapter, not core.
|
|||||||
|
|
||||||
## Status (rolling)
|
## Status (rolling)
|
||||||
|
|
||||||
`bash lib/content/conformance.sh` → **763/763** (Phases 1–4 COMPLETE + ~34 extensions, hardened: HTML/SX escaping, Markdown render + import/export incl. tables & frontmatter (full round-trip), CvRDT flat + nested-tree + durable replication, tree-aware validation, snapshot cache, doc metadata, plain-text render, nested block trees + deep editing + flatten + relative reorder, doc stats + summary + multi-doc index, table + callout + media blocks, HTML page wrapper + SEO page, doc composition + id-remap, portable data + wire serialization, block query + transforms + find/replace, TOC + anchored headings + outline, normalization)
|
`bash lib/content/conformance.sh` → **772/772** (Phases 1–4 COMPLETE + ~34 extensions, hardened: HTML/SX escaping, Markdown render + import/export incl. tables & frontmatter (full round-trip), CvRDT flat + nested-tree + durable replication, tree-aware validation, snapshot cache, doc metadata, plain-text render, nested block trees + deep editing + flatten + relative reorder, doc stats + summary + multi-doc index, table + callout + media blocks, HTML page wrapper + SEO page, doc composition + id-remap, portable data + wire serialization, block query + transforms + find/replace, TOC + anchored headings + outline, normalization)
|
||||||
|
|
||||||
## Ground rules
|
## Ground rules
|
||||||
|
|
||||||
@@ -113,6 +113,18 @@ lib/content/api.sx ── (content/edit) (content/render) (content/history) ─
|
|||||||
|
|
||||||
## Progress log
|
## Progress log
|
||||||
|
|
||||||
|
- 2026-06-07 — Hardening: `content/diff` (and `content/diff-versions`) are now
|
||||||
|
TREE-WIDE. They enumerated ids via `doc-ids`/`doc-find` (top-level only), so a
|
||||||
|
diff between two versions of a document containing sections silently missed
|
||||||
|
every nested-block add/remove/change — the same class of seam as the by-id
|
||||||
|
op-log bug. Now ids come from `doc-tree-ids` and lookups from `doc-deep-find`,
|
||||||
|
so nested changes surface precisely. Section containers are excluded from
|
||||||
|
`:changed` (they hold no own content; a child change reports as that child),
|
||||||
|
while whole-section add/remove still shows in `:added`/`:removed`. Flat-doc
|
||||||
|
diffs are unchanged (deep == top-level with no sections). +9 store tests
|
||||||
|
(nested add = section+child, nested change = child only, nested remove,
|
||||||
|
no-op). 772/772.
|
||||||
|
|
||||||
- 2026-06-07 — Feature: in-document prose search. `content/search-text` (and
|
- 2026-06-07 — Feature: in-document prose search. `content/search-text` (and
|
||||||
`content/search-text-ids`) return every content block, tree-wide, whose
|
`content/search-text-ids`) return every content block, tree-wide, whose
|
||||||
`(asText b)` contains a term — so search spans text/heading/code/quote/callout
|
`(asText b)` contains a term — so search spans text/heading/code/quote/callout
|
||||||
|
|||||||
Reference in New Issue
Block a user