instar 1.3.524 → 1.3.526

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 (64) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +77 -5
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  5. package/dist/config/ConfigDefaults.js +23 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/ConflictStore.d.ts +150 -0
  8. package/dist/core/ConflictStore.d.ts.map +1 -0
  9. package/dist/core/ConflictStore.js +247 -0
  10. package/dist/core/ConflictStore.js.map +1 -0
  11. package/dist/core/PostUpdateMigrator.d.ts +1 -0
  12. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  13. package/dist/core/PostUpdateMigrator.js +116 -0
  14. package/dist/core/PostUpdateMigrator.js.map +1 -1
  15. package/dist/core/ReplicatedStoreReader.d.ts +83 -0
  16. package/dist/core/ReplicatedStoreReader.d.ts.map +1 -0
  17. package/dist/core/ReplicatedStoreReader.js +100 -0
  18. package/dist/core/ReplicatedStoreReader.js.map +1 -0
  19. package/dist/core/ReplicationBudget.d.ts +183 -0
  20. package/dist/core/ReplicationBudget.d.ts.map +1 -0
  21. package/dist/core/ReplicationBudget.js +253 -0
  22. package/dist/core/ReplicationBudget.js.map +1 -0
  23. package/dist/core/RollbackUnmerge.d.ts +134 -0
  24. package/dist/core/RollbackUnmerge.d.ts.map +1 -0
  25. package/dist/core/RollbackUnmerge.js +286 -0
  26. package/dist/core/RollbackUnmerge.js.map +1 -0
  27. package/dist/core/UnionReader.d.ts +161 -0
  28. package/dist/core/UnionReader.d.ts.map +1 -0
  29. package/dist/core/UnionReader.js +0 -0
  30. package/dist/core/UnionReader.js.map +1 -0
  31. package/dist/monitoring/ActiveWorkSilenceSentinel.d.ts +56 -2
  32. package/dist/monitoring/ActiveWorkSilenceSentinel.d.ts.map +1 -1
  33. package/dist/monitoring/ActiveWorkSilenceSentinel.js +174 -10
  34. package/dist/monitoring/ActiveWorkSilenceSentinel.js.map +1 -1
  35. package/dist/monitoring/PromiseBeacon.d.ts +48 -0
  36. package/dist/monitoring/PromiseBeacon.d.ts.map +1 -1
  37. package/dist/monitoring/PromiseBeacon.js +186 -41
  38. package/dist/monitoring/PromiseBeacon.js.map +1 -1
  39. package/dist/monitoring/SubagentTracker.d.ts +9 -0
  40. package/dist/monitoring/SubagentTracker.d.ts.map +1 -1
  41. package/dist/monitoring/SubagentTracker.js +11 -0
  42. package/dist/monitoring/SubagentTracker.js.map +1 -1
  43. package/dist/monitoring/sentinelWiring.d.ts +20 -1
  44. package/dist/monitoring/sentinelWiring.d.ts.map +1 -1
  45. package/dist/monitoring/sentinelWiring.js +26 -0
  46. package/dist/monitoring/sentinelWiring.js.map +1 -1
  47. package/dist/scaffold/templates.d.ts.map +1 -1
  48. package/dist/scaffold/templates.js +13 -0
  49. package/dist/scaffold/templates.js.map +1 -1
  50. package/dist/server/AgentServer.d.ts +7 -0
  51. package/dist/server/AgentServer.d.ts.map +1 -1
  52. package/dist/server/AgentServer.js +3 -0
  53. package/dist/server/AgentServer.js.map +1 -1
  54. package/dist/server/routes.d.ts +10 -0
  55. package/dist/server/routes.d.ts.map +1 -1
  56. package/dist/server/routes.js +67 -0
  57. package/dist/server/routes.js.map +1 -1
  58. package/package.json +1 -1
  59. package/src/data/builtin-manifest.json +64 -64
  60. package/src/scaffold/templates.ts +13 -0
  61. package/upgrades/1.3.525.md +32 -0
  62. package/upgrades/1.3.526.md +83 -0
  63. package/upgrades/side-effects/hlc-step4-5-union-rollback-bounds.md +148 -0
  64. package/upgrades/side-effects/honest-progress-messaging.md +150 -0
