opencode-swarm 7.164.11 → 7.164.12

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 (107) hide show
  1. package/.opencode/skills/engineering-conventions/SKILL.md +1 -1
  2. package/dist/background/pending-delegations.d.ts +7 -2
  3. package/dist/background/pr-subscriptions.d.ts +3 -3
  4. package/dist/cli/{coder-settlement-0rfk8y11.js → coder-settlement-yh1q5fr3.js} +15 -16
  5. package/dist/cli/{config-doctor-j17b7qq2.js → config-doctor-bd4yzmh7.js} +3 -3
  6. package/dist/cli/{core-evhjjrtt.js → core-y0s1vmyc.js} +2 -2
  7. package/dist/cli/{curation-policy-vj6z5hvr.js → curation-policy-gs1qakn7.js} +5 -6
  8. package/dist/cli/{curator-vqd5yeck.js → curator-brqack0j.js} +48 -46
  9. package/dist/cli/{curator-drift-ez81vg2t.js → curator-drift-1sg9yk2m.js} +14 -12
  10. package/dist/cli/curator-llm-factory-dtsjc3zf.js +70 -0
  11. package/dist/cli/{dispatch-dqybvj8v.js → dispatch-c7xjt7js.js} +3 -3
  12. package/dist/cli/{evidence-summary-service-937x7rbg.js → evidence-summary-service-nqbcvp30.js} +24 -23
  13. package/dist/cli/{gate-evidence-q1cehr4k.js → gate-evidence-9stdtpej.js} +9 -10
  14. package/dist/cli/guardrail-explain-3ff1z3fm.js +71 -0
  15. package/dist/cli/{guardrail-log-3g93f3g2.js → guardrail-log-sf39x00b.js} +10 -10
  16. package/dist/cli/{guardrail-reset-qe2r5qzq.js → guardrail-reset-9wr6a60s.js} +48 -46
  17. package/dist/cli/hive-promoter-bqtj8261.js +85 -0
  18. package/dist/cli/{index-kv2tdkk5.js → index-03enxdaq.js} +1 -1
  19. package/dist/cli/{index-ggrp3v00.js → index-0byep4pe.js} +15 -17
  20. package/dist/cli/{index-2bgr671r.js → index-2bhz5k91.js} +20 -2
  21. package/dist/cli/{index-mmjamc9b.js → index-3fnym6ac.js} +5 -5
  22. package/dist/cli/{index-d9entpn3.js → index-40twgsyh.js} +4 -4
  23. package/dist/cli/{index-ggtmsm84.js → index-4mjmnysc.js} +1 -1
  24. package/dist/cli/{index-e90em0x4.js → index-4vb56w2k.js} +12 -17
  25. package/dist/cli/{index-r656jh9j.js → index-5wc5pbzc.js} +3700 -2609
  26. package/dist/cli/{index-w3gtqey0.js → index-6chhqq2n.js} +4 -4
  27. package/dist/cli/{index-qbw3yw2d.js → index-888pq4xx.js} +11 -11
  28. package/dist/cli/{index-3146wmgv.js → index-8d90gzaz.js} +1 -1
  29. package/dist/cli/{index-5jx22kwc.js → index-8vw2pjgr.js} +544 -15
  30. package/dist/cli/{index-b2nj04q6.js → index-9ss2m4rs.js} +1 -434
  31. package/dist/cli/{index-x43debgx.js → index-ad9064j9.js} +15 -17
  32. package/dist/cli/{index-5kcvrpkt.js → index-b25t91a3.js} +1 -1
  33. package/dist/cli/index-c45662wc.js +361 -0
  34. package/dist/cli/{index-aqppaaqx.js → index-cgewrpw5.js} +3 -2
  35. package/dist/cli/{index-zfs3zqeq.js → index-d3mpxsmy.js} +771 -21
  36. package/dist/cli/{index-y1yqmkv7.js → index-d7j3n5ra.js} +52 -50
  37. package/dist/cli/{index-4vb6qa73.js → index-ebce775n.js} +36 -2
  38. package/dist/cli/{index-dsg9dw3b.js → index-eed0gkv6.js} +2 -2
  39. package/dist/cli/index-es64amqt.js +675 -0
  40. package/dist/cli/{index-9gxbjkts.js → index-ewv8qa8q.js} +22 -19
  41. package/dist/cli/{index-1ww7wj8d.js → index-hb7bh4r2.js} +15 -15
  42. package/dist/cli/{index-kc2tgcm7.js → index-hg09xemc.js} +4 -4
  43. package/dist/cli/{index-w95vxjde.js → index-hvz4ad2t.js} +39 -8
  44. package/dist/cli/{index-77z98j76.js → index-k0s8n6m5.js} +314 -130
  45. package/dist/cli/{index-7m32fjb4.js → index-kxv193aq.js} +9 -9
  46. package/dist/cli/{index-hp22pzn5.js → index-m8xrbdmn.js} +2 -2
  47. package/dist/cli/{index-4gqxppcx.js → index-mee9maf5.js} +2 -2
  48. package/dist/cli/{index-r3mvsbtq.js → index-p612v1mv.js} +7 -1
  49. package/dist/cli/{index-r5q5gd2a.js → index-qgajv9rd.js} +2 -2
  50. package/dist/cli/{index-zjg4chy2.js → index-qqq04n70.js} +2 -2
  51. package/dist/cli/{index-ww6zm5hr.js → index-r2p0pgst.js} +3 -3
  52. package/dist/cli/{index-47kh2103.js → index-r69gzes2.js} +14 -14
  53. package/dist/cli/{index-vdn32nws.js → index-sphahrmq.js} +3 -3
  54. package/dist/cli/{index-rhqdsc1x.js → index-vw66q1rw.js} +9 -9
  55. package/dist/cli/{index-kch6yenm.js → index-wasdrhev.js} +7 -7
  56. package/dist/cli/{index-c5v1w13g.js → index-wknpkfm8.js} +807 -629
  57. package/dist/cli/{index-pp2nya9v.js → index-x39hjdf1.js} +2 -4
  58. package/dist/cli/{index-n8688d12.js → index-xxfk62zd.js} +2 -2
  59. package/dist/cli/index-yvqg6kn5.js +429 -0
  60. package/dist/cli/{index-nqsj0g5y.js → index-ztm0q26v.js} +8 -8
  61. package/dist/cli/index.js +50 -48
  62. package/dist/cli/{knowledge-escalator-j04g450p.js → knowledge-escalator-01vva51d.js} +17 -18
  63. package/dist/cli/{knowledge-events-53z45rs0.js → knowledge-events-dpfwh1hs.js} +15 -16
  64. package/dist/cli/{knowledge-link-a6kfg3qm.js → knowledge-link-2rvp966j.js} +4 -5
  65. package/dist/cli/{knowledge-store-whm0gagt.js → knowledge-store-aw58ae7b.js} +6 -7
  66. package/dist/cli/{knowledge-validator-h8m7065g.js → knowledge-validator-yhd31ryf.js} +8 -9
  67. package/dist/cli/{pending-delegations-6hkpe5ze.js → pending-delegations-fjetcz8e.js} +17 -8
  68. package/dist/cli/{pr-subscriptions-11zca1mh.js → pr-subscriptions-cwwtfs8w.js} +17 -8
  69. package/dist/cli/{pr-workflow-gate-6g29rkg7.js → pr-workflow-gate-5xwtg1k2.js} +50 -48
  70. package/dist/cli/{runner-5abw3t35.js → runner-mb51twxt.js} +6 -7
  71. package/dist/cli/{scan-cursor-mkfx410t.js → scan-cursor-j11qwf0v.js} +9 -10
  72. package/dist/cli/{schema-jbbam3ev.js → schema-0xp3bde3.js} +1 -1
  73. package/dist/cli/{scope-persistence-q1ct4k42.js → scope-persistence-brrk806y.js} +19 -12
  74. package/dist/cli/{skill-generator-cv8fg96x.js → skill-generator-j8qzmet9.js} +20 -21
  75. package/dist/cli/snapshot-coordination-init-h0xmtftq.js +84 -0
  76. package/dist/cli/{telemetry-mbqqamsj.js → telemetry-m84ap0pp.js} +1 -1
  77. package/dist/cli/{workspace-snapshot-eh18vgkn.js → workspace-snapshot-6z9rk7xw.js} +3 -3
  78. package/dist/cli/{worktree-collision-ownership-xgnn8dsq.js → worktree-collision-ownership-j9zykj18.js} +21 -12
  79. package/dist/cli/{worktree-isolation-k2dgfmeg.js → worktree-isolation-jd39xz6h.js} +48 -46
  80. package/dist/commands/close.d.ts +13 -6
  81. package/dist/commands/reset-session.d.ts +4 -0
  82. package/dist/db/coordination-store.d.ts +85 -0
  83. package/dist/db/durability.d.ts +22 -5
  84. package/dist/db/index.d.ts +1 -0
  85. package/dist/db/project-db.d.ts +12 -0
  86. package/dist/index.js +810 -705
  87. package/dist/memory/schema.d.ts +3 -3
  88. package/dist/pr-review/authorization.d.ts +5 -4
  89. package/dist/pr-review/persistence.d.ts +10 -1
  90. package/dist/scope/scope-binding.d.ts +10 -0
  91. package/dist/scope/scope-persistence.d.ts +10 -0
  92. package/dist/services/handoff-service.d.ts +2 -0
  93. package/dist/services/status-service.d.ts +4 -0
  94. package/dist/session/snapshot-coordination-init.d.ts +49 -0
  95. package/dist/session/snapshot-reader.d.ts +2 -0
  96. package/dist/session/snapshot-store.d.ts +42 -0
  97. package/dist/session/snapshot-writer.d.ts +4 -1
  98. package/dist/state.d.ts +6 -3
  99. package/dist/turbo/epic/state.d.ts +4 -20
  100. package/dist/utils/bun-compat.d.ts +10 -0
  101. package/dist/utils/external-tool-runner.d.ts +2 -0
  102. package/package.json +3 -2
  103. package/dist/cli/curator-llm-factory-sxcpmcqq.js +0 -68
  104. package/dist/cli/guardrail-explain-xv9aw1bf.js +0 -69
  105. package/dist/cli/hive-promoter-09yc9mp5.js +0 -83
  106. package/dist/cli/index-xnb3tk7x.js +0 -24
  107. package/dist/cli/{ast-diff-ekd9v10f.js → ast-diff-b0esr2kf.js} +3 -3
