@unbrained/pm-cli 2026.9.4 → 2026.9.6

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 (277) hide show
  1. package/.agents/skills/HARNESS_COMPATIBILITY.md +1 -1
  2. package/.agents/skills/README.md +2 -2
  3. package/.agents/skills/pm-developer/SKILL.md +4 -1
  4. package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +10 -0
  5. package/.agents/skills/pm-developer/references/GRAPH_AND_RELATIONSHIPS.md +3 -1
  6. package/.agents/skills/pm-extensions/SKILL.md +2 -2
  7. package/.agents/skills/pm-extensions/references/LIFECYCLE.md +3 -3
  8. package/.agents/skills/pm-extensions/references/TROUBLESHOOTING.md +3 -3
  9. package/.agents/skills/pm-user/SKILL.md +2 -0
  10. package/.agents/skills/pm-user/references/BACKLOG_SHAPING.md +5 -0
  11. package/.agents/skills/pm-user/references/WORKFLOWS.md +20 -5
  12. package/.claude-plugin/marketplace.json +2 -2
  13. package/CHANGELOG.md +58 -3
  14. package/dist/cli/commander-usage.d.ts +2 -0
  15. package/dist/cli/commander-usage.js +15 -12
  16. package/dist/cli/help-json-payload.js +12 -7
  17. package/dist/cli/main.js +22 -11
  18. package/dist/cli/register-list-query.js +4 -2
  19. package/dist/cli/register-mutation.js +4 -2
  20. package/dist/cli/register-operations.js +17 -8
  21. package/dist/cli-bundle/bundle-manifest.json +153 -161
  22. package/dist/cli-bundle/chunks/chunk-AZDFJGH6.js +9 -0
  23. package/dist/cli-bundle/chunks/{chunk-V72UV7QS.js → chunk-BAX2BYUW.js} +73 -73
  24. package/dist/cli-bundle/chunks/{chunk-HI6BHPCU.js → chunk-CKOL5U7J.js} +51 -51
  25. package/dist/cli-bundle/chunks/{chunk-A24WAVE6.js → chunk-JT6XF6GE.js} +2 -2
  26. package/dist/cli-bundle/chunks/{chunk-I2AS367G.js → chunk-JX4DH4WC.js} +2 -2
  27. package/dist/cli-bundle/chunks/{chunk-YW4QK2G7.js → chunk-NPN5I67J.js} +2 -2
  28. package/dist/cli-bundle/chunks/chunk-OF76PSDQ.js +5 -0
  29. package/dist/cli-bundle/chunks/chunk-QUWFABDM.js +232 -0
  30. package/dist/cli-bundle/chunks/{chunk-RI4XNOYS.js → chunk-RWWBOWJ5.js} +2 -2
  31. package/dist/cli-bundle/chunks/chunk-WGAOYSYH.js +40 -0
  32. package/dist/cli-bundle/chunks/{chunk-X6CMGV6L.js → chunk-WI2AYPA7.js} +2 -2
  33. package/dist/cli-bundle/chunks/{register-list-query-Z5YYSJJG.js → register-list-query-5JL52D33.js} +3 -3
  34. package/dist/cli-bundle/chunks/{register-mutation-ELSP6I4I.js → register-mutation-ZPG7KMVP.js} +4 -4
  35. package/dist/cli-bundle/chunks/register-operations-I5W3KMGB.js +2 -0
  36. package/dist/cli-bundle/chunks/{register-setup-E2AE72QG.js → register-setup-H4SVAVX7.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-3646BZ4W.js → chunk-3BZEOXLT.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-AL5ON6K6.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-CHC2IDXA.js +6 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-CK6QFAPZ.js +20 -0
  41. package/dist/cli-bundle/focused-chunks/{chunk-NBMJD6MK.js → chunk-EZR3GHFE.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/chunk-H2WJWK7L.js +56 -0
  43. package/dist/cli-bundle/focused-chunks/{chunk-53IHPICX.js → chunk-LIJIA4TY.js} +2 -2
  44. package/dist/cli-bundle/focused-chunks/{chunk-25UJBH4M.js → chunk-M6PBRFSH.js} +2 -2
  45. package/dist/cli-bundle/focused-chunks/{chunk-AJR3OP2E.js → chunk-NTPY5GSO.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/chunk-P5O7MP5Y.js +2 -0
  47. package/dist/cli-bundle/focused-chunks/chunk-PDMW2E36.js +177 -0
  48. package/dist/cli-bundle/focused-chunks/{chunk-MHKUIPJW.js → chunk-PEFABF7E.js} +2 -2
  49. package/dist/cli-bundle/focused-chunks/{chunk-6PWPQKDN.js → chunk-QTVCLMSZ.js} +2 -2
  50. package/dist/cli-bundle/focused-chunks/{chunk-7N2TV7WZ.js → chunk-QTZRBUYA.js} +2 -2
  51. package/dist/cli-bundle/focused-chunks/chunk-SGQST3PQ.js +12 -0
  52. package/dist/cli-bundle/focused-chunks/chunk-UKHASMMP.js +10 -0
  53. package/dist/cli-bundle/focused-chunks/chunk-ZPEKQHOC.js +2 -0
  54. package/dist/cli-bundle/main.js +22 -15
  55. package/dist/cli-bundle/sdk-authoring.js +1 -1
  56. package/dist/cli-bundle/sdk-contracts.js +2 -2
  57. package/dist/cli-bundle/sdk-core.js +37 -37
  58. package/dist/cli-bundle/sdk-governance.js +1 -1
  59. package/dist/cli-bundle/sdk-graph.js +1 -1
  60. package/dist/cli-bundle/sdk-merge.js +42 -39
  61. package/dist/cli-bundle/sdk-query.js +1 -1
  62. package/dist/cli-bundle/sdk-runtime.js +1 -1
  63. package/dist/cli-bundle/sdk-testing.js +1 -1
  64. package/dist/cli-bundle/sdk.js +9 -9
  65. package/dist/core/extensions/extension-types.d.ts +2 -0
  66. package/dist/core/extensions/extension-types.js +2 -2
  67. package/dist/core/extensions/module-graph-snapshot.js +5 -4
  68. package/dist/core/fs/host-environment-errors.d.ts +27 -0
  69. package/dist/core/fs/host-environment-errors.js +142 -0
  70. package/dist/core/history/drift-scan.d.ts +5 -0
  71. package/dist/core/history/drift-scan.js +16 -6
  72. package/dist/core/history/event-classification.d.ts +1 -1
  73. package/dist/core/history/event-classification.js +3 -2
  74. package/dist/core/history/history.js +37 -5
  75. package/dist/core/history/identity.d.ts +15 -0
  76. package/dist/core/history/identity.js +31 -0
  77. package/dist/core/history/projection.js +50 -17
  78. package/dist/core/history/read.js +6 -3
  79. package/dist/core/history/replay.d.ts +1 -2
  80. package/dist/core/history/replay.js +4 -4
  81. package/dist/core/history/version-address.d.ts +13 -0
  82. package/dist/core/history/version-address.js +33 -0
  83. package/dist/core/item/actionability.d.ts +21 -1
  84. package/dist/core/item/actionability.js +45 -10
  85. package/dist/core/item/id.js +8 -4
  86. package/dist/core/item/item-type-definition.d.ts +2 -0
  87. package/dist/core/item/item-type-definition.js +9 -2
  88. package/dist/core/item/type-registry.d.ts +5 -1
  89. package/dist/core/item/type-registry.js +9 -4
  90. package/dist/core/output/mutation-projection.js +12 -5
  91. package/dist/core/shared/errors.d.ts +8 -0
  92. package/dist/core/shared/errors.js +2 -2
  93. package/dist/core/store/item-metadata-query-index.js +13 -10
  94. package/dist/core/store/item-metadata-read-work.d.ts +19 -0
  95. package/dist/core/store/item-metadata-read-work.js +44 -0
  96. package/dist/core/store/item-store.d.ts +14 -0
  97. package/dist/core/store/item-store.js +52 -10
  98. package/dist/core/store/settings-validator.js +3 -2
  99. package/dist/core/validate/fix-planning.d.ts +1 -1
  100. package/dist/core/validate/fix-planning.js +2 -2
  101. package/dist/core/validate/missing-link-owners.d.ts +1 -1
  102. package/dist/core/validate/missing-link-owners.js +5 -3
  103. package/dist/core/validate/stale-file-classification.d.ts +6 -4
  104. package/dist/core/validate/stale-file-classification.js +20 -5
  105. package/dist/mcp/server.js +12 -5
  106. package/dist/sdk/actionability.d.ts +1 -1
  107. package/dist/sdk/actionability.js +3 -3
  108. package/dist/sdk/cli-contracts/command-aliases.js +11 -2
  109. package/dist/sdk/cli-contracts/completeness.js +4 -3
  110. package/dist/sdk/cli-contracts/flag-contracts.d.ts +2 -0
  111. package/dist/sdk/cli-contracts/flag-contracts.js +31 -5
  112. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +21 -13
  113. package/dist/sdk/cli-contracts/grammar-contracts.js +11 -2
  114. package/dist/sdk/cli-contracts/runtime-contracts.js +5 -2
  115. package/dist/sdk/cli-contracts/schemas/assurance-definition.d.ts +254 -5
  116. package/dist/sdk/cli-contracts/schemas/assurance-definition.js +52 -2
  117. package/dist/sdk/cli-contracts/tool-parameter-tables.js +30 -6
  118. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  119. package/dist/sdk/cli-contracts/tool-schema.js +32 -7
  120. package/dist/sdk/context/usage-ledger.d.ts +41 -0
  121. package/dist/sdk/context/usage-ledger.js +186 -0
  122. package/dist/sdk/context-intent-contracts.js +3 -3
  123. package/dist/sdk/context-usage.d.ts +18 -9
  124. package/dist/sdk/context-usage.js +44 -121
  125. package/dist/sdk/define.d.ts +1 -1
  126. package/dist/sdk/define.js +2 -2
  127. package/dist/sdk/dependency-provenance.d.ts +7 -0
  128. package/dist/sdk/dependency-provenance.js +14 -4
  129. package/dist/sdk/duplicates.d.ts +2 -0
  130. package/dist/sdk/duplicates.js +3 -2
  131. package/dist/sdk/environment/host-environment-errors.d.ts +5 -27
  132. package/dist/sdk/environment/host-environment-errors.js +4 -137
  133. package/dist/sdk/extension/install-runtime.js +9 -6
  134. package/dist/sdk/extension/install-snapshot.js +7 -6
  135. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +104 -56
  136. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +50 -2
  137. package/dist/sdk/governance/assurance-action.d.ts +3 -2
  138. package/dist/sdk/governance/assurance-action.js +23 -11
  139. package/dist/sdk/governance/assurance-runtime.d.ts +2 -0
  140. package/dist/sdk/governance/assurance-runtime.js +6 -3
  141. package/dist/sdk/governance/defect-recurrence-coverage.d.ts +93 -0
  142. package/dist/sdk/governance/defect-recurrence-coverage.js +189 -0
  143. package/dist/sdk/governance/defect-recurrence-graph.d.ts +33 -0
  144. package/dist/sdk/governance/defect-recurrence-graph.js +121 -0
  145. package/dist/sdk/governance/defect-recurrence.d.ts +8 -0
  146. package/dist/sdk/governance/defect-recurrence.js +118 -17
  147. package/dist/sdk/governance/health.d.ts +17 -2
  148. package/dist/sdk/governance/health.js +31 -10
  149. package/dist/sdk/governance/linked-file-report.d.ts +41 -0
  150. package/dist/sdk/governance/linked-file-report.js +72 -0
  151. package/dist/sdk/governance/validate-history-drift.js +16 -5
  152. package/dist/sdk/governance/validate.d.ts +5 -2
  153. package/dist/sdk/governance/validate.js +21 -37
  154. package/dist/sdk/governance.d.ts +2 -0
  155. package/dist/sdk/governance.js +4 -2
  156. package/dist/sdk/guide-topics.js +116 -2
  157. package/dist/sdk/history/salvage.d.ts +35 -0
  158. package/dist/sdk/history/salvage.js +248 -0
  159. package/dist/sdk/history-compact.js +32 -37
  160. package/dist/sdk/history-read.d.ts +3 -2
  161. package/dist/sdk/history-read.js +6 -3
  162. package/dist/sdk/history-repair.d.ts +12 -0
  163. package/dist/sdk/history-repair.js +60 -13
  164. package/dist/sdk/lifecycle/claim.d.ts +4 -0
  165. package/dist/sdk/lifecycle/claim.js +9 -3
  166. package/dist/sdk/lifecycle/close.d.ts +8 -0
  167. package/dist/sdk/lifecycle/close.js +16 -8
  168. package/dist/sdk/lifecycle/create.js +9 -12
  169. package/dist/sdk/lifecycle/restore.d.ts +1 -0
  170. package/dist/sdk/lifecycle/restore.js +19 -7
  171. package/dist/sdk/lifecycle/update.js +12 -12
  172. package/dist/sdk/lifecycle-policy.d.ts +3 -0
  173. package/dist/sdk/lifecycle-policy.js +2 -2
  174. package/dist/sdk/merge/abandoned-receipts.d.ts +15 -0
  175. package/dist/sdk/merge/abandoned-receipts.js +47 -0
  176. package/dist/sdk/merge/driver.js +4 -2
  177. package/dist/sdk/merge/index.d.ts +1 -0
  178. package/dist/sdk/merge/index.js +2 -2
  179. package/dist/sdk/merge/receipt-operation.d.ts +15 -0
  180. package/dist/sdk/merge/receipt-operation.js +97 -0
  181. package/dist/sdk/merge/receipt-schema.d.ts +20 -0
  182. package/dist/sdk/merge/receipt-schema.js +398 -0
  183. package/dist/sdk/merge/receipts.d.ts +16 -7
  184. package/dist/sdk/merge/receipts.js +54 -340
  185. package/dist/sdk/merge/reconcile.d.ts +2 -0
  186. package/dist/sdk/merge/reconcile.js +11 -4
  187. package/dist/sdk/output-projection.d.ts +3 -1
  188. package/dist/sdk/output-projection.js +11 -3
  189. package/dist/sdk/public-surface.json +1 -0
  190. package/dist/sdk/query/context.d.ts +9 -0
  191. package/dist/sdk/query/context.js +33 -21
  192. package/dist/sdk/query/duplicate-candidates.d.ts +7 -0
  193. package/dist/sdk/query/duplicate-candidates.js +71 -0
  194. package/dist/sdk/query/get.d.ts +2 -2
  195. package/dist/sdk/query/get.js +9 -8
  196. package/dist/sdk/query/history.d.ts +11 -0
  197. package/dist/sdk/query/history.js +34 -10
  198. package/dist/sdk/query/next.d.ts +15 -1
  199. package/dist/sdk/query/next.js +43 -20
  200. package/dist/sdk/query.d.ts +3 -0
  201. package/dist/sdk/query.js +5 -2
  202. package/dist/sdk/read-output-session.js +5 -3
  203. package/dist/sdk/runtime-primitives.d.ts +1 -1
  204. package/dist/sdk/runtime-primitives.js +3 -3
  205. package/dist/sdk/runtime.js +4 -2
  206. package/dist/sdk/similarity.d.ts +24 -3
  207. package/dist/sdk/similarity.js +50 -55
  208. package/dist/types.d.ts +2 -0
  209. package/dist/types.js +2 -2
  210. package/docs/AGENT_GUIDE.md +58 -1
  211. package/docs/BOUNDED_CONTEXT_READS.md +185 -0
  212. package/docs/CLOSE_EVIDENCE_RECOVERY.md +35 -0
  213. package/docs/COMMANDS.md +30 -8
  214. package/docs/CONTEXT_RELEVANCE.md +16 -4
  215. package/docs/DEFECT_RECURRENCE.md +80 -6
  216. package/docs/EXTENSIONS.md +18 -19
  217. package/docs/HISTORY_RECOVERY.md +146 -0
  218. package/docs/MERGE_SAFETY.md +50 -0
  219. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +66 -0
  220. package/docs/PR_REVIEW_LOOP.md +18 -3
  221. package/docs/README.md +19 -1
  222. package/docs/RELEASING.md +31 -13
  223. package/docs/SDK_CONTEXT_TRUTH_CONTRACTS.md +24 -1
  224. package/docs/SDK_EXECUTION_CONTRACTS.md +105 -0
  225. package/docs/SDK_LIFECYCLE.md +27 -0
  226. package/docs/SDK_RUNTIME_BOUNDARIES.md +11 -0
  227. package/docs/TESTING.md +5 -0
  228. package/docs/agent-task-token-baseline.json +17 -17
  229. package/docs/agent-task-transcripts.json +2 -1
  230. package/docs/generated/FLAG_LEXICON_BUDGETS.md +11 -11
  231. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +11 -3
  232. package/marketplace.json +2 -2
  233. package/package.json +8 -6
  234. package/packages/pm-beads/package.json +1 -1
  235. package/packages/pm-calendar/package.json +1 -1
  236. package/packages/pm-command-kit/package.json +1 -1
  237. package/packages/pm-digital-twin/package.json +1 -1
  238. package/packages/pm-governance-audit/README.md +10 -1
  239. package/packages/pm-governance-audit/extensions/governance-audit/dedupe-audit.ts +50 -65
  240. package/packages/pm-governance-audit/extensions/governance-audit/index.ts +2 -2
  241. package/packages/pm-governance-audit/extensions/governance-audit/sdk.ts +1 -0
  242. package/packages/pm-governance-audit/package.json +1 -1
  243. package/packages/pm-guide-shell/package.json +1 -1
  244. package/packages/pm-kanban/package.json +1 -1
  245. package/packages/pm-lifecycle-hooks/package.json +1 -1
  246. package/packages/pm-linked-test-adapters/package.json +1 -1
  247. package/packages/pm-search-advanced/package.json +1 -1
  248. package/packages/pm-templates/package.json +1 -1
  249. package/packages/pm-todos/package.json +1 -1
  250. package/packages/pm-vcs/package.json +1 -1
  251. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  252. package/plugins/pm-claude/skills/pm-audit/SKILL.md +2 -2
  253. package/plugins/pm-claude/skills/pm-developer/SKILL.md +2 -2
  254. package/plugins/pm-claude/skills/pm-planner/SKILL.md +2 -2
  255. package/plugins/pm-claude/skills/pm-release/SKILL.md +2 -2
  256. package/plugins/pm-claude/skills/pm-workflow/SKILL.md +3 -3
  257. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  258. package/plugins/pm-codex/skills/pm-auditor/SKILL.md +2 -2
  259. package/plugins/pm-codex/skills/pm-native/SKILL.md +3 -4
  260. package/plugins/pm-codex/skills/pm-release/SKILL.md +2 -2
  261. package/scripts/finalize-build.mjs +13 -1
  262. package/dist/cli-bundle/chunks/chunk-6XSKPNCF.js +0 -230
  263. package/dist/cli-bundle/chunks/chunk-OGBVU67G.js +0 -5
  264. package/dist/cli-bundle/chunks/chunk-TXW6VL33.js +0 -38
  265. package/dist/cli-bundle/chunks/chunk-VGLHECAP.js +0 -9
  266. package/dist/cli-bundle/chunks/register-operations-OH7R3ZT4.js +0 -2
  267. package/dist/cli-bundle/focused-chunks/chunk-4YRAVI43.js +0 -12
  268. package/dist/cli-bundle/focused-chunks/chunk-5P4HVOWG.js +0 -176
  269. package/dist/cli-bundle/focused-chunks/chunk-636VVQ7F.js +0 -54
  270. package/dist/cli-bundle/focused-chunks/chunk-B3A6MJYE.js +0 -2
  271. package/dist/cli-bundle/focused-chunks/chunk-BFDDJ2T2.js +0 -2
  272. package/dist/cli-bundle/focused-chunks/chunk-BHUIRZWQ.js +0 -9
  273. package/dist/cli-bundle/focused-chunks/chunk-JADHHPY6.js +0 -6
  274. package/dist/cli-bundle/focused-chunks/chunk-NSKBLHVI.js +0 -20
  275. package/dist/cli-bundle/focused-chunks/chunk-P2GOAOQN.js +0 -2
  276. package/dist/cli-bundle/focused-chunks/chunk-YARWJM2B.js +0 -2
  277. package/sdk/public-surface.json +0 -33034
@@ -0,0 +1,47 @@
1
+
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]="cd45ee09-e4c3-5fc6-8253-1f1dc709a3a8")}catch(e){}}();
3
+ import { runHistoryRepair } from "../history-repair.js";
4
+ import { isOriginalGitStateRestored } from "./receipt-operation.js";
5
+ import { markMergeReceiptReconciled, summarizeMergeReceipt, } from "./receipts.js";
6
+ /** Preview or record restored-origin dispositions without treating them as applied merges. */
7
+ export async function settleAbandonedMergeReceipts(params) {
8
+ if (params.cwd === null)
9
+ return [];
10
+ const settled = [];
11
+ for (const receipt of params.receipts) {
12
+ if (receipt.operation === undefined ||
13
+ !(await isOriginalGitStateRestored(params.cwd, receipt.item_path, receipt.operation)))
14
+ continue;
15
+ const mergeAbandonmentProof = { gitWorkspaceRoot: params.cwd, receipt };
16
+ await runHistoryRepair(receipt.item_id, { dryRun: true, mergeAbandonmentProof }, params.global);
17
+ if (!params.dryRun) {
18
+ await runHistoryRepair(receipt.item_id, {
19
+ author: params.author ?? params.global.author,
20
+ mergeAbandonmentProof,
21
+ message: params.message ??
22
+ "Settle rebase receipt after exact original Git state restoration",
23
+ auditOperation: "merge_reconcile",
24
+ forceAuditEntry: true,
25
+ auditContext: {
26
+ merge: {
27
+ abandoned_receipts: [
28
+ {
29
+ ...summarizeMergeReceipt(receipt),
30
+ reason: "original_git_state_restored",
31
+ operation: receipt.operation,
32
+ },
33
+ ],
34
+ },
35
+ },
36
+ }, params.global);
37
+ await markMergeReceiptReconciled(params.cwd, receipt, {
38
+ requireExisting: true,
39
+ settlement: "original_git_state_restored",
40
+ });
41
+ }
42
+ settled.push(receipt.id);
43
+ }
44
+ return settled;
45
+ }
46
+ //# sourceMappingURL=abandoned-receipts.js.map
47
+ //# debugId=cd45ee09-e4c3-5fc6-8253-1f1dc709a3a8
@@ -9,7 +9,7 @@
9
9
  * merge never corrupts tracker storage the way raw conflict markers do.
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]="5dc5f70c-0b04-51bb-983b-49b15be11587")}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]="22b1f103-54de-5a63-a2d2-2f99181a73e2")}catch(e){}}();
13
13
  import { readFile, writeFile } from "node:fs/promises";
