diff --git a/corpus/paragraph-edit/README.md b/corpus/paragraph-edit/README.md
index 162562834f34293f17959d97333e96bee9d54e71..a8ed082d25ee9a53dda45247767161dabf90562a 100644
--- a/corpus/paragraph-edit/README.md
+++ b/corpus/paragraph-edit/README.md
@@ -60,3 +60,11 @@ XML, preserve exact child/content identities across reopening, and compare
the native-rendered result to the corresponding keyboard-generated control.
`export_native_paragraph_joins` regenerates candidates in a new directory specified
by `ONESTORE_PARAGRAPH_JOIN_OUTPUT`.
+
+`offline` retains reconciled split/join candidates and cold native captures.
+The cache queues edits before observing independent remote `☂` prefixes, reopens
+between acknowledgements, and confirms modeled lost replies without replaying
+operations. Tests compare native text/styles to the original keyboard controls,
+account for the remote prefixes, and retain exact graph identities after reopening.
+The ignored offline test `export_native_offline_paragraphs` regenerates candidates
+in a new directory specified by `ONESTORE_OFFLINE_PARAGRAPH_OUTPUT`.
diff --git a/corpus/paragraph-edit/offline/inheritance/candidate/synthetic.one b/corpus/paragraph-edit/offline/inheritance/candidate/synthetic.one
new file mode 100644
index 0000000000000000000000000000000000000000..e64cafc0ec2e467c73f2709679fe0acfda707323
Binary files /dev/null and b/corpus/paragraph-edit/offline/inheritance/candidate/synthetic.one differ
diff --git a/corpus/paragraph-edit/offline/inheritance/manifest.json b/corpus/paragraph-edit/offline/inheritance/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..8905aa0adf6878941da6efe2ffbf7aebf840af61
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/manifest.json
@@ -0,0 +1,62 @@
+[
+ {
+ "case": "Join inherited styles",
+ "id": 1,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{10952FC5-A849-0DE5-03A7-5D9304627DA7},44",
+ "right": "{10952FC5-A849-0DE5-03A7-5D9304627DA7},50"
+ },
+ "remote_prefix": true,
+ "revision": "{E109048E-057D-41DC-B2A8-B9F11839B9C9},1",
+ "space": "{D6CEB12D-D1B7-033B-20A2-E9B6FF501602},1"
+ },
+ {
+ "case": "Join empty left tag",
+ "id": 2,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{8BAFC64A-8457-0253-3906-57D27F8546B7},45",
+ "right": "{8BAFC64A-8457-0253-3906-57D27F8546B7},51"
+ },
+ "remote_prefix": false,
+ "revision": "{19A52AE8-D897-467C-956F-26CA42CEDA1B},1",
+ "space": "{F6E7A6F0-805F-0858-2B5A-4B1260940CF9},1"
+ },
+ {
+ "case": "Join right tag",
+ "id": 3,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{2E9A20A9-1E0E-0F04-0FCE-8497CF032068},44",
+ "right": "{2E9A20A9-1E0E-0F04-0FCE-8497CF032068},49"
+ },
+ "remote_prefix": true,
+ "revision": "{BDDFFFE4-439A-4466-9D85-20B88D5A5E61},1",
+ "space": "{04EE80E5-584B-04E0-1F05-07D86902DFA1},1"
+ },
+ {
+ "case": "Join both tags",
+ "id": 4,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{7A5195E3-F792-0C49-1F3C-EBBFCE774FE6},44",
+ "right": "{7A5195E3-F792-0C49-1F3C-EBBFCE774FE6},50"
+ },
+ "remote_prefix": true,
+ "revision": "{694A9A93-E1AA-48DD-AD67-6BD2C0EF5D6F},1",
+ "space": "{631A0B54-DBB1-0988-22E1-96E8B1202B59},1"
+ },
+ {
+ "case": "Join both children",
+ "id": 5,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{0384F5F6-5F0B-0164-32C6-6E97A4F1B661},49",
+ "right": "{0384F5F6-5F0B-0164-32C6-6E97A4F1B661},54"
+ },
+ "remote_prefix": true,
+ "revision": "{B3100F21-58B5-4417-9797-B397AC0F7BE3},1",
+ "space": "{D7D982D1-A3D3-0690-1181-A9703CF717E5},1"
+ }
+]
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/inheritance/native/notebook/Open Notebook.onetoc2 b/corpus/paragraph-edit/offline/inheritance/native/notebook/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..8ecc576e8f13a07ffe261f2fdbe028a37cb11351
Binary files /dev/null and b/corpus/paragraph-edit/offline/inheritance/native/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/paragraph-edit/offline/inheritance/native/notebook/synthetic.one b/corpus/paragraph-edit/offline/inheritance/native/notebook/synthetic.one
new file mode 100644
index 0000000000000000000000000000000000000000..8b4bece77818ba8f070380e73523c35b9ab5b19a
Binary files /dev/null and b/corpus/paragraph-edit/offline/inheritance/native/notebook/synthetic.one differ
diff --git a/corpus/paragraph-edit/offline/inheritance/native/read/environment.json b/corpus/paragraph-edit/offline/inheritance/native/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..9ce6d758f071c4bb49737eb8104c0ee15ce86363
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-6061365E",
+ "cold": true,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/paragraph-edit/offline/inheritance/native/read/hierarchy.xml b/corpus/paragraph-edit/offline/inheritance/native/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..92079e1969cd7a99c75f19db52f7e085a591b431
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/paragraph-edit/offline/inheritance/native/read/page-000.xml b/corpus/paragraph-edit/offline/inheritance/native/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7ec799793802bacdd3ab47100a46821273f490bd
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/read/page-000.xml
@@ -0,0 +1,5 @@
+
+☂LeftBold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/inheritance/native/read/page-001.xml b/corpus/paragraph-edit/offline/inheritance/native/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e7da941a9f1ab49daedf6b4599443ae39a0269b6
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/read/page-001.xml
@@ -0,0 +1,5 @@
+
+☂LeftBold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/inheritance/native/read/page-002.xml b/corpus/paragraph-edit/offline/inheritance/native/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9f7ebac7baae2c38efceca18ccd5d33d6fa668e9
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/read/page-002.xml
@@ -0,0 +1,20 @@
+
+Bold 🦀 italic é color 東京
+End]]>Fictitious: café, 東京, مرحبا]]>
diff --git a/corpus/paragraph-edit/offline/inheritance/native/read/page-003.xml b/corpus/paragraph-edit/offline/inheritance/native/read/page-003.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b4d1f59c82406908740081c4710a489002085ac2
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/read/page-003.xml
@@ -0,0 +1,5 @@
+
+☂LeftRight italic]]>
diff --git a/corpus/paragraph-edit/offline/inheritance/native/read/page-004.xml b/corpus/paragraph-edit/offline/inheritance/native/read/page-004.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2f7e50d49be4739acae06d0e4fb7c70663d60644
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/read/page-004.xml
@@ -0,0 +1,5 @@
+
+☂Left childBold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/inheritance/native/read/page-005.xml b/corpus/paragraph-edit/offline/inheritance/native/read/page-005.xml
new file mode 100644
index 0000000000000000000000000000000000000000..82c73e5a1daf3735d8e9a40de6251a3f839b6a89
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/read/page-005.xml
@@ -0,0 +1,5 @@
+
+Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/inheritance/native/read/payloads.json b/corpus/paragraph-edit/offline/inheritance/native/read/payloads.json
new file mode 120000
index 0000000000000000000000000000000000000000..787b4bae85038cda922f8befac6c8ed361fa8035
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/read/payloads.json
@@ -0,0 +1 @@
+../../../../before/read/payloads.json
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/inheritance/native/run.json b/corpus/paragraph-edit/offline/inheritance/native/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..2fbf78ac8b624e29cbc1ef5a140dc7bb0c388002
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/run.json
@@ -0,0 +1,18 @@
+{
+ "notebook": "/Users/clo/dev/one/evidence/m10/offline-paragraph-native-01/inheritance/candidate",
+ "expected_pages": 6,
+ "author": null,
+ "author_timeout_seconds": 600,
+ "inspect": false,
+ "collect_notebook": true,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41"
+ }
+}
diff --git a/corpus/paragraph-edit/offline/inheritance/native/source.json b/corpus/paragraph-edit/offline/inheritance/native/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..02e8e0326bf665e52ba695484dafd587f263b7c0
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/source.json
@@ -0,0 +1,8 @@
+[
+ {
+ "path": "synthetic.one",
+ "bytes": 59016,
+ "sha256": "828e330b0dfda41ecd5ade77ea65398cf5a070170757b3f650ca4600520d99bb",
+ "mtime_ns": 1788863184799153800
+ }
+]
diff --git a/corpus/paragraph-edit/offline/inheritance/native/teardown.json b/corpus/paragraph-edit/offline/inheritance/native/teardown.json
new file mode 120000
index 0000000000000000000000000000000000000000..47a1192a30ea7da90676d631bb9a2687d829b43e
--- /dev/null
+++ b/corpus/paragraph-edit/offline/inheritance/native/teardown.json
@@ -0,0 +1 @@
+../../../joined/teardown.json
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/joins/candidate/synthetic.one b/corpus/paragraph-edit/offline/joins/candidate/synthetic.one
new file mode 100644
index 0000000000000000000000000000000000000000..b3fefe96ad2e3c9de97a136f760b521bcb11bda4
Binary files /dev/null and b/corpus/paragraph-edit/offline/joins/candidate/synthetic.one differ
diff --git a/corpus/paragraph-edit/offline/joins/manifest.json b/corpus/paragraph-edit/offline/joins/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..9931ce4a575276ec439d92ff937a49dfcf9b79f1
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/manifest.json
@@ -0,0 +1,146 @@
+[
+ {
+ "case": "Split middle",
+ "id": 1,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{DFE950EF-6BEB-0F5B-2403-7B74F1AE5002},44",
+ "right": "{0BA342F5-0690-087C-24FE-8EE6D81C2041},12"
+ },
+ "remote_prefix": true,
+ "revision": "{D3E40C30-D3F7-48FC-8163-10191C9874A2},1",
+ "space": "{FE224563-B517-023E-0D75-9904B1952A26},1"
+ },
+ {
+ "case": "Split style boundary",
+ "id": 2,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{5FB484E2-1E3E-0A9D-1989-271A3EBBEC42},44",
+ "right": "{051BC1DE-5AE6-0D04-0CEF-9D05F53C8EAE},12"
+ },
+ "remote_prefix": true,
+ "revision": "{B4AD652D-1152-4BFB-9F7C-3F5A37D526CC},1",
+ "space": "{DED48A05-0DB1-0712-086D-09237C982341},1"
+ },
+ {
+ "case": "Split start",
+ "id": 3,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{012ED94B-43B3-079A-3050-B1CBE25C1C09},44",
+ "right": "{B230A510-9428-0CE7-1B89-7CA970F8F676},12"
+ },
+ "remote_prefix": false,
+ "revision": "{14C83ACF-0D2F-4D6C-8A85-D4B883407F57},1",
+ "space": "{7919E933-0809-00D8-1754-A21684215860},1"
+ },
+ {
+ "case": "Split end",
+ "id": 4,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{DAF723A3-C2A3-0798-2EAB-34613166A794},44",
+ "right": "{601DAE0A-2A53-0C28-3077-1FE8FCB26FC2},12"
+ },
+ "remote_prefix": true,
+ "revision": "{BAE72407-55A9-4703-922B-67E6B8ADE435},1",
+ "space": "{927F2809-173C-036E-37B6-60A6568F7723},1"
+ },
+ {
+ "case": "Split empty",
+ "id": 5,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{0B467F31-D287-08CD-09F4-A6FBB2F5418F},45",
+ "right": "{EACB4A2C-2444-0A1F-03D6-68187F2FB345},12"
+ },
+ "remote_prefix": false,
+ "revision": "{28937B4C-67BC-4ED8-A531-E63A6FB99F38},1",
+ "space": "{E4D8C4CC-508B-04D2-36CE-C5B8C9D2FEC4},1"
+ },
+ {
+ "case": "Split parent",
+ "id": 6,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{3860EA13-D444-0598-35D6-CF96769C6A21},44",
+ "right": "{C84CA5DB-61A8-0F9B-3F91-D6E0C74536F7},12"
+ },
+ "remote_prefix": true,
+ "revision": "{E154B8F7-C84E-4AED-B436-D610448E37F1},1",
+ "space": "{7A363D4C-73C2-0ABE-0931-E6F62A90B95E},1"
+ },
+ {
+ "case": "Split child",
+ "id": 7,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{B587CBC6-0BCC-0A23-16F6-E1B0B3C5D061},49",
+ "right": "{DC240C63-FC8E-0267-0AFD-A53062C25702},12"
+ },
+ "remote_prefix": true,
+ "revision": "{01D54762-D53E-4DF2-AD44-7789FAE785CE},1",
+ "space": "{3CC2C4DC-651A-07C9-2F7B-375C6663F3CA},1"
+ },
+ {
+ "case": "Split bullet",
+ "id": 8,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{8C65E427-477C-02ED-1807-2F10E277019D},44",
+ "right": "{28F3F5EC-E687-02CA-2CFD-E73A184B875F},12"
+ },
+ "remote_prefix": true,
+ "revision": "{BE53841B-A5D6-447A-B1F3-C1E2D11EBBCE},1",
+ "space": "{942EF5E4-FFD1-0FE4-23D2-7429899577BD},1"
+ },
+ {
+ "case": "Split number",
+ "id": 9,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{D4ABBE4F-2165-0122-1F9F-ACE3FE92C703},44",
+ "right": "{DD3FA0E7-15A6-01C4-1B77-F85F779BCBC8},12"
+ },
+ "remote_prefix": true,
+ "revision": "{53CAB283-B0D5-4D3A-B325-9FD8B214FE21},1",
+ "space": "{A4AE40FF-DD80-0003-2856-17D8295C1B91},1"
+ },
+ {
+ "case": "Split tag",
+ "id": 10,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{37AA5877-2980-0227-169F-8B1E8EE0795A},44",
+ "right": "{1480427A-E9ED-04EC-0409-0653AE11FF78},12"
+ },
+ "remote_prefix": true,
+ "revision": "{BC2C9E0D-BA56-48ED-8510-CFBF7FCBDEED},1",
+ "space": "{739DB38B-4239-0A7C-2652-4E525D219638},1"
+ },
+ {
+ "case": "Split cell",
+ "id": 11,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{F0832BC9-1578-0A96-08AF-99344644FFB8},50",
+ "right": "{1E353497-5C26-0459-3C0C-0E8D29ED8C5A},12"
+ },
+ "remote_prefix": true,
+ "revision": "{392FD304-233C-4A9C-A90E-16EA8F916073},1",
+ "space": "{4432E3EC-5E5F-0A04-2108-E9A2C17BDBEC},1"
+ },
+ {
+ "case": "Split soft break",
+ "id": 12,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{88189BA1-2F19-0282-05ED-A0B83153F143},44",
+ "right": "{C1568E82-003F-016B-041A-CA304E1A5138},12"
+ },
+ "remote_prefix": true,
+ "revision": "{D3D41323-E06E-4253-BAA5-B69A3AAA40E5},1",
+ "space": "{D8E15673-A45E-0914-291A-7767C3222F19},1"
+ }
+]
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/joins/native/notebook/Open Notebook.onetoc2 b/corpus/paragraph-edit/offline/joins/native/notebook/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..dbc25757edc4679de12642bd8cb897bfff94488f
Binary files /dev/null and b/corpus/paragraph-edit/offline/joins/native/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/paragraph-edit/offline/joins/native/notebook/synthetic.one b/corpus/paragraph-edit/offline/joins/native/notebook/synthetic.one
new file mode 100644
index 0000000000000000000000000000000000000000..58dbfc67d9a1df59104476713767fd5062c96b2f
Binary files /dev/null and b/corpus/paragraph-edit/offline/joins/native/notebook/synthetic.one differ
diff --git a/corpus/paragraph-edit/offline/joins/native/read/environment.json b/corpus/paragraph-edit/offline/joins/native/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..552ef9a6189225c2022dccd1e57702c7939ff4da
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-37FF299E",
+ "cold": true,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/paragraph-edit/offline/joins/native/read/hierarchy.xml b/corpus/paragraph-edit/offline/joins/native/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9a5218400d4c87069f2b31b2c580618a7d756a3d
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-000.xml b/corpus/paragraph-edit/offline/joins/native/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9201082b94066f46a988d536c2ef7494840dfbf2
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-000.xml
@@ -0,0 +1,5 @@
+
+☂Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-001.xml b/corpus/paragraph-edit/offline/joins/native/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..62fe84b91cb277cec1462d1a02afd2446f6dee21
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-001.xml
@@ -0,0 +1,3 @@
+
+
+soft break]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-002.xml b/corpus/paragraph-edit/offline/joins/native/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bf17bb70560f2566974524307321791177e331a1
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-002.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-003.xml b/corpus/paragraph-edit/offline/joins/native/read/page-003.xml
new file mode 100644
index 0000000000000000000000000000000000000000..adec3d788188349daf59a08d4d872658c04ef724
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-003.xml
@@ -0,0 +1,5 @@
+
+☂Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-004.xml b/corpus/paragraph-edit/offline/joins/native/read/page-004.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fe7660933a9e562a29e298825fb22266375d80c8
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-004.xml
@@ -0,0 +1,5 @@
+
+☂Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-005.xml b/corpus/paragraph-edit/offline/joins/native/read/page-005.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4227be5eb03a241bf6ce0dfe0fbb1ece9965dade
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-005.xml
@@ -0,0 +1,6 @@
+
+☂Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-006.xml b/corpus/paragraph-edit/offline/joins/native/read/page-006.xml
new file mode 100644
index 0000000000000000000000000000000000000000..167287870795f4b510eebf42241cafb673ad0940
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-006.xml
@@ -0,0 +1,5 @@
+
+☂Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-007.xml b/corpus/paragraph-edit/offline/joins/native/read/page-007.xml
new file mode 100644
index 0000000000000000000000000000000000000000..402252d7c06456b9a4f6d4461f03bcbd71640661
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-007.xml
@@ -0,0 +1,5 @@
+
+☂Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-008.xml b/corpus/paragraph-edit/offline/joins/native/read/page-008.xml
new file mode 100644
index 0000000000000000000000000000000000000000..552b927b47f79cfc1a3d1cd2e2b46c2c79adcff7
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-008.xml
@@ -0,0 +1,20 @@
+
+Bold 🦀 italic é color 東京
+End]]>Fictitious: café, 東京, مرحبا]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-009.xml b/corpus/paragraph-edit/offline/joins/native/read/page-009.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4301ba0550eaa621005b78bc9e7aeba3f76a2f6e
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-009.xml
@@ -0,0 +1,5 @@
+
+☂Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-010.xml b/corpus/paragraph-edit/offline/joins/native/read/page-010.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dae5fdb3cbcd6aaa85cd91b2ab029238544058b3
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-010.xml
@@ -0,0 +1,3 @@
+
+Link label tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-011.xml b/corpus/paragraph-edit/offline/joins/native/read/page-011.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b31bc7ec4baddcf2e98da0146a56658a8ad390f8
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-011.xml
@@ -0,0 +1,5 @@
+
+☂Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-012.xml b/corpus/paragraph-edit/offline/joins/native/read/page-012.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9fcb04f0bd3028c095bfaaab0c8cbf2add4e8c6c
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-012.xml
@@ -0,0 +1,5 @@
+
+Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/page-013.xml b/corpus/paragraph-edit/offline/joins/native/read/page-013.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6dd7d0276272223468796769d7447b6b7d25fc04
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/page-013.xml
@@ -0,0 +1,5 @@
+
+☂Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/joins/native/read/payloads.json b/corpus/paragraph-edit/offline/joins/native/read/payloads.json
new file mode 120000
index 0000000000000000000000000000000000000000..787b4bae85038cda922f8befac6c8ed361fa8035
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/read/payloads.json
@@ -0,0 +1 @@
+../../../../before/read/payloads.json
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/joins/native/run.json b/corpus/paragraph-edit/offline/joins/native/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..d6d739820dca141e877c521937893a9db95b9010
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/run.json
@@ -0,0 +1,18 @@
+{
+ "notebook": "/Users/clo/dev/one/evidence/m10/offline-paragraph-native-01/joins/candidate",
+ "expected_pages": 14,
+ "author": null,
+ "author_timeout_seconds": 600,
+ "inspect": false,
+ "collect_notebook": true,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41"
+ }
+}
diff --git a/corpus/paragraph-edit/offline/joins/native/source.json b/corpus/paragraph-edit/offline/joins/native/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..699cc709a58444d4fb5d3cdaaf8e4edece01bf0c
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/source.json
@@ -0,0 +1,8 @@
+[
+ {
+ "path": "synthetic.one",
+ "bytes": 150648,
+ "sha256": "2fd036cddb33de5e42f66cb6a6b7647cbfeac374a4055e9afc15bf3b0d6ec1b2",
+ "mtime_ns": 1788863184056850411
+ }
+]
diff --git a/corpus/paragraph-edit/offline/joins/native/teardown.json b/corpus/paragraph-edit/offline/joins/native/teardown.json
new file mode 120000
index 0000000000000000000000000000000000000000..47a1192a30ea7da90676d631bb9a2687d829b43e
--- /dev/null
+++ b/corpus/paragraph-edit/offline/joins/native/teardown.json
@@ -0,0 +1 @@
+../../../joined/teardown.json
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/splits/candidate/synthetic.one b/corpus/paragraph-edit/offline/splits/candidate/synthetic.one
new file mode 100644
index 0000000000000000000000000000000000000000..66148d4f93647580dd893d2c5fcdac50745a26bb
Binary files /dev/null and b/corpus/paragraph-edit/offline/splits/candidate/synthetic.one differ
diff --git a/corpus/paragraph-edit/offline/splits/manifest.json b/corpus/paragraph-edit/offline/splits/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..262dff2900ded1a50e82606439626bb8b6212703
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/manifest.json
@@ -0,0 +1,374 @@
+[
+ {
+ "case": "Split middle",
+ "id": 1,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324660,
+ "guid": [
+ 228,
+ 60,
+ 65,
+ 246,
+ 194,
+ 244,
+ 184,
+ 76,
+ 170,
+ 5,
+ 19,
+ 130,
+ 32,
+ 122,
+ 148,
+ 241
+ ],
+ "offset": 2,
+ "text": "{DFE950EF-6BEB-0F5B-2403-7B74F1AE5002},44"
+ },
+ "remote_prefix": true,
+ "revision": "{BF0EE2EB-19D1-47B7-B28D-A9ABD19EBAAE},1",
+ "space": "{FE224563-B517-023E-0D75-9904B1952A26},1"
+ },
+ {
+ "case": "Split style boundary",
+ "id": 2,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324660,
+ "guid": [
+ 163,
+ 91,
+ 197,
+ 73,
+ 195,
+ 225,
+ 53,
+ 68,
+ 140,
+ 152,
+ 83,
+ 168,
+ 172,
+ 124,
+ 226,
+ 213
+ ],
+ "offset": 4,
+ "text": "{5FB484E2-1E3E-0A9D-1989-271A3EBBEC42},44"
+ },
+ "remote_prefix": true,
+ "revision": "{167F0C99-70D5-4B0C-8339-4A5D4FA8D101},1",
+ "space": "{DED48A05-0DB1-0712-086D-09237C982341},1"
+ },
+ {
+ "case": "Split start",
+ "id": 3,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324660,
+ "guid": [
+ 135,
+ 16,
+ 214,
+ 248,
+ 73,
+ 35,
+ 2,
+ 69,
+ 148,
+ 213,
+ 125,
+ 28,
+ 43,
+ 124,
+ 81,
+ 35
+ ],
+ "offset": 0,
+ "text": "{012ED94B-43B3-079A-3050-B1CBE25C1C09},44"
+ },
+ "remote_prefix": false,
+ "revision": "{F45BB0FF-3E33-4039-B5DE-C436C126C52E},1",
+ "space": "{7919E933-0809-00D8-1754-A21684215860},1"
+ },
+ {
+ "case": "Split end",
+ "id": 4,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324660,
+ "guid": [
+ 37,
+ 220,
+ 149,
+ 138,
+ 85,
+ 3,
+ 106,
+ 69,
+ 168,
+ 172,
+ 49,
+ 82,
+ 247,
+ 99,
+ 160,
+ 139
+ ],
+ "offset": 22,
+ "text": "{DAF723A3-C2A3-0798-2EAB-34613166A794},44"
+ },
+ "remote_prefix": true,
+ "revision": "{B408F227-A7DE-4350-8068-D91005E0B8E5},1",
+ "space": "{927F2809-173C-036E-37B6-60A6568F7723},1"
+ },
+ {
+ "case": "Split empty",
+ "id": 5,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324660,
+ "guid": [
+ 232,
+ 204,
+ 91,
+ 132,
+ 180,
+ 27,
+ 239,
+ 79,
+ 158,
+ 115,
+ 213,
+ 165,
+ 40,
+ 35,
+ 189,
+ 93
+ ],
+ "offset": 0,
+ "text": "{0B467F31-D287-08CD-09F4-A6FBB2F5418F},45"
+ },
+ "remote_prefix": false,
+ "revision": "{4F8EB252-1808-40AA-9B37-DB3DE1C5C3F2},1",
+ "space": "{E4D8C4CC-508B-04D2-36CE-C5B8C9D2FEC4},1"
+ },
+ {
+ "case": "Split parent",
+ "id": 6,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324660,
+ "guid": [
+ 54,
+ 186,
+ 114,
+ 171,
+ 44,
+ 189,
+ 95,
+ 71,
+ 135,
+ 190,
+ 71,
+ 95,
+ 23,
+ 246,
+ 37,
+ 157
+ ],
+ "offset": 2,
+ "text": "{3860EA13-D444-0598-35D6-CF96769C6A21},44"
+ },
+ "remote_prefix": true,
+ "revision": "{A291D6A6-08A8-4E06-BF29-0479851E9E12},1",
+ "space": "{7A363D4C-73C2-0ABE-0931-E6F62A90B95E},1"
+ },
+ {
+ "case": "Split child",
+ "id": 7,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324660,
+ "guid": [
+ 160,
+ 247,
+ 235,
+ 152,
+ 30,
+ 98,
+ 166,
+ 77,
+ 157,
+ 204,
+ 141,
+ 45,
+ 114,
+ 151,
+ 225,
+ 195
+ ],
+ "offset": 2,
+ "text": "{B587CBC6-0BCC-0A23-16F6-E1B0B3C5D061},49"
+ },
+ "remote_prefix": true,
+ "revision": "{FFE1F804-FA27-4EFD-B53C-9E1D07384DA6},1",
+ "space": "{3CC2C4DC-651A-07C9-2F7B-375C6663F3CA},1"
+ },
+ {
+ "case": "Split bullet",
+ "id": 8,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324660,
+ "guid": [
+ 53,
+ 245,
+ 214,
+ 76,
+ 159,
+ 56,
+ 134,
+ 76,
+ 178,
+ 90,
+ 131,
+ 182,
+ 74,
+ 114,
+ 31,
+ 24
+ ],
+ "offset": 2,
+ "text": "{8C65E427-477C-02ED-1807-2F10E277019D},44"
+ },
+ "remote_prefix": true,
+ "revision": "{822FBEAD-035E-47B5-9ED6-CDAE96AA5837},1",
+ "space": "{942EF5E4-FFD1-0FE4-23D2-7429899577BD},1"
+ },
+ {
+ "case": "Split number",
+ "id": 9,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324661,
+ "guid": [
+ 196,
+ 133,
+ 251,
+ 251,
+ 193,
+ 175,
+ 8,
+ 65,
+ 175,
+ 180,
+ 126,
+ 16,
+ 31,
+ 185,
+ 237,
+ 66
+ ],
+ "offset": 2,
+ "text": "{D4ABBE4F-2165-0122-1F9F-ACE3FE92C703},44"
+ },
+ "remote_prefix": true,
+ "revision": "{F5ABC207-F9C7-45A3-B213-AA675126A592},1",
+ "space": "{A4AE40FF-DD80-0003-2856-17D8295C1B91},1"
+ },
+ {
+ "case": "Split tag",
+ "id": 10,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324661,
+ "guid": [
+ 40,
+ 122,
+ 110,
+ 77,
+ 185,
+ 255,
+ 122,
+ 69,
+ 128,
+ 15,
+ 14,
+ 166,
+ 54,
+ 156,
+ 154,
+ 91
+ ],
+ "offset": 2,
+ "text": "{37AA5877-2980-0227-169F-8B1E8EE0795A},44"
+ },
+ "remote_prefix": true,
+ "revision": "{8021DAB3-4163-49C4-A7A1-898ACCAB3981},1",
+ "space": "{739DB38B-4239-0A7C-2652-4E525D219638},1"
+ },
+ {
+ "case": "Split cell",
+ "id": 11,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324661,
+ "guid": [
+ 150,
+ 222,
+ 89,
+ 134,
+ 80,
+ 167,
+ 167,
+ 67,
+ 179,
+ 23,
+ 93,
+ 176,
+ 27,
+ 184,
+ 119,
+ 185
+ ],
+ "offset": 2,
+ "text": "{F0832BC9-1578-0A96-08AF-99344644FFB8},50"
+ },
+ "remote_prefix": true,
+ "revision": "{0E8285DD-840A-4C8E-BFDB-CA3589436ECD},1",
+ "space": "{4432E3EC-5E5F-0A04-2108-E9A2C17BDBEC},1"
+ },
+ {
+ "case": "Split soft break",
+ "id": 12,
+ "intent": {
+ "author": "Rust split author",
+ "created": 1473324661,
+ "guid": [
+ 199,
+ 62,
+ 94,
+ 81,
+ 29,
+ 157,
+ 236,
+ 77,
+ 159,
+ 34,
+ 22,
+ 137,
+ 163,
+ 57,
+ 215,
+ 79
+ ],
+ "offset": 2,
+ "text": "{88189BA1-2F19-0282-05ED-A0B83153F143},44"
+ },
+ "remote_prefix": true,
+ "revision": "{89D96DD2-2D50-433E-B54B-50018E3C2108},1",
+ "space": "{D8E15673-A45E-0914-291A-7767C3222F19},1"
+ }
+]
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/splits/native/notebook/Open Notebook.onetoc2 b/corpus/paragraph-edit/offline/splits/native/notebook/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..e6462b78708e6fcff91d8fb9b4059776e8c1e932
Binary files /dev/null and b/corpus/paragraph-edit/offline/splits/native/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/paragraph-edit/offline/splits/native/notebook/synthetic.one b/corpus/paragraph-edit/offline/splits/native/notebook/synthetic.one
new file mode 100644
index 0000000000000000000000000000000000000000..82323b80ecdf4626a8e3d11bda99eb0013358efd
Binary files /dev/null and b/corpus/paragraph-edit/offline/splits/native/notebook/synthetic.one differ
diff --git a/corpus/paragraph-edit/offline/splits/native/read/environment.json b/corpus/paragraph-edit/offline/splits/native/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..f25cbe4815b561c24a9a6bdd4809e070238ab820
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-97095A74",
+ "cold": true,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/paragraph-edit/offline/splits/native/read/hierarchy.xml b/corpus/paragraph-edit/offline/splits/native/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..569b27bb107f308aa2724d4e3d5a3f408803df0a
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-000.xml b/corpus/paragraph-edit/offline/splits/native/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..552bdb1a543eab002e577f984ca924481b2c0415
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-000.xml
@@ -0,0 +1,3 @@
+
+
+soft break]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-001.xml b/corpus/paragraph-edit/offline/splits/native/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d23d6f76d2f1ee76c8e096c59bacda45afb3626b
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-001.xml
@@ -0,0 +1,5 @@
+
+☂Bold]]> italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-002.xml b/corpus/paragraph-edit/offline/splits/native/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..302f68bcfd20c4f470d4442980f39ff29626f744
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-002.xml
@@ -0,0 +1,6 @@
+
+☂Bo]]>ld italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-003.xml b/corpus/paragraph-edit/offline/splits/native/read/page-003.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8a6966323f4370b6414fa6d324ed432fc6dee0fd
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-003.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-004.xml b/corpus/paragraph-edit/offline/splits/native/read/page-004.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5f29ed91e8c01061ac3d14843d92c35a9d8a5024
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-004.xml
@@ -0,0 +1,5 @@
+
+☂Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-005.xml b/corpus/paragraph-edit/offline/splits/native/read/page-005.xml
new file mode 100644
index 0000000000000000000000000000000000000000..47a49619f20d80f0cd3ec8a191e25c3ae776898d
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-005.xml
@@ -0,0 +1,6 @@
+
+☂Bo]]>ld italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-006.xml b/corpus/paragraph-edit/offline/splits/native/read/page-006.xml
new file mode 100644
index 0000000000000000000000000000000000000000..20a04fd89bdc80ba54ac6d41499b42fa651fa476
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-006.xml
@@ -0,0 +1,6 @@
+
+☂Bo]]>ld italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-007.xml b/corpus/paragraph-edit/offline/splits/native/read/page-007.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e8e34a6b4409d0ca62fd300775ae8ab05e8e187e
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-007.xml
@@ -0,0 +1,6 @@
+
+☂Bo]]>ld italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-008.xml b/corpus/paragraph-edit/offline/splits/native/read/page-008.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c98920929247120cedf4f5d8821acde295b9ddd8
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-008.xml
@@ -0,0 +1,5 @@
+
+Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-009.xml b/corpus/paragraph-edit/offline/splits/native/read/page-009.xml
new file mode 100644
index 0000000000000000000000000000000000000000..239094503552c6db3c0ad713177654e64e916fe3
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-009.xml
@@ -0,0 +1,6 @@
+
+☂Bo]]>ld italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-010.xml b/corpus/paragraph-edit/offline/splits/native/read/page-010.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7589342d47d156ffa1b91da04b0d26bbb452e12a
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-010.xml
@@ -0,0 +1,6 @@
+
+☂Bo]]>ld italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-011.xml b/corpus/paragraph-edit/offline/splits/native/read/page-011.xml
new file mode 100644
index 0000000000000000000000000000000000000000..44401c90c4c34eed1906e8886f606578698472d7
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-011.xml
@@ -0,0 +1,3 @@
+
+Link label tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-012.xml b/corpus/paragraph-edit/offline/splits/native/read/page-012.xml
new file mode 100644
index 0000000000000000000000000000000000000000..94b7c097ba40e651efb699b7b83e8f31997e3fbc
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-012.xml
@@ -0,0 +1,20 @@
+
+Bold 🦀 italic é color 東京
+End]]>Fictitious: café, 東京, مرحبا]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/page-013.xml b/corpus/paragraph-edit/offline/splits/native/read/page-013.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b45f6367cbe094cff50050a691df1a253377e0df
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/page-013.xml
@@ -0,0 +1,6 @@
+
+☂Bo]]>ld italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/splits/native/read/payloads.json b/corpus/paragraph-edit/offline/splits/native/read/payloads.json
new file mode 120000
index 0000000000000000000000000000000000000000..787b4bae85038cda922f8befac6c8ed361fa8035
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/read/payloads.json
@@ -0,0 +1 @@
+../../../../before/read/payloads.json
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/splits/native/run.json b/corpus/paragraph-edit/offline/splits/native/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..b59c126e1d874ea3b45cc19732aef5e66ac457ba
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/run.json
@@ -0,0 +1,18 @@
+{
+ "notebook": "/Users/clo/dev/one/evidence/m10/offline-paragraph-native-01/splits/candidate",
+ "expected_pages": 14,
+ "author": null,
+ "author_timeout_seconds": 600,
+ "inspect": false,
+ "collect_notebook": true,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41"
+ }
+}
diff --git a/corpus/paragraph-edit/offline/splits/native/source.json b/corpus/paragraph-edit/offline/splits/native/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..efa07a806149800f245ae2315d9a27dcb421ff6e
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/source.json
@@ -0,0 +1,8 @@
+[
+ {
+ "path": "synthetic.one",
+ "bytes": 119736,
+ "sha256": "ea8264bf8df579b69755c47918ab45f77a7fdd4a4cc7564ab84813220def9322",
+ "mtime_ns": 1788863181033354746
+ }
+]
diff --git a/corpus/paragraph-edit/offline/splits/native/teardown.json b/corpus/paragraph-edit/offline/splits/native/teardown.json
new file mode 120000
index 0000000000000000000000000000000000000000..47a1192a30ea7da90676d631bb9a2687d829b43e
--- /dev/null
+++ b/corpus/paragraph-edit/offline/splits/native/teardown.json
@@ -0,0 +1 @@
+../../../joined/teardown.json
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/tags/candidate/synthetic.one b/corpus/paragraph-edit/offline/tags/candidate/synthetic.one
new file mode 100644
index 0000000000000000000000000000000000000000..f14e719947b2b79e1a76d8c9e320f66d883af1c9
Binary files /dev/null and b/corpus/paragraph-edit/offline/tags/candidate/synthetic.one differ
diff --git a/corpus/paragraph-edit/offline/tags/manifest.json b/corpus/paragraph-edit/offline/tags/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..91e171dadf3b1b404da916620fe17ee5242e4838
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/manifest.json
@@ -0,0 +1,38 @@
+[
+ {
+ "case": "Join empty left right tag",
+ "id": 1,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{0D11242E-6A38-0F74-261A-A6FC444FA262},45",
+ "right": "{0D11242E-6A38-0F74-261A-A6FC444FA262},50"
+ },
+ "remote_prefix": false,
+ "revision": "{E906B028-AA9F-4772-877F-CCBED45A8645},1",
+ "space": "{FC64EF0A-5CCB-0557-20E0-6C847015B691},1"
+ },
+ {
+ "case": "Join empty left two tags",
+ "id": 2,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{84061027-77E3-03F1-3E0D-A8F744B19C18},45",
+ "right": "{84061027-77E3-03F1-3E0D-A8F744B19C18},51"
+ },
+ "remote_prefix": false,
+ "revision": "{98BB5C37-4F68-408F-AE2F-4D9EFD75022F},1",
+ "space": "{A05CB25D-C95D-043F-3B9A-CE333AD171AD},1"
+ },
+ {
+ "case": "Join different tags",
+ "id": 3,
+ "intent": {
+ "author": "Rust join author",
+ "left": "{4FBF7D4A-6F4D-048E-0913-5BD9A184C25B},44",
+ "right": "{4FBF7D4A-6F4D-048E-0913-5BD9A184C25B},50"
+ },
+ "remote_prefix": true,
+ "revision": "{AA6105EC-5A3B-4038-887A-DDADB2911713},1",
+ "space": "{6F0F6556-17D3-03AF-20EF-C465A856A153},1"
+ }
+]
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/tags/native/notebook/Open Notebook.onetoc2 b/corpus/paragraph-edit/offline/tags/native/notebook/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..0dda8d5a7eefbb1b6b34b03bd871912e528d9d9a
Binary files /dev/null and b/corpus/paragraph-edit/offline/tags/native/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/paragraph-edit/offline/tags/native/notebook/synthetic.one b/corpus/paragraph-edit/offline/tags/native/notebook/synthetic.one
new file mode 100644
index 0000000000000000000000000000000000000000..abb666b9c89afffb0acebc1f74a94ffd641936e5
Binary files /dev/null and b/corpus/paragraph-edit/offline/tags/native/notebook/synthetic.one differ
diff --git a/corpus/paragraph-edit/offline/tags/native/read/environment.json b/corpus/paragraph-edit/offline/tags/native/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..b348dd06a6fd4897cb12c8e784c1cf7817fe7da6
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/native/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-C920A6E7",
+ "cold": true,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/paragraph-edit/offline/tags/native/read/hierarchy.xml b/corpus/paragraph-edit/offline/tags/native/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..aae999471c108dab6b93ccdf7922138bcc4e42f4
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/native/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/paragraph-edit/offline/tags/native/read/page-000.xml b/corpus/paragraph-edit/offline/tags/native/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9b852a0b5177c4b307766f1da26fd363570f79f2
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/native/read/page-000.xml
@@ -0,0 +1,20 @@
+
+Bold 🦀 italic é color 東京
+End]]>Fictitious: café, 東京, مرحبا]]>
diff --git a/corpus/paragraph-edit/offline/tags/native/read/page-001.xml b/corpus/paragraph-edit/offline/tags/native/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a518cfa62728716735c00eba3f549cd8ceed68ed
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/native/read/page-001.xml
@@ -0,0 +1,5 @@
+
+☂LeftBold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/tags/native/read/page-002.xml b/corpus/paragraph-edit/offline/tags/native/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6c9bad30a1ce9ab4890efd523db2c4d8686fa73d
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/native/read/page-002.xml
@@ -0,0 +1,5 @@
+
+Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/tags/native/read/page-003.xml b/corpus/paragraph-edit/offline/tags/native/read/page-003.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f8a75604a6d26a6ae036923d879551df7dbf6ee9
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/native/read/page-003.xml
@@ -0,0 +1,5 @@
+
+Bold italic 🦀 é tail]]>
diff --git a/corpus/paragraph-edit/offline/tags/native/read/payloads.json b/corpus/paragraph-edit/offline/tags/native/read/payloads.json
new file mode 120000
index 0000000000000000000000000000000000000000..787b4bae85038cda922f8befac6c8ed361fa8035
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/native/read/payloads.json
@@ -0,0 +1 @@
+../../../../before/read/payloads.json
\ No newline at end of file
diff --git a/corpus/paragraph-edit/offline/tags/native/run.json b/corpus/paragraph-edit/offline/tags/native/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..5a64074979038e3aff0716e3caf13ae9c03b43f3
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/native/run.json
@@ -0,0 +1,18 @@
+{
+ "notebook": "/Users/clo/dev/one/evidence/m10/offline-paragraph-native-01/tags/candidate",
+ "expected_pages": 4,
+ "author": null,
+ "author_timeout_seconds": 600,
+ "inspect": false,
+ "collect_notebook": true,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41"
+ }
+}
diff --git a/corpus/paragraph-edit/offline/tags/native/source.json b/corpus/paragraph-edit/offline/tags/native/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..5253d2f66a6d0335f7eac687493ecba83043724c
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/native/source.json
@@ -0,0 +1,8 @@
+[
+ {
+ "path": "synthetic.one",
+ "bytes": 42688,
+ "sha256": "4d6efc70b84628a281d1a64dbb3cf96d7c682d558ed7fbef708b493c51b01794",
+ "mtime_ns": 1788863185189921064
+ }
+]
diff --git a/corpus/paragraph-edit/offline/tags/native/teardown.json b/corpus/paragraph-edit/offline/tags/native/teardown.json
new file mode 120000
index 0000000000000000000000000000000000000000..47a1192a30ea7da90676d631bb9a2687d829b43e
--- /dev/null
+++ b/corpus/paragraph-edit/offline/tags/native/teardown.json
@@ -0,0 +1 @@
+../../../joined/teardown.json
\ No newline at end of file
diff --git a/crates/onestore-offline/README.md b/crates/onestore-offline/README.md
index bae1c8bdb444b6cdb1156697916e728d9da5077f..cc8593c7be7f02433d59f2c70d3d78e73d59ef0d 100644
--- a/crates/onestore-offline/README.md
+++ b/crates/onestore-offline/README.md
@@ -1,7 +1,7 @@
# onestore-offline
Durable local editing for a OneNote file, in an optional sibling crate. The current
-foundation stores a complete working image and typed text, insertion and formatting intents in a local
+foundation stores a complete working image and typed editing intents in a local
SQLite database. `sync_once` provides a reconciliation step and `start_sync` owns
automatic polling and reconnects. Local success does not
acknowledge publication to a shared notebook.
@@ -29,12 +29,30 @@ assert_eq!(pending.last().map(|edit| edit.id), local_id);
intent identities. `Insertion::with_formatting` queues text and
its styles as one intent and one publication. Keep that value across retries; its `text_object()` identifies
the new text for subsequent offline edits. Pending entries expose
-`Operation::Text(TextEdit)`, `Operation::Insert(Insertion)` or
-`Operation::Format(FormatEdit)` through their
+`Operation::Text(TextEdit)`, `Operation::Insert(Insertion)`,
+`Operation::Format(FormatEdit)`, `Operation::Split(SplitEdit)` and
+`Operation::Join(JoinEdit)` through their
`operation` field. Synchronization applies these in queue order, so an inserted
outline can precede its paragraphs and their later edits. Missing anchors or
existing insertion identities preserve a conflict and the complete local image.
+`split` and `join` accept the core `ParagraphSplit` and `ParagraphJoin` intents.
+Splits retain allocated identities when the original UTF-16 boundary rebases;
+dependent edits can address `ParagraphSplit::text_object()` immediately after
+local acknowledgement. Both operations retain observed parent paths, children,
+indentation, paragraph/list state and tags. Changed structure produces
+`StructureChanged`. Remote text changes must leave unambiguous boundary mappings;
+current remote character styles are preserved. Reconciliation does not silently
+discard a newly added right tag or move an unobserved child.
+
+`rebase_conflict` accepts a zero-length reviewed range for a split, preserving its
+allocated identities. `rebase_join_conflict(id, local, remote)` reviews the original
+join against current images. It rejects a change in which text identity survives,
+because dependent edits still address that identity. As with other conflict
+reviews, stale images, pending operations and uncertain attempts cannot be rebased.
+An uncertain structural operation requires its original attempted revision for
+confirmation; matching text or structure does not establish a receipt.
+
`rebase_paragraph_conflict(id, local, remote, parent, before)` and
`rebase_outline_conflict(id, local, remote, page, x, y)` accept reviewed replacement
placements for the oldest insertion conflict. They preserve creation time, text,
@@ -70,8 +88,9 @@ requested value is accepted. Enabling superscript or subscript also checks the
opposite attribute that the operation clears. If the remote image already satisfies
the whole operation, guarded confirmation still precedes a durable receipt.
-Recognized version-one through version-three caches migrate transactionally to the typed
-queue. The migration retains images, local IDs, publication attempts, conflicts,
+Recognized earlier caches migrate transactionally to version six, which adds
+paragraph split/join intents and structural conflicts. The migration retains
+images, local IDs, publication attempts, conflicts,
receipts and the autoincrement sequence; it does not reuse acknowledged IDs when
the pending queue is empty.
diff --git a/crates/onestore-offline/examples/cache_probe.rs b/crates/onestore-offline/examples/cache_probe.rs
index abd7ff2cf78c670c46635acf394fdd001aba39ff..8f016e89f1cee8b67dc9f82b971ec26e1cce240b 100644
--- a/crates/onestore-offline/examples/cache_probe.rs
+++ b/crates/onestore-offline/examples/cache_probe.rs
@@ -138,6 +138,9 @@ fn main() -> Result<(), Box> {
font_size = Some(*value);
*value as u64 - 6
}
+ onestore_offline::Operation::Split(_) | onestore_offline::Operation::Join(_) => {
+ panic!("This fixture does not generate paragraph boundary edits")
+ }
};
assert!(operations.last().is_none_or(|last| *last < operation));
assert!(ids.last().is_none_or(|last| *last < pending.id));
diff --git a/crates/onestore-offline/src/lib.rs b/crates/onestore-offline/src/lib.rs
index 31c7c9505ecf1fbc406f82fe2ac3df17ef9c0407..47dc8da3c60b5fcf2cba95c2ea33775402a11ea0 100644
--- a/crates/onestore-offline/src/lib.rs
+++ b/crates/onestore-offline/src/lib.rs
@@ -10,10 +10,12 @@ use std::{fs::OpenOptions, io, ops::Range, path::Path, sync::Mutex, time::Durati
mod assets;
mod formatting;
+mod paragraph;
mod rebase;
mod recovery;
mod schema;
pub use formatting::FormatEdit;
+pub use paragraph::{JoinEdit, SplitEdit};
pub use recovery::{Recovery, RecoverySummary};
mod sync;
pub use sync::{ConflictKind, EditStatus, Remote};
@@ -45,7 +47,7 @@ pub enum Error {
type Result = std::result::Result;
const APPLICATION_ID: u32 = 0x4f4e454f;
-const SCHEMA_VERSION: u32 = 5;
+const SCHEMA_VERSION: u32 = 6;
/// Text and its observed precondition, retained across cache reopen and rebasing.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
@@ -63,6 +65,8 @@ pub enum Operation {
Text(TextEdit),
Insert(Insertion),
Format(FormatEdit),
+ Split(SplitEdit),
+ Join(JoinEdit),
}
/// A locally acknowledged intent; its ID remains stable across cache reopen.
diff --git a/crates/onestore-offline/src/paragraph.rs b/crates/onestore-offline/src/paragraph.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4454bc7c110c4dba5a7c196ad982a0318886ab5a
--- /dev/null
+++ b/crates/onestore-offline/src/paragraph.rs
@@ -0,0 +1,241 @@
+use super::*;
+use onestore::{ParagraphJoin, ParagraphSplit};
+use serde::{Deserialize, Serialize};
+use serde_json::{Value, json};
+use std::collections::{BTreeMap, BTreeSet};
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[serde(deny_unknown_fields)]
+struct Observed {
+ text: String,
+ structure: Value,
+}
+
+/// A stable split intent and the paragraph state observed before local publication.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[serde(deny_unknown_fields)]
+pub struct SplitEdit {
+ pub intent: ParagraphSplit,
+ observed: Observed,
+}
+
+/// A join intent and both paragraphs' observed text, placement and tag ownership.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[serde(deny_unknown_fields)]
+pub struct JoinEdit {
+ pub intent: ParagraphJoin,
+ observed: [Observed; 2],
+}
+
+fn observe(source: &[u8], space: ExGuid, texts: &[ExGuid]) -> Result