@@ -1,10 +1,21 @@
1
1
  // @bun
2
2
  import {
3
3
  validateSwarmPath
4
- } from "./index-zjg4chy2.js";
4
+ } from "./index-qqq04n70.js";
5
+ import {
6
+ deleteCoordinationState,
7
+ importCoordinationOnce,
8
+ listCoordinationStates,
9
+ transitionCoordinationState,
10
+ withCoordinationTransaction
11
+ } from "./index-c45662wc.js";
12
+ import {
13
+ getProjectDb,
14
+ projectDbExists
15
+ } from "./index-es64amqt.js";
5
16
  import {
6
17
  observeStoreHealth
7
- } from "./index-dsg9dw3b.js";
18
+ } from "./index-eed0gkv6.js";
8
19
  import {
9
20
  canonicalRootKey,
10
21
  canonicalRootKeyFresh,
@@ -12,13 +23,13 @@ import {
12
23
  } from "./index-7qefgq4j.js";
13
24
  import {
14
25
  withEvidenceLock
15
- } from "./index-2bgr671r.js";
26
+ } from "./index-2bhz5k91.js";
16
27
  import {
17
28
  atomicWriteSwarmFileSync
18
- } from "./index-9gxbjkts.js";
29
+ } from "./index-ewv8qa8q.js";
19
30
  import {
20
31
  telemetry
21
- } from "./index-ww6zm5hr.js";
32
+ } from "./index-r2p0pgst.js";
22
33
  import {
23
34
  exports_external,
24
35
  init_zod
@@ -29,6 +40,7 @@ import {
29
40
 
30
41
  // src/background/pr-subscriptions.ts
31
42
  init_zod();
43
+ import { createHash } from "crypto";
32
44
  import * as fs from "fs";
33
45
  import * as path from "path";
34
46
  var PR_SUBSCRIPTIONS_FILE = "pr-monitor/subscriptions.jsonl";
@@ -38,6 +50,7 @@ var LEGACY_ARCHIVE_FILE = "pr-monitor/subscriptions.legacy.jsonl";
38
50
  var FOREIGN_CHECKPOINT_FILE = "pr-monitor/subscriptions.checkpoint.foreign.json";
39
51
  var CORRUPT_CHECKPOINT_FILE = "pr-monitor/subscriptions.checkpoint.corrupt.json";
40
52
  var FOREIGN_LEGACY_FILE = "pr-monitor/subscriptions.legacy.foreign.jsonl";
53
+ var PR_SUBSCRIPTION_COORDINATION_NAMESPACE = "background.pr-subscription";
41
54
  var STORE_LOCK_AGENT = "pr-monitor";
42
55
  var STORE_LOCK_TASK = "pr-subscriptions";
43
56
  var PR_SUBSCRIPTION_LIMITS = {
@@ -154,12 +167,482 @@ function fileExistsStrict(filePath) {
154
167
  throw err;
155
168
  }
156
169
  }
170
+ function importedShadowPath(filePath) {
171
+ return `${filePath}.imported`;
172
+ }
173
+ function projectionMarkerPath(filePath) {
174
+ return `${filePath}.sqlite-projection`;
175
+ }
157
176
  function foreignSlotPath(directory) {
158
177
  return validateSwarmPath(directory, FOREIGN_CHECKPOINT_FILE);
159
178
  }
160
179
  function corruptSlotPath(directory) {
161
180
  return validateSwarmPath(directory, CORRUPT_CHECKPOINT_FILE);
162
181
  }
182
+ function readCoordinationSubscriptions(directory) {
183
+ if (!projectDbExists(directory))
184
+ return null;
185
+ try {
186
+ return Object.fromEntries(listCoordinationStates(directory, PR_SUBSCRIPTION_COORDINATION_NAMESPACE).map((row) => {
187
+ const parsed = RecordSchema.safeParse(JSON.parse(row.payload));
188
+ if (!parsed.success || parsed.data.correlationId !== row.entityKey || row.generation !== 1 || parsed.data.status !== row.status) {
189
+ throw new Error(`PR subscription coordination row failed schema or authority binding validation for ${row.entityKey}`);
190
+ }
191
+ return [row.entityKey, parsed.data];
192
+ }));
193
+ } catch (error) {
194
+ const detail = error instanceof Error ? error.message : String(error);
195
+ log(`[pr-monitor] coordination read failed closed: ${detail}`);
196
+ throw new Error(`PR subscription coordination authority is unreadable: ${detail}`);
197
+ }
198
+ }
199
+ function projectionSequence(directory) {
200
+ const read = readCheckpoint(directory);
201
+ return read.kind === "ok" ? read.value.sequence : 0;
202
+ }
203
+ function writeCoordinationProjection(directory, view, baseCheckpoint) {
204
+ ensureSwarmDir(directory);
205
+ const prepared = prepareCoordinationPersistence(directory, view, baseCheckpoint);
206
+ assertCoordinationProjectionCapacity(directory, prepared.view, prepared.checkpoint);
207
+ const sequence = projectionSequence(directory) + 1;
208
+ const checkpoint = prepared.checkpoint;
209
+ checkpoint.sequence = sequence;
210
+ checkpoint.updatedAt = Date.now();
211
+ _internals.writeCheckpointFile(directory, checkpoint);
212
+ fs.writeFileSync(projectionMarkerPath(checkpointPath(directory)), "", "utf-8");
213
+ const legacyPath = storePath(directory);
214
+ if (fs.existsSync(legacyPath)) {
215
+ const legacyPayload = Object.values(checkpoint.records).map((entry) => JSON.stringify(entry)).join(`
216
+ `) + (Object.keys(checkpoint.records).length > 0 ? `
217
+ ` : "");
218
+ writeLegacyStoreFile(legacyPath, legacyPayload);
219
+ fs.writeFileSync(projectionMarkerPath(legacyPath), "", "utf-8");
220
+ if (checkpoint.migration?.done === true) {
221
+ try {
222
+ const legacyStat = fs.statSync(legacyPath);
223
+ checkpoint.migration.sourceBytes = legacyStat.size;
224
+ checkpoint.migration.scannedBytes = legacyStat.size;
225
+ checkpoint.migration.sourceMtimeMs = legacyStat.mtimeMs;
226
+ _internals.writeCheckpointFile(directory, checkpoint);
227
+ } catch {}
228
+ }
229
+ }
230
+ }
231
+ function writeLegacyStoreFile(targetPath, content) {
232
+ atomicWriteSwarmFileSync(targetPath, content);
233
+ }
234
+ function hasLegacyReconciliationArtifacts(directory) {
235
+ return [
236
+ legacyArchiveNextPath(directory),
237
+ legacyArchivePreviousPath(directory)
238
+ ].some((filePath) => fileExistsStrict(filePath));
239
+ }
240
+ function loadLegacyPrSubscriptionViewStrict(directory) {
241
+ const checkpoint = readCheckpoint(directory);
242
+ if (checkpoint.kind === "invalid") {
243
+ throw new Error("legacy checkpoint is invalid");
244
+ }
245
+ if (checkpoint.kind === "ok") {
246
+ if (!sameProjectRoot(checkpoint.value.rootPath, directory)) {
247
+ throw new Error("legacy checkpoint belongs to a different project root");
248
+ }
249
+ const overlaid = overlayLegacy(directory, checkpoint.value);
250
+ if (overlaid.overLimit) {
251
+ throw new Error("legacy subscription source exceeds migration ceiling");
252
+ }
253
+ if (overlaid.aborted) {
254
+ throw new Error("legacy subscription source is unreadable");
255
+ }
256
+ return overlaid.view;
257
+ }
258
+ const legacy = storePath(directory);
259
+ const stat = fileSizeOrNull(legacy);
260
+ if (stat === null)
261
+ return {};
262
+ if (stat > PR_SUBSCRIPTION_LIMITS.legacySourceMaxBytes) {
263
+ throw new Error("legacy subscription source exceeds migration ceiling");
264
+ }
265
+ const fold = foldLegacyRegion(legacy, 0, Number.MAX_SAFE_INTEGER);
266
+ if (fold.ioError || fold.aborted) {
267
+ throw new Error("PR subscription legacy migration read failed (I/O error)");
268
+ }
269
+ const view = {};
270
+ mergeFoldedRecords(view, fold.folded);
271
+ return view;
272
+ }
273
+ function buildCoordinationProjectionCheckpoint(directory, view, baseCheckpoint) {
274
+ const records = { ...view };
275
+ const terminals = Object.values(records).filter((entry) => entry.status !== "active");
276
+ const current = readCheckpoint(directory);
277
+ const checkpoint = baseCheckpoint ? cloneCheckpoint(baseCheckpoint) : current.kind === "ok" && sameProjectRoot(current.value.rootPath, directory) ? cloneCheckpoint(current.value) : freshCheckpoint(directory);
278
+ const priorSummary = { ...checkpoint.terminalSummary };
279
+ checkpoint.records = records;
280
+ checkpoint.terminalSummary = {
281
+ removed: priorSummary.removed,
282
+ expired: priorSummary.expired,
283
+ lastTerminalAt: terminals.length ? Math.max(priorSummary.lastTerminalAt ?? 0, ...terminals.map((entry) => entry.updatedAt)) : priorSummary.lastTerminalAt
284
+ };
285
+ checkpoint.rootPath = path.resolve(directory);
286
+ return checkpoint;
287
+ }
288
+ function assertCoordinationProjectionCapacity(directory, view, baseCheckpoint) {
289
+ const checkpoint = buildCoordinationProjectionCheckpoint(directory, view, baseCheckpoint);
290
+ const recordCount = Object.keys(checkpoint.records).length;
291
+ if (recordCount > PR_SUBSCRIPTION_LIMITS.maxCheckpointRecords) {
292
+ throw new Error(`PR subscription store over checkpoint capacity: ${recordCount} records > ${PR_SUBSCRIPTION_LIMITS.maxCheckpointRecords}. The folded state exceeds the bounded checkpoint \u2014 archive or split .swarm/pr-monitor/subscriptions.jsonl (subscriptions can be re-created with /swarm pr subscribe), then remove it.`);
293
+ }
294
+ const contentBytes = Buffer.byteLength(`${JSON.stringify(checkpoint)}
295
+ `, "utf-8");
296
+ if (contentBytes > PR_SUBSCRIPTION_LIMITS.checkpointHardReadBytes) {
297
+ throw new Error(`PR subscription store over checkpoint capacity: ${contentBytes} bytes > ${PR_SUBSCRIPTION_LIMITS.checkpointHardReadBytes}. The folded state exceeds the bounded checkpoint \u2014 archive or split .swarm/pr-monitor/subscriptions.jsonl (subscriptions can be re-created with /swarm pr subscribe), then remove it.`);
298
+ }
299
+ }
300
+ function prepareCoordinationPersistence(directory, view, baseCheckpoint) {
301
+ const preparedView = { ...view };
302
+ const checkpoint = buildCoordinationProjectionCheckpoint(directory, preparedView, baseCheckpoint);
303
+ const compacted = compactTerminalRecords(preparedView, checkpoint) !== null;
304
+ checkpoint.records = { ...preparedView };
305
+ return { view: preparedView, checkpoint, compacted };
306
+ }
307
+ function coordinationShadowDigest(directory) {
308
+ const hash = createHash("sha256");
309
+ for (const filePath of [
310
+ storePath(directory),
311
+ checkpointPath(directory),
312
+ auditPath(directory)
313
+ ]) {
314
+ if (!fileExistsStrict(filePath))
315
+ continue;
316
+ hash.update(path.basename(filePath));
317
+ hash.update(fs.readFileSync(filePath));
318
+ }
319
+ return hash.digest("hex");
320
+ }
321
+ function isDeepStrictEqualByJson(left, right) {
322
+ return JSON.stringify(left) === JSON.stringify(right);
323
+ }
324
+ function coordinationProjectionMatches(directory, view) {
325
+ try {
326
+ const legacyPath = storePath(directory);
327
+ if (!fileExistsStrict(legacyPath)) {
328
+ const checkpointRead = readCheckpoint(directory);
329
+ if (checkpointRead.kind !== "ok")
330
+ return false;
331
+ const prepared = prepareCoordinationPersistence(directory, view, checkpointRead.value).checkpoint;
332
+ return isDeepStrictEqualByJson(checkpointRead.value.records, prepared.records) && isDeepStrictEqualByJson(checkpointRead.value.terminalSummary, prepared.terminalSummary);
333
+ }
334
+ return isDeepStrictEqualByJson(loadLegacyPrSubscriptionViewStrict(directory), view);
335
+ } catch {
336
+ return false;
337
+ }
338
+ }
339
+ function coordinationImportShadowState(directory) {
340
+ if (!projectDbExists(directory))
341
+ return "absent";
342
+ const statement = getProjectDb(directory).prepare("SELECT source_digest FROM coordination_import WHERE source = ?");
343
+ try {
344
+ const row = statement.get(PR_SUBSCRIPTIONS_CHECKPOINT_FILE);
345
+ if (!row)
346
+ return "absent";
347
+ return row.source_digest === coordinationShadowDigest(directory) ? "matches" : "mismatch";
348
+ } finally {
349
+ statement.finalize();
350
+ }
351
+ }
352
+ function legacyProjectionFingerprintMatches(directory, checkpoint) {
353
+ const migration = checkpoint.migration;
354
+ const legacyPath = storePath(directory);
355
+ if (!fileExistsStrict(legacyPath))
356
+ return true;
357
+ if (!fileExistsStrict(projectionMarkerPath(legacyPath)) || !migration?.done) {
358
+ return false;
359
+ }
360
+ try {
361
+ const stat = fs.statSync(legacyPath);
362
+ return stat.size === migration.sourceBytes && stat.mtimeMs === migration.sourceMtimeMs;
363
+ } catch {
364
+ return false;
365
+ }
366
+ }
367
+ function archiveShadowFileBestEffort(filePath) {
368
+ if (!fileExistsStrict(filePath))
369
+ return;
370
+ const importedPath = importedShadowPath(filePath);
371
+ if (fileExistsStrict(importedPath))
372
+ return;
373
+ try {
374
+ renameWithRetry(filePath, importedPath);
375
+ } catch (error) {
376
+ log(`[pr-monitor] shadow archive failed for ${path.basename(filePath)}: ${error instanceof Error ? error.message : String(error)}`);
377
+ }
378
+ }
379
+ function retireAuthoritativeLegacyShadow(directory) {
380
+ const legacyPath = storePath(directory);
381
+ if (!fileExistsStrict(legacyPath))
382
+ return true;
383
+ if (fileExistsStrict(projectionMarkerPath(legacyPath)))
384
+ return true;
385
+ const importedPath = importedShadowPath(legacyPath);
386
+ try {
387
+ if (!fileExistsStrict(importedPath)) {
388
+ _internals.renameWithRetry(legacyPath, importedPath);
389
+ } else {
390
+ fs.unlinkSync(legacyPath);
391
+ }
392
+ return !fileExistsStrict(legacyPath);
393
+ } catch (error) {
394
+ log(`[pr-monitor] authoritative legacy shadow retirement failed: ${error instanceof Error ? error.message : String(error)}`);
395
+ return false;
396
+ }
397
+ }
398
+ function ensurePrSubscriptionCoordinationImported(directory, createIfNeeded) {
399
+ if (!createIfNeeded && !projectDbExists(directory))
400
+ return false;
401
+ const coordinationView = readCoordinationSubscriptions(directory);
402
+ if (coordinationView !== null && Object.keys(coordinationView).length > 0) {
403
+ const checkpointRead2 = readCheckpoint(directory);
404
+ const migrationPending = checkpointRead2.kind === "ok" && sameProjectRoot(checkpointRead2.value.rootPath, directory) && checkpointRead2.value.migration?.done === false;
405
+ if (migrationPending)
406
+ return false;
407
+ const legacyPath = storePath(directory);
408
+ if (fileExistsStrict(legacyPath) && !fileExistsStrict(projectionMarkerPath(legacyPath))) {
409
+ let legacyStat2 = null;
410
+ try {
411
+ legacyStat2 = _internals.statSync(legacyPath);
412
+ } catch (error) {
413
+ if (error.code !== "ENOENT") {
414
+ throw new Error(`PR subscription legacy migration read failed (I/O error); refusing mutation: ${error instanceof Error ? error.message : String(error)}`);
415
+ }
416
+ }
417
+ if (legacyStat2 && legacyStat2.size > PR_SUBSCRIPTION_LIMITS.migrationMaxBytesPerOperation && (checkpointRead2.kind !== "ok" || checkpointRead2.value.migration === null || checkpointRead2.value.migration.done === false)) {
418
+ return false;
419
+ }
420
+ }
421
+ if (fileExistsStrict(legacyPath)) {
422
+ let legacyStat2 = null;
423
+ try {
424
+ legacyStat2 = _internals.statSync(legacyPath);
425
+ } catch (error) {
426
+ if (error.code !== "ENOENT") {
427
+ throw new Error(`PR subscription legacy migration read failed (I/O error); refusing mutation: ${error instanceof Error ? error.message : String(error)}`);
428
+ }
429
+ }
430
+ if (legacyStat2 && legacyStat2.size > PR_SUBSCRIPTION_LIMITS.migrationMaxBytesPerOperation && (checkpointRead2.kind !== "ok" || checkpointRead2.value.migration === null || checkpointRead2.value.migration.done === false)) {
431
+ return false;
432
+ }
433
+ }
434
+ const projectionNeedsRepair = !fileExistsStrict(projectionMarkerPath(checkpointPath(directory))) || fileExistsStrict(legacyPath) && !fileExistsStrict(projectionMarkerPath(legacyPath)) || !coordinationProjectionMatches(directory, coordinationView);
435
+ if (projectionNeedsRepair) {
436
+ const checkpointProjectionPublished = fileExistsStrict(projectionMarkerPath(checkpointPath(directory)));
437
+ const settledProjection = checkpointProjectionPublished && checkpointRead2.kind === "ok" && (checkpointRead2.value.migration === null || checkpointRead2.value.migration.archived === true);
438
+ const importShadowState = coordinationImportShadowState(directory);
439
+ if (importShadowState === "mismatch" && !settledProjection || !checkpointProjectionPublished && importShadowState !== "matches" || hasLegacyReconciliationArtifacts(directory)) {
440
+ return false;
441
+ }
442
+ for (const filePath of [
443
+ storePath(directory),
444
+ checkpointPath(directory),
445
+ auditPath(directory)
446
+ ]) {
447
+ archiveShadowFileBestEffort(filePath);
448
+ }
449
+ writeCoordinationProjection(directory, coordinationView);
450
+ }
451
+ return true;
452
+ }
453
+ const hasLegacyArtifacts = [
454
+ storePath(directory),
455
+ checkpointPath(directory),
456
+ auditPath(directory),
457
+ legacyArchivePath(directory)
458
+ ].some((filePath) => fileExistsStrict(filePath));
459
+ if (!hasLegacyArtifacts)
460
+ return createIfNeeded || projectDbExists(directory);
461
+ const checkpointRead = readCheckpoint(directory);
462
+ const baseCheckpoint = checkpointRead.kind === "ok" && sameProjectRoot(checkpointRead.value.rootPath, directory) ? cloneCheckpoint(checkpointRead.value) : undefined;
463
+ if (checkpointRead.kind === "invalid" || checkpointRead.kind === "ok" && !sameProjectRoot(checkpointRead.value.rootPath, directory)) {
464
+ return false;
465
+ }
466
+ let legacyStat = null;
467
+ try {
468
+ legacyStat = _internals.statSync(storePath(directory));
469
+ } catch (error) {
470
+ if (error.code !== "ENOENT") {
471
+ throw new Error(`PR subscription legacy migration read failed (I/O error); refusing mutation: ${error instanceof Error ? error.message : String(error)}`);
472
+ }
473
+ }
474
+ if (legacyStat !== null && legacyStat.size > PR_SUBSCRIPTION_LIMITS.migrationMaxBytesPerOperation && (checkpointRead.kind !== "ok" || checkpointRead.value.migration === null || checkpointRead.value.migration.done === false)) {
475
+ return false;
476
+ }
477
+ const legacyView = loadLegacyPrSubscriptionViewStrict(directory);
478
+ const preparedImport = prepareCoordinationPersistence(directory, legacyView, baseCheckpoint);
479
+ if (legacyStat) {
480
+ preparedImport.checkpoint.migration = {
481
+ scannedBytes: legacyStat.size,
482
+ sourceBytes: legacyStat.size,
483
+ sourceMtimeMs: legacyStat.mtimeMs,
484
+ corruptLines: preparedImport.checkpoint.maintenance.corruptLegacyRecords,
485
+ discardingOversizeLine: false,
486
+ baselineRecords: {},
487
+ baselineTerminalSummary: {
488
+ removed: 0,
489
+ expired: 0,
490
+ lastTerminalAt: null
491
+ },
492
+ done: true,
493
+ archived: false,
494
+ startedAt: Date.now()
495
+ };
496
+ }
497
+ assertCoordinationProjectionCapacity(directory, preparedImport.view, preparedImport.checkpoint);
498
+ const outcome = importCoordinationOnce(directory, {
499
+ source: PR_SUBSCRIPTIONS_CHECKPOINT_FILE,
500
+ sourceDigest: coordinationShadowDigest(directory),
501
+ rowCount: Object.keys(preparedImport.view).length,
502
+ emptyNamespace: PR_SUBSCRIPTION_COORDINATION_NAMESPACE
503
+ }, () => {
504
+ for (const record of Object.values(preparedImport.view)) {
505
+ const payload = JSON.stringify(record);
506
+ const result = transitionCoordinationState(directory, {
507
+ namespace: PR_SUBSCRIPTION_COORDINATION_NAMESPACE,
508
+ entityKey: record.correlationId,
509
+ expectedRevision: null,
510
+ generation: 1,
511
+ status: record.status,
512
+ payload
513
+ });
514
+ if (result.outcome !== "applied") {
515
+ throw new Error(`subscription import failed for ${record.correlationId}: ${result.outcome}`);
516
+ }
517
+ }
518
+ });
519
+ const importedView = readCoordinationSubscriptions(directory) ?? preparedImport.view;
520
+ if (outcome !== "state_exists") {
521
+ for (const filePath of [
522
+ storePath(directory),
523
+ checkpointPath(directory),
524
+ auditPath(directory),
525
+ legacyArchivePath(directory)
526
+ ]) {
527
+ archiveShadowFileBestEffort(filePath);
528
+ }
529
+ const importedLegacy = importedShadowPath(storePath(directory));
530
+ if (legacyStat) {
531
+ if (!fileExistsStrict(storePath(directory)) && fileExistsStrict(importedLegacy)) {
532
+ fs.copyFileSync(importedLegacy, storePath(directory));
533
+ } else if (fileExistsStrict(storePath(directory)) && !fileExistsStrict(importedLegacy)) {
534
+ fs.copyFileSync(storePath(directory), importedLegacy);
535
+ }
536
+ }
537
+ }
538
+ writeCoordinationProjection(directory, importedView, preparedImport.checkpoint);
539
+ if (outcome !== "state_exists" && preparedImport.checkpoint.migration) {
540
+ if (fileExistsStrict(storePath(directory))) {
541
+ const projected = _internals.statSync(storePath(directory));
542
+ legacyStat = projected;
543
+ preparedImport.checkpoint.migration.sourceBytes = projected.size;
544
+ preparedImport.checkpoint.migration.scannedBytes = projected.size;
545
+ preparedImport.checkpoint.migration.sourceMtimeMs = projected.mtimeMs;
546
+ }
547
+ const archiveEvent = maybeArchiveLegacy(directory, preparedImport.checkpoint);
548
+ if (archiveEvent) {
549
+ preparedImport.checkpoint.sequence += 1;
550
+ preparedImport.checkpoint.updatedAt = Date.now();
551
+ _internals.writeCheckpointFile(directory, preparedImport.checkpoint);
552
+ flushAuditEvents(directory, [{ kind: "migrate-complete" }, archiveEvent], preparedImport.checkpoint.sequence, preparedImport.checkpoint.maintenance);
553
+ emitHealthTelemetry(directory, "migrate-complete", preparedImport.checkpoint);
554
+ emitHealthTelemetry(directory, "archive", preparedImport.checkpoint);
555
+ } else {
556
+ if (legacyStat) {
557
+ try {
558
+ const currentLegacyStat = _internals.statSync(storePath(directory));
559
+ if (currentLegacyStat.size !== legacyStat.size || currentLegacyStat.mtimeMs !== legacyStat.mtimeMs) {
560
+ try {
561
+ fs.unlinkSync(projectionMarkerPath(storePath(directory)));
562
+ } catch (error) {
563
+ if (error.code !== "ENOENT") {
564
+ return false;
565
+ }
566
+ }
567
+ return false;
568
+ }
569
+ } catch {
570
+ return false;
571
+ }
572
+ }
573
+ flushAuditEvents(directory, [{ kind: "migrate-complete" }], preparedImport.checkpoint.sequence, preparedImport.checkpoint.maintenance);
574
+ emitHealthTelemetry(directory, "migrate-complete", preparedImport.checkpoint);
575
+ }
576
+ }
577
+ return true;
578
+ }
579
+ function canUseCoordinationWritePath(directory) {
580
+ if (hasLegacyReconciliationArtifacts(directory))
581
+ return false;
582
+ const checkpoint = readCheckpoint(directory);
583
+ if (checkpoint.kind === "ok" && checkpoint.value.migration?.done === false) {
584
+ return false;
585
+ }
586
+ const ensured = ensurePrSubscriptionCoordinationImported(directory, true);
587
+ return ensured;
588
+ }
589
+ function persistCoordinationSubscriptions(directory, view, writeProjection = true, events = []) {
590
+ const prepared = prepareCoordinationPersistence(directory, view);
591
+ if (writeProjection) {
592
+ writeCoordinationProjection(directory, prepared.view, prepared.checkpoint);
593
+ }
594
+ withCoordinationTransaction(directory, () => {
595
+ const current = new Map(listCoordinationStates(directory, PR_SUBSCRIPTION_COORDINATION_NAMESPACE).map((row) => [row.entityKey, row]));
596
+ const nextIds = new Set(Object.keys(prepared.view));
597
+ for (const [entityKey, row] of current) {
598
+ if (nextIds.has(entityKey))
599
+ continue;
600
+ const deleted = deleteCoordinationState(directory, PR_SUBSCRIPTION_COORDINATION_NAMESPACE, entityKey, row.revision);
601
+ if (!deleted) {
602
+ throw new Error(`subscription delete conflict for ${entityKey}`);
603
+ }
604
+ }
605
+ for (const record of Object.values(prepared.view)) {
606
+ const row = current.get(record.correlationId);
607
+ const payload = JSON.stringify(record);
608
+ const result = transitionCoordinationState(directory, {
609
+ namespace: PR_SUBSCRIPTION_COORDINATION_NAMESPACE,
610
+ entityKey: record.correlationId,
611
+ expectedRevision: row?.revision ?? null,
612
+ generation: 1,
613
+ status: record.status,
614
+ payload,
615
+ event: {
616
+ streamId: record.correlationId,
617
+ idempotencyKey: createHash("sha256").update(payload).digest("hex"),
618
+ eventType: record.status,
619
+ payload
620
+ }
621
+ });
622
+ if (result.outcome !== "applied" && result.outcome !== "duplicate") {
623
+ throw new Error(`subscription write failed for ${record.correlationId}: ${result.outcome}`);
624
+ }
625
+ }
626
+ });
627
+ if (events.length === 0)
628
+ return;
629
+ const checkpoint = readCheckpoint(directory);
630
+ if (checkpoint.kind !== "ok")
631
+ return;
632
+ const auditCompaction = prepareAuditCompaction(directory);
633
+ if (applyAuditCompaction(directory, auditCompaction) && auditCompaction) {
634
+ checkpoint.value.maintenance.droppedAuditTransitions += auditCompaction.dropped;
635
+ try {
636
+ _internals.writeCheckpointFile(directory, checkpoint.value);
637
+ } catch (error) {
638
+ log(`[pr-monitor] audit accounting write deferred: ${storageErrorCode(error)}`);
639
+ }
640
+ }
641
+ flushAuditEvents(directory, events, checkpoint.value.sequence, checkpoint.value.maintenance);
642
+ if (prepared.compacted) {
643
+ emitHealthTelemetry(directory, "compact", prepared.checkpoint);
644
+ }
645
+ }
163
646
  function foreignLegacySlotPath(directory) {
164
647
  return validateSwarmPath(directory, FOREIGN_LEGACY_FILE);
165
648
  }
@@ -508,6 +991,25 @@ function validateRecord(record) {
508
991
  throw new Error(`Invalid subscription record: ${result.error.message}`);
509
992
  }
510
993
  }
994
+ function snapshotUpdateHasChanges(match, updates) {
995
+ const immutableKeys = new Set([
996
+ "correlationId",
997
+ "sessionID",
998
+ "prNumber",
999
+ "repoFullName",
1000
+ "prUrl",
1001
+ "createdAt",
1002
+ "updatedAt"
1003
+ ]);
1004
+ for (const [key, value] of Object.entries(updates)) {
1005
+ if (immutableKeys.has(key))
1006
+ continue;
1007
+ if (!Object.is(match[key], value)) {
1008
+ return true;
1009
+ }
1010
+ }
1011
+ return false;
1012
+ }
511
1013
  function writeCheckpointFile(directory, checkpoint) {
512
1014
  const recordCount = Object.keys(checkpoint.records).length;
513
1015
  if (recordCount > PR_SUBSCRIPTION_LIMITS.maxCheckpointRecords) {
@@ -837,8 +1339,7 @@ function reconcileLegacyArchiveStaging(directory, migration) {
837
1339
  archiveExists = true;
838
1340
  previousExists = false;
839
1341
  if (nextExists) {
840
- _internals.renameWithRetry(next, legacy);
841
- migration.archived = false;
1342
+ fs.unlinkSync(next);
842
1343
  legacyExists = true;
843
1344
  nextExists = false;
844
1345
  }
@@ -995,11 +1496,66 @@ function overlayLegacy(directory, checkpoint) {
995
1496
  };
996
1497
  }
997
1498
  async function loadViewForRead(directory) {
1499
+ if (ensurePrSubscriptionCoordinationImported(directory, false)) {
1500
+ const coordinationView = readCoordinationSubscriptions(directory) ?? {};
1501
+ const checkpointRead = readCheckpoint(directory);
1502
+ if (checkpointRead.kind === "ok" && sameProjectRoot(checkpointRead.value.rootPath, directory) && checkpointRead.value.migration?.done === false) {
1503
+ const pendingCheckpoint = cloneCheckpoint(checkpointRead.value);
1504
+ pendingCheckpoint.records = {
1505
+ ...pendingCheckpoint.records,
1506
+ ...coordinationView
1507
+ };
1508
+ const overlaid = overlayLegacy(directory, pendingCheckpoint);
1509
+ return {
1510
+ view: overlaid.view,
1511
+ recoverySource: overlaid.usedLegacy ? "checkpoint+legacy" : "checkpoint"
1512
+ };
1513
+ }
1514
+ return {
1515
+ view: coordinationView,
1516
+ recoverySource: "checkpoint"
1517
+ };
1518
+ }
998
1519
  const read = readCheckpoint(directory);
1520
+ const pendingCoordination = readCoordinationSubscriptions(directory);
1521
+ if (read.kind === "ok" && sameProjectRoot(read.value.rootPath, directory) && read.value.migration?.done === false && pendingCoordination !== null && Object.keys(pendingCoordination).length > 0) {
1522
+ const pendingCheckpoint = cloneCheckpoint(read.value);
1523
+ pendingCheckpoint.records = {
1524
+ ...pendingCheckpoint.records,
1525
+ ...pendingCoordination
1526
+ };
1527
+ const overlaid = overlayLegacy(directory, pendingCheckpoint);
1528
+ return {
1529
+ view: overlaid.view,
1530
+ recoverySource: overlaid.usedLegacy ? "checkpoint+legacy" : "checkpoint"
1531
+ };
1532
+ }
999
1533
  if (read.kind === "ok") {
1000
1534
  if (!sameProjectRoot(read.value.rootPath, directory)) {
1001
1535
  return { view: {}, recoverySource: "foreign" };
1002
1536
  }
1537
+ if (read.value.migration?.done === true) {
1538
+ if (!legacyProjectionFingerprintMatches(directory, read.value)) {
1539
+ if (fileExistsStrict(legacyArchivePath(directory))) {
1540
+ retireAuthoritativeLegacyShadow(directory);
1541
+ return {
1542
+ view: { ...read.value.records },
1543
+ recoverySource: "checkpoint"
1544
+ };
1545
+ }
1546
+ const overlaid2 = overlayLegacy(directory, read.value);
1547
+ if (overlaid2.usedLegacy && !overlaid2.aborted) {
1548
+ return {
1549
+ view: overlaid2.view,
1550
+ recoverySource: "checkpoint+legacy"
1551
+ };
1552
+ }
1553
+ }
1554
+ return {
1555
+ view: { ...read.value.records },
1556
+ recoverySource: "checkpoint"
1557
+ };
1558
+ }
1003
1559
  const overlaid = overlayLegacy(directory, read.value);
1004
1560
  return {
1005
1561
  view: overlaid.view,
@@ -1123,6 +1679,7 @@ function loadViewForWrite(directory) {
1123
1679
  let reboundForeign = false;
1124
1680
  let checkpoint;
1125
1681
  const read = readCheckpoint(directory);
1682
+ let archiveDeferredForExternalRewrite = false;
1126
1683
  if (read.kind === "ok" && sameProjectRoot(read.value.rootPath, directory)) {
1127
1684
  checkpoint = cloneCheckpoint(read.value);
1128
1685
  } else if (read.kind === "ok") {
@@ -1188,7 +1745,9 @@ function loadViewForWrite(directory) {
1188
1745
  legacyStat = null;
1189
1746
  if (legacyStat === null) {
1190
1747
  view = { ...checkpoint.records };
1191
- if (checkpoint.migration?.done && !checkpoint.migration.archived) {
1748
+ const importedShadowExists = fileExistsStrict(importedShadowPath(legacy));
1749
+ const archiveExists = fileExistsStrict(legacyArchivePath(directory));
1750
+ if (checkpoint.migration?.done && !checkpoint.migration.archived && (archiveExists || !importedShadowExists)) {
1192
1751
  checkpoint.migration.archived = true;
1193
1752
  dirty = true;
1194
1753
  }
@@ -1198,6 +1757,21 @@ function loadViewForWrite(directory) {
1198
1757
  warnLegacyOverLimit(directory, legacyStat.size);
1199
1758
  throw new Error(`PR subscription legacy store exceeds the ${PR_SUBSCRIPTION_LIMITS.legacySourceMaxBytes}-byte migration ceiling; refusing mutation until the file is archived or split`);
1200
1759
  }
1760
+ if (checkpoint.migration?.done === true && checkpoint.migration.archived && legacyProjectionFingerprintMatches(directory, checkpoint)) {
1761
+ checkpoint.migration.archived = false;
1762
+ dirty = true;
1763
+ }
1764
+ if (checkpoint.migration?.done === true && !legacyProjectionFingerprintMatches(directory, checkpoint)) {
1765
+ try {
1766
+ fs.unlinkSync(projectionMarkerPath(legacy));
1767
+ } catch (error) {
1768
+ if (error.code !== "ENOENT")
1769
+ throw error;
1770
+ }
1771
+ checkpoint.migration = null;
1772
+ dirty = true;
1773
+ archiveDeferredForExternalRewrite = true;
1774
+ }
1201
1775
  const legacySize = legacyStat.size;
1202
1776
  const legacyMtime = legacyStat.mtimeMs;
1203
1777
  const migration = checkpoint.migration;
@@ -1265,7 +1839,8 @@ function loadViewForWrite(directory) {
1265
1839
  warnMigrationCapacity(directory, Object.keys(view).length);
1266
1840
  break;
1267
1841
  }
1268
- if (!fold.eof) {
1842
+ const deferLargeInitialGeneration = !sameGeneration && legacySize > PR_SUBSCRIPTION_LIMITS.migrationMaxBytesPerOperation && foldedThisOperation >= PR_SUBSCRIPTION_LIMITS.migrationMaxBytesPerOperation;
1843
+ if (!fold.eof || deferLargeInitialGeneration) {
1269
1844
  const progress = cloneCheckpoint(checkpoint);
1270
1845
  progress.records = { ...view };
1271
1846
  progress.migration = {
@@ -1303,7 +1878,7 @@ function loadViewForWrite(directory) {
1303
1878
  lastTerminalAt: null
1304
1879
  },
1305
1880
  done: true,
1306
- archived: false,
1881
+ archived: archiveDeferredForExternalRewrite,
1307
1882
  startedAt
1308
1883
  };
1309
1884
  audit.push({ kind: "migrate-complete" });
@@ -1315,17 +1890,15 @@ function loadViewForWrite(directory) {
1315
1890
  }
1316
1891
  return { checkpoint, view, audit, dirty };
1317
1892
  }
1318
- const overlaid = overlayLegacy(directory, checkpoint);
1319
- view = overlaid.view;
1320
- if (overlaid.usedLegacy && !overlaid.aborted) {
1321
- migration.sourceBytes = legacySize;
1322
- migration.sourceMtimeMs = legacyMtime;
1323
- migration.scannedBytes = legacySize;
1324
- migration.archived = false;
1325
- checkpoint.maintenance.corruptLegacyRecords += overlaid.corruptLines;
1326
- dirty = true;
1893
+ if (!legacyProjectionFingerprintMatches(directory, checkpoint)) {
1894
+ const legacyPath = storePath(directory);
1895
+ if (fileExistsStrict(projectionMarkerPath(legacyPath))) {
1896
+ const overlaid = overlayLegacy(directory, checkpoint);
1897
+ return { checkpoint, view: overlaid.view, audit, dirty };
1898
+ }
1899
+ retireAuthoritativeLegacyShadow(directory);
1327
1900
  }
1328
- return { checkpoint, view, audit, dirty };
1901
+ return { checkpoint, view: { ...checkpoint.records }, audit, dirty };
1329
1902
  }
1330
1903
  function finalizeWrite(directory, loaded, opAudit) {
1331
1904
  const { checkpoint } = loaded;
@@ -1352,6 +1925,23 @@ function finalizeWrite(directory, loaded, opAudit) {
1352
1925
  checkpoint.sequence += 1;
1353
1926
  const auditCompaction = prepareAuditCompaction(directory);
1354
1927
  persistCheckpointWithAuditCompaction(directory, checkpoint, auditCompaction);
1928
+ if (checkpoint.migration?.done === true) {
1929
+ const legacyPath = storePath(directory);
1930
+ try {
1931
+ let legacyStat = fs.statSync(legacyPath);
1932
+ if (legacyStat.size !== checkpoint.migration.sourceBytes || legacyStat.mtimeMs !== checkpoint.migration.sourceMtimeMs) {
1933
+ const legacyPayload = Object.values(checkpoint.records).map((entry) => JSON.stringify(entry)).join(`
1934
+ `) + (Object.keys(checkpoint.records).length > 0 ? `
1935
+ ` : "");
1936
+ writeLegacyStoreFile(legacyPath, legacyPayload);
1937
+ fs.writeFileSync(projectionMarkerPath(legacyPath), "", "utf-8");
1938
+ legacyStat = fs.statSync(legacyPath);
1939
+ }
1940
+ checkpoint.migration.sourceBytes = legacyStat.size;
1941
+ checkpoint.migration.scannedBytes = legacyStat.size;
1942
+ checkpoint.migration.sourceMtimeMs = legacyStat.mtimeMs;
1943
+ } catch {}
1944
+ }
1355
1945
  flushAuditEvents(directory, events, checkpoint.sequence, checkpoint.maintenance);
1356
1946
  const archiveEvent = maybeArchiveLegacy(directory, checkpoint);
1357
1947
  if (archiveEvent) {
@@ -1361,6 +1951,10 @@ function finalizeWrite(directory, loaded, opAudit) {
1361
1951
  flushAuditEvents(directory, [archiveEvent], checkpoint.sequence, checkpoint.maintenance);
1362
1952
  }
1363
1953
  cleanupStaleLegacyArchive(directory);
1954
+ const coordinationView = readCoordinationSubscriptions(directory);
1955
+ if (coordinationView !== null && !isDeepStrictEqualByJson(coordinationView, loaded.view)) {
1956
+ persistCoordinationSubscriptions(directory, { ...loaded.view });
1957
+ }
1364
1958
  for (const kind of [
1365
1959
  "compact",
1366
1960
  "migrate-complete",
@@ -1392,6 +1986,42 @@ async function subscribe(directory, input) {
1392
1986
  const correlationId = buildCorrelationId(input.sessionID, input.repoFullName, input.prNumber);
1393
1987
  const now = Date.now();
1394
1988
  return withEvidenceLock(directory, PR_SUBSCRIPTIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
1989
+ if (canUseCoordinationWritePath(directory)) {
1990
+ const view = readCoordinationSubscriptions(directory) ?? {};
1991
+ const match2 = Object.values(view).find((r) => r.correlationId === correlationId && r.status === "active");
1992
+ if (match2) {
1993
+ onSubscriptionCreated?.(directory, match2);
1994
+ return match2;
1995
+ }
1996
+ if (input.maxSubscriptions === undefined || input.maxSubscriptions > 0) {
1997
+ const limit = input.maxSubscriptions !== undefined ? input.maxSubscriptions : PR_SUBSCRIPTION_LIMITS.defaultMaxActiveSubscriptions;
1998
+ const activeCount = Object.values(view).filter((r) => r.status === "active").length;
1999
+ if (activeCount >= limit) {
2000
+ throw new Error(`PR subscription limit reached: ${activeCount}/${limit}`);
2001
+ }
2002
+ }
2003
+ const record2 = {
2004
+ correlationId,
2005
+ sessionID: input.sessionID,
2006
+ prNumber: input.prNumber,
2007
+ repoFullName: input.repoFullName,
2008
+ prUrl: input.prUrl,
2009
+ lastCheckedAt: now,
2010
+ isWatching: true,
2011
+ hasUnaddressedEvents: false,
2012
+ status: "active",
2013
+ createdAt: now,
2014
+ updatedAt: now,
2015
+ errorCount: 0
2016
+ };
2017
+ validateRecord(record2);
2018
+ view[correlationId] = record2;
2019
+ persistCoordinationSubscriptions(directory, view, true, [
2020
+ { kind: "subscribe", correlationId }
2021
+ ]);
2022
+ onSubscriptionCreated?.(directory, record2);
2023
+ return record2;
2024
+ }
1395
2025
  const loaded = loadViewForWrite(directory);
1396
2026
  const match = Object.values(loaded.view).find((r) => r.correlationId === correlationId && r.status === "active");
1397
2027
  if (match) {
@@ -1432,6 +2062,24 @@ async function unsubscribe(directory, correlationId) {
1432
2062
  if (!correlationId)
1433
2063
  return null;
1434
2064
  return withEvidenceLock(directory, PR_SUBSCRIPTIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
2065
+ if (canUseCoordinationWritePath(directory)) {
2066
+ const view = readCoordinationSubscriptions(directory) ?? {};
2067
+ const match2 = Object.values(view).find((r) => r.correlationId === correlationId && r.status === "active");
2068
+ if (!match2) {
2069
+ return null;
2070
+ }
2071
+ const removed2 = {
2072
+ ...match2,
2073
+ status: "removed",
2074
+ isWatching: false,
2075
+ updatedAt: Date.now()
2076
+ };
2077
+ view[correlationId] = removed2;
2078
+ persistCoordinationSubscriptions(directory, view, true, [
2079
+ { kind: "unsubscribe", correlationId }
2080
+ ]);
2081
+ return removed2;
2082
+ }
1435
2083
  const loaded = loadViewForWrite(directory);
1436
2084
  const match = Object.values(loaded.view).find((r) => r.correlationId === correlationId && r.status === "active");
1437
2085
  if (!match) {
@@ -1470,12 +2118,65 @@ async function updateSnapshot(directory, correlationId, updates) {
1470
2118
  if (!correlationId)
1471
2119
  return null;
1472
2120
  return withEvidenceLock(directory, PR_SUBSCRIPTIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
2121
+ const existingCoordination = readCoordinationSubscriptions(directory);
2122
+ if (existingCoordination !== null && !fileExistsStrict(storePath(directory)) && !hasLegacyReconciliationArtifacts(directory)) {
2123
+ const match2 = Object.values(existingCoordination).find((r) => r.correlationId === correlationId && r.status === "active");
2124
+ if (!match2)
2125
+ return null;
2126
+ if (!snapshotUpdateHasChanges(match2, updates)) {
2127
+ return match2;
2128
+ }
2129
+ const updated2 = {
2130
+ ...match2,
2131
+ ...updates,
2132
+ correlationId,
2133
+ sessionID: match2.sessionID,
2134
+ repoFullName: match2.repoFullName,
2135
+ prNumber: match2.prNumber,
2136
+ prUrl: match2.prUrl,
2137
+ createdAt: match2.createdAt,
2138
+ updatedAt: Date.now()
2139
+ };
2140
+ validateRecord(updated2);
2141
+ existingCoordination[correlationId] = updated2;
2142
+ persistCoordinationSubscriptions(directory, existingCoordination);
2143
+ return updated2;
2144
+ }
2145
+ if (canUseCoordinationWritePath(directory)) {
2146
+ const view = readCoordinationSubscriptions(directory) ?? {};
2147
+ const match2 = Object.values(view).find((r) => r.correlationId === correlationId && r.status === "active");
2148
+ if (!match2) {
2149
+ return null;
2150
+ }
2151
+ if (!snapshotUpdateHasChanges(match2, updates)) {
2152
+ return match2;
2153
+ }
2154
+ const updated2 = {
2155
+ ...match2,
2156
+ ...updates,
2157
+ correlationId,
2158
+ sessionID: match2.sessionID,
2159
+ repoFullName: match2.repoFullName,
2160
+ prNumber: match2.prNumber,
2161
+ prUrl: match2.prUrl,
2162
+ createdAt: match2.createdAt,
2163
+ updatedAt: Date.now()
2164
+ };
2165
+ validateRecord(updated2);
2166
+ view[correlationId] = updated2;
2167
+ persistCoordinationSubscriptions(directory, view);
2168
+ return updated2;
2169
+ }
1473
2170
  const loaded = loadViewForWrite(directory);
1474
2171
  const match = Object.values(loaded.view).find((r) => r.correlationId === correlationId && r.status === "active");
1475
2172
  if (!match) {
1476
2173
  finalizeWrite(directory, loaded, []);
1477
2174
  return null;
1478
2175
  }
2176
+ if (!snapshotUpdateHasChanges(match, updates)) {
2177
+ finalizeWrite(directory, loaded, []);
2178
+ return match;
2179
+ }
1479
2180
  const updated = {
1480
2181
  ...match,
1481
2182
  ...updates,
@@ -1501,6 +2202,36 @@ async function sweepStale(directory, ttlDays, mergedPrs) {
1501
2202
  const now = Date.now();
1502
2203
  try {
1503
2204
  return await withEvidenceLock(directory, PR_SUBSCRIPTIONS_FILE, STORE_LOCK_AGENT, STORE_LOCK_TASK, async () => {
2205
+ if (canUseCoordinationWritePath(directory)) {
2206
+ const view = readCoordinationSubscriptions(directory) ?? {};
2207
+ let swept2 = 0;
2208
+ const expiredCorrelationIds = [];
2209
+ const now2 = Date.now();
2210
+ for (const record of Object.values(view)) {
2211
+ if (record.status !== "active")
2212
+ continue;
2213
+ const prKey = `${record.repoFullName}::${record.prNumber}`;
2214
+ const isMerged = mergedPrs?.has(prKey) ?? false;
2215
+ const isStale = now2 - record.updatedAt > ttlMs;
2216
+ if (isMerged || isStale && !record.hasUnaddressedEvents) {
2217
+ view[record.correlationId] = {
2218
+ ...record,
2219
+ status: "expired",
2220
+ isWatching: false,
2221
+ updatedAt: now2
2222
+ };
2223
+ expiredCorrelationIds.push(record.correlationId);
2224
+ swept2 += 1;
2225
+ }
2226
+ }
2227
+ if (swept2 > 0) {
2228
+ persistCoordinationSubscriptions(directory, view, true, expiredCorrelationIds.map((correlationId) => ({
2229
+ kind: "expired",
2230
+ correlationId
2231
+ })));
2232
+ }
2233
+ return swept2;
2234
+ }
1504
2235
  const loaded = loadViewForWrite(directory);
1505
2236
  const opAudit = [];
1506
2237
  let swept = 0;
@@ -1552,7 +2283,18 @@ async function sweepStale(directory, ttlDays, mergedPrs) {
1552
2283
  }
1553
2284
  async function getPrSubscriptionHealth(directory) {
1554
2285
  try {
2286
+ const coordinationView = readCoordinationSubscriptions(directory);
1555
2287
  const read = readCheckpoint(directory);
2288
+ const migrationPending = read.kind === "ok" && sameProjectRoot(read.value.rootPath, directory) && read.value.migration?.done === false;
2289
+ if (coordinationView !== null && Object.keys(coordinationView).length > 0 && !migrationPending) {
2290
+ const audit2 = auditStats(directory);
2291
+ const archiveBytes2 = fileSizeOrNull(legacyArchivePath(directory)) ?? 0;
2292
+ const checkpointBytes2 = fileSizeOrNull(checkpointPath(directory)) ?? 0;
2293
+ const health2 = healthFromView(coordinationView, buildCoordinationProjectionCheckpoint(directory, coordinationView));
2294
+ health2.recoverySource = "checkpoint";
2295
+ applyFileStats(health2, checkpointBytes2, audit2, archiveBytes2);
2296
+ return health2;
2297
+ }
1556
2298
  const audit = auditStats(directory);
1557
2299
  const archiveBytes = fileSizeOrNull(legacyArchivePath(directory)) ?? 0;
1558
2300
  const checkpointBytes = fileSizeOrNull(checkpointPath(directory)) ?? 0;
@@ -1587,7 +2329,15 @@ async function getPrSubscriptionHealth(directory) {
1587
2329
  }
1588
2330
  health.recoverySource = legacySize === null ? "empty" : "legacy-log";
1589
2331
  } else {
1590
- const overlaid = overlayLegacy(directory, read.value);
2332
+ const pendingCheckpoint = migrationPending && coordinationView !== null ? (() => {
2333
+ const merged = cloneCheckpoint(read.value);
2334
+ merged.records = {
2335
+ ...merged.records,
2336
+ ...coordinationView
2337
+ };
2338
+ return merged;
2339
+ })() : read.value;
2340
+ const overlaid = overlayLegacy(directory, pendingCheckpoint);
1591
2341
  health = healthFromView(overlaid.view, read.value);
1592
2342
  health.recoverySource = overlaid.usedLegacy ? "checkpoint+legacy" : "checkpoint";
1593
2343
  }