14
14
  import path from "node:path";
15
15
  import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
@@ -22,6 +22,7 @@ import { nowIso } from "../../core/shared/time.js";
22
22
  import { getSettingsPath, resolvePmRoot } from "../../core/store/paths.js";
23
23
  import { readSettings } from "../../core/store/settings.js";
24
24
  import { hashItemScalarDecisionValue, mergeHistoryStreams, mergeItemDocuments, mergeJsonDocuments, mergeRelationshipEventStreams, } from "./three-way.js";
25
+ import { captureMergeReceiptOperation } from "./receipt-operation.js";
25
26
  import { summarizeMergeReceipt, writeMergeReceipt, } from "./receipts.js";
26
27
  /** Supported values accepted by the merge driver artifact contract across CLI, SDK, and MCP surfaces. */
27
28
  export const MERGE_DRIVER_ARTIFACT_VALUES = [
@@ -166,6 +167,7 @@ export async function runMergeDriver(options, global) {
166
167
  hashItemScalarDecisionValue(field === "body" ? mergedDocument.body : mergedMetadata[field]),
167
168
  ])),
168
169
  decisions: itemMerge.conflict_decisions,
170
+ operation: await captureMergeReceiptOperation(process.cwd(), options.itemPath),
169
171
  };
170
172
  }
