@unbrained/pm-cli 2026.9.3 → 2026.9.4

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 (120) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +27 -0
  3. package/dist/cli-bundle/bundle-manifest.json +146 -146
  4. package/dist/cli-bundle/chunks/{chunk-E5RBBCXO.js → chunk-6XSKPNCF.js} +75 -75
  5. package/dist/cli-bundle/chunks/chunk-A24WAVE6.js +8 -0
  6. package/dist/cli-bundle/chunks/{chunk-DKGP3QLE.js → chunk-HI6BHPCU.js} +2 -2
  7. package/dist/cli-bundle/chunks/{chunk-Y4PJ6HPK.js → chunk-I2AS367G.js} +2 -2
  8. package/dist/cli-bundle/chunks/{chunk-IEHHE6GA.js → chunk-OGBVU67G.js} +2 -2
  9. package/dist/cli-bundle/chunks/{chunk-EDPIS7EB.js → chunk-RI4XNOYS.js} +2 -2
  10. package/dist/cli-bundle/chunks/chunk-TXW6VL33.js +38 -0
  11. package/dist/cli-bundle/chunks/{chunk-LENC656S.js → chunk-V72UV7QS.js} +76 -79
  12. package/dist/cli-bundle/chunks/chunk-VGLHECAP.js +9 -0
  13. package/dist/cli-bundle/chunks/{chunk-34HASX3S.js → chunk-X6CMGV6L.js} +2 -2
  14. package/dist/cli-bundle/chunks/{chunk-UPPM2FOZ.js → chunk-YW4QK2G7.js} +2 -2
  15. package/dist/cli-bundle/chunks/{register-list-query-W2ZTOMKP.js → register-list-query-Z5YYSJJG.js} +2 -2
  16. package/dist/cli-bundle/chunks/{register-mutation-RVND5NCG.js → register-mutation-ELSP6I4I.js} +2 -2
  17. package/dist/cli-bundle/chunks/{register-operations-TTROIVC4.js → register-operations-OH7R3ZT4.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-setup-D3ZFHY3L.js → register-setup-E2AE72QG.js} +2 -2
  19. package/dist/cli-bundle/focused-chunks/{chunk-Q45NHPF2.js → chunk-25UJBH4M.js} +2 -2
  20. package/dist/cli-bundle/focused-chunks/{chunk-4HJFY4MB.js → chunk-3646BZ4W.js} +2 -2
  21. package/dist/cli-bundle/focused-chunks/{chunk-TXE4TSHM.js → chunk-4YRAVI43.js} +2 -2
  22. package/dist/cli-bundle/focused-chunks/{chunk-M5M6SVHS.js → chunk-53IHPICX.js} +2 -2
  23. package/dist/cli-bundle/focused-chunks/chunk-5P4HVOWG.js +176 -0
  24. package/dist/cli-bundle/focused-chunks/chunk-636VVQ7F.js +54 -0
  25. package/dist/cli-bundle/focused-chunks/{chunk-D7KXFBXV.js → chunk-6PWPQKDN.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/{chunk-S27XMBIN.js → chunk-7N2TV7WZ.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/{chunk-5RFXUPN5.js → chunk-AJR3OP2E.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/{chunk-KZ2NB3ET.js → chunk-B3A6MJYE.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/{chunk-BWKHEIVF.js → chunk-BFDDJ2T2.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/{chunk-4WR46RMG.js → chunk-BHUIRZWQ.js} +8 -8
  31. package/dist/cli-bundle/focused-chunks/{chunk-4RRHC3S4.js → chunk-JADHHPY6.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-GT7MXDLW.js → chunk-MHKUIPJW.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/chunk-NBMJD6MK.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/chunk-NSKBLHVI.js +20 -0
  35. package/dist/cli-bundle/focused-chunks/{chunk-OL5EH6I3.js → chunk-P2GOAOQN.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/{chunk-UCLO3XDU.js → chunk-YARWJM2B.js} +2 -2
  37. package/dist/cli-bundle/main.js +2 -2
  38. package/dist/cli-bundle/sdk-authoring.js +1 -1
  39. package/dist/cli-bundle/sdk-contracts.js +1 -1
  40. package/dist/cli-bundle/sdk-core.js +28 -31
  41. package/dist/cli-bundle/sdk-governance.js +1 -1
  42. package/dist/cli-bundle/sdk-graph.js +1 -1
  43. package/dist/cli-bundle/sdk-merge.js +42 -37
  44. package/dist/cli-bundle/sdk-query.js +1 -1
  45. package/dist/cli-bundle/sdk-runtime.js +1 -1
  46. package/dist/cli-bundle/sdk-testing.js +1 -1
  47. package/dist/cli-bundle/sdk.js +10 -10
  48. package/dist/core/diagnostics/remediation.js +4 -4
  49. package/dist/core/history/history.d.ts +37 -1
  50. package/dist/core/history/history.js +278 -58
  51. package/dist/core/history/replay.js +68 -34
  52. package/dist/core/lock/lock.d.ts +10 -0
  53. package/dist/core/lock/lock.js +42 -3
  54. package/dist/core/shared/author.d.ts +3 -1
  55. package/dist/core/shared/author.js +111 -23
  56. package/dist/sdk/environment/agent-environment.d.ts +296 -0
  57. package/dist/sdk/environment/agent-environment.js +467 -0
  58. package/dist/sdk/environment/project-runtime-compatibility.d.ts +19 -1
  59. package/dist/sdk/environment/project-runtime-compatibility.js +118 -21
  60. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +14 -2
  61. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +14 -14
  62. package/dist/sdk/governance/health.js +13 -44
  63. package/dist/sdk/governance/merge-receipt-history.d.ts +62 -0
  64. package/dist/sdk/governance/merge-receipt-history.js +207 -0
  65. package/dist/sdk/governance/provenance-health.js +9 -3
  66. package/dist/sdk/history-compact.js +14 -5
  67. package/dist/sdk/history-redact.js +58 -23
  68. package/dist/sdk/history-repair.js +16 -6
  69. package/dist/sdk/index.d.ts +2 -0
  70. package/dist/sdk/index.js +4 -2
  71. package/dist/sdk/merge/driver.d.ts +3 -1
  72. package/dist/sdk/merge/driver.js +9 -9
  73. package/dist/sdk/merge/index.d.ts +1 -1
  74. package/dist/sdk/merge/index.js +3 -3
  75. package/dist/sdk/merge/receipts.d.ts +17 -6
  76. package/dist/sdk/merge/receipts.js +246 -75
  77. package/dist/sdk/merge/reconcile.d.ts +9 -7
  78. package/dist/sdk/merge/reconcile.js +175 -12
  79. package/dist/sdk/merge/three-way.d.ts +22 -2
  80. package/dist/sdk/merge/three-way.js +92 -25
  81. package/dist/sdk/runtime-primitives.d.ts +2 -2
  82. package/dist/sdk/runtime-primitives.js +4 -4
  83. package/dist/types.d.ts +25 -0
  84. package/dist/types.js +2 -2
  85. package/docs/AGENT_ENVIRONMENTS.md +47 -0
  86. package/docs/AGENT_PROVENANCE_ADR.md +17 -2
  87. package/docs/CONTEXT_INTEGRITY_CONTRACTS.md +38 -1
  88. package/docs/MERGE_SAFETY.md +119 -23
  89. package/docs/README.md +1 -0
  90. package/docs/SDK_CONTEXT_EVIDENCE_CONTRACTS.md +24 -2
  91. package/docs/SDK_RUNTIME_BOUNDARIES.md +17 -1
  92. package/docs/examples/sdk-agent-environment/README.md +14 -0
  93. package/docs/examples/sdk-agent-environment/package.json +8 -0
  94. package/docs/examples/sdk-agent-environment/run-agent-environment.mjs +154 -0
  95. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +4 -3
  96. package/marketplace.json +2 -2
  97. package/package.json +2 -2
  98. package/packages/pm-beads/package.json +1 -1
  99. package/packages/pm-calendar/package.json +1 -1
  100. package/packages/pm-command-kit/package.json +1 -1
  101. package/packages/pm-digital-twin/package.json +1 -1
  102. package/packages/pm-governance-audit/package.json +1 -1
  103. package/packages/pm-guide-shell/package.json +1 -1
  104. package/packages/pm-kanban/package.json +1 -1
  105. package/packages/pm-lifecycle-hooks/package.json +1 -1
  106. package/packages/pm-linked-test-adapters/package.json +1 -1
  107. package/packages/pm-search-advanced/package.json +1 -1
  108. package/packages/pm-templates/package.json +1 -1
  109. package/packages/pm-todos/package.json +1 -1
  110. package/packages/pm-vcs/package.json +1 -1
  111. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  112. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  113. package/sdk/public-surface.json +534 -24
  114. package/dist/cli-bundle/chunks/chunk-AY324UJ5.js +0 -9
  115. package/dist/cli-bundle/chunks/chunk-L7B7343B.js +0 -8
  116. package/dist/cli-bundle/chunks/chunk-LISESA7W.js +0 -35
  117. package/dist/cli-bundle/focused-chunks/chunk-3ZBI35HU.js +0 -176
  118. package/dist/cli-bundle/focused-chunks/chunk-GHCZ3YFR.js +0 -20
  119. package/dist/cli-bundle/focused-chunks/chunk-KBLZRU62.js +0 -2
  120. package/dist/cli-bundle/focused-chunks/chunk-QKHCSUGJ.js +0 -51
@@ -1,12 +1,6 @@
1
- /**
2
- * @module sdk/merge/reconcile
3
- *
4
- * Provides the post-merge SDK workflow that previews or repairs every drifted
5
- * item-history stream, then verifies history and storage integrity in one call.
6
- */
7
1
  import type { GlobalOptions } from "../../core/shared/command-types.js";
8
- import { type HistoryRepairAllResult } from "../history-repair.js";
9
2
  import { type ValidateResult } from "../governance/validate.js";
3
+ import { type HistoryRepairAllResult } from "../history-repair.js";
10
4
  import { type MergeDecisionReceiptSummary } from "./receipts.js";
11
5
  /** Options for the audited post-merge reconciliation workflow. */
12
6
  export interface MergeReconcileOptions {
@@ -36,6 +30,14 @@ export interface MergeReconcileResult {
36
30
  pending_before: number;
37
31
  reconciled: number;
38
32
  summaries: MergeDecisionReceiptSummary[];
33
+ /** Missing durable references discovered before this reconciliation. */
34
+ missing_history_references_before: number;
35
+ /** Pre-durable references eligible for explicit audited disposition. */
36
+ legacy_disposition_eligible: number;
37
+ /** Eligible references dispositioned by this apply pass. */
38
+ legacy_disposition_recorded: number;
39
+ /** Missing durable references still blocking health after this pass. */
40
+ missing_history_references_after: number;
39
41
  };
40
42
  /** Post-operation history-drift and storage-integrity validation. */
41
43
  validation: ValidateResult;
@@ -1,14 +1,135 @@
1
+ /**
2
+ * @module sdk/merge/reconcile
3
+ *
4
+ * Provides the post-merge SDK workflow that previews or repairs every drifted
5
+ * item-history stream, then verifies history and storage integrity in one call.
6
+ */
1
7
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="8d85d220-dad2-5234-8819-65632275eb47")}catch(e){}}();
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3a3372be-37e4-578a-8be9-9ad7bc585dbd")}catch(e){}}();
9
+ import { readFile } from "node:fs/promises";
10
+ import path from "node:path";
3
11
  import { EXIT_CODE } from "../../core/shared/constants.js";
4
12
  import { PmCliError } from "../../core/shared/errors.js";
13
+ import { compareTimestampStrings } from "../../core/shared/time.js";
5
14
  import { resolvePmRoot } from "../../core/store/paths.js";
6
- import { runHistoryRepair, runHistoryRepairAll, } from "../history-repair.js";
15
+ import { mapWithFixedConcurrency } from "../extension/concurrency.js";
16
+ import { runHealth } from "../governance/health.js";
17
+ import { DURABLE_MERGE_RECEIPT_INTRODUCED_AT, extractHistoryMergeReceiptReferences, isPreDurableDispositionEligibleReference, } from "../governance/merge-receipt-history.js";
7
18
  import { runValidate } from "../governance/validate.js";
8
- import { inspectMergeReceiptEvidence, markMergeReceiptReconciled, summarizeMergeReceipt, } from "./receipts.js";
19
+ import { runHistoryRepair, runHistoryRepairAll, } from "../history-repair.js";
9
20
  import { findGitWorkspaceRoot } from "./install.js";
10
- import { mapWithFixedConcurrency } from "../extension/concurrency.js";
21
+ import { inspectMergeReceiptEvidence, markMergeReceiptReconciled, summarizeMergeReceipt, } from "./receipts.js";
11
22
  const RECEIPT_ONLY_REPAIR_CONCURRENCY = 4;
23
+ /** Extract bounded item, line, and receipt coordinates from health evidence. */
24
+ function missingReceiptCoordinates(health) {
25
+ const integrity = health.checks.find((check) => check.name === "integrity");
26
+ const counts = integrity?.details.counts;
27
+ const count = typeof counts === "object" &&
28
+ counts !== null &&
29
+ !Array.isArray(counts) &&
30
+ typeof counts
31
+ .missing_merge_receipt_history_references === "number"
32
+ ? counts
33
+ .missing_merge_receipt_history_references
34
+ : 0;
35
+ const details = integrity?.details.missing_merge_receipt_history_reference_details;
36
+ const truncated = integrity?.details
37
+ .missing_merge_receipt_history_reference_details_truncated === true;
38
+ const coordinates = Array.isArray(details)
39
+ ? details.flatMap((detail) => {
40
+ if (typeof detail !== "object" ||
41
+ detail === null ||
42
+ Array.isArray(detail)) {
43
+ return [];
44
+ }
45
+ const record = detail;
46
+ return typeof record.item_id === "string" &&
47
+ typeof record.history_line === "number" &&
48
+ Number.isSafeInteger(record.history_line) &&
49
+ typeof record.receipt_id === "string"
50
+ ? [
51
+ {
52
+ item_id: record.item_id,
53
+ history_line: record.history_line,
54
+ receipt_id: record.receipt_id,
55
+ },
56
+ ]
57
+ : [];
58
+ })
59
+ : [];
60
+ return { count, truncated, coordinates };
61
+ }
62
+ /** Re-read history and retain only exact pre-durable clone-local references. */
63
+ async function identifyLegacyDispositionCandidates(pmRoot, coordinates) {
64
+ const candidates = [];
65
+ for (const coordinate of coordinates) {
66
+ let raw;
67
+ try {
68
+ raw = await readFile(path.join(pmRoot, "history", `${coordinate.item_id}.jsonl`), "utf8");
69
+ }
70
+ catch {
71
+ continue;
72
+ }
73
+ const line = raw.split(/\r?\n/)[coordinate.history_line - 1];
74
+ if (line === undefined || line.trim().length === 0)
75
+ continue;
76
+ let parsed;
77
+ try {
78
+ parsed = JSON.parse(line);
79
+ }
80
+ catch {
81
+ continue;
82
+ }
83
+ const reference = extractHistoryMergeReceiptReferences(parsed, coordinate.item_id).find((entry) => entry.receiptId === coordinate.receipt_id);
84
+ if (reference === undefined ||
85
+ !isPreDurableDispositionEligibleReference(reference) ||
86
+ reference.eventTimestamp === undefined ||
87
+ compareTimestampStrings(reference.eventTimestamp, DURABLE_MERGE_RECEIPT_INTRODUCED_AT) >= 0) {
88
+ continue;
89
+ }
90
+ candidates.push({
91
+ ...coordinate,
92
+ original_event_ts: reference.eventTimestamp,
93
+ });
94
+ }
95
+ return candidates;
96
+ }
97
+ /** Append auditable dispositions for eligible receipts that cannot be restored. */
98
+ async function recordLegacyReceiptDispositions(params) {
99
+ if (params.options.dryRun === true ||
100
+ params.options.force !== true ||
101
+ params.candidates.length === 0) {
102
+ return 0;
103
+ }
104
+ const byItem = new Map();
105
+ for (const candidate of params.candidates) {
106
+ const itemCandidates = byItem.get(candidate.item_id) ?? [];
107
+ itemCandidates.push(candidate);
108
+ byItem.set(candidate.item_id, itemCandidates);
109
+ }
110
+ for (const [itemId, itemCandidates] of byItem) {
111
+ await runHistoryRepair(itemId, {
112
+ author: params.options.author ?? params.global.author,
113
+ message: params.options.message ??
114
+ "accept unrecoverable pre-durable merge receipt references",
115
+ force: true,
116
+ auditOperation: "merge_reconcile",
117
+ forceAuditEntry: true,
118
+ auditContext: {
119
+ merge: {
120
+ missing_receipt_dispositions: itemCandidates.map((candidate) => ({
121
+ receipt_id: candidate.receipt_id,
122
+ original_history_line: candidate.history_line,
123
+ original_event_ts: candidate.original_event_ts,
124
+ reason: "legacy_clone_local_only",
125
+ })),
126
+ },
127
+ },
128
+ }, params.global);
129
+ }
130
+ return params.candidates.length;
131
+ }
132
+ /** Execute one receipt-backed repair while preserving its settled result. */
12
133
  async function runReceiptOnlyRepair(params) {
13
134
  try {
14
135
  return {
@@ -30,6 +151,7 @@ async function runReceiptOnlyRepair(params) {
30
151
  return { status: "rejected", reason };
31
152
  }
32
153
  }
154
+ /** Fold settled receipt repairs into the aggregate reconciliation result. */
33
155
  function appendReceiptOnlyResults(params) {
34
156
  params.results.forEach((settled, index) => {
35
157
  const id = params.receiptOnlyIds[index];
@@ -59,15 +181,14 @@ function appendReceiptOnlyResults(params) {
59
181
  params.repair.totals.failed += 1;
60
182
  });
61
183
  }
184
+ /** Settle only receipts named by successful trusted repair evidence. */
62
185
  async function settleProvenReceipts(params) {
63
186
  if (params.dryRun || params.repair.totals.failed > 0)
64
187
  return 0;
65
188
  const trustedReceiptIds = new Set(params.repair.streams.flatMap((stream) => stream.merge_receipt_proof?.trusted
66
189
  ? stream.merge_receipt_proof.receipt_ids
67
190
  : []));
68
- const receiptsToSettle = params.force
69
- ? params.pendingReceipts
70
- : params.pendingReceipts.filter((receipt) => trustedReceiptIds.has(receipt.id));
191
+ const receiptsToSettle = params.pendingReceipts.filter((receipt) => trustedReceiptIds.has(receipt.id));
71
192
  await Promise.all(receiptsToSettle.map((receipt) => markMergeReceiptReconciled(params.gitWorkspaceRoot ?? process.cwd(), receipt, { requireExisting: true })));
72
193
  return receiptsToSettle.length;
73
194
  }
@@ -80,6 +201,14 @@ async function settleProvenReceipts(params) {
80
201
  export async function runMergeReconcile(options, global) {
81
202
  const dryRun = options.dryRun === true;
82
203
  const pmRoot = resolvePmRoot(process.cwd(), global.path);
204
+ const preflightHealth = await runHealth(global, {
205
+ checkOnly: true,
206
+ full: true,
207
+ skipVectors: true,
208
+ skipDrift: true,
209
+ });
210
+ const missingBefore = missingReceiptCoordinates(preflightHealth);
211
+ const legacyDispositionCandidates = await identifyLegacyDispositionCandidates(pmRoot, missingBefore.coordinates);
83
212
  const gitWorkspaceRoot = await findGitWorkspaceRoot(pmRoot);
84
213
  const receiptEvidence = await inspectMergeReceiptEvidence(gitWorkspaceRoot ?? process.cwd(), {
85
214
  includeLossless: true,
@@ -94,6 +223,11 @@ export async function runMergeReconcile(options, global) {
94
223
  },
95
224
  });
96
225
  }
226
+ const recordedLegacyDispositionCount = await recordLegacyReceiptDispositions({
227
+ candidates: legacyDispositionCandidates,
228
+ options,
229
+ global,
230
+ });
97
231
  const pendingReceipts = receiptEvidence.receipts;
98
232
  const receiptsByItem = new Map();
99
233
  for (const receipt of pendingReceipts) {
@@ -140,20 +274,38 @@ export async function runMergeReconcile(options, global) {
140
274
  });
141
275
  const reconciledReceiptCount = await settleProvenReceipts({
142
276
  dryRun,
143
- force: options.force === true,
144
277
  pendingReceipts,
145
278
  repair,
146
279
  gitWorkspaceRoot,
147
280
  });
148
281
  const validation = await runValidate({ checkHistoryDrift: true, checkStorageIntegrity: true }, global);
282
+ const postflightHealth = recordedLegacyDispositionCount === 0
283
+ ? preflightHealth
284
+ : await runHealth(global, {
285
+ checkOnly: true,
286
+ full: true,
287
+ skipVectors: true,
288
+ skipDrift: true,
289
+ });
290
+ const missingAfter = missingReceiptCoordinates(postflightHealth);
149
291
  const mergeChecksGreen = validation.checks.every((check) => check.status === "ok");
150
292
  const receiptSettlementComplete = dryRun
151
293
  ? pendingReceipts.length === 0
152
294
  : reconciledReceiptCount === pendingReceipts.length;
153
- const ok = repair.totals.failed === 0 && mergeChecksGreen && receiptSettlementComplete;
154
- const guidance = dryRun
295
+ const ok = [
296
+ repair.totals.failed === 0,
297
+ mergeChecksGreen,
298
+ receiptSettlementComplete,
299
+ missingAfter.count === 0,
300
+ !missingAfter.truncated,
301
+ ].every(Boolean);
302
+ const boundedBatchGuidance = missingBefore.truncated || missingAfter.truncated
303
+ ? "Missing receipt coordinates exceed the bounded detail response; each reviewed --force pass can disposition only the reported eligible batch. Repeat dry-run and --force passes until receipts.missing_history_references_after=0 and pm merge reconcile returns ok=true."
304
+ : undefined;
305
+ const guidance = (dryRun
155
306
  ? [
156
307
  "Review repair.streams, then rerun pm merge reconcile without --dry-run to apply audited repairs.",
308
+ boundedBatchGuidance,
157
309
  "No Git hook is installed automatically; invoke this command from an explicit post-merge hook only when your repository policy opts in.",
158
310
  ]
159
311
  : ok
@@ -163,8 +315,15 @@ export async function runMergeReconcile(options, global) {
163
315
  ]
164
316
  : [
165
317
  "Reconciliation remains incomplete; inspect repair failures and non-green validation checks before retrying.",
318
+ boundedBatchGuidance,
319
+ ...(legacyDispositionCandidates.length > 0 &&
320
+ recordedLegacyDispositionCount === 0
321
+ ? [
322
+ "Pre-durable clone-local-only receipt references are eligible for an audited disposition; rerun with --force after reviewing receipts.legacy_disposition_eligible.",
323
+ ]
324
+ : []),
166
325
  "Do not commit repaired history streams as reconciled until pm merge reconcile returns ok=true.",
167
- ];
326
+ ]).filter((message) => message !== undefined);
168
327
  return {
169
328
  ok,
170
329
  dry_run: dryRun,
@@ -173,6 +332,10 @@ export async function runMergeReconcile(options, global) {
173
332
  pending_before: pendingReceipts.length,
174
333
  reconciled: reconciledReceiptCount,
175
334
  summaries: pendingReceipts.map(summarizeMergeReceipt),
335
+ missing_history_references_before: missingBefore.count,
336
+ legacy_disposition_eligible: legacyDispositionCandidates.length,
337
+ legacy_disposition_recorded: recordedLegacyDispositionCount,
338
+ missing_history_references_after: missingAfter.count,
176
339
  },
177
340
  validation,
178
341
  guidance,
@@ -180,4 +343,4 @@ export async function runMergeReconcile(options, global) {
180
343
  };
181
344
  }
182
345
  //# sourceMappingURL=reconcile.js.map
183
- //# debugId=8d85d220-dad2-5234-8819-65632275eb47
346
+ //# debugId=3a3372be-37e4-578a-8be9-9ad7bc585dbd
@@ -1,6 +1,20 @@
1
1
  import { type ItemDocumentFormatOptions } from "../../core/item/item-format.js";
2
2
  /** Restricts which side of a three-way merge wins an unresolvable conflict. */
3
3
  export type MergePreferredSide = "ours" | "theirs";
4
+ /** Declares how competing item scalar writes select their retained value. */
5
+ export type ItemScalarConflictResolution = "preferred_side" | "stable_value_order" | "latest_document_update";
6
+ /** Explains the exact evidence used to select one conflicting item scalar. */
7
+ export type ItemScalarResolutionBasis = "requested_preference" | "document_updated_at" | "stable_value_tiebreak";
8
+ /** JSON-stable marker used when a conflict decision refers to an absent scalar. */
9
+ export declare const ITEM_SCALAR_MISSING_VALUE: Readonly<{
10
+ readonly pm_item_scalar_missing: true;
11
+ }>;
12
+ /** Return whether receipt evidence represents an absent item scalar. */
13
+ export declare function isItemScalarMissingValue(value: unknown): value is typeof ITEM_SCALAR_MISSING_VALUE;
14
+ /** Encode an absent scalar so every decision property survives JSON serialization. */
15
+ export declare function encodeItemScalarDecisionValue(value: unknown): unknown;
16
+ /** Hash scalars in disjoint present/missing domains for collision-free evidence. */
17
+ export declare function hashItemScalarDecisionValue(value: unknown): string;
4
18
  /** Restricts the strategy labels reported by the history stream merge. */
5
19
  export type HistoryMergeStrategy = "identical" | "fast_forward_ours" | "fast_forward_theirs" | "union_reanchor";
6
20
  /** Documents the history stream merge result payload exchanged by command, SDK, and package integrations. */
@@ -87,6 +101,10 @@ export interface ItemMergeConflictDecision {
87
101
  retained: unknown;
88
102
  /** Value not retained in the merged item. */
89
103
  discarded: unknown;
104
+ /** Branch coordinate whose value was retained. */
105
+ retained_side?: MergePreferredSide;
106
+ /** Evidence class that selected the retained value. */
107
+ resolution_basis?: ItemScalarResolutionBasis;
90
108
  }
91
109
  /** Documents the item document merge result payload exchanged by command, SDK, and package integrations. */
92
110
  export interface ItemDocumentMergeResult {
@@ -102,8 +120,10 @@ export interface ItemDocumentMergeResult {
102
120
  conflict_decisions: ItemMergeConflictDecision[];
103
121
  /** Side requested by the caller; stable-value decisions can retain either side. */
104
122
  requested_preference: MergePreferredSide;
123
+ /** Whether the requested side participates in item scalar selection under the declared policy. */
124
+ requested_preference_applied: boolean;
105
125
  /** Scalar-conflict selection contract used for this merge. */
106
- conflict_resolution: "preferred_side" | "stable_value_order";
126
+ conflict_resolution: ItemScalarConflictResolution;
107
127
  }
108
128
  /**
109
129
  * Field-aware three-way merge of one tracker item document. Commutative
@@ -121,7 +141,7 @@ export declare function mergeItemDocuments(baseRaw: string, oursRaw: string, the
121
141
  /** Side that wins unresolvable conflicts (default "ours"). */
122
142
  preferred?: MergePreferredSide;
123
143
  /** Direction-independent scalar selection used by the Git driver. */
124
- conflictResolution?: "preferred_side" | "stable_value_order";
144
+ conflictResolution?: ItemScalarConflictResolution;
125
145
  }): ItemDocumentMergeResult;
126
146
  /** Documents the JSON document merge result payload exchanged by command, SDK, and package integrations. */
127
147
  export interface JsonDocumentMergeResult {
@@ -9,14 +9,37 @@
9
9
  * definition for multi-branch agent workflows.
10
10
  */
11
11
 
12
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7e8fcddf-09fe-5979-b23a-78915bf7f896")}catch(e){}}();
12
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4d5434c7-544b-5061-89eb-6e4074e58e30")}catch(e){}}();
13
13
  import { historyEntriesToRaw, reanchorHistoryEntries, } from "../../core/history/replay.js";
14
14
  import { canonicalDocument, parseItemDocument, serializeItemDocument, } from "../../core/item/item-format.js";
15
+ import { splitAcceptanceCriteria } from "../../core/item/parse.js";
15
16
  import { EXIT_CODE } from "../../core/shared/constants.js";
16
17
  import { findFirstMergeConflictMarker } from "../../core/shared/conflict-markers.js";
17
18
  import { PmCliError } from "../../core/shared/errors.js";
18
- import { stableStringify } from "../../core/shared/serialization.js";
19
+ import { sha256Hex, stableStringify } from "../../core/shared/serialization.js";
19
20
  import { compareTimestampStrings } from "../../core/shared/time.js";
21
+ /** JSON-stable marker used when a conflict decision refers to an absent scalar. */
22
+ export const ITEM_SCALAR_MISSING_VALUE = Object.freeze({
23
+ pm_item_scalar_missing: true,
24
+ });
25
+ /** Return whether receipt evidence represents an absent item scalar. */
26
+ export function isItemScalarMissingValue(value) {
27
+ return (typeof value === "object" &&
28
+ value !== null &&
29
+ !Array.isArray(value) &&
30
+ Object.keys(value).length === 1 &&
31
+ value.pm_item_scalar_missing === true);
32
+ }
33
+ /** Encode an absent scalar so every decision property survives JSON serialization. */
34
+ export function encodeItemScalarDecisionValue(value) {
35
+ return value === undefined ? ITEM_SCALAR_MISSING_VALUE : value;
36
+ }
37
+ /** Hash scalars in disjoint present/missing domains for collision-free evidence. */
38
+ export function hashItemScalarDecisionValue(value) {
39
+ return sha256Hex(stableStringify(value === undefined
40
+ ? { pm_item_scalar_presence: "missing" }
41
+ : { pm_item_scalar_presence: "present", value }));
42
+ }
20
43
  function parseHistoryJsonl(raw, label) {
21
44
  const conflictMarker = findFirstMergeConflictMarker(raw);
22
45
  if (conflictMarker) {
@@ -294,10 +317,15 @@ export const ITEM_UNION_COLLECTION_FIELDS = [
294
317
  "reminders",
295
318
  "events",
296
319
  ];
320
+ /** Delimiter-backed list fields that use the same lossless element merge as native metadata collections while retaining their legacy scalar storage shape. */
321
+ const ITEM_UNION_DELIMITED_FIELDS = ["acceptance_criteria"];
297
322
  /** Item metadata fields resolved by deterministic latest-timestamp-wins instead of three-way conflict, so disjoint-field edits never conflict on the shared freshness scalar every mutation rewrites. */
298
323
  export const ITEM_LATEST_TIMESTAMP_FIELDS = ["updated_at"];
299
324
  function jsonEquals(left, right) {
300
- return stableStringify(left ?? null) === stableStringify(right ?? null);
325
+ if (left === undefined || right === undefined) {
326
+ return left === undefined && right === undefined;
327
+ }
328
+ return stableStringify(left) === stableStringify(right);
301
329
  }
302
330
  function unionCollection(base, ours, theirs) {
303
331
  const toEntries = (value) => Array.isArray(value) ? value : [];
@@ -393,24 +421,51 @@ function mergeScalarThreeWay(base, ours, theirs, preferred) {
393
421
  conflict: true,
394
422
  };
395
423
  }
396
- function mergeItemScalarThreeWay(base, ours, theirs, preferred, conflictResolution) {
424
+ /** Resolve one item scalar with explicit recency and deterministic tie evidence. */
425
+ function mergeItemScalarThreeWay(base, ours, theirs, preferred, conflictResolution, oursUpdatedAt, theirsUpdatedAt) {
397
426
  const outcome = mergeScalarThreeWay(base, ours, theirs, preferred);
398
- if (!outcome.conflict || conflictResolution === "preferred_side") {
399
- return outcome;
427
+ if (!outcome.conflict) {
428
+ return { ...outcome, conflict: false };
429
+ }
430
+ if (conflictResolution === "preferred_side") {
431
+ return {
432
+ ...outcome,
433
+ resolution_basis: "requested_preference",
434
+ retained_side: outcome.from_theirs ? "theirs" : "ours",
435
+ };
436
+ }
437
+ const timestampOrder = compareTimestampStrings(oursUpdatedAt, theirsUpdatedAt);
438
+ if (conflictResolution === "latest_document_update" && timestampOrder !== 0) {
439
+ const retainTheirs = timestampOrder < 0;
440
+ return {
441
+ value: retainTheirs ? theirs : ours,
442
+ from_theirs: retainTheirs,
443
+ conflict: true,
444
+ resolution_basis: "document_updated_at",
445
+ retained_side: retainTheirs ? "theirs" : "ours",
446
+ };
400
447
  }
401
- const retainTheirs = stableStringify(theirs).localeCompare(stableStringify(ours)) < 0;
448
+ const retainTheirs = stableStringify(encodeItemScalarDecisionValue(theirs)).localeCompare(stableStringify(encodeItemScalarDecisionValue(ours))) < 0;
402
449
  return {
403
450
  value: retainTheirs ? theirs : ours,
404
451
  from_theirs: retainTheirs,
405
452
  conflict: true,
453
+ resolution_basis: "stable_value_tiebreak",
454
+ retained_side: retainTheirs ? "theirs" : "ours",
406
455
  };
407
456
  }
408
457
  function toMetadataRecord(document) {
409
458
  return { ...document.metadata };
410
459
  }
460
+ /** Parse one merge side and reject values reserved for internal absence evidence. */
411
461
  function parseItemMergeSide(raw, label, options) {
412
462
  try {
413
- return canonicalDocument(parseItemDocument(raw, options), options);
463
+ const document = canonicalDocument(parseItemDocument(raw, options), options);
464
+ const reservedField = Object.entries(document.metadata).find(([, value]) => isItemScalarMissingValue(value))?.[0];
465
+ if (reservedField !== undefined) {
466
+ throw new TypeError(`metadata field ${reservedField} uses the reserved missing-scalar evidence marker`);
467
+ }
468
+ return document;
414
469
  }
415
470
  catch (error) {
416
471
  throw new PmCliError(`Item merge input (${label}) is not a readable item document: ${String(error)}`, EXIT_CODE.GENERIC_FAILURE);
@@ -422,7 +477,9 @@ function mergeItemUnionField(params) {
422
477
  return false;
423
478
  const union = params.field === "tests"
424
479
  ? unionLinkedTests(params.baseValue, params.oursValue, params.theirsValue)
425
- : unionCollection(params.baseValue, params.oursValue, params.theirsValue);
480
+ : params.field === "acceptance_criteria"
481
+ ? unionCollection(splitAcceptanceCriteria(params.baseValue), splitAcceptanceCriteria(params.oursValue), splitAcceptanceCriteria(params.theirsValue)).join("; ")
482
+ : unionCollection(params.baseValue, params.oursValue, params.theirsValue);
426
483
  params.accumulator.merged[params.field] = union;
427
484
  if (!jsonEquals(union, params.oursValue)) {
428
485
  params.accumulator.unionFields.push(params.field);
@@ -431,7 +488,7 @@ function mergeItemUnionField(params) {
431
488
  }
432
489
  /** Merge one scalar field while preserving conflict evidence. */
433
490
  function mergeItemScalarField(params) {
434
- const outcome = mergeItemScalarThreeWay(params.baseValue, params.oursValue, params.theirsValue, params.preferred, params.conflictResolution);
491
+ const outcome = mergeItemScalarThreeWay(params.baseValue, params.oursValue, params.theirsValue, params.preferred, params.conflictResolution, params.oursUpdatedAt, params.theirsUpdatedAt);
435
492
  if (outcome.value !== undefined) {
436
493
  params.accumulator.merged[params.field] = outcome.value;
437
494
  }
@@ -439,19 +496,24 @@ function mergeItemScalarField(params) {
439
496
  params.accumulator.conflictFields.push(params.field);
440
497
  params.accumulator.conflictDecisions.push({
441
498
  field: params.field,
442
- base: params.baseValue,
443
- ours: params.oursValue,
444
- theirs: params.theirsValue,
445
- retained: outcome.value,
446
- discarded: outcome.from_theirs ? params.oursValue : params.theirsValue,
499
+ base: encodeItemScalarDecisionValue(params.baseValue),
500
+ ours: encodeItemScalarDecisionValue(params.oursValue),
501
+ theirs: encodeItemScalarDecisionValue(params.theirsValue),
502
+ retained: encodeItemScalarDecisionValue(outcome.value),
503
+ discarded: encodeItemScalarDecisionValue(outcome.from_theirs ? params.oursValue : params.theirsValue),
504
+ retained_side: outcome.retained_side,
505
+ resolution_basis: outcome.resolution_basis,
447
506
  });
448
507
  }
449
508
  else if (outcome.from_theirs) {
450
509
  params.accumulator.fieldsFromTheirs.push(params.field);
451
510
  }
452
511
  }
453
- function mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferred, conflictResolution) {
454
- const unionFieldSet = new Set(ITEM_UNION_COLLECTION_FIELDS);
512
+ function mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferred, conflictResolution, oursUpdatedAt, theirsUpdatedAt) {
513
+ const unionFieldSet = new Set([
514
+ ...ITEM_UNION_COLLECTION_FIELDS,
515
+ ...ITEM_UNION_DELIMITED_FIELDS,
516
+ ]);
455
517
  const latestFieldSet = new Set(ITEM_LATEST_TIMESTAMP_FIELDS);
456
518
  const fieldNames = [
457
519
  ...new Set([...Object.keys(oursRecord), ...Object.keys(theirsRecord)]),
@@ -491,6 +553,8 @@ function mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferre
491
553
  theirsValue,
492
554
  preferred,
493
555
  conflictResolution,
556
+ oursUpdatedAt,
557
+ theirsUpdatedAt,
494
558
  accumulator,
495
559
  });
496
560
  }
@@ -523,17 +587,19 @@ export function mergeItemDocuments(baseRaw, oursRaw, theirsRaw, options = {}) {
523
587
  : {};
524
588
  const oursRecord = toMetadataRecord(ours);
525
589
  const theirsRecord = toMetadataRecord(theirs);
526
- const { merged, conflictFields, fieldsFromTheirs, unionFields, conflictDecisions, } = mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferred, conflictResolution);
527
- const bodyOutcome = mergeItemScalarThreeWay(hasBase ? base.body : "", ours.body, theirs.body, preferred, conflictResolution);
590
+ const { merged, conflictFields, fieldsFromTheirs, unionFields, conflictDecisions, } = mergeItemMetadataRecords(baseRecord, oursRecord, theirsRecord, preferred, conflictResolution, ours.metadata.updated_at, theirs.metadata.updated_at);
591
+ const bodyOutcome = mergeItemScalarThreeWay(hasBase ? base.body : "", ours.body, theirs.body, preferred, conflictResolution, ours.metadata.updated_at, theirs.metadata.updated_at);
528
592
  if (bodyOutcome.conflict) {
529
593
  conflictFields.push("body");
530
594
  conflictDecisions.push({
531
595
  field: "body",
532
- base: hasBase ? base.body : "",
533
- ours: ours.body,
534
- theirs: theirs.body,
535
- retained: bodyOutcome.value,
536
- discarded: bodyOutcome.from_theirs ? ours.body : theirs.body,
596
+ base: encodeItemScalarDecisionValue(hasBase ? base.body : ""),
597
+ ours: encodeItemScalarDecisionValue(ours.body),
598
+ theirs: encodeItemScalarDecisionValue(theirs.body),
599
+ retained: encodeItemScalarDecisionValue(bodyOutcome.value),
600
+ discarded: encodeItemScalarDecisionValue(bodyOutcome.from_theirs ? ours.body : theirs.body),
601
+ retained_side: bodyOutcome.retained_side,
602
+ resolution_basis: bodyOutcome.resolution_basis,
537
603
  });
538
604
  }
539
605
  else if (bodyOutcome.from_theirs) {
@@ -550,6 +616,7 @@ export function mergeItemDocuments(baseRaw, oursRaw, theirsRaw, options = {}) {
550
616
  union_fields: unionFields,
551
617
  conflict_decisions: conflictDecisions,
552
618
  requested_preference: preferred,
619
+ requested_preference_applied: conflictResolution === "preferred_side",
553
620
  conflict_resolution: conflictResolution,
554
621
  };
555
622
  }
@@ -706,4 +773,4 @@ export function mergeJsonDocuments(baseRaw, oursRaw, theirsRaw, options = {}) {
706
773
  };
707
774
  }
708
775
  //# sourceMappingURL=three-way.js.map
709
- //# debugId=7e8fcddf-09fe-5979-b23a-78915bf7f896
776
+ //# debugId=4d5434c7-544b-5061-89eb-6e4074e58e30
@@ -17,10 +17,10 @@ export { isFileMissingError, pathExists, readFileIfExists, removeFileIfExists, w
17
17
  export { normalizeBulkIdsValue, parseBulkIdsText, resolveCliBulkIdsInput, type BulkIdsFileReader, type BulkIdsInputReaders, type BulkIdsStdinReader, type BulkIdsValue, } from "../core/io/bulk-ids-input.js";
18
18
  export { type HistoryDiffValueEntry, computeHistoryDiff, patchPathToChangedField, } from "../core/history/history-diff.js";
19
19
  export { enforceHistoryStreamPolicyForItem, enforceHistoryStreamPolicyForItems, } from "../core/history/history-stream-policy.js";
20
- export { appendHistoryEntry, CURRENT_HISTORY_ITEM_HASH_VERSION, createHistoryEntry, hashDocument, hashDocumentForVersion, hashEmptyDocument, SUPPORTED_HISTORY_ITEM_HASH_VERSIONS, type HistoryItemHashVersion, } from "../core/history/history.js";
20
+ export { appendHistoryEntry, CURRENT_HISTORY_ITEM_HASH_VERSION, CURRENT_HISTORY_RECORD_HASH_VERSION, createHistoryEntry, hashDocument, hashDocumentForVersion, hashEmptyDocument, hashHistoryPatch, hashHistoryRecord, hashHistoryStream, resealHistoryRewrite, sealHistoryRecord, SUPPORTED_HISTORY_ITEM_HASH_VERSIONS, SUPPORTED_HISTORY_RECORD_HASH_VERSIONS, verifyHistoryRecordHash, verifyHistoryRewriteEvidence, type HistoryItemHashVersion, type HistoryRecordHashVersion, } from "../core/history/history.js";
21
21
  export { readHistoryEntries } from "../core/history/read.js";
22
22
  export { appendWorkspaceAuditEvent, appendWorkspaceHistoryChange, getWorkspaceHistoryPath, inspectWorkspaceHistoryState, mutateWorkspaceJsonWithHistory, reconcileWorkspaceJsonHistory, restoreWorkspaceJsonFromHistory, WORKSPACE_HISTORY_ID, writeWorkspaceJsonWithHistory, type WorkspaceAuditEventOptions, type WorkspaceHistoryChange, type WorkspaceHistoryStateAgreement, type WorkspaceJsonMutation, type WorkspaceJsonMutationOptions, type WorkspaceJsonReconciliationOptions, type WorkspaceJsonRestoreOptions, type WorkspaceJsonRestoreResult, type WorkspaceJsonWriteOptions, } from "../core/history/workspace-history.js";
23
- export { normalizeReplayPatchOps, replayToCanonicalItemDocument, replayToItemDocument, toReplayDocument, verifyHistoryChain, verifyHistoryChainWithVersion, } from "../core/history/replay.js";
23
+ export { normalizeReplayPatchOps, reanchorHistoryEntries, replayToCanonicalItemDocument, replayToItemDocument, toReplayDocument, verifyHistoryChain, verifyHistoryChainWithVersion, } from "../core/history/replay.js";
24
24
  export { resolveBodyFileContent } from "../core/io/body-file.js";
25
25
  export { generateItemId, normalizeItemId, normalizeRawItemId, } from "../core/item/id.js";
26
26
  export { canonicalDocument, normalizeItemMetadata, parseItemDocument, serializeItemDocument, } from "../core/item/item-format.js";
@@ -7,7 +7,7 @@
7
7
  * prefer the typed operations exported by the main SDK barrel.
8
8
  */
9
9
 
10
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d45775e5-6826-521b-80b3-429ed22947d9")}catch(e){}}();
10
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="aba5403c-0694-5c51-95a8-ae7e7575347b")}catch(e){}}();
11
11
  export { createCheckpointId, loadMutationCheckpoint, restoreCheckpointItems, writeMutationCheckpoint, } from "../core/checkpoint/mutation-checkpoint.js";
12
12
  export { flattenFlagListValue, resolveFlagValueKind, } from "../core/extensions/flag-value-types.js";
13
13
  export { createUnknownSubcommandError, } from "./agent/subcommand-recovery.js";
@@ -19,10 +19,10 @@ export { isFileMissingError, pathExists, readFileIfExists, removeFileIfExists, w
19
19
  export { normalizeBulkIdsValue, parseBulkIdsText, resolveCliBulkIdsInput, } from "../core/io/bulk-ids-input.js";
20
20
  export { computeHistoryDiff, patchPathToChangedField, } from "../core/history/history-diff.js";
21
21
  export { enforceHistoryStreamPolicyForItem, enforceHistoryStreamPolicyForItems, } from "../core/history/history-stream-policy.js";
22
- export { appendHistoryEntry, CURRENT_HISTORY_ITEM_HASH_VERSION, createHistoryEntry, hashDocument, hashDocumentForVersion, hashEmptyDocument, SUPPORTED_HISTORY_ITEM_HASH_VERSIONS, } from "../core/history/history.js";
22
+ export { appendHistoryEntry, CURRENT_HISTORY_ITEM_HASH_VERSION, CURRENT_HISTORY_RECORD_HASH_VERSION, createHistoryEntry, hashDocument, hashDocumentForVersion, hashEmptyDocument, hashHistoryPatch, hashHistoryRecord, hashHistoryStream, resealHistoryRewrite, sealHistoryRecord, SUPPORTED_HISTORY_ITEM_HASH_VERSIONS, SUPPORTED_HISTORY_RECORD_HASH_VERSIONS, verifyHistoryRecordHash, verifyHistoryRewriteEvidence, } from "../core/history/history.js";
23
23
  export { readHistoryEntries } from "../core/history/read.js";
24
24
  export { appendWorkspaceAuditEvent, appendWorkspaceHistoryChange, getWorkspaceHistoryPath, inspectWorkspaceHistoryState, mutateWorkspaceJsonWithHistory, reconcileWorkspaceJsonHistory, restoreWorkspaceJsonFromHistory, WORKSPACE_HISTORY_ID, writeWorkspaceJsonWithHistory, } from "../core/history/workspace-history.js";
25
- export { normalizeReplayPatchOps, replayToCanonicalItemDocument, replayToItemDocument, toReplayDocument, verifyHistoryChain, verifyHistoryChainWithVersion, } from "../core/history/replay.js";
25
+ export { normalizeReplayPatchOps, reanchorHistoryEntries, replayToCanonicalItemDocument, replayToItemDocument, toReplayDocument, verifyHistoryChain, verifyHistoryChainWithVersion, } from "../core/history/replay.js";
26
26
  export { resolveBodyFileContent } from "../core/io/body-file.js";
27
27
  export { generateItemId, normalizeItemId, normalizeRawItemId, } from "../core/item/id.js";
28
28
  export { canonicalDocument, normalizeItemMetadata, parseItemDocument, serializeItemDocument, } from "../core/item/item-format.js";
@@ -82,4 +82,4 @@ export { maybeRunFirstUseTelemetryPrompt } from "../core/telemetry/consent.js";
82
82
  export { deriveTelemetryCommandResolution, } from "../core/telemetry/observability.js";
83
83
  export { emitTelemetryErrorEvent, finishTelemetryCommand, startTelemetryCommand, } from "../core/telemetry/runtime.js";
84
84
  //# sourceMappingURL=runtime-primitives.js.map
85
- //# debugId=d45775e5-6826-521b-80b3-429ed22947d9
85
+ //# debugId=aba5403c-0694-5c51-95a8-ae7e7575347b
package/dist/types.d.ts CHANGED
@@ -789,6 +789,25 @@ export interface HistoryPatchOp {
789
789
  /** Value that configures or reports value for this contract. */
790
790
  value?: unknown;
791
791
  }
792
+ /** Immutable evidence retained whenever history maintenance replaces anchors or patch operations. */
793
+ export interface HistoryReanchorEvidence {
794
+ /** Before-state anchor present on the record before re-anchoring. */
795
+ before_hash: string;
796
+ /** After-state anchor present on the record before re-anchoring. */
797
+ after_hash: string;
798
+ /** Item hash epoch present before re-anchoring. */
799
+ item_hash_version?: number;
800
+ /** Hash of the original patch, retained even when redaction forbids retaining its values. */
801
+ patch_hash: string;
802
+ /** Original patch operations when repair replaced or skipped recorded intent. */
803
+ patch?: HistoryPatchOp[];
804
+ /** Record-integrity epoch protecting the pre-re-anchor record. */
805
+ record_hash_version?: number;
806
+ /** Record hash protecting the pre-re-anchor attribution and operation metadata. */
807
+ record_hash?: string;
808
+ /** Exact prior sealed record when maintenance can retain its non-sensitive payload. */
809
+ record?: HistoryEntry;
810
+ }
792
811
  /** Documents the history entry payload exchanged by command, SDK, and package integrations. */
793
812
  export interface HistoryEntry {
794
813
  /** Value that configures or reports ts for this contract. */
@@ -839,6 +858,12 @@ export interface HistoryEntry {
839
858
  after_hash: string;
840
859
  /** Version of the item canonicalization used by before_hash and after_hash. Absent on legacy streams. */
841
860
  item_hash_version?: number;
861
+ /** Version of the canonical immutable-record envelope. Absent on legacy entries whose coverage is item-state-only. */
862
+ record_hash_version?: number;
863
+ /** SHA-256 hash covering this event's immutable metadata, patch, and item anchors. */
864
+ record_hash?: string;
865
+ /** Append-only evidence for every prior anchor or patch representation replaced by maintenance. */
866
+ reanchor_evidence?: HistoryReanchorEvidence[];
842
867
  /** Human-readable explanation suitable for logs and agent-facing output. */
843
868
  message?: string;
844
869
  /** Structured audit metadata that does not alter replayed item state. */