@@ -0,0 +1,134 @@
1
+ /**
2
+ * RollbackUnmerge — deterministic origin-drop un-merge with no dangling references
3
+ * (WS2 replicated-store foundation, Component 6 / §7.4).
4
+ *
5
+ * Spec: docs/specs/multi-machine-replicated-store-foundation.md §7.4 (disabling
6
+ * stateSync.<store> for a peer atomically DROPS that origin's foreign namespace —
7
+ * a real un-merge, not a flag; the union recomputes live; the dropped streams +
8
+ * per-peer meta + snapshot cache are QUARANTINED-ASIDE, reversible + auditable,
9
+ * NEVER a destructive delete; no dangling references; a conflictId whose
10
+ * version-pair referenced the dropped origin auto-RESOLVES + its attention item
11
+ * closes), §11 (machine-local-by-design — a machine un-merges ITS OWN copy of a
12
+ * peer's namespace, never reaches into the peer).
13
+ *
14
+ * THE LIVE-RECOMPUTE INVARIANT (why this is a real un-merge, §7.4 step 1+3).
15
+ * Because records are keyed by (recordKey, origin) and the union reader computes
16
+ * the union LIVE from the per-origin namespaces, REMOVING an origin from the set
17
+ * of participating namespaces instantly removes its contribution — no rewrite of
18
+ * any surviving record is needed. A key whose winning value came from the dropped
19
+ * origin reverts to the HLC-latest among the REMAINING origins (or to "no record"
20
+ * if none remains). The DroppedOriginRegistry is the live exclusion set the
21
+ * union reader consults; the quarantine-aside is the durability/reversibility leg.
22
+ *
23
+ * QUARANTINE-ASIDE (§7.4 step 2 — NEVER a destructive delete). The dropped
24
+ * origin's replica streams + per-peer meta + snapshot-cache entries are RENAMED
25
+ * aside (rename is non-destructive: node:fs renameSync, mirroring
26
+ * JournalSyncApplier.quarantineReplica) into a bounded-retain ring; the ONLY
27
+ * actual deletion — pruning the oldest retained un-merge past the ring bound —
28
+ * goes through SafeFsExecutor.safeRmSync (the destructive-fs funnel + audit
29
+ * trail). A re-merge restores from the ring (reversibility).
30
+ */
31
+ /** How many un-merge quarantine sets to retain per (store, origin) before the
32
+ * oldest is pruned (mirrors JournalSyncApplier.MAX_QUARANTINE_PER_STREAM). */
33
+ export declare const MAX_UNMERGE_RETAIN = 2;
34
+ /** Persistent record of which (store, origin) pairs are currently un-merged. The
35
+ * union reader consults this so a dropped origin is excluded LIVE — the
36
+ * un-merge survives restarts. */
37
+ export interface DroppedOriginEntry {
38
+ store: string;
39
+ origin: string;
40
+ droppedAt: string;
41
+ }
42
+ /** The durable set of un-merged (store, origin) pairs. Consulted by the union
43
+ * reader's participating-namespace filter (§7.4). */
44
+ export declare class DroppedOriginRegistry {
45
+ private readonly stateDir;
46
+ private readonly readFileSync;
47
+ private readonly existsSync;
48
+ private doc;
49
+ constructor(seams: {
50
+ stateDir: string;
51
+ readFileSync?: (p: string) => string;
52
+ existsSync?: (p: string) => boolean;
53
+ });
54
+ private docPath;
55
+ private load;
56
+ private persist;
57
+ /** Is (store, origin) currently un-merged (excluded from the union)? */
58
+ isDropped(store: string, origin: string): boolean;
59
+ /** The set of dropped origins for a store (for the reader's namespace filter). */
60
+ droppedOrigins(store: string): Set<string>;
61
+ add(store: string, origin: string, at: string): void;
62
+ remove(store: string, origin: string): void;
63
+ list(): DroppedOriginEntry[];
64
+ }
65
+ /** The seams RollbackUnmerge needs from the journal/applier (DI'd for testability). */
66
+ export interface RollbackUnmergeSeams {
67
+ /** Absolute path to the peers/ replica directory
68
+ * (`<stateDir>/state/coherence-journal/peers`). */
69
+ peersDir: () => string;
70
+ /** The journal kinds a store rides (so we move every replica stream for it). */
71
+ kindsForStore: (store: string) => string[];
72
+ /** Injected wall clock. */
73
+ now: () => Date;
74
+ /** Drop every snapshot-cache entry for the origin (§7.4 step 2 — the cache leg).
75
+ * Wired to SnapshotCache.dropOrigin. */
76
+ dropSnapshotCacheForOrigin: (origin: string) => void;
77
+ /** Auto-resolve conflicts referencing the dropped origin (§7.4) — returns the
78
+ * closed conflictIds. Wired to ConflictStore.autoResolveForDroppedOrigin. */
79
+ autoResolveConflicts: (origin: string) => string[];
80
+ /** Close an attention item for a conflictId (signal-only; idempotent). */
81
+ closeAttention?: (conflictId: string) => void;
82
+ /** Optional fs seams (tests); default node:fs. */
83
+ existsSync?: (p: string) => boolean;
84
+ readdirSync?: (p: string) => string[];
85
+ renameSync?: (from: string, to: string) => void;
86
+ /** Optional structured logger (default no-op). */
87
+ log?: (event: string, detail: Record<string, unknown>) => void;
88
+ }
89
+ /** Result of an un-merge (§7.4) — auditable. */
90
+ export interface UnmergeResult {
91
+ store: string;
92
+ origin: string;
93
+ /** Replica stream files quarantined-aside. */
94
+ movedStreams: number;
95
+ /** Per-peer meta files quarantined-aside. */
96
+ movedMeta: number;
97
+ /** Conflict ids auto-resolved because they referenced the dropped origin. */
98
+ closedConflicts: string[];
99
+ at: string;
100
+ }
101
+ /**
102
+ * RollbackUnmerge — drops a (store, origin) from the union deterministically
103
+ * (§7.4). The order matters:
104
+ * 1. Register the drop in DroppedOriginRegistry FIRST — the union recomputes
105
+ * live, so from this instant every read excludes the origin (no dangling refs).
106
+ * 2. Quarantine-aside the replica streams + per-peer meta + snapshot cache
107
+ * (rename + bounded-retain; reversible, auditable; the prune leg through
108
+ * SafeFsExecutor).
109
+ * 3. Auto-resolve every conflict that referenced the dropped origin + close its
110
+ * attention item.
111
+ * Reversible: reMerge() restores the registry entry + the most-recent quarantined
112
+ * streams.
113
+ */
114
+ export declare class RollbackUnmerge {
115
+ private readonly registry;
116
+ private readonly seams;
117
+ private readonly existsSync;
118
+ private readonly readdirSync;
119
+ private readonly renameSync;
120
+ private readonly log;
121
+ constructor(registry: DroppedOriginRegistry, seams: RollbackUnmergeSeams);
122
+ /** Un-merge (store, origin) — §7.4. Idempotent: a re-call after the drop is a
123
+ * no-op for the registry but still re-attempts any leftover quarantine. */
124
+ unmergeOrigin(store: string, origin: string): UnmergeResult;
125
+ /** Re-merge (reversibility, §7.4): remove the drop + restore the MOST RECENT
126
+ * quarantined streams/meta for (store, origin). Returns the count restored. */
127
+ reMerge(store: string, origin: string): {
128
+ restored: number;
129
+ };
130
+ /** Prune retained un-merge sets past MAX_UNMERGE_RETAIN per base (the ONLY
131
+ * destructive leg — through SafeFsExecutor, audited). */
132
+ private pruneRetained;
133
+ }
134
+ //# sourceMappingURL=RollbackUnmerge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RollbackUnmerge.d.ts","sourceRoot":"","sources":["../../src/core/RollbackUnmerge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,286 @@
1
+ /**
2
+ * RollbackUnmerge — deterministic origin-drop un-merge with no dangling references
3
+ * (WS2 replicated-store foundation, Component 6 / §7.4).
4
+ *
5
+ * Spec: docs/specs/multi-machine-replicated-store-foundation.md §7.4 (disabling
6
+ * stateSync.<store> for a peer atomically DROPS that origin's foreign namespace —
7
+ * a real un-merge, not a flag; the union recomputes live; the dropped streams +
8
+ * per-peer meta + snapshot cache are QUARANTINED-ASIDE, reversible + auditable,
9
+ * NEVER a destructive delete; no dangling references; a conflictId whose
10
+ * version-pair referenced the dropped origin auto-RESOLVES + its attention item
11
+ * closes), §11 (machine-local-by-design — a machine un-merges ITS OWN copy of a
12
+ * peer's namespace, never reaches into the peer).
13
+ *
14
+ * THE LIVE-RECOMPUTE INVARIANT (why this is a real un-merge, §7.4 step 1+3).
15
+ * Because records are keyed by (recordKey, origin) and the union reader computes
16
+ * the union LIVE from the per-origin namespaces, REMOVING an origin from the set
17
+ * of participating namespaces instantly removes its contribution — no rewrite of
18
+ * any surviving record is needed. A key whose winning value came from the dropped
19
+ * origin reverts to the HLC-latest among the REMAINING origins (or to "no record"
20
+ * if none remains). The DroppedOriginRegistry is the live exclusion set the
21
+ * union reader consults; the quarantine-aside is the durability/reversibility leg.
22
+ *
23
+ * QUARANTINE-ASIDE (§7.4 step 2 — NEVER a destructive delete). The dropped
24
+ * origin's replica streams + per-peer meta + snapshot-cache entries are RENAMED
25
+ * aside (rename is non-destructive: node:fs renameSync, mirroring
26
+ * JournalSyncApplier.quarantineReplica) into a bounded-retain ring; the ONLY
27
+ * actual deletion — pruning the oldest retained un-merge past the ring bound —
28
+ * goes through SafeFsExecutor.safeRmSync (the destructive-fs funnel + audit
29
+ * trail). A re-merge restores from the ring (reversibility).
30
+ */
31
+ import fs from 'node:fs';
32
+ import path from 'node:path';
33
+ import { SafeFsExecutor } from './SafeFsExecutor.js';
34
+ import { sanitizeMachineId } from './CoherenceJournal.js';
35
+ /** How many un-merge quarantine sets to retain per (store, origin) before the
36
+ * oldest is pruned (mirrors JournalSyncApplier.MAX_QUARANTINE_PER_STREAM). */
37
+ export const MAX_UNMERGE_RETAIN = 2;
38
+ /** The durable set of un-merged (store, origin) pairs. Consulted by the union
39
+ * reader's participating-namespace filter (§7.4). */
40
+ export class DroppedOriginRegistry {
41
+ stateDir;
42
+ readFileSync;
43
+ existsSync;
44
+ doc;
45
+ constructor(seams) {
46
+ this.stateDir = seams.stateDir;
47
+ this.readFileSync = seams.readFileSync ?? ((p) => fs.readFileSync(p, 'utf-8'));
48
+ this.existsSync = seams.existsSync ?? ((p) => fs.existsSync(p));
49
+ this.doc = this.load();
50
+ }
51
+ docPath() {
52
+ return path.join(this.stateDir, 'state', 'state-sync', 'dropped-origins.json');
53
+ }
54
+ load() {
55
+ const p = this.docPath();
56
+ if (!this.existsSync(p))
57
+ return { version: 1, dropped: [] };
58
+ try {
59
+ const raw = JSON.parse(this.readFileSync(p));
60
+ const dropped = Array.isArray(raw.dropped)
61
+ ? raw.dropped.filter((d) => !!d && typeof d.store === 'string' && typeof d.origin === 'string')
62
+ : [];
63
+ return { version: 1, dropped };
64
+ }
65
+ catch {
66
+ // @silent-fallback-ok: a corrupt dropped-origins file degrades to "nothing
67
+ // dropped" — the CONSERVATIVE direction is to FAIL CLOSED (treat a corrupt
68
+ // un-merge ledger as no un-merge ⇒ the origin participates again). That is
69
+ // safe: a stale-but-readable peer namespace re-joining the union is a normal
70
+ // re-merge, never data loss; the operator re-issues the un-merge if needed.
71
+ return { version: 1, dropped: [] };
72
+ }
73
+ }
74
+ persist() {
75
+ SafeFsExecutor.atomicWriteJsonSync(this.docPath(), this.doc, { operation: 'dropped-origin-registry:persist' });
76
+ }
77
+ /** Is (store, origin) currently un-merged (excluded from the union)? */
78
+ isDropped(store, origin) {
79
+ return this.doc.dropped.some((d) => d.store === store && d.origin === origin);
80
+ }
81
+ /** The set of dropped origins for a store (for the reader's namespace filter). */
82
+ droppedOrigins(store) {
83
+ return new Set(this.doc.dropped.filter((d) => d.store === store).map((d) => d.origin));
84
+ }
85
+ add(store, origin, at) {
86
+ if (this.isDropped(store, origin))
87
+ return;
88
+ this.doc.dropped.push({ store, origin, droppedAt: at });
89
+ this.persist();
90
+ }
91
+ remove(store, origin) {
92
+ const before = this.doc.dropped.length;
93
+ this.doc.dropped = this.doc.dropped.filter((d) => !(d.store === store && d.origin === origin));
94
+ if (this.doc.dropped.length !== before)
95
+ this.persist();
96
+ }
97
+ list() {
98
+ return [...this.doc.dropped];
99
+ }
100
+ }
101
+ /**
102
+ * RollbackUnmerge — drops a (store, origin) from the union deterministically
103
+ * (§7.4). The order matters:
104
+ * 1. Register the drop in DroppedOriginRegistry FIRST — the union recomputes
105
+ * live, so from this instant every read excludes the origin (no dangling refs).
106
+ * 2. Quarantine-aside the replica streams + per-peer meta + snapshot cache
107
+ * (rename + bounded-retain; reversible, auditable; the prune leg through
108
+ * SafeFsExecutor).
109
+ * 3. Auto-resolve every conflict that referenced the dropped origin + close its
110
+ * attention item.
111
+ * Reversible: reMerge() restores the registry entry + the most-recent quarantined
112
+ * streams.
113
+ */
114
+ export class RollbackUnmerge {
115
+ registry;
116
+ seams;
117
+ existsSync;
118
+ readdirSync;
119
+ renameSync;
120
+ log;
121
+ constructor(registry, seams) {
122
+ this.registry = registry;
123
+ this.seams = seams;
124
+ this.existsSync = seams.existsSync ?? ((p) => fs.existsSync(p));
125
+ this.readdirSync = seams.readdirSync ?? ((p) => fs.readdirSync(p));
126
+ this.renameSync = seams.renameSync ?? ((from, to) => fs.renameSync(from, to));
127
+ this.log = seams.log ?? (() => { });
128
+ }
129
+ /** Un-merge (store, origin) — §7.4. Idempotent: a re-call after the drop is a
130
+ * no-op for the registry but still re-attempts any leftover quarantine. */
131
+ unmergeOrigin(store, origin) {
132
+ const at = this.seams.now().toISOString();
133
+ // (1) Register the drop FIRST — the live union excludes the origin instantly.
134
+ this.registry.add(store, origin, at);
135
+ const stamp = this.seams.now().getTime();
136
+ const peers = this.seams.peersDir();
137
+ const safe = sanitizeMachineId(origin);
138
+ let movedStreams = 0;
139
+ let movedMeta = 0;
140
+ if (this.existsSync(peers)) {
141
+ let names = [];
142
+ try {
143
+ names = this.readdirSync(peers);
144
+ }
145
+ catch {
146
+ names = [];
147
+ }
148
+ const kinds = new Set(this.seams.kindsForStore(store));
149
+ for (const name of names) {
150
+ // Replica stream for this origin + one of the store's kinds:
151
+ // <safe>.<kind>.jsonl (NOT a .quarantine. or numbered-archive file)
152
+ const streamMatch = /^(.+)\.([^.]+)\.jsonl$/.exec(name);
153
+ if (streamMatch && streamMatch[1] === safe && kinds.has(streamMatch[2]) && !name.includes('.quarantine.')) {
154
+ const from = path.join(peers, name);
155
+ const to = path.join(peers, `${name}.unmerge.${stamp}`);
156
+ try {
157
+ this.renameSync(from, to);
158
+ movedStreams++;
159
+ }
160
+ catch (e) {
161
+ this.log('unmerge-rename-failed', { name, error: e?.message });
162
+ }
163
+ continue;
164
+ }
165
+ // Per-peer meta for this origin: <safe>.meta.json
166
+ if (name === `${safe}.meta.json`) {
167
+ const from = path.join(peers, name);
168
+ const to = path.join(peers, `${name}.unmerge.${stamp}`);
169
+ try {
170
+ this.renameSync(from, to);
171
+ movedMeta++;
172
+ }
173
+ catch (e) {
174
+ this.log('unmerge-meta-rename-failed', { name, error: e?.message });
175
+ }
176
+ }
177
+ }
178
+ this.pruneRetained(peers, safe);
179
+ }
180
+ // (2 cont.) drop the snapshot-cache entries for the origin.
181
+ try {
182
+ this.seams.dropSnapshotCacheForOrigin(origin);
183
+ }
184
+ catch (e) {
185
+ this.log('unmerge-cache-drop-failed', { origin, error: e?.message });
186
+ }
187
+ // (3) Auto-resolve conflicts referencing the dropped origin + close attention.
188
+ let closedConflicts = [];
189
+ try {
190
+ closedConflicts = this.seams.autoResolveConflicts(origin);
191
+ for (const id of closedConflicts)
192
+ this.seams.closeAttention?.(id);
193
+ }
194
+ catch (e) {
195
+ this.log('unmerge-conflict-resolve-failed', { origin, error: e?.message });
196
+ }
197
+ this.log('unmerge', { store, origin, movedStreams, movedMeta, closedConflicts: closedConflicts.length });
198
+ return { store, origin, movedStreams, movedMeta, closedConflicts, at };
199
+ }
200
+ /** Re-merge (reversibility, §7.4): remove the drop + restore the MOST RECENT
201
+ * quarantined streams/meta for (store, origin). Returns the count restored. */
202
+ reMerge(store, origin) {
203
+ this.registry.remove(store, origin);
204
+ const peers = this.seams.peersDir();
205
+ if (!this.existsSync(peers))
206
+ return { restored: 0 };
207
+ const safe = sanitizeMachineId(origin);
208
+ let names = [];
209
+ try {
210
+ names = this.readdirSync(peers);
211
+ }
212
+ catch {
213
+ return { restored: 0 };
214
+ }
215
+ // Group quarantined files by their base name; restore the newest stamp.
216
+ const re = new RegExp(`^(${escapeRegExp(safe)}\\..+)\\.unmerge\\.(\\d+)$`);
217
+ const byBase = new Map();
218
+ for (const name of names) {
219
+ const m = re.exec(name);
220
+ if (!m)
221
+ continue;
222
+ const base = m[1];
223
+ const stamp = Number(m[2]);
224
+ const prev = byBase.get(base);
225
+ if (!prev || stamp > prev.stamp)
226
+ byBase.set(base, { stamp, file: name });
227
+ }
228
+ let restored = 0;
229
+ for (const [base, { file }] of byBase) {
230
+ // Only restore if the live file isn't already present (a re-merge after a
231
+ // fresh tail must not clobber newer data — leave the quarantine as audit).
232
+ const liveTarget = path.join(peers, base);
233
+ if (this.existsSync(liveTarget))
234
+ continue;
235
+ try {
236
+ this.renameSync(path.join(peers, file), liveTarget);
237
+ restored++;
238
+ }
239
+ catch (e) {
240
+ this.log('remerge-restore-failed', { file, error: e?.message });
241
+ }
242
+ }
243
+ this.log('remerge', { store, origin, restored });
244
+ return { restored };
245
+ }
246
+ /** Prune retained un-merge sets past MAX_UNMERGE_RETAIN per base (the ONLY
247
+ * destructive leg — through SafeFsExecutor, audited). */
248
+ pruneRetained(peers, safe) {
249
+ let names = [];
250
+ try {
251
+ names = this.readdirSync(peers);
252
+ }
253
+ catch {
254
+ return;
255
+ }
256
+ const re = new RegExp(`^(${escapeRegExp(safe)}\\..+)\\.unmerge\\.(\\d+)$`);
257
+ const byBase = new Map();
258
+ for (const name of names) {
259
+ const m = re.exec(name);
260
+ if (!m)
261
+ continue;
262
+ const base = m[1];
263
+ const arr = byBase.get(base) ?? [];
264
+ arr.push({ stamp: Number(m[2]), name });
265
+ byBase.set(base, arr);
266
+ }
267
+ for (const arr of byBase.values()) {
268
+ if (arr.length <= MAX_UNMERGE_RETAIN)
269
+ continue;
270
+ arr.sort((a, b) => a.stamp - b.stamp); // oldest first
271
+ const drop = arr.slice(0, arr.length - MAX_UNMERGE_RETAIN);
272
+ for (const d of drop) {
273
+ try {
274
+ SafeFsExecutor.safeRmSync(path.join(peers, d.name), { force: true, operation: 'rollback-unmerge:prune-retained' });
275
+ }
276
+ catch (e) {
277
+ this.log('unmerge-prune-failed', { name: d.name, error: e?.message });
278
+ }
279
+ }
280
+ }
281
+ }
282
+ }
283
+ function escapeRegExp(s) {
284
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
285
+ }
286
+ //# sourceMappingURL=RollbackUnmerge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RollbackUnmerge.js","sourceRoot":"","sources":["../../src/core/RollbackUnmerge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * UnionReader — the no-clobber union merge rule + the SOUND last-writer-witness
3
+ * concurrency detector (WS2 replicated-store foundation, Component 6 / build-order
4
+ * step 4, §7.2).
5
+ *
6
+ * Spec: docs/specs/multi-machine-replicated-store-foundation.md §7.2 (the union
7
+ * merge rule + the concurrency detector that closes BLOCKER-4), §9 (N-machine
8
+ * convergence — a version SET, never N-choose-2 pairwise conflicts), §12 #5 (the
9
+ * adversarial-clock err-direction invariant), §14 (the provable err-toward-flag
10
+ * decision).
11
+ *
12
+ * PURE LOGIC. Imports ONLY the HLC primitive + node:crypto (for the stable
13
+ * conflictId hash). No fs, no Date, no network. Every function is a pure function
14
+ * of its inputs — so the load-bearing BLOCKER-4 detector is unit-testable against
15
+ * an adversarial clock layout (§12 #5).
16
+ *
17
+ * THE BLOCKER-4 PRIMITIVE (the load-bearing reason this module exists). Plain
18
+ * `HybridLogicalClock.compare` is a TOTAL order: it NEVER returns "concurrent",
19
+ * so it is UNSOUND as a concurrency test in BOTH directions — it over-flags
20
+ * sequential edits AND silently HLC-resolves (clobbers) a genuinely concurrent
21
+ * pair. We never use `compare(w2.hlc, w1.hlc)` to decide concurrency. Instead we
22
+ * use the **last-writer-witness**: a record carries `observed` = the HLC the
23
+ * author had ALREADY merged for THIS recordKey before writing. W2 is
24
+ * sequential-after W1 IFF `compare(w2.observed, w1.hlc) >= 0` (W2's author
25
+ * provably saw ≥ W1). A missing/below witness CANNOT prove W2 saw W1 ⇒ FLAG.
26
+ * The only path to "sequential" is a POSITIVE witness — the error direction is
27
+ * always toward flag, never toward silent clobber.
28
+ */
29
+ import { type HlcTimestamp } from './HybridLogicalClock.js';
30
+ import type { ReplicatedEnvelope } from './ReplicatedRecordEnvelope.js';
31
+ /**
32
+ * A store's impact tier governs the CONCURRENT-edit behavior (§7.2):
33
+ * - `high` (preferences, relationships): APPEND-BOTH-AND-FLAG — both versions
34
+ * preserved, ONE deduped conflict, never a silent overwrite.
35
+ * - `low` (scores, manifests): field-level HLC-wins WITH a divergence flag —
36
+ * the latest HLC wins but the overwrite is FLAGGED, never silent.
37
+ * A store declares its tier at registration; the union reader looks it up per
38
+ * read. There is no third "silent" tier by construction — every concurrent
39
+ * resolution is surfaced.
40
+ */
41
+ export type ImpactTier = 'high' | 'low';
42
+ /**
43
+ * One origin's current record for a recordKey, as the union reader sees it. The
44
+ * `envelope` carries the load-bearing `hlc` (the total order), `op` (put/delete
45
+ * tombstone), `origin` (author machine id — preserved end-to-end so rollback is
46
+ * a real un-merge, §7.1), and the `observed` witness (§7.2). `data` is the
47
+ * store-specific portion (envelope fields stripped).
48
+ */
49
+ export interface OriginRecord {
50
+ origin: string;
51
+ envelope: ReplicatedEnvelope;
52
+ data: Record<string, unknown>;
53
+ }
54
+ /** The verdict for a PAIR of records on the same recordKey from different origins. */
55
+ export type WitnessVerdict = 'sequential-after' | 'concurrent';
56
+ /**
57
+ * Classify a PAIR (w1, w2) on the SAME recordKey from DIFFERENT origins (§7.2).
58
+ *
59
+ * Returns `'sequential-after'` ONLY when w2 provably saw w1 — i.e. w2 carries an
60
+ * `observed` witness AND `compare(w2.observed, w1.hlc) >= 0` (w2's author had
61
+ * already merged w1's version-or-later before writing). EVERY other case is
62
+ * `'concurrent'` ⇒ FLAG:
63
+ * - w2.observed absent → cannot prove w2 saw w1 → concurrent.
64
+ * - w2.observed below w1.hlc → w2's author had NOT yet seen w1 → concurrent.
65
+ *
66
+ * PROVABLE err-toward-flag (§7.2 / §12 #5): a clock arranged to make w1/w2
67
+ * `compare` cleanly (one appears strictly later by wall clock) CANNOT make
68
+ * `observed >= the-other.hlc` true unless the author genuinely merged it first.
69
+ * The WITNESS, not the wall clock, decides — and the error is always toward flag.
70
+ *
71
+ * This function deliberately NEVER consults `compare(w1.hlc, w2.hlc)` to decide
72
+ * concurrency — that scalar comparison is the unsound test §7.2 replaces.
73
+ */
74
+ export declare function classifyPair(w1: ReplicatedEnvelope, w2: ReplicatedEnvelope): WitnessVerdict;
75
+ /**
76
+ * Is the SET of distinct-origin records for one recordKey mutually sequential
77
+ * (i.e. a clean linear edit history with a single defensible winner), or are at
78
+ * least two of them concurrent (⇒ FLAG)? Returns `{ concurrent: false, winner }`
79
+ * for a clean chain (the HLC-max record whose author witness-chains over the
80
+ * others), or `{ concurrent: true, versions }` for the version SET to preserve.
81
+ *
82
+ * N-machine correctness (§9): N concurrent edits to one key produce ONE conflict
83
+ * with all N in the version set — NOT N-choose-2 pairwise conflicts. We compute
84
+ * the HLC-max candidate, then verify it is sequential-after EVERY other origin's
85
+ * record (its author provably saw them all). If any other origin is concurrent
86
+ * with the candidate, the whole set is concurrent.
87
+ *
88
+ * Err-toward-flag is structural: the candidate is "the winner" ONLY when it
89
+ * witness-dominates every peer; one missing/below witness collapses the verdict
90
+ * to concurrent.
91
+ */
92
+ export declare function classifyOriginSet(records: OriginRecord[]): {
93
+ concurrent: false;
94
+ winner: OriginRecord;
95
+ } | {
96
+ concurrent: true;
97
+ versions: OriginRecord[];
98
+ };
99
+ /**
100
+ * A stable, deterministic conflict id = `hash(recordKey, sorted version-set)`
101
+ * (§7.2). Idempotent on `(recordKey, version-set)`: re-discovering the SAME
102
+ * unresolved conflict yields the SAME id, so append-both never appends a third
103
+ * copy and the attention surface dedupes on it. The version set is sorted by HLC
104
+ * key first so the id is independent of origin iteration order (N-machine safe).
105
+ */
106
+ export declare function conflictId(recordKey: string, versions: HlcTimestamp[]): string;
107
+ /**
108
+ * The per-recordKey union result (§7.2):
109
+ * - `value` — the resolved winning record, or null when the key resolves to "no
110
+ * record" (every origin's latest is a delete tombstone, or there are no
111
+ * origins). For a HIGH-impact unresolved conflict `value` is null (neither
112
+ * version clobbers the other — both are preserved in `conflict.versions` for
113
+ * the operator to resolve). For a LOW-impact divergence the HLC-winner is the
114
+ * `value` and `divergenceFlag` is set.
115
+ * - `conflict` — a ConflictRecord when a HIGH-impact concurrent divergence was
116
+ * detected (append-both-and-flag), else null.
117
+ * - `divergenceFlag` — true when a LOW-impact concurrent overwrite occurred (the
118
+ * HLC-winner won but the overwrite is FLAGGED, never silent).
119
+ */
120
+ export interface UnionResult {
121
+ recordKey: string;
122
+ value: OriginRecord | null;
123
+ conflict: ConflictDescriptor | null;
124
+ divergenceFlag: boolean;
125
+ }
126
+ /**
127
+ * The conflict descriptor the union reader EMITS (the pure, content-addressed
128
+ * shape). The durable lifecycle (recurrence count, forced-resolution flag,
129
+ * first-seen timestamp, resolution) lives in ConflictStore — this is the
130
+ * detection-time fact: the stable id + the preserved version SET. `versions`
131
+ * carries every concurrent origin's record (a SET, §9 — never just a pair).
132
+ */
133
+ export interface ConflictDescriptor {
134
+ conflictId: string;
135
+ recordKey: string;
136
+ versions: OriginRecord[];
137
+ }
138
+ /**
139
+ * The no-clobber union merge for one recordKey (§7.2). `perOrigin` is the set of
140
+ * distinct-origin current records for the key (own + every non-dropped peer
141
+ * namespace; the caller has already excluded a rolled-back origin, §7.4). `tier`
142
+ * is the store's impact tier.
143
+ *
144
+ * Rules (§7.2):
145
+ * - 0 origins ⇒ no record.
146
+ * - 1 origin ⇒ return it (a delete tombstone resolves to "no record" for the
147
+ * value, but is still the authoritative latest).
148
+ * - multiple origins, provably SEQUENTIAL (witness-dominated clean chain) ⇒ the
149
+ * HLC-winner wins, no conflict, no divergence flag (a normal edit history).
150
+ * - multiple origins, CONCURRENT ⇒ tier-dependent:
151
+ * - high ⇒ APPEND-BOTH-AND-FLAG: value=null (neither clobbers), conflict set.
152
+ * - low ⇒ HLC-wins WITH divergenceFlag=true (winner returned, overwrite flagged).
153
+ *
154
+ * A delete tombstone participates in the merge as a normal HLC-positioned record
155
+ * (so a concurrent delete↔put is a real conflict / divergence, never a silent
156
+ * resurrection or silent loss). When the resolved winner is a delete, `value`
157
+ * resolves to null (the key is deleted) but the winning RECORD is still returned
158
+ * via the conflict/divergence path so the caller can see WHY.
159
+ */
160
+ export declare function readUnion(recordKey: string, perOrigin: OriginRecord[], tier: ImpactTier): UnionResult;
161
+ //# sourceMappingURL=UnionReader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnionReader.d.ts","sourceRoot":"","sources":["../../src/core/UnionReader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAMxE;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,CAAC;AAMxC;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAMD,sFAAsF;AACtF,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG,YAAY,CAAC;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,kBAAkB,GAAG,cAAc,CAM3F;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EAAE,GACtB;IAAE,UAAU,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;CAAE,CAyB9F;AAWD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,CAS9E;AAMD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,UAAU,GAAG,WAAW,CAuDrG"}
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnionReader.js","sourceRoot":"","sources":["../../src/core/UnionReader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EACL,kBAAkB,EAClB,eAAe,GAEhB,MAAM,yBAAyB,CAAC;AA2CjC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,YAAY,CAAC,EAAsB,EAAE,EAAsB;IACzE,MAAM,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC5B,4EAA4E;IAC5E,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,YAAY,CAAC;IAC/C,iEAAiE;IACjE,OAAO,kBAAkB,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,YAAY,CAAC;AAC9F,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAuB;IAEvB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3E,6EAA6E;IAC7E,+EAA+E;IAC/E,mDAAmD;IACnD,IAAI,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,IAAI,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACpF,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,gFAAgF;IAChF,iFAAiF;IACjF,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM;YAAE,SAAS;QAChD,IAAI,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,KAAK,kBAAkB,EAAE,CAAC;YAC5E,yEAAyE;YACzE,4EAA4E;YAC5E,iDAAiD;YACjD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/D,CAAC;IACH,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAClD,CAAC;AAED,qFAAqF;AACrF,SAAS,YAAY,CAAC,OAAuB;IAC3C,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;AACjG,CAAC;AAED,8EAA8E;AAC9E,qEAAqE;AACrE,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,QAAwB;IACpE,MAAM,UAAU,GAAG,CAAC,GAAG,QAAQ,CAAC;SAC7B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SAChD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpB,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACd,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAuCD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,SAAS,CAAC,SAAiB,EAAE,SAAyB,EAAE,IAAgB;IACtF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;IAC3E,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC1B,OAAO;YACL,SAAS;YACT,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;YAClD,QAAQ,EAAE,IAAI;YACd,cAAc,EAAE,KAAK;SACtB,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,OAAO;YACL,SAAS;YACT,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YACtD,QAAQ,EAAE,IAAI;YACd,cAAc,EAAE,KAAK;SACtB,CAAC;IACJ,CAAC;IAED,yBAAyB;IACzB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,6EAA6E;QAC7E,2EAA2E;QAC3E,wEAAwE;QACxE,OAAO;YACL,SAAS;YACT,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE;gBACR,UAAU,EAAE,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACtE,SAAS;gBACT,QAAQ;aACT;YACD,cAAc,EAAE,KAAK;SACtB,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,8EAA8E;IAC9E,IAAI,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,MAAM,GAAG,CAAC,CAAC;IACtF,CAAC;IACD,OAAO;QACL,SAAS;QACT,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;QACtD,QAAQ,EAAE,IAAI;QACd,cAAc,EAAE,IAAI;KACrB,CAAC;AACJ,CAAC"}