171
173
  }
@@ -208,4 +210,4 @@ export async function runMergeDriver(options, global) {
208
210
  };
209
211
  }
210
212
  //# sourceMappingURL=driver.js.map
211
- //# debugId=5dc5f70c-0b04-51bb-983b-49b15be11587
213
+ //# debugId=22b1f103-54de-5a63-a2d2-2f99181a73e2
@@ -11,3 +11,4 @@ export { inspectMergeReceiptEvidence, listMergeReceipts, markMergeReceiptReconci
11
11
  export { MERGE_DRIVER_ARTIFACT_VALUES, runMergeDriver, type MergeDriverArtifact, type MergeDriverOptions, type MergeDriverResult, } from "./driver.js";
12
12
  export { auditMergeAttributeFence, auditMergeDriverConfiguration, buildMergeAttributePatterns, findGitWorkspaceRoot, PM_GITATTRIBUTES_END, PM_GITATTRIBUTES_START, PM_GITATTRIBUTES_V2_END, PM_GITATTRIBUTES_V2_START, refreshMergeAttributeFenceIfInstalled, resolveMergeInstallContext, runMergeInstall, type MergeFenceAuditResult, type MergeDriverConfigurationAuditResult, type MergeFenceRefreshOutcome, type MergeInstallOptions, type MergeInstallContextResolution, type MergeInstallResult, } from "./install.js";
13
13
  export { runMergeReconcile, type MergeReconcileOptions, type MergeReconcileResult, } from "./reconcile.js";
14
+ export type { MergeReceiptOperation } from "./receipt-operation.js";
@@ -7,11 +7,11 @@
7
7
  * this module so packages and hosts can reuse the same merge behavior.
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]="925be497-bdd3-527b-9a88-cf9c9f682ab2")}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]="5079995b-ef00-5fe3-b87d-0882ef976d10")}catch(e){}}();
11
11
  export { ITEM_SCALAR_MISSING_VALUE, ITEM_LATEST_TIMESTAMP_FIELDS, ITEM_UNION_COLLECTION_FIELDS, encodeItemScalarDecisionValue, hashItemScalarDecisionValue, isItemScalarMissingValue, mergeHistoryStreams, mergeItemDocuments, mergeJsonDocuments, mergeRelationshipEventStreams, } from "./three-way.js";
