@remnic/core 9.34.0 → 9.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/access-admin-ops-surface.js +10 -10
  2. package/dist/access-authorization-probe.js +11 -11
  3. package/dist/access-boundary.js +10 -10
  4. package/dist/access-cli.js +26 -26
  5. package/dist/access-extraction-force-flush.js +10 -10
  6. package/dist/access-http.js +17 -17
  7. package/dist/access-identity-continuity-surface.js +10 -10
  8. package/dist/access-lcm-surface.js +10 -10
  9. package/dist/access-mcp.js +15 -15
  10. package/dist/access-namespace-preflight.js +10 -10
  11. package/dist/access-observe-write-surface.js +10 -10
  12. package/dist/access-operations-batch.js +12 -12
  13. package/dist/access-operations.js +14 -14
  14. package/dist/access-recall-concurrency.js +10 -10
  15. package/dist/access-recall-response.js +10 -10
  16. package/dist/access-recall-surface.js +10 -10
  17. package/dist/access-schema.js +3 -3
  18. package/dist/access-service.js +10 -10
  19. package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
  20. package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
  21. package/dist/{chunk-RSFWAI4D.js → chunk-2NUC473W.js} +8 -8
  22. package/dist/{chunk-KJT3PZAR.js → chunk-2SHIWGY7.js} +2 -2
  23. package/dist/{chunk-FYO2GAZJ.js → chunk-3F2LKUMU.js} +2 -2
  24. package/dist/{chunk-FL3AM5KE.js → chunk-5FHE4JI7.js} +3 -3
  25. package/dist/{chunk-DG2R7M7K.js → chunk-5HTAJCTO.js} +5 -5
  26. package/dist/{chunk-7CCVK67W.js → chunk-65ZPVYTA.js} +5 -5
  27. package/dist/{chunk-HULSU7PS.js → chunk-AHZYNPLX.js} +2 -2
  28. package/dist/{chunk-6HXQ2YO5.js → chunk-EH5U2YCX.js} +2 -2
  29. package/dist/{chunk-QEPNZBBC.js → chunk-GHR7WHZX.js} +3 -3
  30. package/dist/{chunk-LRYAQGDR.js → chunk-HEVWWPTL.js} +18 -18
  31. package/dist/{chunk-SUZI6BHQ.js → chunk-IOQFPVGY.js} +2 -2
  32. package/dist/{chunk-5W7CTZ2E.js → chunk-KQAFEZQX.js} +4 -4
  33. package/dist/{chunk-PYHOAXTI.js → chunk-LRGSLV7M.js} +4 -4
  34. package/dist/{chunk-WFYWGDM6.js → chunk-SA3WJJF2.js} +2 -2
  35. package/dist/{chunk-RLOVRJZN.js → chunk-SLPLPGJQ.js} +6 -6
  36. package/dist/{chunk-OXBPHIKH.js → chunk-T4HH5Q3U.js} +5 -5
  37. package/dist/{chunk-RQBF54FC.js → chunk-TJLLYV2X.js} +3 -3
  38. package/dist/{chunk-Y6XRTQMV.js → chunk-UZQCNOPS.js} +4 -4
  39. package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
  40. package/dist/cli.js +25 -25
  41. package/dist/contradiction/index.js +4 -4
  42. package/dist/corpus-watermark.js +6 -6
  43. package/dist/index.js +31 -31
  44. package/dist/namespaces/migrate.js +5 -5
  45. package/dist/namespaces/search.js +4 -4
  46. package/dist/operator-doctor-corpus.js +7 -7
  47. package/dist/operator-toolkit.js +8 -8
  48. package/dist/orchestrator.js +26 -26
  49. package/dist/reconcile/plan.d.ts +175 -0
  50. package/dist/reconcile/plan.js +420 -0
  51. package/dist/reconcile/plan.js.map +1 -0
  52. package/dist/search/factory.js +3 -3
  53. package/dist/search/index.js +7 -7
  54. package/dist/transfer/backup.js +2 -2
  55. package/dist/transfer/capsule-export.js +2 -2
  56. package/dist/transfer/capsule-import.js +2 -2
  57. package/dist/transfer/import-sqlite.js +2 -2
  58. package/package.json +12 -2
  59. package/src/reconcile/plan.test.ts +1013 -0
  60. package/src/reconcile/plan.ts +813 -0
  61. /package/dist/{capsule-crypto-GWVG7LGC.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
  62. /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
  63. /package/dist/{chunk-RSFWAI4D.js.map → chunk-2NUC473W.js.map} +0 -0
  64. /package/dist/{chunk-KJT3PZAR.js.map → chunk-2SHIWGY7.js.map} +0 -0
  65. /package/dist/{chunk-FYO2GAZJ.js.map → chunk-3F2LKUMU.js.map} +0 -0
  66. /package/dist/{chunk-FL3AM5KE.js.map → chunk-5FHE4JI7.js.map} +0 -0
  67. /package/dist/{chunk-DG2R7M7K.js.map → chunk-5HTAJCTO.js.map} +0 -0
  68. /package/dist/{chunk-7CCVK67W.js.map → chunk-65ZPVYTA.js.map} +0 -0
  69. /package/dist/{chunk-HULSU7PS.js.map → chunk-AHZYNPLX.js.map} +0 -0
  70. /package/dist/{chunk-6HXQ2YO5.js.map → chunk-EH5U2YCX.js.map} +0 -0
  71. /package/dist/{chunk-QEPNZBBC.js.map → chunk-GHR7WHZX.js.map} +0 -0
  72. /package/dist/{chunk-LRYAQGDR.js.map → chunk-HEVWWPTL.js.map} +0 -0
  73. /package/dist/{chunk-SUZI6BHQ.js.map → chunk-IOQFPVGY.js.map} +0 -0
  74. /package/dist/{chunk-5W7CTZ2E.js.map → chunk-KQAFEZQX.js.map} +0 -0
  75. /package/dist/{chunk-PYHOAXTI.js.map → chunk-LRGSLV7M.js.map} +0 -0
  76. /package/dist/{chunk-WFYWGDM6.js.map → chunk-SA3WJJF2.js.map} +0 -0
  77. /package/dist/{chunk-RLOVRJZN.js.map → chunk-SLPLPGJQ.js.map} +0 -0
  78. /package/dist/{chunk-OXBPHIKH.js.map → chunk-T4HH5Q3U.js.map} +0 -0
  79. /package/dist/{chunk-RQBF54FC.js.map → chunk-TJLLYV2X.js.map} +0 -0
  80. /package/dist/{chunk-Y6XRTQMV.js.map → chunk-UZQCNOPS.js.map} +0 -0
  81. /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
@@ -2,16 +2,16 @@ import {
2
2
  NamespaceSearchRouter,
3
3
  namespaceCollectionName,
4
4
  normalizeQmdResultPath
5
- } from "../chunk-SUZI6BHQ.js";
6
- import "../chunk-Y6XRTQMV.js";
5
+ } from "../chunk-IOQFPVGY.js";
6
+ import "../chunk-UZQCNOPS.js";
7
+ import "../chunk-ES36HTSC.js";
7
8
  import "../chunk-3Y224NYC.js";
8
9
  import "../chunk-H24WNPA2.js";
9
10
  import "../chunk-BLAKS3CP.js";
10
11
  import "../chunk-S2BBISHU.js";
11
- import "../chunk-CYEPCZN5.js";
12
- import "../chunk-ES36HTSC.js";
13
12
  import "../chunk-W7IGLX4G.js";
14
13
  import "../chunk-CINZGPSJ.js";
14
+ import "../chunk-CYEPCZN5.js";
15
15
  import "../chunk-LEPOP46W.js";
16
16
  import "../chunk-72A4L5JJ.js";
17
17
  import "../chunk-A7Q43EUZ.js";
@@ -2,21 +2,21 @@ import {
2
2
  corpusWatermarksFromCheck,
3
3
  summarizeCorpusAndReplica,
4
4
  summarizeCorpusWatermark
5
- } from "./chunk-HULSU7PS.js";
5
+ } from "./chunk-AHZYNPLX.js";
6
6
  import "./chunk-WDISJRMG.js";
7
7
  import "./chunk-XOQSEF7X.js";
8
- import "./chunk-WFYWGDM6.js";
9
- import "./chunk-KJT3PZAR.js";
10
- import "./chunk-SUZI6BHQ.js";
11
- import "./chunk-Y6XRTQMV.js";
8
+ import "./chunk-SA3WJJF2.js";
9
+ import "./chunk-2SHIWGY7.js";
10
+ import "./chunk-IOQFPVGY.js";
11
+ import "./chunk-UZQCNOPS.js";
12
+ import "./chunk-ES36HTSC.js";
12
13
  import "./chunk-3Y224NYC.js";