12
12
  export { inspectMergeReceiptEvidence, listMergeReceipts, markMergeReceiptReconciled, runMergeReceiptEvidenceReport, runMergeReceiptReport, summarizeMergeReceipt, } from "./receipts.js";
13
13
  export { MERGE_DRIVER_ARTIFACT_VALUES, runMergeDriver, } from "./driver.js";
14
14
  export { auditMergeAttributeFence, auditMergeDriverConfiguration, buildMergeAttributePatterns, findGitWorkspaceRoot, PM_GITATTRIBUTES_END, PM_GITATTRIBUTES_START, PM_GITATTRIBUTES_V2_END, PM_GITATTRIBUTES_V2_START, refreshMergeAttributeFenceIfInstalled, resolveMergeInstallContext, runMergeInstall, } from "./install.js";
15
15
  export { runMergeReconcile, } from "./reconcile.js";
16
16
  //# sourceMappingURL=index.js.map
17
- //# debugId=925be497-bdd3-527b-9a88-cf9c9f682ab2
17
+ //# debugId=5079995b-ef00-5fe3-b87d-0882ef976d10
@@ -0,0 +1,15 @@
1
+ /** Immutable pre-rebase coordinates, without branch names or item contents. */
2
+ export interface MergeReceiptOperation {
3
+ /** Git operation whose original state can be checked after it ends. */
4
+ kind: "rebase";
5
+ /** Commit from which the rebase began. */
6
+ original_head: string;
7
+ /** Original item blob at that commit. */
8
+ original_blob: string;
9
+ }
10
+ /** Capture a rebase's original commit and item blob, or omit unprovable coordinates. */
11
+ export declare function captureMergeReceiptOperation(cwd: string, itemPath: string): Promise<MergeReceiptOperation | undefined>;
12
+ /** Validate only the bounded, privacy-safe operation schema accepted by receipt readers. */
13
+ export declare function isMergeReceiptOperation(value: unknown): value is MergeReceiptOperation;
14
+ /** Prove HEAD, index, and working item all match the captured origin after Git leaves the operation. */
15
+ export declare function isOriginalGitStateRestored(cwd: string, itemPath: string, operation: MergeReceiptOperation, expectedItemRaw?: string): Promise<boolean>;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @module sdk/merge/receipt-operation
3
+ * Records Git coordinates that let reconciliation prove a rebase restored its original state.
4
+ */
5
+
6
+ !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]="7bef66b4-7131-5998-84bd-07983a25d762")}catch(e){}}();
7
+ import { execFile } from "node:child_process";
8
+ import { createHash } from "node:crypto";
9
+ import { readdir } from "node:fs/promises";
10
+ import path from "node:path";
11
+ import { promisify } from "node:util";
12
+ import { readBoundedRegularFile } from "./receipt-file-boundary.js";
13
+ const execFileAsync = promisify(execFile);
14
+ const GIT_OBJECT_ID = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u;
15
+ /** Capture a rebase's original commit and item blob, or omit unprovable coordinates. */
16
+ export async function captureMergeReceiptOperation(cwd, itemPath) {
17
+ const trimmedItemPath = itemPath.trim();
18
+ const normalizedItemPath = /^(['"])(.*)\1$/su.exec(trimmedItemPath)?.[2] ?? trimmedItemPath;
19
+ for (const backend of ["rebase-merge", "rebase-apply"]) {
20
+ try {
21
+ const { stdout: marker } = await execFileAsync("git", [
22
+ "rev-parse",
23
+ "--path-format=absolute",
24
+ "--git-path",
25
+ `${backend}/orig-head`,
26
+ ], { cwd, timeout: 10_000 });
27
+ const originalHead = (await readBoundedRegularFile(marker.trim(), 128))?.trim();
28
+ if (originalHead === undefined || !GIT_OBJECT_ID.test(originalHead))
29
+ continue;
30
+ const { stdout } = await execFileAsync("git", ["rev-parse", "--verify", `${originalHead}:${normalizedItemPath}`], { cwd, timeout: 10_000 });
31
+ const originalBlob = stdout.trim();
32
+ return {
33
+ kind: "rebase",
34
+ original_head: originalHead,
35
+ original_blob: originalBlob,
36
+ };
37
+ }
38
+ catch {
39
+ // No evidence is safer than inferring an abandoned operation from item drift.
40
+ }
41
+ }
42
+ return undefined;
43
+ }
44
+ /** Validate only the bounded, privacy-safe operation schema accepted by receipt readers. */
45
+ export function isMergeReceiptOperation(value) {
46
+ if (typeof value !== "object" || value === null || Array.isArray(value))
47
+ return false;
48
+ const record = value;
49
+ return (Object.keys(record).length === 3 &&
50
+ record.kind === "rebase" &&
51
+ typeof record.original_head === "string" &&
52
+ GIT_OBJECT_ID.test(record.original_head) &&
53
+ typeof record.original_blob === "string" &&
54
+ GIT_OBJECT_ID.test(record.original_blob));
55
+ }
56
+ /** Prove HEAD, index, and working item all match the captured origin after Git leaves the operation. */
57
+ export async function isOriginalGitStateRestored(cwd, itemPath, operation, expectedItemRaw) {
58
+ if (expectedItemRaw !== undefined) {
59
+ const snapshotBlob = createHash(operation.original_blob.length === 40 ? "sha1" : "sha256")
60
+ .update(`blob ${Buffer.byteLength(expectedItemRaw)}\0`)
61
+ .update(expectedItemRaw)
62
+ .digest("hex");
63
+ if (snapshotBlob !== operation.original_blob)
64
+ return false;
65
+ }
66
+ try {
67
+ const { stdout: gitDirectory } = await execFileAsync("git", ["rev-parse", "--absolute-git-dir"], { cwd, timeout: 10_000 });
68
+ const activeMarkers = new Set([
69
+ "rebase-merge",
70
+ "rebase-apply",
71
+ "MERGE_HEAD",
72
+ "CHERRY_PICK_HEAD",
73
+ "REVERT_HEAD",
74
+ ]);
75
+ if ((await readdir(gitDirectory.trim())).some((entry) => activeMarkers.has(entry)))
76
+ return false;
77
+ const { stdout: head } = await execFileAsync("git", ["rev-parse", "HEAD"], {
78
+ cwd,
79
+ timeout: 10_000,
80
+ });
81
+ if (head.trim() !== operation.original_head)
82
+ return false;
83
+ const { stdout: originalBlob } = await execFileAsync("git", ["rev-parse", "--verify", `HEAD:${itemPath}`], { cwd, timeout: 10_000 });
84
+ if (originalBlob.trim() !== operation.original_blob)
85
+ return false;
86
+ const { stdout: blob } = await execFileAsync("git", ["hash-object", "--", path.resolve(cwd, itemPath)], { cwd, timeout: 10_000 });
87
+ if (blob.trim() !== operation.original_blob)
88
+ return false;
89
+ await execFileAsync("git", ["diff", "--cached", "--quiet", "HEAD", "--", itemPath], { cwd, timeout: 10_000 });
90
+ return true;
91
+ }
92
+ catch {
93
+ return false;
94
+ }
95
+ }
96
+ //# sourceMappingURL=receipt-operation.js.map
97
+ //# debugId=7bef66b4-7131-5998-84bd-07983a25d762
@@ -0,0 +1,20 @@
1
+ import type { MergeDecisionReceipt, MergeReceiptInvalidEvidence } from "./receipts.js";
2
+ /** Return whether a receipt id is safe to expose in cleartext diagnostics. */
3
+ export declare function isSafeReceiptId(value: string): boolean;
4
+ /** Versioned allowlist controlling which scalar evidence may enter tracked receipt sidecars. */
5
+ export declare const DURABLE_VALUE_POLICY: "bounded_non_sensitive_scalars_v1";
6
+ /** Return the narrowest validation class for untrusted receipt evidence. */
7
+ export declare function receiptValidationError(value: unknown, evidenceSource: "clone_local" | "durable"): MergeReceiptInvalidEvidence["validation_error"] | null;
8
+ /** Restore omitted non-selected slots from identified v1 clone-local writer contracts; never infer a missing retained or discarded value. */
9
+ export declare function normalizeLegacyReceipt(value: unknown): unknown;
10
+ /** Identify the rejected collection without exposing its values. */
11
+ export declare function receiptCollectionValidationPath(value: unknown): string | undefined;
12
+ /** Validate a complete receipt against its clone-local or durable evidence policy. */
13
+ export declare function isMergeDecisionReceipt(value: unknown, evidenceSource: "clone_local" | "durable"): value is MergeDecisionReceipt;
14
+ /** Extract a structurally valid precomputed SHA-256 decision hash. */
15
+ export declare function isPrehashedValue(value: unknown): string | undefined;
16
+ /** Convert a raw scalar into hashed evidence with an allowlisted preview. */
17
+ export declare function durableValueEvidence(field: string, value: unknown): {
18
+ pm_value_hash: string;
19
+ pm_value?: unknown;
20
+ };
@@ -0,0 +1,398 @@
1
+ /**
2
+ * @module sdk/merge/receipt-schema
3
+ * Validates historical and current receipt encodings and enforces durable-value privacy policy.
4
+ */
5
+
6
+ !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]="1f6aa507-79da-5d11-a580-72141b725947")}catch(e){}}();
7
+ import path from "node:path";
8
+ import { stableStringify } from "../../core/shared/serialization.js";
9
+ import { isRfc3339DateTime } from "../../core/shared/time.js";
10
+ import { encodeItemScalarDecisionValue, hashItemScalarDecisionValue, isItemScalarMissingValue, } from "./three-way.js";
11
+ import { isMergeReceiptOperation } from "./receipt-operation.js";
12
+ const RECEIPT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/u;
13
+ const RECEIPT_ITEM_ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u;
14
+ const RECEIPT_FIELD_MAX = 2_048;
15
+ const RECEIPT_VALUE_MAX_DEPTH = 64;
16
+ const RECEIPT_VALUE_MAX_NODES = 100_000;
17
+ const RECEIPT_KEYS = new Set([
18
+ "version",
19
+ "id",
20
+ "item_path",
21
+ "item_id",
22
+ "requested_preference",
23
+ "requested_preference_applied",
24
+ "preferred",
25
+ "conflict_resolution",
26
+ "fields_from_theirs",
27
+ "union_fields",
28
+ "merged_field_hashes",
29
+ "decisions",
30
+ "state",
31
+ "created_at",
32
+ "reconciled_at",
33
+ "value_availability",
34
+ "value_policy",
35
+ "evidence_source",
36
+ "operation",
37
+ "settlement",
38
+ ]);
39
+ /** Return whether a receipt id is safe to expose in cleartext diagnostics. */
40
+ export function isSafeReceiptId(value) {
41
+ return RECEIPT_ID_PATTERN.test(value);
42
+ }
43
+ const RECEIPT_DECISION_KEYS = new Set([
44
+ "field",
45
+ "base",
46
+ "ours",
47
+ "theirs",
48
+ "retained",
49
+ "discarded",
50
+ "retained_side",
51
+ "resolution_basis",
52
+ ]);
53
+ /** Versioned allowlist controlling which scalar evidence may enter tracked receipt sidecars. */
54
+ export const DURABLE_VALUE_POLICY = "bounded_non_sensitive_scalars_v1";
55
+ const REVIEWABLE_BUILTIN_STATUSES = new Set([
56
+ "open",
57
+ "in_progress",
58
+ "blocked",
59
+ "closed",
60
+ "canceled",
61
+ ]);
62
+ const REVIEWABLE_ORDINAL_FIELDS = new Set(["risk", "confidence", "severity"]);
63
+ const REVIEWABLE_ORDINAL_VALUES = new Set([
64
+ "low",
65
+ "medium",
66
+ "high",
67
+ "critical",
68
+ ]);
69
+ const REVIEWABLE_DURABLE_VALUES_BY_FIELD = new Map([
70
+ ["priority", new Set([0, 1, 2, 3, 4].map(stableStringify))],
71
+ ["status", new Set([...REVIEWABLE_BUILTIN_STATUSES].map(stableStringify))],
72
+ ...[...REVIEWABLE_ORDINAL_FIELDS].map((field) => [
73
+ field,
74
+ new Set([...REVIEWABLE_ORDINAL_VALUES].map(stableStringify)),
75
+ ]),
76
+ ]);
77
+ /** Narrow untrusted JSON to a non-array object before schema inspection. */
78
+ function isRecord(value) {
79
+ return typeof value === "object" && value !== null && !Array.isArray(value);
80
+ }
81
+ /** Refuse receipt extensions outside the versioned schema key set. */
82
+ function hasOnlyKeys(value, allowed) {
83
+ return Object.keys(value).every((key) => allowed.has(key));
84
+ }
85
+ /** Validate bounded dense string collections used in receipt coordinates. */
86
+ function isStringArray(value) {
87
+ return (Array.isArray(value) &&
88
+ value.length <= RECEIPT_FIELD_MAX &&
89
+ value.every((entry) => typeof entry === "string" && entry.length > 0 && entry.length <= 256));
90
+ }
91
+ /** Bound JSON depth and node count before retaining untrusted decision values. */
92
+ function hasBoundedJsonStructure(value) {
93
+ const pending = [
94
+ { value, depth: 0 },
95
+ ];
96
+ let nodes = 0;
97
+ while (pending.length > 0) {
98
+ const current = pending.pop();
99
+ nodes += 1;
100
+ if (nodes > RECEIPT_VALUE_MAX_NODES ||
101
+ current.depth > RECEIPT_VALUE_MAX_DEPTH) {
102
+ return false;
103
+ }
104
+ if (Array.isArray(current.value)) {
105
+ for (const entry of current.value) {
106
+ pending.push({ value: entry, depth: current.depth + 1 });
107
+ }
108
+ }
109
+ else if (isRecord(current.value)) {
110
+ for (const entry of Object.values(current.value)) {
111
+ pending.push({ value: entry, depth: current.depth + 1 });
112
+ }
113
+ }
114
+ }
115
+ return true;
116
+ }
117
+ /** Validate one clone-local scalar decision and its optional resolution evidence. */
118
+ function isMergeDecision(value) {
119
+ if (!isRecord(value))
120
+ return false;
121
+ const retainedSides = [undefined, "ours", "theirs"];
122
+ const resolutionBases = [
123
+ undefined,
124
+ "requested_preference",
125
+ "document_updated_at",
126
+ "stable_value_tiebreak",
127
+ ];
128
+ return [
129
+ hasOnlyKeys(value, RECEIPT_DECISION_KEYS),
130
+ typeof value.field === "string",
131
+ typeof value.field === "string" && value.field.length > 0,
132
+ typeof value.field === "string" && value.field.length <= 256,
133
+ Object.hasOwn(value, "base"),
134
+ Object.hasOwn(value, "ours"),
135
+ Object.hasOwn(value, "theirs"),
136
+ Object.hasOwn(value, "retained"),
137
+ Object.hasOwn(value, "discarded"),
138
+ hasBoundedJsonStructure(value.base),
139
+ hasBoundedJsonStructure(value.ours),
140
+ hasBoundedJsonStructure(value.theirs),
141
+ hasBoundedJsonStructure(value.retained),
142
+ hasBoundedJsonStructure(value.discarded),
143
+ retainedSides.includes(value.retained_side),
144
+ resolutionBases.includes(value.resolution_basis),
145
+ ].every(Boolean);
146
+ }
147
+ /** Validate the complete field-to-SHA-256 proof map used during repair. */
148
+ function isMergedFieldHashes(value) {
149
+ if (!isRecord(value))
150
+ return false;
151
+ const entries = Object.entries(value);
152
+ return (entries.length <= RECEIPT_FIELD_MAX &&
153
+ entries.every(([field, hash]) => field.length > 0 &&
154
+ field.length <= 256 &&
155
+ typeof hash === "string" &&
156
+ /^[a-f0-9]{64}$/u.test(hash)));
157
+ }
158
+ /** Require a normalized tracker item path whose basename matches the receipt item. */
159
+ function isSafeReceiptItemPath(value, itemId) {
160
+ if (typeof value !== "string" ||
161
+ value.length === 0 ||
162
+ value.length > 4_096 ||
163
+ value.includes("\0") ||
164
+ value.includes("\\") ||
165
+ path.posix.isAbsolute(value)) {
166
+ return false;
167
+ }
168
+ const normalized = path.posix.normalize(value);
169
+ return (normalized !== ".." &&
170
+ !normalized.startsWith("../") &&
171
+ path.posix.basename(normalized, path.posix.extname(normalized)) === itemId);
172
+ }
173
+ /** Validate receipt arrays and optional merged-field proof coverage. */
174
+ function hasValidReceiptCollections(value) {
175
+ return !isStringArray(value.fields_from_theirs) ||
176
+ !isStringArray(value.union_fields) ||
177
+ !Array.isArray(value.decisions) ||
178
+ value.decisions.length > RECEIPT_FIELD_MAX ||
179
+ !value.decisions.every(isMergeDecision)
180
+ ? false
181
+ : value.merged_field_hashes === undefined ||
182
+ isMergedFieldHashes(value.merged_field_hashes);
183
+ }
184
+ /** Validate every closed-domain lifecycle and merge-policy field. */
185
+ function hasValidReceiptEnums(value) {
186
+ const preferences = [undefined, "ours", "theirs"];
187
+ const resolutions = [
188
+ undefined,
189
+ "preferred_side",
190
+ "stable_value_order",
191
+ "latest_document_update",
192
+ ];
193
+ const availabilities = [
194
+ undefined,
195
+ "clone_local",
196
+ "hash_only",
197
+ "bounded_inline",
198
+ "mixed",
199
+ ];
200
+ const sources = [undefined, "clone_local", "durable"];
201
+ return (preferences.includes(value.requested_preference) &&
202
+ preferences.includes(value.preferred) &&
203
+ (value.requested_preference_applied === undefined ||
204
+ typeof value.requested_preference_applied === "boolean") &&
205
+ resolutions.includes(value.conflict_resolution) &&
206
+ (value.state === "pending" || value.state === "reconciled") &&
207
+ availabilities.includes(value.value_availability) &&
208
+ (value.value_policy === undefined ||
209
+ value.value_policy === DURABLE_VALUE_POLICY) &&
210
+ sources.includes(value.evidence_source));
211
+ }
212
+ /** Validate creation and optional settlement timestamps as RFC 3339 values. */
213
+ function hasValidReceiptTimestamps(value) {
214
+ return (typeof value.created_at === "string" &&
215
+ isRfc3339DateTime(value.created_at) &&
216
+ (value.reconciled_at === undefined ||
217
+ (typeof value.reconciled_at === "string" &&
218
+ isRfc3339DateTime(value.reconciled_at))));
219
+ }
220
+ /** Validate one hash-only or policy-bounded durable decision value. */
221
+ function hasDurableDecisionValue(field, value) {
222
+ if (!isRecord(value) || isPrehashedValue(value) === undefined)
223
+ return false;
224
+ const keys = Object.keys(value);
225
+ if (!keys.every((key) => key === "pm_value_hash" || key === "pm_value")) {
226
+ return false;
227
+ }
228
+ if (!Object.hasOwn(value, "pm_value"))
229
+ return keys.length === 1;
230
+ const preview = value.pm_value;
231
+ const allowed = preview === null ||
232
+ REVIEWABLE_DURABLE_VALUES_BY_FIELD.get(field)?.has(stableStringify(preview)) === true;
233
+ return (allowed && hashItemScalarDecisionValue(preview) === value.pm_value_hash);
234
+ }
235
+ /** Validate durable decision redaction and the declared availability summary. */
236
+ function hasValidDurableDecisions(receipt) {
237
+ if (!receipt.decisions.every((decision) => decision.base === null &&
238
+ decision.ours === null &&
239
+ decision.theirs === null &&
240
+ hasDurableDecisionValue(decision.field, decision.retained) &&
241
+ hasDurableDecisionValue(decision.field, decision.discarded))) {
242
+ return false;
243
+ }
244
+ const inlineValueCount = receipt.decisions.reduce((count, decision) => count +
245
+ (isRecord(decision.retained) &&
246
+ Object.hasOwn(decision.retained, "pm_value")
247
+ ? 1
248
+ : 0) +
249
+ (isRecord(decision.discarded) &&
250
+ Object.hasOwn(decision.discarded, "pm_value")
251
+ ? 1
252
+ : 0), 0);
253
+ const expectedAvailability = inlineValueCount === 0
254
+ ? "hash_only"
255
+ : inlineValueCount === receipt.decisions.length * 2
256
+ ? "bounded_inline"
257
+ : "mixed";
258
+ return (receipt.value_availability === expectedAvailability &&
259
+ (inlineValueCount === 0 || receipt.value_policy === DURABLE_VALUE_POLICY));
260
+ }
261
+ /** Require restored-origin settlements to retain the immutable operation they disposition. */
262
+ function hasValidReceiptOperation(value) {
263
+ if (value.operation !== undefined &&
264
+ !isMergeReceiptOperation(value.operation))
265
+ return false;
266
+ return (value.settlement === undefined ||
267
+ (value.settlement === "original_git_state_restored" &&
268
+ value.state === "reconciled" &&
269
+ value.operation !== undefined));
270
+ }
271
+ /** Validate schema identity before inspecting values or lifecycle policy. */
272
+ function receiptIdentityValidationError(value) {
273
+ for (const key of [
274
+ "version",
275
+ "id",
276
+ "item_path",
277
+ "item_id",
278
+ "fields_from_theirs",
279
+ "union_fields",
280
+ "decisions",
281
+ "state",
282
+ "created_at",
283
+ ]) {
284
+ if (!Object.hasOwn(value, key))
285
+ return "required_fields";
286
+ }
287
+ if (value.version !== 1 || !hasOnlyKeys(value, RECEIPT_KEYS)) {
288
+ return "required_fields";
289
+ }
290
+ if (typeof value.id !== "string" || !isSafeReceiptId(value.id)) {
291
+ return "receipt_id";
292
+ }
293
+ if (typeof value.item_id !== "string" ||
294
+ !RECEIPT_ITEM_ID_PATTERN.test(value.item_id)) {
295
+ return "item_id";
296
+ }
297
+ if (!isSafeReceiptItemPath(value.item_path, value.item_id)) {
298
+ return "item_path";
299
+ }
300
+ return null;
301
+ }
302
+ /** Return the narrowest validation class for untrusted receipt evidence. */
303
+ export function receiptValidationError(value, evidenceSource) {
304
+ if (!isRecord(value))
305
+ return "required_fields";
306
+ const identityError = receiptIdentityValidationError(value);
307
+ if (identityError !== null)
308
+ return identityError;
309
+ if (!hasValidReceiptOperation(value))
310
+ return "operation";
311
+ if (!hasValidReceiptCollections(value))
312
+ return "collections";
313
+ if (!hasValidReceiptEnums(value))
314
+ return "enums";
315
+ if (!hasValidReceiptTimestamps(value))
316
+ return "timestamps";
317
+ if (evidenceSource === "durable" &&
318
+ !hasValidDurableDecisions(value)) {
319
+ return "durable_decisions";
320
+ }
321
+ return null;
322
+ }
323
+ /** Restore omitted non-selected slots from identified v1 clone-local writer contracts; never infer a missing retained or discarded value. */
324
+ export function normalizeLegacyReceipt(value) {
325
+ if (!isRecord(value) ||
326
+ value.version !== 1 ||
327
+ value.value_policy !== undefined ||
328
+ !Array.isArray(value.decisions))
329
+ return value;
330
+ const preferredEra = (value.preferred === "ours" || value.preferred === "theirs") &&
331
+ value.requested_preference === undefined;
332
+ const requestedPreferenceEra = (value.requested_preference === "ours" ||
333
+ value.requested_preference === "theirs") &&
334
+ value.value_availability === "clone_local";
335
+ if (!preferredEra && !requestedPreferenceEra)
336
+ return value;
337
+ return {
338
+ ...value,
339
+ decisions: value.decisions.map((decision) => {
340
+ if (!isRecord(decision) ||
341
+ !Object.hasOwn(decision, "retained") ||
342
+ !Object.hasOwn(decision, "discarded"))
343
+ return decision;
344
+ return {
345
+ base: encodeItemScalarDecisionValue(undefined),
346
+ ours: encodeItemScalarDecisionValue(undefined),
347
+ theirs: encodeItemScalarDecisionValue(undefined),
348
+ ...decision,
349
+ };
350
+ }),
351
+ };
352
+ }
353
+ /** Identify the rejected collection without exposing its values. */
354
+ export function receiptCollectionValidationPath(value) {
355
+ if (!isRecord(value))
356
+ return undefined;
357
+ if (!isStringArray(value.fields_from_theirs))
358
+ return "fields_from_theirs";
359
+ if (!isStringArray(value.union_fields))
360
+ return "union_fields";
361
+ if (!Array.isArray(value.decisions) ||
362
+ value.decisions.length > RECEIPT_FIELD_MAX)
363
+ return "decisions";
364
+ const index = value.decisions.findIndex((decision) => !isMergeDecision(decision));
365
+ if (index >= 0)
366
+ return `decisions[${index}]`;
367
+ return "merged_field_hashes";
368
+ }
369
+ /** Validate a complete receipt against its clone-local or durable evidence policy. */
370
+ export function isMergeDecisionReceipt(value, evidenceSource) {
371
+ return receiptValidationError(value, evidenceSource) === null;
372
+ }
373
+ /** Extract a structurally valid precomputed SHA-256 decision hash. */
374
+ export function isPrehashedValue(value) {
375
+ if (typeof value !== "object" ||
376
+ value === null ||
377
+ Array.isArray(value) ||
378
+ !("pm_value_hash" in value)) {
379
+ return undefined;
380
+ }
381
+ const hash = value.pm_value_hash;
382
+ return typeof hash === "string" && /^[a-f0-9]{64}$/u.test(hash)
383
+ ? hash
384
+ : undefined;
385
+ }
386
+ /** Convert a raw scalar into hashed evidence with an allowlisted preview. */
387
+ export function durableValueEvidence(field, value) {
388
+ const missing = isItemScalarMissingValue(value);
389
+ const pmValueHash = hashItemScalarDecisionValue(missing ? undefined : value);
390
+ const reviewable = !missing &&
391
+ (value === null ||
392
+ REVIEWABLE_DURABLE_VALUES_BY_FIELD.get(field)?.has(stableStringify(value)) === true);
393
+ return reviewable
394
+ ? { pm_value_hash: pmValueHash, pm_value: value }
395
+ : { pm_value_hash: pmValueHash };
396
+ }
397
+ //# sourceMappingURL=receipt-schema.js.map
398
+ //# debugId=1f6aa507-79da-5d11-a580-72141b725947