13
14
  import "./chunk-H24WNPA2.js";
14
15
  import "./chunk-BLAKS3CP.js";
15
16
  import "./chunk-S2BBISHU.js";
16
- import "./chunk-CYEPCZN5.js";
17
- import "./chunk-ES36HTSC.js";
18
17
  import "./chunk-W7IGLX4G.js";
19
18
  import "./chunk-CINZGPSJ.js";
19
+ import "./chunk-CYEPCZN5.js";
20
20
  import "./chunk-Y2ZD2RVG.js";
21
21
  import "./chunk-LEPOP46W.js";
22
22
  import "./chunk-72A4L5JJ.js";
@@ -14,11 +14,11 @@ import {
14
14
  summarizeProvenanceCoverage,
15
15
  summarizeTierDistribution,
16
16
  summarizeTombstoneStatus
17
- } from "./chunk-RQBF54FC.js";
17
+ } from "./chunk-TJLLYV2X.js";
18
18
  import "./chunk-TCIWHK3N.js";
19
19
  import "./chunk-KFY3SGN7.js";
20
20
  import "./chunk-CMTINOFS.js";
21
- import "./chunk-HULSU7PS.js";
21
+ import "./chunk-AHZYNPLX.js";
22
22
  import "./chunk-WDISJRMG.js";
23
23
  import "./chunk-OZKOPQCM.js";
24
24
  import "./chunk-2DQ2XREU.js";
@@ -37,18 +37,18 @@ import "./chunk-KFZY2OFH.js";
37
37
  import "./chunk-J6SLNQRO.js";
38
38
  import "./chunk-ULARSG5Z.js";
39
39
  import "./chunk-XOQSEF7X.js";
40
- import "./chunk-WFYWGDM6.js";
41
- import "./chunk-KJT3PZAR.js";
42
- import "./chunk-SUZI6BHQ.js";
43
- import "./chunk-Y6XRTQMV.js";
40
+ import "./chunk-SA3WJJF2.js";
41
+ import "./chunk-2SHIWGY7.js";
42
+ import "./chunk-IOQFPVGY.js";
43
+ import "./chunk-UZQCNOPS.js";
44
+ import "./chunk-ES36HTSC.js";
44
45
  import "./chunk-3Y224NYC.js";
45
46
  import "./chunk-H24WNPA2.js";
46
47
  import "./chunk-BLAKS3CP.js";
47
48
  import "./chunk-S2BBISHU.js";
48
- import "./chunk-CYEPCZN5.js";
49
- import "./chunk-ES36HTSC.js";
50
49
  import "./chunk-W7IGLX4G.js";
51
50
  import "./chunk-CINZGPSJ.js";
51
+ import "./chunk-CYEPCZN5.js";
52
52
  import "./chunk-Y2ZD2RVG.js";
53
53
  import "./chunk-LEPOP46W.js";
54
54
  import "./chunk-72A4L5JJ.js";
@@ -4,7 +4,7 @@ import {
4
4
  filterRecallCandidates,
5
5
  graphPathRelativeToStorage,
6
6
  mergeGraphExpandedResults
7
- } from "./chunk-RSFWAI4D.js";
7
+ } from "./chunk-2NUC473W.js";
8
8
  import "./chunk-UV72DEZD.js";
9
9
  import "./chunk-I74SUMNI.js";
10
10
  import "./chunk-ILNPU5W5.js";
@@ -95,21 +95,21 @@ import "./chunk-VEWZZM3H.js";
95
95
  import "./chunk-Q6IP2ARR.js";
96
96
  import "./chunk-6HEM6HTQ.js";
97
97
  import "./chunk-3I4FUAVJ.js";
98
- import "./chunk-LRYAQGDR.js";
98
+ import "./chunk-HEVWWPTL.js";
99
99
  import "./chunk-MMACM5G2.js";
100
100
  import "./chunk-54BGIBCR.js";
101
101
  import "./chunk-H4CKS5B2.js";
102
102
  import "./chunk-FXZTL7F7.js";
103
103
  import "./chunk-HDDRVXX4.js";
104
- import "./chunk-PYHOAXTI.js";
105
- import "./chunk-OWHERGF2.js";
106
- import "./chunk-UP6MOYCB.js";
107
- import "./chunk-BV2TIZ4F.js";
108
- import "./chunk-Z734BLO3.js";
104
+ import "./chunk-LRGSLV7M.js";
109
105
  import "./chunk-OZKVVUJB.js";
110
106
  import "./chunk-2DL3OFLD.js";
111
107
  import "./chunk-G4IAEX6D.js";
108
+ import "./chunk-2NLLXCJG.js";
109
+ import "./chunk-UP6MOYCB.js";
112
110
  import "./chunk-765K3SAT.js";
111
+ import "./chunk-BV2TIZ4F.js";
112
+ import "./chunk-Z734BLO3.js";
113
113
  import "./chunk-FGDKJJZS.js";
114
114
  import "./chunk-AJVLDJU6.js";
115
115
  import "./chunk-XL24QQ63.js";
@@ -136,12 +136,12 @@ import "./chunk-ZRWB5D4H.js";
136
136
  import "./chunk-HYQJQGD3.js";
137
137
  import "./chunk-DMT2RGAZ.js";
138
138
  import "./chunk-33JBK2XP.js";
139
- import "./chunk-RQBF54FC.js";
139
+ import "./chunk-TJLLYV2X.js";
140
140
  import "./chunk-TCIWHK3N.js";
141
141
  import "./chunk-KFY3SGN7.js";
142
142
  import "./chunk-3T74IZB3.js";
143
143
  import "./chunk-CMTINOFS.js";
144
- import "./chunk-HULSU7PS.js";
144
+ import "./chunk-AHZYNPLX.js";
145
145
  import "./chunk-WDISJRMG.js";
146
146
  import "./chunk-UR6PBX2J.js";
147
147
  import "./chunk-6GUG4YNM.js";
@@ -183,7 +183,7 @@ import "./chunk-OO42R444.js";
183
183
  import "./chunk-PIG4WJFQ.js";
184
184
  import "./chunk-KFZY2OFH.js";
185
185
  import "./chunk-LBJBNWS2.js";
186
- import "./chunk-RLOVRJZN.js";
186
+ import "./chunk-SLPLPGJQ.js";
187
187
  import "./chunk-V7IZKJI3.js";
188
188
  import "./chunk-SEDEKFYQ.js";
189
189
  import "./chunk-RKNJBZ55.js";
@@ -193,32 +193,32 @@ import "./chunk-42NQ7AVG.js";
193
193
  import "./chunk-TMSXWOBZ.js";
194
194
  import "./chunk-7RXCMVFQ.js";
195
195
  import "./chunk-MKQBXQKU.js";
196
- import "./chunk-FL3AM5KE.js";
196
+ import "./chunk-5FHE4JI7.js";
197
197
  import "./chunk-57QKLRBV.js";
198
198
  import "./chunk-D24OXEPB.js";
199
- import "./chunk-QEPNZBBC.js";
200
- import "./chunk-OXBPHIKH.js";
199
+ import "./chunk-GHR7WHZX.js";
200
+ import "./chunk-T4HH5Q3U.js";
201
201
  import "./chunk-HNZKVLAN.js";
202
202
  import "./chunk-EQWEIIKQ.js";
203
- import "./chunk-WG5HSQ4P.js";
204
203
  import "./chunk-UTRO5UJU.js";
204
+ import "./chunk-WG5HSQ4P.js";
205
205
  import "./chunk-35NN3ZFA.js";
206
206
  import "./chunk-CEN6DW6X.js";
207
207
  import "./chunk-T4WDJPEZ.js";
208
- import "./chunk-FYO2GAZJ.js";
208
+ import "./chunk-3F2LKUMU.js";
209
209
  import "./chunk-56BSXSHC.js";
210
210
  import "./chunk-WWUVONGW.js";
211
211
  import "./chunk-5IVLTFOV.js";
212
212
  import "./chunk-VCD4PSK2.js";
213
213
  import "./chunk-OADWQ5CR.js";
214
- import "./chunk-6HXQ2YO5.js";
214
+ import "./chunk-EH5U2YCX.js";
215
215
  import {
216
216
  hasIdentityRecoveryIntent,
217
217
  resolveEffectiveIdentityInjectionMode
218
- } from "./chunk-DG2R7M7K.js";
218
+ } from "./chunk-5HTAJCTO.js";
219
219
  import "./chunk-GDASG7NC.js";
220
220
  import "./chunk-GDB4J2H3.js";
221
- import "./chunk-7CCVK67W.js";
221
+ import "./chunk-65ZPVYTA.js";
222
222
  import "./chunk-VQ7B2Q6M.js";
223
223
  import "./chunk-PJSK6OJM.js";
224
224
  import "./chunk-GNVZQ5JN.js";
@@ -250,18 +250,18 @@ import "./chunk-NLE6TSLG.js";
250
250
  import "./chunk-QDW3E4RD.js";
251
251
  import "./chunk-PH3ZWL3W.js";
252
252
  import "./chunk-C37ZRXX7.js";
253
- import "./chunk-WFYWGDM6.js";
254
- import "./chunk-KJT3PZAR.js";
255
- import "./chunk-SUZI6BHQ.js";
256
- import "./chunk-Y6XRTQMV.js";
253
+ import "./chunk-SA3WJJF2.js";
254
+ import "./chunk-2SHIWGY7.js";
255
+ import "./chunk-IOQFPVGY.js";
256
+ import "./chunk-UZQCNOPS.js";
257
+ import "./chunk-ES36HTSC.js";
257
258
  import "./chunk-3Y224NYC.js";
258
259
  import "./chunk-H24WNPA2.js";
259
260
  import "./chunk-BLAKS3CP.js";
260
261
  import "./chunk-S2BBISHU.js";
261
- import "./chunk-CYEPCZN5.js";
262
- import "./chunk-ES36HTSC.js";
263
262
  import "./chunk-W7IGLX4G.js";
264
263
  import "./chunk-CINZGPSJ.js";
264
+ import "./chunk-CYEPCZN5.js";
265
265
  import "./chunk-Y2ZD2RVG.js";
266
266
  import "./chunk-LEPOP46W.js";
267
267
  import "./chunk-72A4L5JJ.js";
@@ -381,9 +381,9 @@ import "./chunk-6MKAMLQL.js";
381
381
  import "./chunk-JAKIDQB2.js";
382
382
  import "./chunk-TBBDFYXW.js";
383
383
  import "./chunk-ZBJMUXZH.js";
384
- import "./chunk-5W7CTZ2E.js";
385
- import "./chunk-UVYI6VIX.js";
384
+ import "./chunk-KQAFEZQX.js";
386
385
  import "./chunk-WEHSQBFR.js";
386
+ import "./chunk-X7Y7WX73.js";
387
387
  import "./chunk-J4EB7DNW.js";
388
388
  import "./chunk-BJMBJZ2Y.js";
389
389
  import "./chunk-UKJAGEXH.js";
@@ -0,0 +1,175 @@
1
+ import { OfflineSyncFileState } from '../offline-sync.js';
2
+
3
+ /**
4
+ * Bootstrap reconciliation planner for two peer daemons whose corpora have
5
+ * already diverged (issue #2150).
6
+ *
7
+ * The offline-sync protocol assumes a satellite that shares a common base with
8
+ * one daemon: every path it does not know about is a pull, and a conflict is
9
+ * rare. Two long-lived daemons are the opposite case — each side holds months
10
+ * of history the other never saw, both sides are authoritative, and there is
11
+ * NO common base on the first run. That is a different decision problem, so it
12
+ * gets its own planner rather than another mode flag inside `applyOfflineSync*`.
13
+ *
14
+ * This module is deliberately pure: it takes two file censuses and returns what
15
+ * to do. No I/O, no transport, no clock. Everything that makes reconciliation
16
+ * risky — which side wins, what counts as converged, what is reported — is
17
+ * decided here where it can be exhaustively tested, and the transport layer is
18
+ * left to carry out an already-settled plan.
19
+ */
20
+ /** Which way a path must move for the two corpora to agree. */
21
+ type ReconcileAction = "pull" | "push" | "identical" | "conflict" | "suppress";
22
+ /**
23
+ * How a path present on BOTH sides with different content is settled.
24
+ *
25
+ * `manual` is the safe default: on a bootstrap merge the two sides are equally
26
+ * authoritative, so silently picking one is data loss with extra steps. An
27
+ * operator opts into an automatic rule once they know the shape of their split.
28
+ */
29
+ type ReconcileConflictPolicy = "manual" | "newest-wins" | "keep-both";
30
+ /**
31
+ * What the planner decided for a conflicting path.
32
+ *
33
+ * `keep-both` never overwrites: the older side is retained and linked as
34
+ * superseded by the newer one, which is the only resolution that cannot lose a
35
+ * fact neither corpus has seen.
36
+ */
37
+ type ReconcileResolution = "local-wins" | "peer-wins" | "supersede-link" | "unresolved";
38
+ interface ReconcilePlanEntry {
39
+ path: string;
40
+ namespace: string;
41
+ action: ReconcileAction;
42
+ /** Stable machine-readable cause, safe to assert on and to aggregate. */
43
+ reason: ReconcileReason;
44
+ localSha256?: string;
45
+ peerSha256?: string;
46
+ /** Present only when a prior converged run left a cursor covering this path. */
47
+ baseSha256?: string;
48
+ /** Set only when `action` is `conflict`. */
49
+ resolution?: ReconcileResolution;
50
+ /**
51
+ * Which side holds the retracted revision. Set only when `action` is
52
+ * `suppress`: with different digests on each side and only one retracted,
53
+ * the entry would otherwise be identical either way and transport could
54
+ * delete the live copy instead of the retracted one.
55
+ */
56
+ suppressSide?: "local" | "peer" | "both";
57
+ /**
58
+ * Which revision is newer, set only for a `supersede-link` resolution.
59
+ *
60
+ * The contract is that the older revision is linked as superseded by the
61
+ * newer one, so transport needs the direction. Absent when the timestamps
62
+ * cannot order the two - which is exactly when `newest-wins` degrades to
63
+ * `supersede-link` - and the link direction is then an operator decision.
64
+ */
65
+ newerSide?: "local" | "peer";
66
+ }
67
+ type ReconcileReason = "peer_only" | "local_only"
68
+ /** Cursor showed only that side moved since agreement; both still hold the path. */
69
+ | "peer_changed" | "local_changed" | "same_content" | "both_modified" | "peer_deleted" | "local_deleted"
70
+ /** Peer deleted it while this side edited it — the offline-sync delete/modify pair. */
71
+ | "local_modified_peer_deleted" | "local_deleted_peer_modified" | "tombstoned";
72
+ interface ReconcileNamespaceReport {
73
+ namespace: string;
74
+ pull: number;
75
+ push: number;
76
+ identical: number;
77
+ conflict: number;
78
+ /** Local retractions the peer must be told about before the pair agrees. */
79
+ suppress: number;
80
+ /**
81
+ * Conflicts still needing an operator: those the policy declined to settle,
82
+ * plus supersede links whose direction could not be determined.
83
+ */
84
+ unresolved: number;
85
+ }
86
+ interface ReconcilePlan {
87
+ entries: ReconcilePlanEntry[];
88
+ byNamespace: ReconcileNamespaceReport[];
89
+ /**
90
+ * True when the two corpora already agree — every shared path matches and
91
+ * neither side holds a path the other lacks.
92
+ *
93
+ * This is the idempotency contract from #2150: running reconciliation against
94
+ * an already-converged peer must be a no-op, and a caller can skip the whole
95
+ * transfer phase on this flag alone.
96
+ */
97
+ converged: boolean;
98
+ }
99
+ /** Minimal shape the planner needs; `OfflineSyncFileState` satisfies it. */
100
+ type ReconcileFileState = Pick<OfflineSyncFileState, "path" | "sha256"> & Partial<Pick<OfflineSyncFileState, "mtimeMs" | "bytes">>;
101
+ interface ReconcileNamespaceInput {
102
+ namespace: string;
103
+ local: Iterable<ReconcileFileState>;
104
+ peer: Iterable<ReconcileFileState>;
105
+ /**
106
+ * File states agreed at the end of the last converged run with THIS peer.
107
+ * Absent on a bootstrap merge, which is why a path missing from one side is
108
+ * read as "never seen" rather than "deleted".
109
+ */
110
+ base?: Iterable<ReconcileFileState>;
111
+ /**
112
+ * Digests of FILES this side has retracted, in the same form as
113
+ * `ReconcileFileState.sha256` — a hash of the serialized file.
114
+ *
115
+ * Deliberately NOT `TombstoneEntry.contentHash`, which hashes the canonical
116
+ * raw fact text and therefore never equals a file digest (§13: one content
117
+ * form, everywhere). Mapping retracted fact hashes onto the file digests that
118
+ * carry them is the caller's job, because only the caller can read its own
119
+ * corpus; handing this the wrong form would silently plan `pull` and
120
+ * resurrect every retracted fact, so the parameter name states the form.
121
+ */
122
+ tombstonedFileSha256?: Iterable<string>;
123
+ /**
124
+ * Digests the PEER has retracted, same form.
125
+ *
126
+ * Without this a bootstrap merge cannot tell "the peer never had it" from
127
+ * "the peer deliberately retracted it": the peer census simply omits both,
128
+ * so a file we still hold is planned `push` and the peer's retraction is
129
+ * undone. Reconciliation is symmetric, so retraction has to be too.
130
+ *
131
+ * SCOPE: these sets decide what happens to files that still EXIST on one
132
+ * side. Sharing the retraction records themselves is not modelled here -
133
+ * tombstones are corpus files (`state/tombstones.jsonl`), so they reconcile
134
+ * as ordinary paths through this same plan. A digest both censuses have
135
+ * already dropped therefore produces no entry, by design: there is no file
136
+ * left to act on, and synthesizing a path-less entry would hand transport
137
+ * something it cannot apply.
138
+ */
139
+ peerTombstonedFileSha256?: Iterable<string>;
140
+ }
141
+ interface ReconcileOptions {
142
+ conflictPolicy?: ReconcileConflictPolicy;
143
+ }
144
+ /**
145
+ * A census the planner refuses to reason about.
146
+ *
147
+ * Dropping a malformed or contradictory record would let the planner return
148
+ * `converged: true` for a corpus it could not actually read, and transport is
149
+ * invited to skip everything on that flag — so bad input fails loudly instead
150
+ * (§1/§39).
151
+ */
152
+ declare class ReconcilePlanInputError extends Error {
153
+ constructor(message: string);
154
+ }
155
+ /**
156
+ * Plan one namespace. Exposed for callers that stream namespaces one at a time.
157
+ *
158
+ * Residency is one peer index + a set of local paths + the entry list. The
159
+ * local census is consumed as a stream and never materialized, so a 100k-file
160
+ * corpus does not hold two full censuses at once — but this is NOT constant
161
+ * memory, and a caller holding its own census arrays adds to that.
162
+ */
163
+ declare function planNamespaceReconciliation(input: ReconcileNamespaceInput, options?: ReconcileOptions): ReconcilePlanEntry[];
164
+ /** Aggregate entries into the per-namespace convergence report (#2150). */
165
+ declare function summarizeReconcilePlan(entries: readonly ReconcilePlanEntry[]): ReconcileNamespaceReport[];
166
+ /**
167
+ * Plan a full reconciliation across namespaces.
168
+ *
169
+ * `converged` is an affirmative claim that nothing needs to move, so it is
170
+ * derived from the entries rather than tracked alongside them: any action other
171
+ * than `identical` disproves it.
172
+ */
173
+ declare function planReconciliation(namespaces: readonly ReconcileNamespaceInput[], options?: ReconcileOptions): ReconcilePlan;
174
+
175
+ export { type ReconcileAction, type ReconcileConflictPolicy, type ReconcileFileState, type ReconcileNamespaceInput, type ReconcileNamespaceReport, type ReconcileOptions, type ReconcilePlan, type ReconcilePlanEntry, ReconcilePlanInputError, type ReconcileReason, type ReconcileResolution, planNamespaceReconciliation, planReconciliation, summarizeReconcilePlan };