@unbrained/pm-cli 2026.10.25 → 2026.10.27

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 (211) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +44 -1
  3. package/dist/cli/error-guidance.d.ts +5 -0
  4. package/dist/cli/error-guidance.js +2 -2
  5. package/dist/cli/register-list-query.js +5 -5
  6. package/dist/cli/register-mutation.js +2 -2
  7. package/dist/cli-bundle/bundle-manifest.json +1 -1
  8. package/dist/cli-bundle/chunks/{chunk-O3ZTGRIH.js → chunk-25MTQAHX.js} +2 -2
  9. package/dist/cli-bundle/chunks/{chunk-YCCGZHOR.js → chunk-2XUDAQK5.js} +2 -2
  10. package/dist/cli-bundle/chunks/{chunk-VAEB6IUQ.js → chunk-32QK43XG.js} +2 -2
  11. package/dist/cli-bundle/chunks/{chunk-RD6UW3MW.js → chunk-34OQP43K.js} +3 -3
  12. package/dist/cli-bundle/chunks/{chunk-TLMXSYJA.js → chunk-57YPOOED.js} +2 -2
  13. package/dist/cli-bundle/chunks/chunk-5PTMQBKJ.js +30 -0
  14. package/dist/cli-bundle/chunks/{chunk-FNZT4BTE.js → chunk-EKV5GP6P.js} +86 -86
  15. package/dist/cli-bundle/chunks/{chunk-LNKPH2DD.js → chunk-ER7RLJW5.js} +2 -2
  16. package/dist/cli-bundle/chunks/chunk-F26MRQZY.js +6 -0
  17. package/dist/cli-bundle/chunks/chunk-H434DKYU.js +2 -0
  18. package/dist/cli-bundle/chunks/{chunk-MVJGGCGQ.js → chunk-HIX2OMEZ.js} +2 -2
  19. package/dist/cli-bundle/chunks/{chunk-4GD6QOP6.js → chunk-NNIYQHYT.js} +3 -3
  20. package/dist/cli-bundle/chunks/chunk-O3LPT34G.js +20 -0
  21. package/dist/cli-bundle/chunks/{chunk-3CYXE6BV.js → chunk-ONC4BBQP.js} +2 -2
  22. package/dist/cli-bundle/chunks/{chunk-UI3FD3DW.js → chunk-OQOEBWHU.js} +2 -2
  23. package/dist/cli-bundle/chunks/chunk-Q55KZDNF.js +2 -0
  24. package/dist/cli-bundle/chunks/chunk-QQYLNHZK.js +128 -0
  25. package/dist/cli-bundle/chunks/chunk-SPPHDVZE.js +2 -0
  26. package/dist/cli-bundle/chunks/chunk-UHKN6TJM.js +114 -0
  27. package/dist/cli-bundle/chunks/{chunk-7T3H24HN.js → chunk-X5TMR7KC.js} +2 -2
  28. package/dist/cli-bundle/chunks/{chunk-EYCVAAIP.js → chunk-XERBUA6A.js} +2 -2
  29. package/dist/cli-bundle/chunks/indexed-receipt-blobs-63WYG2PY.js +4 -0
  30. package/dist/cli-bundle/chunks/{mutation-admission-747C6IUR.js → mutation-admission-L3A6FSZ7.js} +2 -2
  31. package/dist/cli-bundle/chunks/{register-list-query-COST6KRV.js → register-list-query-YSQ7UTFI.js} +9 -9
  32. package/dist/cli-bundle/chunks/register-mutation-6HRXYASW.js +20 -0
  33. package/dist/cli-bundle/chunks/{register-operations-S4LKLUBJ.js → register-operations-4O3YOYXF.js} +2 -2
  34. package/dist/cli-bundle/chunks/{register-setup-3ITZXPOL.js → register-setup-3WAXNDMY.js} +2 -2
  35. package/dist/cli-bundle/chunks/{settlement-hook-5IVCF6ZY.js → settlement-hook-CRW6PVQK.js} +2 -2
  36. package/dist/cli-bundle/chunks/{stage-3UUIYROS.js → stage-I46WD4EN.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-U6HXIF6G.js → chunk-4TXZBGOR.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/{chunk-22727JYL.js → chunk-B266DDBJ.js} +2 -2
  39. package/dist/cli-bundle/focused-chunks/{chunk-Y4JHHY24.js → chunk-BH7RE2ZY.js} +2 -2
  40. package/dist/cli-bundle/focused-chunks/{chunk-ZYCOMRFT.js → chunk-E5C24MCV.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/chunk-EGN2HFRU.js +2 -0
  42. package/dist/cli-bundle/focused-chunks/chunk-HWD63G54.js +2 -0
  43. package/dist/cli-bundle/focused-chunks/chunk-J67PFM3O.js +91 -0
  44. package/dist/cli-bundle/focused-chunks/{chunk-46KDJH3K.js → chunk-KTXU5WU6.js} +2 -2
  45. package/dist/cli-bundle/focused-chunks/chunk-MVY6JDIH.js +12 -0
  46. package/dist/cli-bundle/focused-chunks/{chunk-NC5HUGHU.js → chunk-NLZUI234.js} +4 -4
  47. package/dist/cli-bundle/focused-chunks/{chunk-B5JOVH5F.js → chunk-R2MTHS2H.js} +2 -2
  48. package/dist/cli-bundle/focused-chunks/chunk-SM2KGD4J.js +79 -0
  49. package/dist/cli-bundle/focused-chunks/{chunk-HMYDUEQK.js → chunk-SYL73MEI.js} +3 -3
  50. package/dist/cli-bundle/focused-chunks/{chunk-77NU7NPN.js → chunk-TAFMNTA5.js} +2 -2
  51. package/dist/cli-bundle/focused-chunks/{chunk-74KC7FUQ.js → chunk-TGUOEOKP.js} +2 -2
  52. package/dist/cli-bundle/focused-chunks/chunk-U4UBQBS7.js +2 -0
  53. package/dist/cli-bundle/focused-chunks/{chunk-URWYYJ7K.js → chunk-WDKZB3V6.js} +2 -2
  54. package/dist/cli-bundle/focused-chunks/chunk-XK3XMZM4.js +2 -0
  55. package/dist/cli-bundle/focused-chunks/{chunk-3M5FZCMC.js → chunk-Y57A4ZDI.js} +55 -55
  56. package/dist/cli-bundle/focused-chunks/chunk-YC6NCPLK.js +22 -0
  57. package/dist/cli-bundle/focused-chunks/{chunk-7DEZT4JN.js → chunk-YQKLKRMW.js} +2 -2
  58. package/dist/cli-bundle/focused-chunks/{chunk-3FM75SBJ.js → chunk-YW5GHQN2.js} +2 -2
  59. package/dist/cli-bundle/focused-chunks/chunk-ZA6AEYIM.js +2 -0
  60. package/dist/cli-bundle/focused-chunks/chunk-ZH7GTEWL.js +3 -0
  61. package/dist/cli-bundle/focused-chunks/indexed-receipt-blobs-GAFC52LQ.js +4 -0
  62. package/dist/cli-bundle/focused-chunks/{mutation-admission-BQBQXILV.js → mutation-admission-ENBFMV2X.js} +2 -2
  63. package/dist/cli-bundle/focused-chunks/{settlement-hook-XVS6P2XI.js → settlement-hook-PUYJJDFL.js} +2 -2
  64. package/dist/cli-bundle/focused-chunks/{stage-VSIL4MTZ.js → stage-MAWZTM2H.js} +2 -2
  65. package/dist/cli-bundle/main.js +4 -4
  66. package/dist/cli-bundle/sdk-authoring.js +1 -1
  67. package/dist/cli-bundle/sdk-contracts.js +2 -2
  68. package/dist/cli-bundle/sdk-core.js +33 -33
  69. package/dist/cli-bundle/sdk-governance.js +1 -1
  70. package/dist/cli-bundle/sdk-graph.js +1 -1
  71. package/dist/cli-bundle/sdk-merge.js +57 -57
  72. package/dist/cli-bundle/sdk-query.js +1 -1
  73. package/dist/cli-bundle/sdk-runtime.js +1 -1
  74. package/dist/cli-bundle/sdk-testing.js +1 -1
  75. package/dist/cli-bundle/sdk.js +9 -9
  76. package/dist/core/extensions/extension-types.d.ts +3 -0
  77. package/dist/core/extensions/extension-types.js +1 -1
  78. package/dist/core/history/event-index.d.ts +6 -0
  79. package/dist/core/history/event-index.js +7 -6
  80. package/dist/core/history/read.d.ts +3 -1
  81. package/dist/core/history/read.js +1 -1
  82. package/dist/core/output/mutation-projection.js +1 -1
  83. package/dist/core/policy/workflow-policy-store.js +1 -1
  84. package/dist/core/shared/errors.d.ts +9 -0
  85. package/dist/core/shared/errors.js +1 -1
  86. package/dist/core/shared/secret-rules.d.ts +17 -0
  87. package/dist/core/shared/secret-rules.js +2 -1
  88. package/dist/core/store/item-mutation-precondition.d.ts +8 -0
  89. package/dist/core/store/item-mutation-precondition.js +1 -1
  90. package/dist/core/store/item-store.js +1 -1
  91. package/dist/mcp/tool-definitions.js +1 -1
  92. package/dist/sdk/cli-contracts/commander-mutation-options.js +1 -1
  93. package/dist/sdk/cli-contracts/completeness.js +1 -1
  94. package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
  95. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
  96. package/dist/sdk/cli-contracts/grammar-contracts.js +1 -1
  97. package/dist/sdk/cli-contracts/registration-helpers.d.ts +1 -1
  98. package/dist/sdk/cli-contracts/registration-helpers.js +1 -1
  99. package/dist/sdk/cli-contracts/tool-option-contracts.d.ts +4 -0
  100. package/dist/sdk/cli-contracts/tool-option-contracts.js +1 -1
  101. package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
  102. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  103. package/dist/sdk/cli-contracts/tool-schema.js +1 -1
  104. package/dist/sdk/context-signal-store.js +2 -2
  105. package/dist/sdk/core.d.ts +1 -1
  106. package/dist/sdk/core.js +1 -1
  107. package/dist/sdk/extension-command-context.js +2 -2
  108. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
  109. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  110. package/dist/sdk/index.d.ts +2 -1
  111. package/dist/sdk/index.js +1 -1
  112. package/dist/sdk/item-transaction.js +1 -1
  113. package/dist/sdk/lifecycle/close-many.d.ts +3 -0
  114. package/dist/sdk/lifecycle/close-many.js +1 -1
  115. package/dist/sdk/lifecycle/close.d.ts +3 -1
  116. package/dist/sdk/lifecycle/close.js +1 -1
  117. package/dist/sdk/lifecycle/create.d.ts +2 -0
  118. package/dist/sdk/lifecycle/create.js +1 -1
  119. package/dist/sdk/lifecycle/plan.d.ts +2 -0
  120. package/dist/sdk/lifecycle/plan.js +1 -1
  121. package/dist/sdk/lifecycle/restore.js +1 -1
  122. package/dist/sdk/lifecycle-policy.d.ts +11 -0
  123. package/dist/sdk/lifecycle-policy.js +1 -1
  124. package/dist/sdk/merge/indexed-receipt-blobs.d.ts +3 -1
  125. package/dist/sdk/merge/indexed-receipt-blobs.js +2 -2
  126. package/dist/sdk/merge/receipt-file-boundary.d.ts +2 -0
  127. package/dist/sdk/merge/receipt-file-boundary.js +1 -1
  128. package/dist/sdk/merge/receipts.js +2 -2
  129. package/dist/sdk/mutation-events.d.ts +6 -0
  130. package/dist/sdk/mutation-events.js +1 -1
  131. package/dist/sdk/mutation-guard.d.ts +6 -1
  132. package/dist/sdk/mutation-guard.js +1 -1
  133. package/dist/sdk/output/fit-envelope.d.ts +98 -0
  134. package/dist/sdk/output/fit-envelope.js +2 -0
  135. package/dist/sdk/public-surface.json +1 -1
  136. package/dist/sdk/query.d.ts +1 -0
  137. package/dist/sdk/query.js +1 -1
  138. package/dist/sdk/read-output-session.d.ts +1 -1
  139. package/dist/sdk/read-output-session.js +1 -1
  140. package/dist/sdk/runtime-extended-actions.js +1 -1
  141. package/dist/sdk/runtime-input.js +1 -1
  142. package/dist/sdk/runtime-primitives.d.ts +1 -1
  143. package/dist/sdk/runtime-primitives.js +1 -1
  144. package/dist/sdk/runtime.d.ts +2 -2
  145. package/dist/sdk/runtime.js +1 -1
  146. package/dist/sdk/scheduling-shortcuts.d.ts +2 -0
  147. package/dist/sdk/scheduling-shortcuts.js +1 -1
  148. package/dist/sdk/test/execution.js +2 -2
  149. package/dist/sdk/test/receipt-index-snapshot.d.ts +2 -0
  150. package/dist/sdk/test/receipt-index-snapshot.js +2 -0
  151. package/dist/sdk/test/workspace-snapshot.d.ts +1 -1
  152. package/dist/sdk/test/workspace-snapshot.js +1 -1
  153. package/dist/sdk/workspace-transaction.d.ts +3 -1
  154. package/dist/sdk/workspace-transaction.js +2 -2
  155. package/docs/COMMANDS.md +2 -1
  156. package/docs/MUTATION_INTEGRITY.md +33 -0
  157. package/docs/PERFORMANCE.md +21 -6
  158. package/docs/QUALITY_EVIDENCE.md +9 -0
  159. package/docs/READ_OUTPUT_CONTRACTS.md +55 -1
  160. package/docs/RELEASING.md +11 -0
  161. package/docs/SDK.md +88 -6
  162. package/docs/SDK_CONTEXT_COORDINATION.md +35 -4
  163. package/docs/TESTING.md +14 -1
  164. package/docs/WORKFLOW_POLICIES.md +14 -1
  165. package/docs/agent-task-token-baseline.json +1 -1
  166. package/docs/agent-task-transcripts.json +1 -1
  167. package/docs/generated/FLAG_LEXICON_BUDGETS.md +4 -4
  168. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +5 -3
  169. package/marketplace.json +1 -1
  170. package/package.json +1 -1
  171. package/packages/pm-beads/package.json +1 -1
  172. package/packages/pm-calendar/extensions/calendar/shortcuts.ts +2 -2
  173. package/packages/pm-calendar/package.json +1 -1
  174. package/packages/pm-command-kit/package.json +1 -1
  175. package/packages/pm-digital-twin/package.json +1 -1
  176. package/packages/pm-governance-audit/package.json +1 -1
  177. package/packages/pm-guide-shell/package.json +1 -1
  178. package/packages/pm-kanban/package.json +1 -1
  179. package/packages/pm-lifecycle-hooks/package.json +1 -1
  180. package/packages/pm-linked-test-adapters/package.json +1 -1
  181. package/packages/pm-search-advanced/package.json +1 -1
  182. package/packages/pm-templates/package.json +1 -1
  183. package/packages/pm-todos/package.json +1 -1
  184. package/packages/pm-vcs/package.json +1 -1
  185. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  186. package/plugins/pm-claude/package.json +1 -1
  187. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  188. package/plugins/pm-codex/package.json +1 -1
  189. package/plugins/pm-codex/plugin.json +1 -1
  190. package/runtime-dependencies.json +1 -1
  191. package/dist/cli-bundle/chunks/chunk-444VPGQT.js +0 -20
  192. package/dist/cli-bundle/chunks/chunk-5ZPT67NJ.js +0 -127
  193. package/dist/cli-bundle/chunks/chunk-7C3GCLOO.js +0 -30
  194. package/dist/cli-bundle/chunks/chunk-HMJSFWHK.js +0 -2
  195. package/dist/cli-bundle/chunks/chunk-RLXK2S7P.js +0 -2
  196. package/dist/cli-bundle/chunks/chunk-RQKDANP7.js +0 -113
  197. package/dist/cli-bundle/chunks/chunk-RUKSEZE7.js +0 -2
  198. package/dist/cli-bundle/chunks/chunk-XYLBOXSH.js +0 -6
  199. package/dist/cli-bundle/chunks/indexed-receipt-blobs-47I6KKDO.js +0 -4
  200. package/dist/cli-bundle/chunks/register-mutation-JZVVJUHY.js +0 -20
  201. package/dist/cli-bundle/focused-chunks/chunk-5LBZR6YW.js +0 -2
  202. package/dist/cli-bundle/focused-chunks/chunk-IVLIUPVV.js +0 -12
  203. package/dist/cli-bundle/focused-chunks/chunk-KIIGME23.js +0 -2
  204. package/dist/cli-bundle/focused-chunks/chunk-LWJ2X75I.js +0 -78
  205. package/dist/cli-bundle/focused-chunks/chunk-MT5TYMEU.js +0 -3
  206. package/dist/cli-bundle/focused-chunks/chunk-NI5SG2M2.js +0 -2
  207. package/dist/cli-bundle/focused-chunks/chunk-QH5KMIYZ.js +0 -91
  208. package/dist/cli-bundle/focused-chunks/chunk-VVRXF4JO.js +0 -2
  209. package/dist/cli-bundle/focused-chunks/chunk-W7MCCFYV.js +0 -2
  210. package/dist/cli-bundle/focused-chunks/chunk-WMPDY5ZU.js +0 -21
  211. package/dist/cli-bundle/focused-chunks/indexed-receipt-blobs-YCLN2ZZG.js +0 -4
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ce212ffb-1f13-5893-9690-31dbb4e0de13")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{normalizeItemFormatVersion,requiredItemFormatVersion}from"../../core/item/item-format-version.js";import{assertProjectRuntimeFormatGuard}from"../environment/project-runtime-compatibility.js";import{enforceWorkflowMutation}from"../../core/policy/workflow-policy-store.js";import jsonPatch from"fast-json-patch";import fs from"node:fs/promises";import{readLocatedItemSnapshot}from"../../core/store/item-store.js";import{readHistorySnapshot}from"../../core/history/read.js";import{verifyHistoryRewriteNoDrift}from"../../core/history/history-rewrite.js";import{resolveHistorySubject}from"../history/subject.js";import{historyVersionOffset}from"../../core/history/version-address.js";import{writeFileAtomic,appendHistoryEntry,createHistoryEntry,replayToCanonicalItemDocument,replayToItemDocument,toReplayDocument,enforceHistoryStreamPolicyForItem,canonicalDocument,serializeItemDocument,resolveItemTypeRegistry,acquireLockLease,EXIT_CODE,PmCliError,nowIso,getActiveExtensionRegistrations,projectAfterCommandItemSnapshot,recordAfterCommandAffectedItem,runActiveOnWriteHooks,runActiveBeforeMutationHooks,createMutationGuardSdk,getItemPath,resolvePmRoot,resolveWorkspaceRoot,readSettings,resolveAuthor}from"../runtime-primitives.js";import{applyHistoryPatch,ensureMaterializedHistoryTarget as ensureMaterializedRestoreTarget,extractPatchFailureContext,replayHistoryToTarget as replayToTarget,resolveHistoryTarget}from"../history-read.js";import{acquireItemMetadataDerivedIndexLock,refreshItemMetadataDerivedIndex}from"../item-metadata-index.js";async function readRestoreItem(located,settings,history){const snapshot=await readLocatedItemSnapshot(located,{schema:settings.schema});return snapshot.error===null?{raw:snapshot.raw,document:snapshot.document}:{raw:snapshot.raw,document:replayCurrentDocument(history),recovered:!0}}function ensureReplayTarget(target,history){return resolveHistoryTarget(target,history,{errorSubject:"restore",allowFutureTimestamp:!0})}function replayCurrentDocument(history){const currentReplay=replayToTarget(history,history.length-1);return Object.keys(currentReplay.metadata).length===0?{metadata:{},body:currentReplay.body}:canonicalDocument(replayToItemDocument(currentReplay))}async function resolveRestoreSubject(pmRoot,id,settings,typeToFolder){const subject=await resolveHistorySubject(pmRoot,id,settings,typeToFolder),historyPolicy=subject.located?await enforceHistoryStreamPolicyForItem({pmRoot,settings,itemId:subject.id,commandLabel:"restore"}):{warnings:[]};return{...subject,historyPolicyWarnings:historyPolicy.warnings}}function changedFields(beforeDocument,afterDocument){const beforeReplay=toReplayDocument(beforeDocument),afterReplay=toReplayDocument(afterDocument),patch=jsonPatch.compare(beforeReplay,afterReplay),fields=new Set;for(const op of patch){if(op.path==="/body"||op.path.startsWith("/body/")){fields.add("body");continue}const segment=op.path.replace(/^\/(?:metadata|front_matter)\/?/,"").split("/")[0];fields.add(segment.replaceAll("~1","/").replaceAll("~0","~"))}return Array.from(fields).sort((a,b)=>a.localeCompare(b))}async function loadRestoreStateUnderLock(params){const verified=await verifyHistoryRewriteNoDrift({pmRoot:params.pmRoot,subject:params.subject,settings:params.settings,typeRegistry:params.typeRegistry,historyRawBeforeLock:params.historyRawBeforeLock,currentItemRawBeforeLock:params.currentItemRawBeforeLock,operation:"restore",readItem:located=>readRestoreItem(located,params.settings,params.history)});return{loadedItemUnderLock:verified.loadedItemUnderLock,existingItemPath:verified.locatedUnderLock?.itemPath??null}}function resolveRestoreCurrentState(loadedItemUnderLock,history){return loadedItemUnderLock?{document:loadedItemUnderLock.document,originalRaw:loadedItemUnderLock.raw}:{document:replayCurrentDocument(history),originalRaw:null}}function collectRestoreOwnershipWarnings(params){const assigned=params.document.metadata.assignee?.trim();if(!(assigned&&assigned!==params.author&&!params.force))return[];if(params.enforcement==="strict")throw new PmCliError(`Item ${params.resolvedId} is assigned to ${assigned}. Use --force to override.`,EXIT_CODE.CONFLICT);return params.enforcement==="warn"?[`ownership_warning:assignee_conflict:${params.resolvedId}:${assigned}`]:[]}async function restorePreviousItemAfterHistoryFailure(params){if(params.existingItemPath&&params.resolvedOriginalRaw!==null&&params.restoredItemPath!==params.existingItemPath){await writeFileAtomic(params.existingItemPath,params.resolvedOriginalRaw),await fs.rm(params.restoredItemPath,{force:!0});return}if(params.existingItemPath&&params.resolvedOriginalRaw!==null){await writeFileAtomic(params.existingItemPath,params.resolvedOriginalRaw);return}await fs.rm(params.restoredItemPath,{force:!0})}async function runRestoreWriteHooks(params){return[...await runActiveOnWriteHooks({path:params.restoredItemPath,scope:"project",op:"restore",item_id:params.after.metadata.id,item_type:params.after.metadata.type,before:params.before,after:params.after,changed_fields:["restored"]}),...await runActiveOnWriteHooks({path:params.historyPath,scope:"project",op:"restore:history",item_id:params.after.metadata.id,item_type:params.after.metadata.type,before:params.before,after:params.after,changed_fields:["restored"]})]}async function runRestore(id,target,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settings=await readSettings(pmRoot),typeRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations()),subject=await resolveRestoreSubject(pmRoot,id,settings,typeRegistry.type_to_folder),resolvedId=subject.id,{raw:historyRawBeforeLock,entries:history}=await readHistorySnapshot(subject.historyPath,resolvedId);if(history.length===0)throw new PmCliError(`No history exists for ${resolvedId}; restore is unavailable.`,EXIT_CODE.NOT_FOUND);const resolvedTarget=ensureReplayTarget(target,history),replayDocument=ensureMaterializedRestoreTarget(replayToTarget(history,resolvedTarget.historyIndex),resolvedTarget),restoredDocument=replayToCanonicalItemDocument(replayDocument,{schema:settings.schema});if(restoredDocument.metadata.id!==resolvedId)throw new PmCliError(`Restore target resolved to item ${restoredDocument.metadata.id}, expected ${resolvedId}.`,EXIT_CODE.GENERIC_FAILURE);const currentItemRawBeforeLock=(subject.located?await readRestoreItem(subject.located,settings,history):null)?.raw??null,author=resolveAuthor(options.author,settings.author_default),lease=await acquireLockLease(pmRoot,resolvedId,settings.locks.ttl_seconds,author,!!options.force,settings.governance.force_required_for_stale_lock,settings.locks.wait_ms,{waitPolicy:"progress"}),releaseLock=lease.release;try{const{loadedItemUnderLock,existingItemPath}=await loadRestoreStateUnderLock({pmRoot,subject,settings,typeRegistry,historyRawBeforeLock,currentItemRawBeforeLock,history}),itemFormat="toon",currentState=resolveRestoreCurrentState(loadedItemUnderLock,history);restoredDocument.metadata.pm_format_version=normalizeItemFormatVersion(Math.max(requiredItemFormatVersion(restoredDocument.metadata),requiredItemFormatVersion(currentState.document.metadata))),assertProjectRuntimeFormatGuard(resolveWorkspaceRoot(pmRoot),requiredItemFormatVersion(restoredDocument.metadata));const ownershipWarnings=collectRestoreOwnershipWarnings({document:currentState.document,author,force:options.force,enforcement:settings.governance.ownership_enforcement,resolvedId}),restoreChangedFields=changedFields(currentState.document,restoredDocument),workflowPolicy=await enforceWorkflowMutation({pmRoot,settings,operation:"restore",author,before:currentState.document,after:restoredDocument});await runActiveBeforeMutationHooks({pm_root:pmRoot,operation:"restore",before:currentState.document,after:restoredDocument,changed_fields:restoreChangedFields,sdk:createMutationGuardSdk({pmRoot,settings,typeToFolder:typeRegistry.type_to_folder})});const serializedRestore=serializeItemDocument(restoredDocument,{format:itemFormat,schema:settings.schema}),restoredItemPath=getItemPath(pmRoot,restoredDocument.metadata.type,resolvedId,itemFormat,typeRegistry.type_to_folder),derivedIndexWarnings=await lease.fence(async()=>{const releaseDerivedIndexLock=await acquireItemMetadataDerivedIndexLock(pmRoot,author);let derivedIndexWarnings2;try{await writeFileAtomic(restoredItemPath,serializedRestore),existingItemPath&&restoredItemPath!==existingItemPath&&await fs.rm(existingItemPath);const historyEntry=createHistoryEntry({nowIso:nowIso(),author,op:"restore",before:currentState.document,after:restoredDocument,message:options.message,context:workflowPolicy.decisions.length===0?void 0:{workflow_policies:workflowPolicy.decisions}});try{await appendHistoryEntry(subject.historyPath,historyEntry)}catch(error){throw await restorePreviousItemAfterHistoryFailure({existingItemPath,resolvedOriginalRaw:currentState.originalRaw,restoredItemPath}),error}derivedIndexWarnings2=await refreshItemMetadataDerivedIndex({pmRoot,preferredFormat:settings.item_format,typeToFolder:typeRegistry.type_to_folder,schema:settings.schema,itemPath:restoredItemPath,previousItemPath:existingItemPath??void 0,document:restoredDocument})}finally{await releaseDerivedIndexLock()}return derivedIndexWarnings2}),hookWarnings=await runRestoreWriteHooks({restoredItemPath,historyPath:subject.historyPath,before:currentState.document,after:restoredDocument});recordAfterCommandAffectedItem({id:restoredDocument.metadata.id,op:"restore",item_type:restoredDocument.metadata.type,previous_status:currentState.document.metadata.status,status:restoredDocument.metadata.status,previous:projectAfterCommandItemSnapshot(currentState.document.metadata,restoreChangedFields),current:projectAfterCommandItemSnapshot(restoredDocument.metadata,restoreChangedFields),changed_fields:restoreChangedFields});const targetEntry=history[resolvedTarget.historyIndex],offset=historyVersionOffset(history,!0);return{item:restoredDocument.metadata,restored_from:{kind:resolvedTarget.kind,target:resolvedTarget.raw,history_index:resolvedTarget.historyIndex+1,version:offset===null?null:offset+resolvedTarget.historyIndex+1,entry_ts:targetEntry.ts,entry_op:targetEntry.op},changed_fields:restoreChangedFields,warnings:[...workflowPolicy.warnings,...loadedItemUnderLock?.recovered?["restore_unreadable_item_recovered"]:[],...subject.historyPolicyWarnings,...ownershipWarnings,...derivedIndexWarnings,...hookWarnings]}}finally{await releaseLock()}}const _testOnlyRestoreCommand={applyHistoryPatch,changedFields,ensureMaterializedRestoreTarget,ensureReplayTarget,extractPatchFailureContext,replayCurrentDocument,replayToTarget,resolveRestoreSubject};export{_testOnlyRestoreCommand,runRestore};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f3145f17-e911-571b-9da3-f014e6fdde1c")}catch{}})();import{assertInitializedTracker}from"../environment/tracker-preflight.js";import{normalizeItemFormatVersion,requiredItemFormatVersion}from"../../core/item/item-format-version.js";import{assertProjectRuntimeFormatGuard}from"../environment/project-runtime-compatibility.js";import{enforceWorkflowMutation}from"../../core/policy/workflow-policy-store.js";import jsonPatch from"fast-json-patch";import fs from"node:fs/promises";import{readLocatedItemSnapshot}from"../../core/store/item-store.js";import{assertItemMutationPreconditions}from"../../core/store/item-mutation-precondition.js";import{readHistorySnapshot}from"../../core/history/read.js";import{verifyHistoryRewriteNoDrift}from"../../core/history/history-rewrite.js";import{resolveHistorySubject}from"../history/subject.js";import{historyVersionOffset}from"../../core/history/version-address.js";import{writeFileAtomic,appendHistoryEntry,createHistoryEntry,replayToCanonicalItemDocument,replayToItemDocument,toReplayDocument,enforceHistoryStreamPolicyForItem,canonicalDocument,serializeItemDocument,resolveItemTypeRegistry,acquireLockLease,EXIT_CODE,PmCliError,nowIso,getActiveExtensionRegistrations,projectAfterCommandItemSnapshot,recordAfterCommandAffectedItem,runActiveOnWriteHooks,runActiveBeforeMutationHooks,createMutationGuardSdk,getItemPath,resolvePmRoot,resolveWorkspaceRoot,readSettings,resolveAuthor}from"../runtime-primitives.js";import{applyHistoryPatch,ensureMaterializedHistoryTarget as ensureMaterializedRestoreTarget,extractPatchFailureContext,replayHistoryToTarget as replayToTarget,resolveHistoryTarget}from"../history-read.js";import{acquireItemMetadataDerivedIndexLock,refreshItemMetadataDerivedIndex}from"../item-metadata-index.js";async function readRestoreItem(located,settings,history){const snapshot=await readLocatedItemSnapshot(located,{schema:settings.schema});return snapshot.error===null?{raw:snapshot.raw,document:snapshot.document}:{raw:snapshot.raw,document:replayCurrentDocument(history),recovered:!0}}function ensureReplayTarget(target,history){return resolveHistoryTarget(target,history,{errorSubject:"restore",allowFutureTimestamp:!0})}function replayCurrentDocument(history){const currentReplay=replayToTarget(history,history.length-1);return Object.keys(currentReplay.metadata).length===0?{metadata:{},body:currentReplay.body}:canonicalDocument(replayToItemDocument(currentReplay))}async function resolveRestoreSubject(pmRoot,id,settings,typeToFolder){const subject=await resolveHistorySubject(pmRoot,id,settings,typeToFolder),historyPolicy=subject.located?await enforceHistoryStreamPolicyForItem({pmRoot,settings,itemId:subject.id,commandLabel:"restore"}):{warnings:[]};return{...subject,historyPolicyWarnings:historyPolicy.warnings}}function changedFields(beforeDocument,afterDocument){const beforeReplay=toReplayDocument(beforeDocument),afterReplay=toReplayDocument(afterDocument),patch=jsonPatch.compare(beforeReplay,afterReplay),fields=new Set;for(const op of patch){if(op.path==="/body"||op.path.startsWith("/body/")){fields.add("body");continue}const segment=op.path.replace(/^\/(?:metadata|front_matter)\/?/,"").split("/")[0];fields.add(segment.replaceAll("~1","/").replaceAll("~0","~"))}return Array.from(fields).sort((a,b)=>a.localeCompare(b))}async function loadRestoreStateUnderLock(params){const verified=await verifyHistoryRewriteNoDrift({pmRoot:params.pmRoot,subject:params.subject,settings:params.settings,typeRegistry:params.typeRegistry,historyRawBeforeLock:params.historyRawBeforeLock,currentItemRawBeforeLock:params.currentItemRawBeforeLock,operation:"restore",readItem:located=>readRestoreItem(located,params.settings,params.history)});return{loadedItemUnderLock:verified.loadedItemUnderLock,existingItemPath:verified.locatedUnderLock?.itemPath??null}}function resolveRestoreCurrentState(loadedItemUnderLock,history){return loadedItemUnderLock?{document:loadedItemUnderLock.document,originalRaw:loadedItemUnderLock.raw}:{document:replayCurrentDocument(history),originalRaw:null}}function collectRestoreOwnershipWarnings(params){const assigned=params.document.metadata.assignee?.trim();if(!(assigned&&assigned!==params.author&&!params.force))return[];if(params.enforcement==="strict")throw new PmCliError(`Item ${params.resolvedId} is assigned to ${assigned}. Use --force to override.`,EXIT_CODE.CONFLICT);return params.enforcement==="warn"?[`ownership_warning:assignee_conflict:${params.resolvedId}:${assigned}`]:[]}async function restorePreviousItemAfterHistoryFailure(params){if(params.existingItemPath&&params.resolvedOriginalRaw!==null&&params.restoredItemPath!==params.existingItemPath){await writeFileAtomic(params.existingItemPath,params.resolvedOriginalRaw),await fs.rm(params.restoredItemPath,{force:!0});return}if(params.existingItemPath&&params.resolvedOriginalRaw!==null){await writeFileAtomic(params.existingItemPath,params.resolvedOriginalRaw);return}await fs.rm(params.restoredItemPath,{force:!0})}async function runRestoreWriteHooks(params){return[...await runActiveOnWriteHooks({path:params.restoredItemPath,scope:"project",op:"restore",item_id:params.after.metadata.id,item_type:params.after.metadata.type,before:params.before,after:params.after,changed_fields:["restored"]}),...await runActiveOnWriteHooks({path:params.historyPath,scope:"project",op:"restore:history",item_id:params.after.metadata.id,item_type:params.after.metadata.type,before:params.before,after:params.after,changed_fields:["restored"]})]}async function runRestore(id,target,options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);await assertInitializedTracker(pmRoot);const settings=await readSettings(pmRoot),typeRegistry=resolveItemTypeRegistry(settings,getActiveExtensionRegistrations()),subject=await resolveRestoreSubject(pmRoot,id,settings,typeRegistry.type_to_folder),resolvedId=subject.id,{raw:historyRawBeforeLock,entries:history}=await readHistorySnapshot(subject.historyPath,resolvedId);if(history.length===0)throw new PmCliError(`No history exists for ${resolvedId}; restore is unavailable.`,EXIT_CODE.NOT_FOUND);const resolvedTarget=ensureReplayTarget(target,history),replayDocument=ensureMaterializedRestoreTarget(replayToTarget(history,resolvedTarget.historyIndex),resolvedTarget),restoredDocument=replayToCanonicalItemDocument(replayDocument,{schema:settings.schema});if(restoredDocument.metadata.id!==resolvedId)throw new PmCliError(`Restore target resolved to item ${restoredDocument.metadata.id}, expected ${resolvedId}.`,EXIT_CODE.GENERIC_FAILURE);const currentItemRawBeforeLock=(subject.located?await readRestoreItem(subject.located,settings,history):null)?.raw??null,author=resolveAuthor(options.author,settings.author_default),lease=await acquireLockLease(pmRoot,resolvedId,settings.locks.ttl_seconds,author,!!options.force,settings.governance.force_required_for_stale_lock,settings.locks.wait_ms,{waitPolicy:"progress"}),releaseLock=lease.release;try{const{loadedItemUnderLock,existingItemPath}=await loadRestoreStateUnderLock({pmRoot,subject,settings,typeRegistry,historyRawBeforeLock,currentItemRawBeforeLock,history}),itemFormat="toon",currentState=resolveRestoreCurrentState(loadedItemUnderLock,history);assertItemMutationPreconditions(pmRoot,currentState.document),restoredDocument.metadata.pm_format_version=normalizeItemFormatVersion(Math.max(requiredItemFormatVersion(restoredDocument.metadata),requiredItemFormatVersion(currentState.document.metadata))),assertProjectRuntimeFormatGuard(resolveWorkspaceRoot(pmRoot),requiredItemFormatVersion(restoredDocument.metadata));const ownershipWarnings=collectRestoreOwnershipWarnings({document:currentState.document,author,force:options.force,enforcement:settings.governance.ownership_enforcement,resolvedId}),restoreChangedFields=changedFields(currentState.document,restoredDocument),workflowPolicy=await enforceWorkflowMutation({pmRoot,settings,operation:"restore",author,before:currentState.document,after:restoredDocument});await runActiveBeforeMutationHooks({pm_root:pmRoot,operation:"restore",before:currentState.document,after:restoredDocument,changed_fields:restoreChangedFields,sdk:createMutationGuardSdk({pmRoot,settings,typeToFolder:typeRegistry.type_to_folder})});const serializedRestore=serializeItemDocument(restoredDocument,{format:itemFormat,schema:settings.schema}),restoredItemPath=getItemPath(pmRoot,restoredDocument.metadata.type,resolvedId,itemFormat,typeRegistry.type_to_folder),derivedIndexWarnings=await lease.fence(async()=>{const releaseDerivedIndexLock=await acquireItemMetadataDerivedIndexLock(pmRoot,author);let derivedIndexWarnings2;try{await writeFileAtomic(restoredItemPath,serializedRestore),existingItemPath&&restoredItemPath!==existingItemPath&&await fs.rm(existingItemPath);const historyEntry=createHistoryEntry({nowIso:nowIso(),author,op:"restore",before:currentState.document,after:restoredDocument,message:options.message,context:workflowPolicy.decisions.length===0?void 0:{workflow_policies:workflowPolicy.decisions}});try{await appendHistoryEntry(subject.historyPath,historyEntry)}catch(error){throw await restorePreviousItemAfterHistoryFailure({existingItemPath,resolvedOriginalRaw:currentState.originalRaw,restoredItemPath}),error}derivedIndexWarnings2=await refreshItemMetadataDerivedIndex({pmRoot,preferredFormat:settings.item_format,typeToFolder:typeRegistry.type_to_folder,schema:settings.schema,itemPath:restoredItemPath,previousItemPath:existingItemPath??void 0,document:restoredDocument})}finally{await releaseDerivedIndexLock()}return derivedIndexWarnings2}),hookWarnings=await runRestoreWriteHooks({restoredItemPath,historyPath:subject.historyPath,before:currentState.document,after:restoredDocument});recordAfterCommandAffectedItem({id:restoredDocument.metadata.id,op:"restore",item_type:restoredDocument.metadata.type,previous_status:currentState.document.metadata.status,status:restoredDocument.metadata.status,previous:projectAfterCommandItemSnapshot(currentState.document.metadata,restoreChangedFields),current:projectAfterCommandItemSnapshot(restoredDocument.metadata,restoreChangedFields),changed_fields:restoreChangedFields});const targetEntry=history[resolvedTarget.historyIndex],offset=historyVersionOffset(history,!0);return{item:restoredDocument.metadata,restored_from:{kind:resolvedTarget.kind,target:resolvedTarget.raw,history_index:resolvedTarget.historyIndex+1,version:offset===null?null:offset+resolvedTarget.historyIndex+1,entry_ts:targetEntry.ts,entry_op:targetEntry.op},changed_fields:restoreChangedFields,warnings:[...workflowPolicy.warnings,...loadedItemUnderLock?.recovered?["restore_unreadable_item_recovered"]:[],...subject.historyPolicyWarnings,...ownershipWarnings,...derivedIndexWarnings,...hookWarnings]}}finally{await releaseLock()}}const _testOnlyRestoreCommand={applyHistoryPatch,changedFields,ensureMaterializedRestoreTarget,ensureReplayTarget,extractPatchFailureContext,replayCurrentDocument,replayToTarget,resolveRestoreSubject};export{_testOnlyRestoreCommand,runRestore};
2
2
  //# sourceMappingURL=restore.js.map
@@ -33,6 +33,8 @@ lifecyclePolicy?:Partial<TerminalTransitionPolicy>;
33
33
  }
34
34
  /** Result returned by the public close-item operation. */
35
35
  export interface CloseOperationResult {
36
+ /** Completion timestamp and the input that supplied it for this close event. */
37
+ completion?:CloseCompletionReceipt;
36
38
  /** Closed item projected as a stable record. */
37
39
  item:Record<string,unknown>;
38
40
  /** Metadata fields changed by the transition. */
@@ -42,6 +44,15 @@ follow_up?:CloseEvidenceFollowUp;
42
44
  /** Validation and lifecycle policy receipts. */
43
45
  warnings:string[];
44
46
  }
47
+ /** Per-close completion provenance, also sealed into the immutable close event. */
48
+ export interface CloseCompletionReceipt {
49
+ /** Actual persisted completed_at value. */
50
+ timestamp:string;
51
+ /** Caller assertion, preserved metadata, or inferred tracker close time. */
52
+ source:"asserted" | "existing" | "close_time";
53
+ /** Actionable correction guidance when completion was inferred. */
54
+ hint?:string;
55
+ }
45
56
  /** Optional evidence completion after a successful, durable close. */
46
57
  export interface CloseEvidenceFollowUp {
47
58
  /** Close succeeded; this guidance does not request another close attempt. */
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="8e07b07a-fde3-5173-bb7c-7e8ade88570c")}catch{}})();import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";const DEFAULT_TERMINAL_TRANSITION_POLICY=Object.freeze({requireCloseReason:!0,orderingEdges:"preserve"});function resolveTerminalReason(input){const candidates=[{source:"explicit",value:input.explicit},{source:"duplicate",value:input.duplicateOf===void 0?void 0:`Duplicate of ${input.duplicateOf}`},{source:"resolution",value:input.resolution},{source:"message",value:input.message}];for(const candidate of candidates){const normalized=candidate.value?.trim();if(normalized)return{closeReason:normalized,source:candidate.source}}return{source:"none"}}function requireTerminalReason(input,required){return requireTerminalReasonWithRecovery(input,required,{operation:"close"})}function requireTerminalReasonWithRecovery(input,required,recovery){const resolution=resolveTerminalReason(input);if(!required||resolution.closeReason!==void 0)return resolution;const examples=recovery.operation==="create"?[`pm create --title "<title>" --type <type> --status ${recovery.status??"closed"} --close-reason "<closing summary>"`,`pm create --title "<title>" --type <type> --status ${recovery.status??"closed"} --resolution "<resolution summary>"`,"pm create --stdin-json < item.json"]:['pm close <id> "Done: <what changed and why>"','pm close <id> --reason "<closing summary>"','pm close <id> -m "<closing summary>"',"pm close <id> --duplicate-of <canonical-id>"];throw new PmCliError("Close reason text is required because governance.require_close_reason is enabled",EXIT_CODE.USAGE,{code:"close_reason_required",required:"Provide an author-controlled closing summary as the reason, message, resolution, or duplicate target.",why:"governance.require_close_reason is enabled, so tools must not invent immutable closure evidence.",examples,nextSteps:[`Re-run the ${recovery.operation} operation with a closing summary.`,"To stop requiring reasons, run: pm config set governance-require-close-reason --policy disabled"]})}function applyTerminalOrderingPolicy(metadata,policy){const previousBlockedBy=typeof metadata.blocked_by=="string"?metadata.blocked_by.trim():"",hadBlockedBy=metadata.blocked_by!==void 0,dependencies=metadata.dependencies??[],predecessorEdges=dependencies.filter(dependency=>dependency.kind==="blocked_by"),hadBlockedReason=metadata.blocked_reason!==void 0,changedFields=[];if(hadBlockedBy&&(delete metadata.blocked_by,changedFields.push("blocked_by")),hadBlockedReason&&(delete metadata.blocked_reason,changedFields.push("blocked_reason")),policy.orderingEdges==="remove"&&predecessorEdges.length>0){const removed=new Set(predecessorEdges),retained=dependencies.filter(dependency=>!removed.has(dependency));retained.length>0?metadata.dependencies=retained:delete metadata.dependencies,changedFields.push("dependencies")}if(policy.orderingEdges==="remove")return{changedFields,warnings:[...previousBlockedBy?[`closed_cleared_blocked_by:${metadata.id}:${previousBlockedBy}`]:[],...predecessorEdges.length>0?[`closed_removed_predecessors:${metadata.id}:${predecessorEdges.map(edge=>edge.id).join(",")}`,`closed_removed_predecessors_count:${metadata.id}:${predecessorEdges.length}`]:[]]};const blockerIds=[...new Set([...previousBlockedBy?[previousBlockedBy]:[],...predecessorEdges.map(dependency=>dependency.id)])];return blockerIds.length===0?{changedFields,warnings:[]}:{changedFields,warnings:[`closed_preserved_predecessors:${metadata.id}:${blockerIds.join(",")}`]}}export{DEFAULT_TERMINAL_TRANSITION_POLICY,applyTerminalOrderingPolicy,requireTerminalReason,requireTerminalReasonWithRecovery,resolveTerminalReason};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1430dcf8-9363-556d-aa33-184cea5421f9")}catch{}})();import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";const DEFAULT_TERMINAL_TRANSITION_POLICY=Object.freeze({requireCloseReason:!0,orderingEdges:"preserve"});function resolveTerminalReason(input){const candidates=[{source:"explicit",value:input.explicit},{source:"duplicate",value:input.duplicateOf===void 0?void 0:`Duplicate of ${input.duplicateOf}`},{source:"resolution",value:input.resolution},{source:"message",value:input.message}];for(const candidate of candidates){const normalized=candidate.value?.trim();if(normalized)return{closeReason:normalized,source:candidate.source}}return{source:"none"}}function requireTerminalReason(input,required){return requireTerminalReasonWithRecovery(input,required,{operation:"close"})}function requireTerminalReasonWithRecovery(input,required,recovery){const resolution=resolveTerminalReason(input);if(!required||resolution.closeReason!==void 0)return resolution;const examples=recovery.operation==="create"?[`pm create --title "<title>" --type <type> --status ${recovery.status??"closed"} --close-reason "<closing summary>"`,`pm create --title "<title>" --type <type> --status ${recovery.status??"closed"} --resolution "<resolution summary>"`,"pm create --stdin-json < item.json"]:['pm close <id> "Done: <what changed and why>"','pm close <id> --reason "<closing summary>"','pm close <id> -m "<closing summary>"',"pm close <id> --duplicate-of <canonical-id>"];throw new PmCliError("Close reason text is required because governance.require_close_reason is enabled",EXIT_CODE.USAGE,{code:"close_reason_required",required:"Provide an author-controlled closing summary as the reason, message, resolution, or duplicate target.",why:"governance.require_close_reason is enabled, so tools must not invent immutable closure evidence.",examples,nextSteps:[`Re-run the ${recovery.operation} operation with a closing summary.`,"To stop requiring reasons, run: pm config set governance-require-close-reason --policy disabled"]})}function applyTerminalOrderingPolicy(metadata,policy){const previousBlockedBy=typeof metadata.blocked_by=="string"?metadata.blocked_by.trim():"",hadBlockedBy=metadata.blocked_by!==void 0,dependencies=metadata.dependencies??[],predecessorEdges=dependencies.filter(dependency=>dependency.kind==="blocked_by"),hadBlockedReason=metadata.blocked_reason!==void 0,changedFields=[];if(hadBlockedBy&&(delete metadata.blocked_by,changedFields.push("blocked_by")),hadBlockedReason&&(delete metadata.blocked_reason,changedFields.push("blocked_reason")),policy.orderingEdges==="remove"&&predecessorEdges.length>0){const removed=new Set(predecessorEdges),retained=dependencies.filter(dependency=>!removed.has(dependency));retained.length>0?metadata.dependencies=retained:delete metadata.dependencies,changedFields.push("dependencies")}if(policy.orderingEdges==="remove")return{changedFields,warnings:[...previousBlockedBy?[`closed_cleared_blocked_by:${metadata.id}:${previousBlockedBy}`]:[],...predecessorEdges.length>0?[`closed_removed_predecessors:${metadata.id}:${predecessorEdges.map(edge=>edge.id).join(",")}`,`closed_removed_predecessors_count:${metadata.id}:${predecessorEdges.length}`]:[]]};const blockerIds=[...new Set([...previousBlockedBy?[previousBlockedBy]:[],...predecessorEdges.map(dependency=>dependency.id)])];return blockerIds.length===0?{changedFields,warnings:[]}:{changedFields,warnings:[`closed_preserved_predecessors:${metadata.id}:${blockerIds.join(",")}`]}}export{DEFAULT_TERMINAL_TRANSITION_POLICY,applyTerminalOrderingPolicy,requireTerminalReason,requireTerminalReasonWithRecovery,resolveTerminalReason};
2
2
  //# sourceMappingURL=lifecycle-policy.js.map
@@ -1,2 +1,4 @@
1
1
  /** Resolve physical roots, list the index once, and batch only requested bounded blobs. */
2
- export declare function readIndexedReceiptBlobs(cwd:string,directory:string,fileNames:string[],maximumBytes:number):Promise<Map<string,string>>;
2
+ export declare function readIndexedReceiptBlobs(cwd:string,directory:string,fileNames:string[],maximumBytes:number,linkedDirectory?:string,policy?:{
3
+ failOnReadError?:boolean;
4
+ }):Promise<Map<string,string>>;
@@ -1,4 +1,4 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a29ee03b-9955-56c6-a72e-1e93182e5de8")}catch{}})();import{execFile}from"node:child_process";import{realpath}from"node:fs/promises";import path from"node:path";import{promisify}from"node:util";import{isPathOutsideRoot}from"../workspace.js";import{gitWorkspaceEnvironment}from"./worktree-config.js";const execFileAsync=promisify(execFile),OBJECT_ID="[a-f0-9]{40}(?:[a-f0-9]{24})?",INDEX_ENTRY=new RegExp(`^100(?:644|755) (${OBJECT_ID}) 0$`,"u"),BLOB_METADATA=new RegExp(`^(${OBJECT_ID}) blob (\\d+)$`,"u");async function gitBatch(args,ids,options){const pending=execFileAsync("git",args,{...options,encoding:"buffer"}),input=pending.child.stdin;return input!==null&&(input.on("error",()=>{pending.child.kill()}),input.end(`${ids.join(`
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="5d2c2c37-5ff2-5dab-84f1-55a7fa8d5a27")}catch{}})();import{execFile}from"node:child_process";import{realpath}from"node:fs/promises";import path from"node:path";import{promisify}from"node:util";import{isPathOutsideRoot}from"../workspace.js";import{gitWorkspaceEnvironment}from"./worktree-config.js";const execFileAsync=promisify(execFile),OBJECT_ID="[a-f0-9]{40}(?:[a-f0-9]{24})?",INDEX_ENTRY=new RegExp(`^100(?:644|755) (${OBJECT_ID}) 0$`,"u"),BLOB_METADATA=new RegExp(`^(${OBJECT_ID}) blob (\\d+)$`,"u");async function gitBatch(args,ids,options){const pending=execFileAsync("git",args,{...options,encoding:"buffer"}),input=pending.child.stdin;return input!==null&&(input.on("error",()=>{pending.child.kill()}),input.end(`${ids.join(`
2
2
  `)}
3
- `)),(await pending).stdout}function indexedFiles(listing,requested){const files=new Map;for(const record of listing.split("\0")){const separator=record.indexOf(" "),file=record.slice(separator+1),metadata=INDEX_ENTRY.exec(record.slice(0,separator));metadata!==null&&requested.has(file)&&files.set(file,metadata[1])}return files}function blobBatches(metadata,requested,maximumBytes){const batches=[];let batch=[],bytes=0;for(const line of metadata.trimEnd().split(/\r?\n/u)){const match=BLOB_METADATA.exec(line);if(match===null||!requested.has(match[1]))continue;const size=Number(match[2]);!Number.isSafeInteger(size)||size>maximumBytes||(batch.length>0&&bytes+size+128>maximumBytes&&(batches.push(batch),batch=[],bytes=0),batch.push({id:match[1],size}),bytes+=size+128)}return batch.length>0&&batches.push(batch),batches}function decodeBlobs(output,entries,blobs){let offset=0;for(const{id,size}of entries){const start=output.indexOf(10,offset)+1;if(start===0||output.subarray(offset,start-1).toString("ascii")!==`${id} blob ${size}`||output[start+size]!==10)throw new Error("Invalid indexed receipt batch framing");blobs.set(id,output.subarray(start,start+size).toString("utf8")),offset=start+size+1}if(offset!==output.length)throw new Error("Unexpected indexed receipt batch data")}async function readIndexedReceiptBlobs(cwd,directory,fileNames,maximumBytes){const env=gitWorkspaceEnvironment();env.GIT_NO_REPLACE_OBJECTS="1",process.env.GIT_INDEX_FILE!==void 0&&(env.GIT_INDEX_FILE=path.resolve(process.env.GIT_INDEX_FILE));const options={cwd,env,windowsHide:!0,timeout:1e4,maxBuffer:maximumBytes+256};try{const root=(await execFileAsync("git",["rev-parse","--show-toplevel"],{...options,encoding:"utf8"})).stdout.trim(),[workspace,physicalDirectory]=await Promise.all([realpath(root),realpath(directory)]),relative=path.relative(workspace,physicalDirectory).replaceAll("\\","/");if(isPathOutsideRoot(relative))return new Map;options.cwd=workspace;const names=new Map(fileNames.map(file=>[`${relative}/${file}`,file])),listing=(await execFileAsync("git",["ls-files","--stage","-z","--",`:(literal)${relative}/`],{...options,encoding:"utf8"})).stdout,files=indexedFiles(listing,new Set(names.keys()));if(files.size===0)return new Map;const ids=new Set(files.values()),metadata=(await gitBatch(["cat-file","--batch-check"],[...ids],options)).toString("utf8"),blobs=new Map;for(const batch of blobBatches(metadata,ids,maximumBytes))decodeBlobs(await gitBatch(["cat-file","--batch"],batch.map(entry=>entry.id),options),batch,blobs);return new Map([...files].filter(([,id])=>blobs.has(id)).map(([file,id])=>[names.get(file),blobs.get(id)]))}catch{return new Map}}export{readIndexedReceiptBlobs};
3
+ `)),(await pending).stdout}function indexedFiles(listing,requested){const files=new Map;for(const record of listing.split("\0")){const separator=record.indexOf(" "),file=record.slice(separator+1),metadata=INDEX_ENTRY.exec(record.slice(0,separator));metadata!==null&&requested.has(file)&&files.set(file,metadata[1])}return files}function blobBatches(metadata,requested,maximumBytes){const batches=[];let batch=[],bytes=0;for(const line of metadata.trimEnd().split(/\r?\n/u)){const match=BLOB_METADATA.exec(line);if(match===null||!requested.has(match[1]))continue;const size=Number(match[2]);!Number.isSafeInteger(size)||size>maximumBytes||(batch.length>0&&bytes+size+128>maximumBytes&&(batches.push(batch),batch=[],bytes=0),batch.push({id:match[1],size}),bytes+=size+128)}return batch.length>0&&batches.push(batch),batches}function decodeBlobs(output,entries,blobs){let offset=0;for(const{id,size}of entries){const start=output.indexOf(10,offset)+1;if(start===0||output.subarray(offset,start-1).toString("ascii")!==`${id} blob ${size}`||output[start+size]!==10)throw new Error("Invalid indexed receipt batch framing");blobs.set(id,output.subarray(start,start+size).toString("utf8")),offset=start+size+1}if(offset!==output.length)throw new Error("Unexpected indexed receipt batch data")}async function readIndexedReceiptBlobs(cwd,directory,fileNames,maximumBytes,linkedDirectory,policy={}){const env=gitWorkspaceEnvironment();env.GIT_NO_REPLACE_OBJECTS="1",linkedDirectory===void 0&&process.env.GIT_INDEX_FILE!==void 0&&(env.GIT_INDEX_FILE=path.resolve(process.env.GIT_INDEX_FILE));const options={cwd,env,windowsHide:!0,timeout:1e4,maxBuffer:maximumBytes+256};try{const root=(await execFileAsync("git",["rev-parse","--show-toplevel"],{...options,encoding:"utf8"})).stdout.trim(),[workspace,physicalDirectory]=await Promise.all([realpath(root),realpath(linkedDirectory??directory)]),relative=path.relative(workspace,linkedDirectory===void 0?physicalDirectory:path.resolve(linkedDirectory)).replaceAll("\\","/");if(isPathOutsideRoot(relative))return new Map;options.cwd=workspace;const names=new Map(fileNames.map(file=>[`${relative}/${file}`,file])),listing=(await execFileAsync("git",["ls-files","--stage","-z","--",`:(literal)${relative}/`],{...options,encoding:"utf8"})).stdout,files=indexedFiles(listing,new Set(names.keys()));if(files.size===0)return new Map;const ids=new Set(files.values()),metadata=(await gitBatch(["cat-file","--batch-check"],[...ids],options)).toString("utf8"),blobs=new Map;for(const batch of blobBatches(metadata,ids,maximumBytes))decodeBlobs(await gitBatch(["cat-file","--batch"],batch.map(entry=>entry.id),options),batch,blobs);if(policy.failOnReadError&&blobs.size!==ids.size)throw new Error("Unable to read every indexed receipt blob");return new Map([...files].filter(([,id])=>blobs.has(id)).map(([file,id])=>[names.get(file),blobs.get(id)]))}catch(error){if(policy.failOnReadError)throw error;return new Map}}export{readIndexedReceiptBlobs};
4
4
  //# sourceMappingURL=indexed-receipt-blobs.js.map
@@ -13,6 +13,8 @@ lstat(receiptPath:string):Promise<Stats>;
13
13
  /** Open the candidate and return the handle used for verified reads. */
14
14
  open(receiptPath:string,flags:number):Promise<FileHandle>;
15
15
  }
16
+ /** Maximum bytes accepted for a portable merge receipt at every read/copy boundary. */
17
+ export declare const MERGE_RECEIPT_FILE_MAX_BYTES:number;
16
18
  /** Resolve the platform no-follow flag, falling back only where Node omits it. */
17
19
  export declare function resolveReceiptNoFollowFlag(constants:{
18
20
  O_NOFOLLOW?:number;
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b063a672-991a-5251-a2f3-6a1c7bcafd2f")}catch{}})();import{constants as fsConstants}from"node:fs";import{lstat,open}from"node:fs/promises";const NODE_RECEIPT_FILE_BOUNDARY={lstat,open};function resolveReceiptNoFollowFlag(constants){return constants.O_NOFOLLOW??0}async function readBoundedRegularFile(receiptPath,maximumBytes,boundary=NODE_RECEIPT_FILE_BOUNDARY){const pathStats=await boundary.lstat(receiptPath);if(pathStats.isSymbolicLink()||!pathStats.isFile()||pathStats.size>maximumBytes)return null;const handle=await boundary.open(receiptPath,fsConstants.O_RDONLY|resolveReceiptNoFollowFlag(fsConstants));try{const openedStats=await handle.stat();if(!openedStats.isFile()||openedStats.size>maximumBytes)return null;const buffer=Buffer.alloc(openedStats.size);let offset=0;for(;offset<buffer.length;){const{bytesRead}=await handle.read(buffer,offset,buffer.length-offset,offset);if(bytesRead===0)break;offset+=bytesRead}return(await handle.stat()).size===openedStats.size&&offset===openedStats.size?buffer.toString("utf8",0,offset):null}finally{await handle.close()}}export{readBoundedRegularFile,resolveReceiptNoFollowFlag};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1439aa83-2e99-5713-891d-92846e62a302")}catch{}})();import{constants as fsConstants}from"node:fs";import{lstat,open}from"node:fs/promises";const MERGE_RECEIPT_FILE_MAX_BYTES=16*1024*1024,NODE_RECEIPT_FILE_BOUNDARY={lstat,open};function resolveReceiptNoFollowFlag(constants){return constants.O_NOFOLLOW??0}async function readBoundedRegularFile(receiptPath,maximumBytes,boundary=NODE_RECEIPT_FILE_BOUNDARY){const pathStats=await boundary.lstat(receiptPath);if(pathStats.isSymbolicLink()||!pathStats.isFile()||pathStats.size>maximumBytes)return null;const handle=await boundary.open(receiptPath,fsConstants.O_RDONLY|resolveReceiptNoFollowFlag(fsConstants));try{const openedStats=await handle.stat();if(!openedStats.isFile()||openedStats.size>maximumBytes)return null;const buffer=Buffer.alloc(openedStats.size);let offset=0;for(;offset<buffer.length;){const{bytesRead}=await handle.read(buffer,offset,buffer.length-offset,offset);if(bytesRead===0)break;offset+=bytesRead}return(await handle.stat()).size===openedStats.size&&offset===openedStats.size?buffer.toString("utf8",0,offset):null}finally{await handle.close()}}export{MERGE_RECEIPT_FILE_MAX_BYTES,readBoundedRegularFile,resolveReceiptNoFollowFlag};
2
2
  //# sourceMappingURL=receipt-file-boundary.js.map
@@ -1,5 +1,5 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="21c15a9e-da38-5fec-9a7d-57556f77ce0a")}catch{}})();import{execFile}from"node:child_process";import{randomUUID}from"node:crypto";import{readdir,stat}from"node:fs/promises";import path from"node:path";import{promisify}from"node:util";import{readHistoryEntries}from"../../core/history/read.js";import{verifyHistoryChainWithVersion}from"../../core/history/replay.js";import{ensureDir,isFileAbsentError,isFileMissingError,pathExists,writeFileAtomic}from"../../core/fs/fs-utils.js";import{sha256Hex,stableStringify}from"../../core/shared/serialization.js";import{nowIso}from"../../core/shared/time.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{encodeItemScalarDecisionValue,hashItemScalarDecisionValue,isItemScalarMissingValue}from"./three-way.js";import{readBoundedRegularFile}from"./receipt-file-boundary.js";import{DURABLE_VALUE_POLICY,durableValueEvidence,isSafeReceiptId,isMergeDecisionReceipt,isPrehashedValue,normalizeLegacyReceipt,receiptCollectionValidationPath,receiptValidationError}from"./receipt-schema.js";import{isSafeReceiptId as isSafeReceiptId2}from"./receipt-schema.js";const execFileAsync=promisify(execFile),RECEIPT_FILE_MAX_BYTES=16*1024*1024,RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT=100;async function resolveReceiptDirectory(cwd){try{const{stdout}=await execFileAsync("git",["rev-parse","--path-format=absolute","--git-path","pm-merge-receipts"],{cwd,encoding:"utf8",windowsHide:!0,timeout:1e4});return stdout.trim()}catch{return null}}async function resolveTrackerRootFromItemPath(cwd,itemPath){let directory=path.dirname(path.resolve(cwd,itemPath));for(let depth=0;depth<12;depth+=1){if(await pathExists(path.join(directory,"settings.json")))return directory;const parent=path.dirname(directory);if(parent===directory)break;directory=parent}return null}function durableReceiptDirectory(pmRoot){return path.join(pmRoot,"merge-receipts")}function receiptFileName(id){return`${id}.json`}async function prepareReceiptSettlement(params){let raw;try{raw=params.readReceipt?await params.readReceipt(params.receiptPath):await readBoundedRegularFile(params.receiptPath,RECEIPT_FILE_MAX_BYTES)}catch(error){if(isFileAbsentError(error))return null;throw error}if(raw===null)throw new Error(`Receipt ${params.receiptId} settlement source is not a bounded regular file.`);let parsed;try{parsed=normalizeLegacyReceipt(JSON.parse(raw))}catch{throw new Error(`Receipt ${params.receiptId} settlement source is not valid JSON.`)}if(!isMergeDecisionReceipt(parsed,params.evidenceSource)||parsed.id!==params.receiptId||params.settlement!==void 0&&parsed.operation===void 0||path.basename(params.receiptPath)!==receiptFileName(parsed.id))throw new Error(`Receipt ${params.receiptId} settlement source failed schema or identity validation.`);const{evidence_source:_evidenceSource,...persistedReceipt}=parsed;return{path:params.receiptPath,fingerprints:receiptProvenanceFingerprints(parsed),modern:parsed.value_policy!==void 0,content:`${JSON.stringify({...persistedReceipt,state:"reconciled",reconciled_at:params.reconciledAt,...params.settlement?{settlement:params.settlement}:{}},null,2)}
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7bab0a8e-842c-5588-a0b2-22ef261ddb06")}catch{}})();import{execFile}from"node:child_process";import{randomUUID}from"node:crypto";import{readdir,realpath,stat}from"node:fs/promises";import path from"node:path";import{promisify}from"node:util";import{readHistoryEntries}from"../../core/history/read.js";import{verifyHistoryChainWithVersion}from"../../core/history/replay.js";import{ensureDir,isFileAbsentError,isFileMissingError,pathExists,writeFileAtomic}from"../../core/fs/fs-utils.js";import{sha256Hex,stableStringify}from"../../core/shared/serialization.js";import{nowIso}from"../../core/shared/time.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{encodeItemScalarDecisionValue,hashItemScalarDecisionValue,isItemScalarMissingValue}from"./three-way.js";import{MERGE_RECEIPT_FILE_MAX_BYTES as RECEIPT_FILE_MAX_BYTES,readBoundedRegularFile}from"./receipt-file-boundary.js";import{DURABLE_VALUE_POLICY,durableValueEvidence,isSafeReceiptId,isMergeDecisionReceipt,isPrehashedValue,normalizeLegacyReceipt,receiptCollectionValidationPath,receiptValidationError}from"./receipt-schema.js";import{isSafeReceiptId as isSafeReceiptId2}from"./receipt-schema.js";const execFileAsync=promisify(execFile),RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT=100;async function resolveReceiptDirectory(cwd){try{const{stdout}=await execFileAsync("git",["rev-parse","--path-format=absolute","--git-path","pm-merge-receipts"],{cwd,encoding:"utf8",windowsHide:!0,timeout:1e4});return stdout.trim()}catch{return null}}async function resolveTrackerRootFromItemPath(cwd,itemPath){let directory=path.dirname(path.resolve(cwd,itemPath));for(let depth=0;depth<12;depth+=1){if(await pathExists(path.join(directory,"settings.json")))return directory;const parent=path.dirname(directory);if(parent===directory)break;directory=parent}return null}function durableReceiptDirectory(pmRoot){return path.join(pmRoot,"merge-receipts")}function receiptFileName(id){return`${id}.json`}async function prepareReceiptSettlement(params){let raw;try{raw=params.readReceipt?await params.readReceipt(params.receiptPath):await readBoundedRegularFile(params.receiptPath,RECEIPT_FILE_MAX_BYTES)}catch(error){if(isFileAbsentError(error))return null;throw error}if(raw===null)throw new Error(`Receipt ${params.receiptId} settlement source is not a bounded regular file.`);let parsed;try{parsed=normalizeLegacyReceipt(JSON.parse(raw))}catch{throw new Error(`Receipt ${params.receiptId} settlement source is not valid JSON.`)}if(!isMergeDecisionReceipt(parsed,params.evidenceSource)||parsed.id!==params.receiptId||params.settlement!==void 0&&parsed.operation===void 0||path.basename(params.receiptPath)!==receiptFileName(parsed.id))throw new Error(`Receipt ${params.receiptId} settlement source failed schema or identity validation.`);const{evidence_source:_evidenceSource,...persistedReceipt}=parsed;return{path:params.receiptPath,fingerprints:receiptProvenanceFingerprints(parsed),modern:parsed.value_policy!==void 0,content:`${JSON.stringify({...persistedReceipt,state:"reconciled",reconciled_at:params.reconciledAt,...params.settlement?{settlement:params.settlement}:{}},null,2)}
2
2
  `}}function receiptProvenanceFingerprint(receipt,legacy=!1){return sha256Hex(stableStringify({id:receipt.id,item_id:receipt.item_id,item_path:receipt.item_path,created_at:receipt.created_at,requested_preference:receipt.requested_preference??receipt.preferred??"ours",requested_preference_applied:receipt.requested_preference_applied??(receipt.conflict_resolution??"preferred_side")==="preferred_side",conflict_resolution:receipt.conflict_resolution??"preferred_side",fields_from_theirs:receipt.fields_from_theirs,union_fields:receipt.union_fields,merged_field_hashes:receipt.merged_field_hashes??null,operation:receipt.operation??null,decisions:legacy?receipt.decisions.map(decision=>({field:decision.field,retained_hash:sha256Hex(stableStringify(encodeItemScalarDecisionValue(decision.retained))),discarded_hash:sha256Hex(stableStringify(encodeItemScalarDecisionValue(decision.discarded)))})):summarizeMergeReceipt(receipt).decisions}))}function receiptProvenanceFingerprints(receipt){const fingerprints=[receiptProvenanceFingerprint(receipt)];return receipt.value_policy===void 0&&(receipt.value_availability===void 0||receipt.value_availability==="clone_local")&&fingerprints.push(receiptProvenanceFingerprint(receipt,!0)),fingerprints}function mergeReceiptCopyLifecycle(local,durable){const{reconciled_at:_reconciledAt,settlement:_settlement,...pendingLocal}=local;return durable.state==="reconciled"?{...pendingLocal,state:"reconciled",reconciled_at:durable.reconciled_at,...durable.settlement===void 0?{}:{settlement:durable.settlement}}:{...pendingLocal,state:"pending"}}async function retainAuditedDurableSettlements(receipts,durableReceipts,pmRoot){const histories=new Map;for(const durable of durableReceipts){const receipt=receipts.get(durable.id);if(durable.state!=="reconciled"||receipt===void 0)continue;let entries=histories.get(receipt.item_id);if(entries===void 0){try{entries=await readHistoryEntries(path.join(pmRoot,"history",`${receipt.item_id}.jsonl`),receipt.item_id),verifyHistoryChainWithVersion(entries).ok||(entries=[])}catch{entries=[]}histories.set(receipt.item_id,entries)}const abandoned=receipt.settlement==="original_git_state_restored",expected=stableStringify({...summarizeMergeReceipt(receipt),...abandoned?{reason:receipt.settlement,operation:receipt.operation}:{}});if(!entries.some(entry=>{if(entry.op!=="merge_reconcile"||entry.record_hash_version===void 0)return!1;const merge=entry.context?.merge;if(typeof merge!="object"||merge===null||Array.isArray(merge))return!1;const summaries=merge[abandoned?"abandoned_receipts":"receipts"];return Array.isArray(summaries)&&summaries.some(summary=>stableStringify(summary)===expected)})){const{reconciled_at:_reconciledAt,settlement:_settlement,...pending}=receipt;receipts.set(receipt.id,{...pending,state:"pending"})}}}function summarizeMergeReceipt(receipt){return{receipt_id:receipt.id,item_id:receipt.item_id,item_path:receipt.item_path,conflict_fields:receipt.decisions.map(decision=>decision.field),fields_from_theirs:receipt.fields_from_theirs,union_fields:receipt.union_fields,requested_preference:receipt.requested_preference??receipt.preferred??"ours",requested_preference_applied:receipt.requested_preference_applied??(receipt.conflict_resolution??"preferred_side")==="preferred_side",conflict_resolution:receipt.conflict_resolution,decisions:receipt.decisions.map(decision=>({field:decision.field,retained_hash:isPrehashedValue(decision.retained)??hashItemScalarDecisionValue(isItemScalarMissingValue(decision.retained)?void 0:decision.retained),discarded_hash:isPrehashedValue(decision.discarded)??hashItemScalarDecisionValue(isItemScalarMissingValue(decision.discarded)?void 0:decision.discarded)}))}}async function writeMergeReceipt(params){const directory=await resolveReceiptDirectory(params.cwd);if(directory===null)return null;const trimmedItemPath=params.itemPath.trim(),itemPath=/^(['"])(.*)\1$/su.exec(trimmedItemPath)?.[2]??trimmedItemPath,itemId=path.basename(itemPath,path.extname(itemPath)),receipt={version:1,id:randomUUID(),item_path:itemPath.replaceAll("\\","/"),item_id:itemId,requested_preference:params.preferred,requested_preference_applied:(params.conflictResolution??"preferred_side")==="preferred_side",conflict_resolution:params.conflictResolution??"preferred_side",fields_from_theirs:[...params.fieldsFromTheirs],union_fields:[...params.unionFields],...params.mergedFieldHashes?{merged_field_hashes:{...params.mergedFieldHashes}}:{},decisions:structuredClone(params.decisions).map(decision=>({...decision,base:encodeItemScalarDecisionValue(decision.base),ours:encodeItemScalarDecisionValue(decision.ours),theirs:encodeItemScalarDecisionValue(decision.theirs),retained:encodeItemScalarDecisionValue(decision.retained),discarded:encodeItemScalarDecisionValue(decision.discarded)})),...params.operation?{operation:params.operation}:{},state:"pending",created_at:nowIso(),value_availability:"clone_local"};await ensureDir(directory),await writeFileAtomic(path.join(directory,receiptFileName(receipt.id)),`${JSON.stringify(receipt,null,2)}
3
3
  `);const trackerRoot=await resolveTrackerRootFromItemPath(params.cwd,receipt.item_path);if(trackerRoot!==null){const durableDirectory=durableReceiptDirectory(trackerRoot),durableDecisions=receipt.decisions.map(decision=>({field:decision.field,base:null,ours:null,theirs:null,retained:durableValueEvidence(decision.field,decision.retained),discarded:durableValueEvidence(decision.field,decision.discarded),retained_side:decision.retained_side,resolution_basis:decision.resolution_basis})),inlineValueCount=durableDecisions.reduce((count,decision)=>count+(Object.hasOwn(decision.retained,"pm_value")?1:0)+(Object.hasOwn(decision.discarded,"pm_value")?1:0),0),durableReceipt={...receipt,decisions:durableDecisions,value_availability:inlineValueCount===0?"hash_only":inlineValueCount===durableDecisions.length*2?"bounded_inline":"mixed",value_policy:DURABLE_VALUE_POLICY};await ensureDir(durableDirectory),await writeFileAtomic(path.join(durableDirectory,receiptFileName(receipt.id)),`${JSON.stringify(durableReceipt,null,2)}
4
- `)}return receipt}async function readReceiptsFromDirectory(directory,evidenceSource){let names;try{names=await readdir(directory)}catch(error){const absent=await receiptDirectoryFailureMeansAbsent(directory,error);return{receipts:[],invalid_evidence_count:absent?0:1,invalid_evidence:absent?[]:[{evidence_source:evidenceSource,reason:"directory_unreadable"}],invalid_evidence_truncated:!1}}const receipts=[];let invalidEvidenceCount=0;const invalidEvidence=[],recordInvalidEvidence=(name,candidate)=>{if(invalidEvidenceCount+=1,invalidEvidence.length>=RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT)return;const receiptId=name.slice(0,-5);invalidEvidence.push({evidence_source:evidenceSource,reason:candidate.reason,...candidate.validation_path?{validation_path:candidate.validation_path}:{},...candidate.validation_error===void 0?{}:{validation_error:candidate.validation_error},...isSafeReceiptId(receiptId)?{receipt_id:receiptId}:{candidate_name_hash:sha256Hex(name)}})};for(const name of names.sort((left,right)=>left.localeCompare(right))){if(!name.endsWith(".json"))continue;const candidate=await inspectReceiptCandidate(directory,name,evidenceSource);candidate.receipt!==void 0?receipts.push(candidate.receipt):recordInvalidEvidence(name,candidate)}return{receipts,invalid_evidence_count:invalidEvidenceCount,invalid_evidence:invalidEvidence,invalid_evidence_truncated:invalidEvidenceCount>invalidEvidence.length}}async function receiptDirectoryFailureMeansAbsent(directory,error,inspectAncestor=stat){if(!isFileMissingError(error))return!1;let ancestor=directory;for(;;)try{return(await inspectAncestor(ancestor)).isDirectory()}catch(ancestorError){if(!isFileMissingError(ancestorError))return!1;const parent=path.dirname(ancestor);if(parent===ancestor)return!0;ancestor=parent}}async function inspectReceiptCandidate(directory,name,evidenceSource){let raw;try{raw=await readBoundedRegularFile(path.join(directory,name),RECEIPT_FILE_MAX_BYTES)}catch{return{reason:"candidate_unreadable"}}if(raw===null)return{reason:"candidate_not_bounded_regular_file"};let parsed;try{parsed=normalizeLegacyReceipt(JSON.parse(raw))}catch{return{reason:"candidate_invalid_json"}}const validationError=receiptValidationError(parsed,evidenceSource);if(validationError!==null)return{reason:new Set(["receipt_id","item_id","item_path"]).has(validationError)?"identity_invalid":"schema_invalid",validation_error:validationError,...validationError==="collections"?{validation_path:receiptCollectionValidationPath(parsed)}:{}};const receipt=parsed;if(name!==receiptFileName(receipt.id))return{reason:"identity_invalid",validation_error:"filename"};const{preferred:legacyPreference,evidence_source:_serializedEvidenceSource,...receiptWithoutRuntimeKeys}=receipt;return{receipt:{...receiptWithoutRuntimeKeys,requested_preference:receipt.requested_preference??legacyPreference??"ours",requested_preference_applied:receipt.requested_preference_applied??(receipt.conflict_resolution??"preferred_side")==="preferred_side",conflict_resolution:receipt.conflict_resolution??"preferred_side",evidence_source:evidenceSource}}}async function indexedDurableReceiptIds(cwd,trackerRoot,receipts){if(receipts.length===0)return[];const{readIndexedReceiptBlobs}=await import("./indexed-receipt-blobs.js"),blobs=await readIndexedReceiptBlobs(cwd,durableReceiptDirectory(trackerRoot),receipts.map(receipt=>receiptFileName(receipt.id)),RECEIPT_FILE_MAX_BYTES);return receipts.flatMap(receipt=>{try{const indexed=normalizeLegacyReceipt(JSON.parse(blobs.get(receiptFileName(receipt.id))??""));if(receiptValidationError(indexed,"durable")!==null)return[];const stored=indexed,fingerprints=receiptProvenanceFingerprints(receipt);return stored.id===receipt.id&&receiptProvenanceFingerprints(stored).some(fingerprint=>fingerprints.includes(fingerprint))?[receipt.id]:[]}catch{return[]}})}async function inspectMergeReceiptEvidence(cwd,options={}){const directory=await resolveReceiptDirectory(cwd),local=directory===null?{receipts:[],invalid_evidence_count:0,invalid_evidence:[],invalid_evidence_truncated:!1}:await readReceiptsFromDirectory(directory,"clone_local"),trackerRoot=options.pmRoot??path.join(cwd,".agents","pm"),durable=await readReceiptsFromDirectory(durableReceiptDirectory(trackerRoot),"durable"),receipts=new Map(durable.receipts.map(receipt=>[receipt.id,receipt]));let divergentCopyCount=0;const divergentCopyEvidence=[];for(const receipt of local.receipts){const durableCopy=receipts.get(receipt.id);if(durableCopy!==void 0&&!(durableCopy.value_policy===void 0?receiptProvenanceFingerprints(receipt):[receiptProvenanceFingerprint(receipt)]).includes(receiptProvenanceFingerprint(durableCopy))){receipts.delete(receipt.id),divergentCopyCount+=1,local.invalid_evidence.length+durable.invalid_evidence.length+divergentCopyEvidence.length<RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT&&divergentCopyEvidence.push({evidence_source:"clone_local_and_durable",reason:"copy_provenance_mismatch",receipt_id:receipt.id});continue}receipts.set(receipt.id,durableCopy===void 0?receipt:mergeReceiptCopyLifecycle(receipt,durableCopy))}await retainAuditedDurableSettlements(receipts,durable.receipts,trackerRoot);const validDurable=durable.receipts.filter(receipt=>receipts.has(receipt.id)),indexedIds=new Set(await indexedDurableReceiptIds(cwd,trackerRoot,validDurable));return{durable_receipt_ids:[...indexedIds],unstaged_durable_receipt_ids:validDurable.filter(receipt=>!indexedIds.has(receipt.id)).map(receipt=>receipt.id),receipts:[...receipts.values()].filter(receipt=>(options.includeReconciled||receipt.state==="pending")&&(options.includeLossless!==!1||receipt.decisions.length>0)).sort((left,right)=>left.created_at.localeCompare(right.created_at)),invalid_evidence_count:local.invalid_evidence_count+durable.invalid_evidence_count+divergentCopyCount,invalid_evidence:[...local.invalid_evidence,...durable.invalid_evidence,...divergentCopyEvidence].slice(0,RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT),invalid_evidence_truncated:local.invalid_evidence_truncated||durable.invalid_evidence_truncated||local.invalid_evidence_count+durable.invalid_evidence_count+divergentCopyCount>RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT,clone_local_evidence_resolved:directory!==null}}async function listMergeReceipts(cwd,options={}){return(await inspectMergeReceiptEvidence(cwd,options)).receipts}function assertPortableMergeReceiptReferences(receiptIds,evidence,preview){const portable=new Set([...evidence?.durable_receipt_ids??[],...preview?evidence?.unstaged_durable_receipt_ids??[]:[]]),nonportable=[...receiptIds].filter(id=>!portable.has(id));if(nonportable.length!==0)throw new PmCliError(`Merge reconciliation refused ${nonportable.length} receipt(s) without validated tracked evidence; no audit history was written.`,EXIT_CODE.CONFLICT,{code:"merge_receipt_not_portable",required:"Every receipt cited by tracked history must have matching validated evidence in the Git index before any audit write.",recovery:{suggested_retry:"pm merge report --include-reconciled"},nextSteps:["Stage valid project sidecars after reviewing git diff, or use pm merge reconcile --stage for guarded lossless settlement. Restore missing original counterparts or preserve unrelated stale receipts outside the active store. Never copy private clone-local values into tracked evidence. Then rerun pm merge reconcile --dry-run."]})}function partitionMergeReceipts(receipts){const pendingDecisions=[],lossless=[];for(const receipt of receipts)(Array.isArray(receipt.decisions)&&receipt.decisions.length>0?pendingDecisions:lossless).push(receipt);return{pendingDecisions,lossless}}async function markMergeReceiptReconciled(cwd,receipt,options={}){const directory=await resolveReceiptDirectory(cwd);if(directory===null){if(options.requireExisting===!0)throw new Error(`Receipt ${receipt.id} disappeared before settlement.`);return}const receiptEvidenceSource=receipt.evidence_source??"clone_local";if(!isMergeDecisionReceipt(receipt,receiptEvidenceSource))throw new Error("Merge receipt trusted settlement input failed schema validation.");const reconciledAt=nowIso(),localPath=path.join(directory,receiptFileName(receipt.id)),trackerRoot=await resolveTrackerRootFromItemPath(cwd,receipt.item_path),durablePath=trackerRoot===null?null:path.join(durableReceiptDirectory(trackerRoot),receiptFileName(receipt.id)),writes=(await Promise.all([prepareReceiptSettlement({receiptPath:localPath,receiptId:receipt.id,evidenceSource:"clone_local",reconciledAt,settlement:options.settlement}),...durablePath===null?[]:[prepareReceiptSettlement({receiptPath:durablePath,receiptId:receipt.id,evidenceSource:"durable",reconciledAt,settlement:options.settlement})]])).filter(entry=>entry!==null);if(writes.length===0){if(options.requireExisting===!0)throw new Error(`Receipt ${receipt.id} disappeared before settlement.`);return}if(!(writes.some(write=>write.modern)?[receiptProvenanceFingerprint(receipt)]:receiptProvenanceFingerprints(receipt)).some(fingerprint=>writes.every(write=>write.fingerprints.includes(fingerprint))))throw new Error(`Receipt ${receipt.id} settlement copies disagree on immutable merge provenance.`);for(const write of writes)await writeFileAtomic(write.path,write.content)}async function runMergeReceiptReport(options){const receipts=await listMergeReceipts(options.cwd??process.cwd(),{...options,includeLossless:!0});return{ok:!0,count:receipts.length,receipts,generated_at:nowIso()}}async function runMergeReceiptEvidenceReport(options){const evidence=await inspectMergeReceiptEvidence(options.cwd??process.cwd(),{...options,includeLossless:!0}),complete=evidence.clone_local_evidence_resolved===!0&&evidence.invalid_evidence_count===0;return{ok:complete,complete,count:evidence.receipts.length,invalid_evidence_count:evidence.invalid_evidence_count,invalid_evidence:evidence.invalid_evidence,invalid_evidence_truncated:evidence.invalid_evidence_truncated,clone_local_evidence_resolved:evidence.clone_local_evidence_resolved,receipts:evidence.receipts,generated_at:nowIso()}}const _testOnlyMergeReceipts={prepareReceiptSettlement,receiptDirectoryFailureMeansAbsent};export{_testOnlyMergeReceipts,assertPortableMergeReceiptReferences,inspectMergeReceiptEvidence,isSafeReceiptId2 as isSafeReceiptId,listMergeReceipts,markMergeReceiptReconciled,partitionMergeReceipts,runMergeReceiptEvidenceReport,runMergeReceiptReport,summarizeMergeReceipt,writeMergeReceipt};
4
+ `)}return receipt}async function readReceiptsFromDirectory(directory,evidenceSource){let names;try{names=await readdir(directory)}catch(error){const absent=await receiptDirectoryFailureMeansAbsent(directory,error);return{receipts:[],invalid_evidence_count:absent?0:1,invalid_evidence:absent?[]:[{evidence_source:evidenceSource,reason:"directory_unreadable"}],invalid_evidence_truncated:!1}}const receipts=[];let invalidEvidenceCount=0;const invalidEvidence=[],recordInvalidEvidence=(name,candidate)=>{if(invalidEvidenceCount+=1,invalidEvidence.length>=RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT)return;const receiptId=name.slice(0,-5);invalidEvidence.push({evidence_source:evidenceSource,reason:candidate.reason,...candidate.validation_path?{validation_path:candidate.validation_path}:{},...candidate.validation_error===void 0?{}:{validation_error:candidate.validation_error},...isSafeReceiptId(receiptId)?{receipt_id:receiptId}:{candidate_name_hash:sha256Hex(name)}})};for(const name of names.sort((left,right)=>left.localeCompare(right))){if(!name.endsWith(".json"))continue;const candidate=await inspectReceiptCandidate(directory,name,evidenceSource);candidate.receipt!==void 0?receipts.push(candidate.receipt):recordInvalidEvidence(name,candidate)}return{receipts,invalid_evidence_count:invalidEvidenceCount,invalid_evidence:invalidEvidence,invalid_evidence_truncated:invalidEvidenceCount>invalidEvidence.length}}async function receiptDirectoryFailureMeansAbsent(directory,error,inspectAncestor=stat){if(!isFileMissingError(error))return!1;let ancestor=directory;for(;;)try{return(await inspectAncestor(ancestor)).isDirectory()}catch(ancestorError){if(!isFileMissingError(ancestorError))return!1;const parent=path.dirname(ancestor);if(parent===ancestor)return!0;ancestor=parent}}async function inspectReceiptCandidate(directory,name,evidenceSource){let raw;try{raw=await readBoundedRegularFile(path.join(directory,name),RECEIPT_FILE_MAX_BYTES)}catch{return{reason:"candidate_unreadable"}}if(raw===null)return{reason:"candidate_not_bounded_regular_file"};let parsed;try{parsed=normalizeLegacyReceipt(JSON.parse(raw))}catch{return{reason:"candidate_invalid_json"}}const validationError=receiptValidationError(parsed,evidenceSource);if(validationError!==null)return{reason:new Set(["receipt_id","item_id","item_path"]).has(validationError)?"identity_invalid":"schema_invalid",validation_error:validationError,...validationError==="collections"?{validation_path:receiptCollectionValidationPath(parsed)}:{}};const receipt=parsed;if(name!==receiptFileName(receipt.id))return{reason:"identity_invalid",validation_error:"filename"};const{preferred:legacyPreference,evidence_source:_serializedEvidenceSource,...receiptWithoutRuntimeKeys}=receipt;return{receipt:{...receiptWithoutRuntimeKeys,requested_preference:receipt.requested_preference??legacyPreference??"ours",requested_preference_applied:receipt.requested_preference_applied??(receipt.conflict_resolution??"preferred_side")==="preferred_side",conflict_resolution:receipt.conflict_resolution??"preferred_side",evidence_source:evidenceSource}}}async function indexedDurableReceiptIds(cwd,trackerRoot,receipts){if(receipts.length===0)return[];const{readIndexedReceiptBlobs}=await import("./indexed-receipt-blobs.js"),linked=await linkedReceiptIndexContext(trackerRoot),blobs=await readIndexedReceiptBlobs(linked?.workspace??cwd,durableReceiptDirectory(trackerRoot),receipts.map(receipt=>receiptFileName(receipt.id)),RECEIPT_FILE_MAX_BYTES,linked?.directory);return receipts.flatMap(receipt=>{try{const indexed=normalizeLegacyReceipt(JSON.parse(blobs.get(receiptFileName(receipt.id))??""));if(receiptValidationError(indexed,"durable")!==null)return[];const stored=indexed,fingerprints=receiptProvenanceFingerprints(receipt);return stored.id===receipt.id&&receiptProvenanceFingerprints(stored).some(fingerprint=>fingerprints.includes(fingerprint))?[receipt.id]:[]}catch{return[]}})}async function linkedReceiptIndexContext(trackerRoot){const workspace=process.env.PM_SOURCE_WORKSPACE_ROOT,source=process.env.PM_SOURCE_PM_PATH,active=process.env.PM_PATH;if(!(process.env.PM_SOURCE_CONTEXT_ACCESS!=="read_only"||!workspace||!source||!active))try{if(await realpath(trackerRoot)!==await realpath(active))return;const relative=path.relative(await realpath(workspace),await realpath(source)),outside=relative===".."||relative.startsWith(`..${path.sep}`)||path.isAbsolute(relative),directory=durableReceiptDirectory(outside?path.join(await realpath(workspace),".agents","pm"):await realpath(source)),[boundDirectory,sourceDirectory]=await Promise.all([realpath(directory),realpath(durableReceiptDirectory(source))]);return boundDirectory!==sourceDirectory?void 0:{workspace,directory}}catch{return}}async function inspectMergeReceiptEvidence(cwd,options={}){const directory=await resolveReceiptDirectory(cwd),local=directory===null?{receipts:[],invalid_evidence_count:0,invalid_evidence:[],invalid_evidence_truncated:!1}:await readReceiptsFromDirectory(directory,"clone_local"),trackerRoot=options.pmRoot??path.join(cwd,".agents","pm"),durable=await readReceiptsFromDirectory(durableReceiptDirectory(trackerRoot),"durable"),receipts=new Map(durable.receipts.map(receipt=>[receipt.id,receipt]));let divergentCopyCount=0;const divergentCopyEvidence=[];for(const receipt of local.receipts){const durableCopy=receipts.get(receipt.id);if(durableCopy!==void 0&&!(durableCopy.value_policy===void 0?receiptProvenanceFingerprints(receipt):[receiptProvenanceFingerprint(receipt)]).includes(receiptProvenanceFingerprint(durableCopy))){receipts.delete(receipt.id),divergentCopyCount+=1,local.invalid_evidence.length+durable.invalid_evidence.length+divergentCopyEvidence.length<RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT&&divergentCopyEvidence.push({evidence_source:"clone_local_and_durable",reason:"copy_provenance_mismatch",receipt_id:receipt.id});continue}receipts.set(receipt.id,durableCopy===void 0?receipt:mergeReceiptCopyLifecycle(receipt,durableCopy))}await retainAuditedDurableSettlements(receipts,durable.receipts,trackerRoot);const validDurable=durable.receipts.filter(receipt=>receipts.has(receipt.id)),indexedIds=new Set(await indexedDurableReceiptIds(cwd,trackerRoot,validDurable));return{durable_receipt_ids:[...indexedIds],unstaged_durable_receipt_ids:validDurable.filter(receipt=>!indexedIds.has(receipt.id)).map(receipt=>receipt.id),receipts:[...receipts.values()].filter(receipt=>(options.includeReconciled||receipt.state==="pending")&&(options.includeLossless!==!1||receipt.decisions.length>0)).sort((left,right)=>left.created_at.localeCompare(right.created_at)),invalid_evidence_count:local.invalid_evidence_count+durable.invalid_evidence_count+divergentCopyCount,invalid_evidence:[...local.invalid_evidence,...durable.invalid_evidence,...divergentCopyEvidence].slice(0,RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT),invalid_evidence_truncated:local.invalid_evidence_truncated||durable.invalid_evidence_truncated||local.invalid_evidence_count+durable.invalid_evidence_count+divergentCopyCount>RECEIPT_INVALID_EVIDENCE_DETAIL_LIMIT,clone_local_evidence_resolved:directory!==null}}async function listMergeReceipts(cwd,options={}){return(await inspectMergeReceiptEvidence(cwd,options)).receipts}function assertPortableMergeReceiptReferences(receiptIds,evidence,preview){const portable=new Set([...evidence?.durable_receipt_ids??[],...preview?evidence?.unstaged_durable_receipt_ids??[]:[]]),nonportable=[...receiptIds].filter(id=>!portable.has(id));if(nonportable.length!==0)throw new PmCliError(`Merge reconciliation refused ${nonportable.length} receipt(s) without validated tracked evidence; no audit history was written.`,EXIT_CODE.CONFLICT,{code:"merge_receipt_not_portable",required:"Every receipt cited by tracked history must have matching validated evidence in the Git index before any audit write.",recovery:{suggested_retry:"pm merge report --include-reconciled"},nextSteps:["Stage valid project sidecars after reviewing git diff, or use pm merge reconcile --stage for guarded lossless settlement. Restore missing original counterparts or preserve unrelated stale receipts outside the active store. Never copy private clone-local values into tracked evidence. Then rerun pm merge reconcile --dry-run."]})}function partitionMergeReceipts(receipts){const pendingDecisions=[],lossless=[];for(const receipt of receipts)(Array.isArray(receipt.decisions)&&receipt.decisions.length>0?pendingDecisions:lossless).push(receipt);return{pendingDecisions,lossless}}async function markMergeReceiptReconciled(cwd,receipt,options={}){const directory=await resolveReceiptDirectory(cwd);if(directory===null){if(options.requireExisting===!0)throw new Error(`Receipt ${receipt.id} disappeared before settlement.`);return}const receiptEvidenceSource=receipt.evidence_source??"clone_local";if(!isMergeDecisionReceipt(receipt,receiptEvidenceSource))throw new Error("Merge receipt trusted settlement input failed schema validation.");const reconciledAt=nowIso(),localPath=path.join(directory,receiptFileName(receipt.id)),trackerRoot=await resolveTrackerRootFromItemPath(cwd,receipt.item_path),durablePath=trackerRoot===null?null:path.join(durableReceiptDirectory(trackerRoot),receiptFileName(receipt.id)),writes=(await Promise.all([prepareReceiptSettlement({receiptPath:localPath,receiptId:receipt.id,evidenceSource:"clone_local",reconciledAt,settlement:options.settlement}),...durablePath===null?[]:[prepareReceiptSettlement({receiptPath:durablePath,receiptId:receipt.id,evidenceSource:"durable",reconciledAt,settlement:options.settlement})]])).filter(entry=>entry!==null);if(writes.length===0){if(options.requireExisting===!0)throw new Error(`Receipt ${receipt.id} disappeared before settlement.`);return}if(!(writes.some(write=>write.modern)?[receiptProvenanceFingerprint(receipt)]:receiptProvenanceFingerprints(receipt)).some(fingerprint=>writes.every(write=>write.fingerprints.includes(fingerprint))))throw new Error(`Receipt ${receipt.id} settlement copies disagree on immutable merge provenance.`);for(const write of writes)await writeFileAtomic(write.path,write.content)}async function runMergeReceiptReport(options){const receipts=await listMergeReceipts(options.cwd??process.cwd(),{...options,includeLossless:!0});return{ok:!0,count:receipts.length,receipts,generated_at:nowIso()}}async function runMergeReceiptEvidenceReport(options){const evidence=await inspectMergeReceiptEvidence(options.cwd??process.cwd(),{...options,includeLossless:!0}),complete=evidence.clone_local_evidence_resolved===!0&&evidence.invalid_evidence_count===0;return{ok:complete,complete,count:evidence.receipts.length,invalid_evidence_count:evidence.invalid_evidence_count,invalid_evidence:evidence.invalid_evidence,invalid_evidence_truncated:evidence.invalid_evidence_truncated,clone_local_evidence_resolved:evidence.clone_local_evidence_resolved,receipts:evidence.receipts,generated_at:nowIso()}}const _testOnlyMergeReceipts={prepareReceiptSettlement,receiptDirectoryFailureMeansAbsent};export{_testOnlyMergeReceipts,assertPortableMergeReceiptReferences,inspectMergeReceiptEvidence,isSafeReceiptId2 as isSafeReceiptId,listMergeReceipts,markMergeReceiptReconciled,partitionMergeReceipts,runMergeReceiptEvidenceReport,runMergeReceiptReport,summarizeMergeReceipt,writeMergeReceipt};
5
5
  //# sourceMappingURL=receipts.js.map
@@ -17,6 +17,8 @@ stream_offset:number;
17
17
  }
18
18
  /** Workspace and filtering controls for mutation-event reads. */
19
19
  export interface ListMutationEventsOptions {
20
+ /** Verify complete selected authoritative chains before filtering or pagination. */
21
+ verify?:boolean;
20
22
  /** Explicit tracker root, equivalent to global `--pm-path`. */
21
23
  pmRoot?:string;
22
24
  /** Working directory used to discover the nearest tracker. */
@@ -71,6 +73,10 @@ provenance?:HistoryProvenanceRow;
71
73
  }
72
74
  /** Bounded event page returned by {@link listMutationEvents}. */
73
75
  export interface MutationEventPage {
76
+ /** Chain verification is opt-in; content cache validation alone is not proof. */
77
+ integrity:"verified" | "unverified";
78
+ /** Complete selected authoritative streams checked when verification is requested. */
79
+ verified_streams?:number;
74
80
  /** Ordered mutation events. */
75
81
  events:MutationEvent[];
76
82
  /** Number of events in this page. */
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="62e71554-f62d-55c5-811c-198f573d9426")}catch{}})();import{assertInitializedTracker}from"./environment/tracker-preflight.js";import{createHash}from"node:crypto";import{setTimeout as delay}from"node:timers/promises";import{readHistoryEventIndexPage,queryHistoryEventStreams,rebuildHistoryEventIndex}from"../core/history/event-index.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{stableStringify}from"../core/shared/serialization.js";import{toErrorMessage}from"../core/shared/primitives.js";import{resolvePmRoot}from"../core/store/paths.js";import{readSettings}from"../core/store/settings.js";import{parseHistoryProvenanceFilters,projectHistoryProvenance,resolveHistoryProvenanceDimensions,summarizeHistoryProvenance}from"./history-provenance.js";const MUTATION_EVENT_CURSOR_VERSION=1,MUTATION_EVENT_CURSOR_PATTERN=/^[A-Za-z0-9_-]+$/,DEFAULT_EVENT_LIMIT=100,MAX_EVENT_LIMIT=1e3,DEFAULT_FOLLOW_INTERVAL_MS=250,MUTATION_EVENT_WIRE_BUDGET=Object.freeze({batch_to_row_ratio:.6,fixture_events:200});function normalizeFilter(value){if(value===void 0)return;const values=typeof value=="string"?[value]:value,normalized=[...new Set(values.flatMap(entry=>entry.split(",")).map(entry=>entry.trim()).filter(Boolean))].sort((left,right)=>left.localeCompare(right));return normalized.length===0?void 0:normalized}function eventQueryFingerprint(options){return createHash("sha256").update(stableStringify({type:normalizeFilter(options.type),author:normalizeFilter(options.author),item:normalizeFilter(options.item),harness:normalizeFilter(options.harness),agent_instance:normalizeFilter(options.agentInstance),provenance:normalizeFilter(options.provenanceFilter)})).digest("hex").slice(0,24)}function encodeMutationEventCursor(event,fingerprint){const envelope={version:MUTATION_EVENT_CURSOR_VERSION,fingerprint,ts:event.entry.ts,stream_id:event.stream_id,stream_offset:event.stream_offset};return Buffer.from(JSON.stringify(envelope),"utf8").toString("base64url")}function isMutationEventCursorEnvelope(value,fingerprint){if(typeof value!="object"||value===null)return!1;const candidate=value;return[candidate.version===MUTATION_EVENT_CURSOR_VERSION,candidate.fingerprint===fingerprint,typeof candidate.ts=="string",typeof candidate.stream_id=="string",typeof candidate.stream_offset=="number",Number.isSafeInteger(candidate.stream_offset),Number(candidate.stream_offset)>=0].every(Boolean)}function decodeMutationEventCursor(cursor,fingerprint){const normalized=cursor.trim();if(normalized.length===0||normalized.length>4096||!MUTATION_EVENT_CURSOR_PATTERN.test(normalized))throw new PmCliError("Invalid mutation event cursor.",EXIT_CODE.USAGE,{code:"invalid_event_cursor"});let parsed;try{parsed=JSON.parse(Buffer.from(normalized,"base64url").toString("utf8"))}catch{throw new PmCliError("Invalid mutation event cursor.",EXIT_CODE.USAGE,{code:"invalid_event_cursor"})}if(!isMutationEventCursorEnvelope(parsed,fingerprint))throw new PmCliError("Mutation event cursor does not match this query.",EXIT_CODE.USAGE,{code:"event_cursor_query_mismatch"});return parsed}function parseMutationEventLimit(value){const limit=value??DEFAULT_EVENT_LIMIT;if(!Number.isSafeInteger(limit)||limit<0||limit>MAX_EVENT_LIMIT)throw new PmCliError(`Mutation event limit must be an integer from 0 to ${MAX_EVENT_LIMIT}.`,EXIT_CODE.USAGE,{code:"invalid_event_limit"});return limit}function isAbortError(error){return error instanceof Error&&error.name==="AbortError"}function resolveMutationEventStart(sinceValue,fingerprint){const since=sinceValue?.trim();if(!since)return{};if(MUTATION_EVENT_CURSOR_PATTERN.test(since))return{cursor:decodeMutationEventCursor(since,fingerprint)};const milliseconds=Date.parse(since);if(!Number.isFinite(milliseconds))throw new PmCliError("Mutation event --since must be an event cursor or ISO timestamp.",EXIT_CODE.USAGE,{code:"invalid_event_since"});return{sinceTimestamp:new Date(milliseconds).toISOString()}}async function resolveIndexedMutationEvents(pmRoot,query){const attempt=await readHistoryEventIndexPage(pmRoot,query);let indexed=attempt.result;if(indexed===null&&attempt.repair_needed)try{await rebuildHistoryEventIndex(pmRoot,{lockWaitMs:0})&&(indexed=(await readHistoryEventIndexPage(pmRoot,query)).result)}catch{}if(indexed===null)try{return{...await queryHistoryEventStreams(pmRoot,query),source:"authoritative_history"}}catch(error){throw error instanceof PmCliError?error:new PmCliError("Mutation events are unavailable because authoritative history could not be read.",EXIT_CODE.GENERIC_FAILURE,{code:"event_index_unavailable",why:toErrorMessage(error)})}return{...indexed,source:"derived_index"}}function resolveMutationEventCursorMode(value){const cursorMode=value??"batch";if(cursorMode!=="batch"&&cursorMode!=="row")throw new PmCliError("Mutation event cursor mode must be batch or row.",EXIT_CODE.USAGE,{code:"invalid_event_cursor_mode"});return cursorMode}async function listMutationEvents(options={}){const pmRoot=resolvePmRoot(options.cwd??process.cwd(),options.pmRoot);await assertInitializedTracker(pmRoot);const requestedLimit=parseMutationEventLimit(options.limit),cursorMode=resolveMutationEventCursorMode(options.cursorMode);if(options.full===!0&&options.provenance===!0)throw new PmCliError("Mutation event projections are mutually exclusive. Use --provenance or --full.",EXIT_CODE.USAGE);const settings=await readSettings(pmRoot),vocabulary=settings.agent_identity.identity_vocabulary,dimensions=resolveHistoryProvenanceDimensions(settings.agent_identity.harness_signals),provenancePredicates=parseHistoryProvenanceFilters({provenance:options.provenanceFilter},dimensions),harnesses=normalizeFilter(options.harness),fingerprint=eventQueryFingerprint(options),{cursor,sinceTimestamp}=resolveMutationEventStart(options.since,fingerprint),query={...cursor?{after_ts:cursor.ts,after_stream_id:cursor.stream_id,after_stream_offset:cursor.stream_offset}:sinceTimestamp?{since_ts:sinceTimestamp}:{},ops:normalizeFilter(options.type),authors:normalizeFilter(options.author),harnesses,harness_alias_authors:harnesses?Object.entries(vocabulary.aliases).filter(([,harness])=>harnesses.includes(harness)).map(([author])=>author):void 0,agent_instances:normalizeFilter(options.agentInstance),provenance:[...provenancePredicates].map(([dimension,values])=>({dimension,values:[...values]})),stream_ids:normalizeFilter(options.item),limit:requestedLimit},indexed=await resolveIndexedMutationEvents(pmRoot,query),events=indexed.events.map(event=>({...cursorMode==="row"?{cursor:encodeMutationEventCursor(event,fingerprint)}:{},item_id:event.stream_id,version:event.stream_offset+1,ts:event.entry.ts,author:event.entry.author,type:event.entry.op,...event.entry.message===void 0?{}:{message:event.entry.message},patch_count:event.entry.patch.length,...options.full===!0?{entry:event.entry}:{},...options.provenance===!0?{provenance:projectHistoryProvenance(event.entry,vocabulary,{itemId:event.stream_id,version:event.stream_offset+1})}:{}}));return{events,count:events.length,has_more:indexed.has_more,cursor_mode:cursorMode,...indexed.events.length===0?{}:{next_cursor:encodeMutationEventCursor(indexed.events[indexed.events.length-1],fingerprint)},source:indexed.source,...options.provenanceSummary===!0?{provenance_summary:summarizeHistoryProvenance(indexed.events.map(event=>event.entry),vocabulary,dimensions)}:{}}}async function*subscribeMutationEventBatches(options={}){const intervalMs=options.intervalMs??DEFAULT_FOLLOW_INTERVAL_MS;if(!Number.isSafeInteger(intervalMs)||intervalMs<10)throw new PmCliError("Mutation event follow interval must be an integer of at least 10ms.",EXIT_CODE.USAGE,{code:"invalid_event_follow_interval"});let cursor=options.since;for(;options.signal?.aborted!==!0;){const page=await listMutationEvents({...options,cursorMode:options.cursorMode??"batch",since:cursor});if(yield page.next_cursor===void 0&&cursor!==void 0?{...page,next_cursor:cursor}:page,page.events.length>0){cursor=page.next_cursor;continue}try{await delay(intervalMs,void 0,{signal:options.signal})}catch(error){if(isAbortError(error))return;throw error}}}async function*subscribeMutationEvents(options={}){for await(const page of subscribeMutationEventBatches({...options,cursorMode:"row"}))for(const event of page.events)yield event}const _testOnlyMutationEvents={decodeMutationEventCursor,encodeMutationEventCursor,eventQueryFingerprint,isAbortError,isMutationEventCursorEnvelope,parseMutationEventLimit,resolveMutationEventStart};export{MUTATION_EVENT_WIRE_BUDGET,_testOnlyMutationEvents,listMutationEvents,subscribeMutationEventBatches,subscribeMutationEvents};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c583f859-9204-565b-b143-59aba53c3e9c")}catch{}})();import{assertInitializedTracker}from"./environment/tracker-preflight.js";import{createHash}from"node:crypto";import{setTimeout as delay}from"node:timers/promises";import{readHistoryEventIndexPage,queryHistoryEventStreams,rebuildHistoryEventIndex}from"../core/history/event-index.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{stableStringify}from"../core/shared/serialization.js";import{toErrorMessage}from"../core/shared/primitives.js";import{resolvePmRoot}from"../core/store/paths.js";import{readSettings}from"../core/store/settings.js";import{parseHistoryProvenanceFilters,projectHistoryProvenance,resolveHistoryProvenanceDimensions,summarizeHistoryProvenance}from"./history-provenance.js";const MUTATION_EVENT_CURSOR_VERSION=1,MUTATION_EVENT_CURSOR_PATTERN=/^[A-Za-z0-9_-]+$/,DEFAULT_EVENT_LIMIT=100,MAX_EVENT_LIMIT=1e3,DEFAULT_FOLLOW_INTERVAL_MS=250,MUTATION_EVENT_WIRE_BUDGET=Object.freeze({batch_to_row_ratio:.6,fixture_events:200});function normalizeFilter(value){if(value===void 0)return;const values=typeof value=="string"?[value]:value,normalized=[...new Set(values.flatMap(entry=>entry.split(",")).map(entry=>entry.trim()).filter(Boolean))].sort((left,right)=>left.localeCompare(right));return normalized.length===0?void 0:normalized}function eventQueryFingerprint(options){return createHash("sha256").update(stableStringify({type:normalizeFilter(options.type),author:normalizeFilter(options.author),item:normalizeFilter(options.item),harness:normalizeFilter(options.harness),agent_instance:normalizeFilter(options.agentInstance),provenance:normalizeFilter(options.provenanceFilter)})).digest("hex").slice(0,24)}function encodeMutationEventCursor(event,fingerprint){const envelope={version:MUTATION_EVENT_CURSOR_VERSION,fingerprint,ts:event.entry.ts,stream_id:event.stream_id,stream_offset:event.stream_offset};return Buffer.from(JSON.stringify(envelope),"utf8").toString("base64url")}function isMutationEventCursorEnvelope(value,fingerprint){if(typeof value!="object"||value===null)return!1;const candidate=value;return[candidate.version===MUTATION_EVENT_CURSOR_VERSION,candidate.fingerprint===fingerprint,typeof candidate.ts=="string",typeof candidate.stream_id=="string",typeof candidate.stream_offset=="number",Number.isSafeInteger(candidate.stream_offset),Number(candidate.stream_offset)>=0].every(Boolean)}function decodeMutationEventCursor(cursor,fingerprint){const normalized=cursor.trim();if(normalized.length===0||normalized.length>4096||!MUTATION_EVENT_CURSOR_PATTERN.test(normalized))throw new PmCliError("Invalid mutation event cursor.",EXIT_CODE.USAGE,{code:"invalid_event_cursor"});let parsed;try{parsed=JSON.parse(Buffer.from(normalized,"base64url").toString("utf8"))}catch{throw new PmCliError("Invalid mutation event cursor.",EXIT_CODE.USAGE,{code:"invalid_event_cursor"})}if(!isMutationEventCursorEnvelope(parsed,fingerprint))throw new PmCliError("Mutation event cursor does not match this query.",EXIT_CODE.USAGE,{code:"event_cursor_query_mismatch"});return parsed}function parseMutationEventLimit(value){const limit=value??DEFAULT_EVENT_LIMIT;if(!Number.isSafeInteger(limit)||limit<0||limit>MAX_EVENT_LIMIT)throw new PmCliError(`Mutation event limit must be an integer from 0 to ${MAX_EVENT_LIMIT}.`,EXIT_CODE.USAGE,{code:"invalid_event_limit"});return limit}function isAbortError(error){return error instanceof Error&&error.name==="AbortError"}function resolveMutationEventStart(sinceValue,fingerprint){const since=sinceValue?.trim();if(!since)return{};if(MUTATION_EVENT_CURSOR_PATTERN.test(since))return{cursor:decodeMutationEventCursor(since,fingerprint)};const milliseconds=Date.parse(since);if(!Number.isFinite(milliseconds))throw new PmCliError("Mutation event --since must be an event cursor or ISO timestamp.",EXIT_CODE.USAGE,{code:"invalid_event_since"});return{sinceTimestamp:new Date(milliseconds).toISOString()}}async function resolveIndexedMutationEvents(pmRoot,query){if(query.verify)return queryAuthoritativeMutationEvents(pmRoot,query);const attempt=await readHistoryEventIndexPage(pmRoot,query);let indexed=attempt.result;if(indexed===null&&attempt.repair_needed)try{await rebuildHistoryEventIndex(pmRoot,{lockWaitMs:0})&&(indexed=(await readHistoryEventIndexPage(pmRoot,query)).result)}catch{}return indexed===null?queryAuthoritativeMutationEvents(pmRoot,query):{...indexed,source:"derived_index"}}async function queryAuthoritativeMutationEvents(pmRoot,query){try{return{...await queryHistoryEventStreams(pmRoot,query),source:"authoritative_history"}}catch(error){throw error instanceof PmCliError?error:new PmCliError("Mutation events are unavailable because authoritative history could not be read.",EXIT_CODE.GENERIC_FAILURE,{code:"event_index_unavailable",why:toErrorMessage(error)})}}function resolveMutationEventCursorMode(value){const cursorMode=value??"batch";if(cursorMode!=="batch"&&cursorMode!=="row")throw new PmCliError("Mutation event cursor mode must be batch or row.",EXIT_CODE.USAGE,{code:"invalid_event_cursor_mode"});return cursorMode}async function listMutationEvents(options={}){const pmRoot=resolvePmRoot(options.cwd??process.cwd(),options.pmRoot);await assertInitializedTracker(pmRoot);const requestedLimit=parseMutationEventLimit(options.limit),cursorMode=resolveMutationEventCursorMode(options.cursorMode);if(options.full===!0&&options.provenance===!0)throw new PmCliError("Mutation event projections are mutually exclusive. Use --provenance or --full.",EXIT_CODE.USAGE);const settings=await readSettings(pmRoot),vocabulary=settings.agent_identity.identity_vocabulary,dimensions=resolveHistoryProvenanceDimensions(settings.agent_identity.harness_signals),provenancePredicates=parseHistoryProvenanceFilters({provenance:options.provenanceFilter},dimensions),harnesses=normalizeFilter(options.harness),fingerprint=eventQueryFingerprint(options),{cursor,sinceTimestamp}=resolveMutationEventStart(options.since,fingerprint),query={verify:options.verify===!0,...cursor?{after_ts:cursor.ts,after_stream_id:cursor.stream_id,after_stream_offset:cursor.stream_offset}:sinceTimestamp?{since_ts:sinceTimestamp}:{},ops:normalizeFilter(options.type),authors:normalizeFilter(options.author),harnesses,harness_alias_authors:harnesses?Object.entries(vocabulary.aliases).filter(([,harness])=>harnesses.includes(harness)).map(([author])=>author):void 0,agent_instances:normalizeFilter(options.agentInstance),provenance:[...provenancePredicates].map(([dimension,values])=>({dimension,values:[...values]})),stream_ids:normalizeFilter(options.item),limit:requestedLimit},indexed=await resolveIndexedMutationEvents(pmRoot,query),events=indexed.events.map(event=>({...cursorMode==="row"?{cursor:encodeMutationEventCursor(event,fingerprint)}:{},item_id:event.stream_id,version:event.stream_offset+1,ts:event.entry.ts,author:event.entry.author,type:event.entry.op,...event.entry.message===void 0?{}:{message:event.entry.message},patch_count:event.entry.patch.length,...options.full===!0?{entry:structuredClone(event.recorded_entry??event.entry)}:{},...options.provenance===!0?{provenance:projectHistoryProvenance(event.entry,vocabulary,{itemId:event.stream_id,version:event.stream_offset+1})}:{}}));return{integrity:options.verify===!0?"verified":"unverified",...indexed.verified_streams===void 0?{}:{verified_streams:indexed.verified_streams},events,count:events.length,has_more:indexed.has_more,cursor_mode:cursorMode,...indexed.events.length===0?{}:{next_cursor:encodeMutationEventCursor(indexed.events[indexed.events.length-1],fingerprint)},source:indexed.source,...options.provenanceSummary===!0?{provenance_summary:summarizeHistoryProvenance(indexed.events.map(event=>event.entry),vocabulary,dimensions)}:{}}}async function*subscribeMutationEventBatches(options={}){const intervalMs=options.intervalMs??DEFAULT_FOLLOW_INTERVAL_MS;if(!Number.isSafeInteger(intervalMs)||intervalMs<10)throw new PmCliError("Mutation event follow interval must be an integer of at least 10ms.",EXIT_CODE.USAGE,{code:"invalid_event_follow_interval"});let cursor=options.since;for(;options.signal?.aborted!==!0;){const page=await listMutationEvents({...options,cursorMode:options.cursorMode??"batch",since:cursor});if(yield page.next_cursor===void 0&&cursor!==void 0?{...page,next_cursor:cursor}:page,page.events.length>0){cursor=page.next_cursor;continue}try{await delay(intervalMs,void 0,{signal:options.signal})}catch(error){if(isAbortError(error))return;throw error}}}async function*subscribeMutationEvents(options={}){for await(const page of subscribeMutationEventBatches({...options,cursorMode:"row"}))for(const event of page.events)yield event}const _testOnlyMutationEvents={decodeMutationEventCursor,encodeMutationEventCursor,eventQueryFingerprint,isAbortError,isMutationEventCursorEnvelope,parseMutationEventLimit,resolveMutationEventStart};export{MUTATION_EVENT_WIRE_BUDGET,_testOnlyMutationEvents,listMutationEvents,subscribeMutationEventBatches,subscribeMutationEvents};
2
2
  //# sourceMappingURL=mutation-events.js.map
@@ -4,7 +4,8 @@
4
4
  * Provides SDK-first pre-write provenance and secret-detection guardrails for
5
5
  * CLI, MCP, and package-owned mutation dispatchers.
6
6
  */
7
- import { SECRET_RULES } from "../core/shared/secret-rules.js";
7
+ import { SECRET_RULES,type SecretNormalizationKind } from "../core/shared/secret-rules.js";
8
+ export type { SecretNormalizationKind } from "../core/shared/secret-rules.js";
8
9
  /** Configurable handling mode for credential-shaped mutation content. */
9
10
  export type SecretGuardMode = "off" | "advise" | "block";
10
11
  /** Settings consumed by the shared mutation guard. */
@@ -22,6 +23,10 @@ export interface SecretGuardFinding {
22
23
  rule:(typeof SECRET_RULES)[number]["mutationRule"];
23
24
  /** Object path or argument index containing the match. */
24
25
  path:string;
26
+ /** Detection-only transformations, without original or decoded coordinate values. */
27
+ normalizations?:SecretNormalizationKind[];
28
+ /** Remaining encoded input exceeded the finite inspection envelope and requires explicit review. */
29
+ normalization_incomplete?:true;
25
30
  }
26
31
  /** Result of a non-blocking or bypassed mutation guard evaluation. */
27
32
  export interface MutationGuardResult {
@@ -1,2 +1,2 @@
1
- (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b75bf20f-d341-5fa2-943d-db02211ca80d")}catch{}})();import{SECRET_RULES,acceptsSecretMatch}from"../core/shared/secret-rules.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";const PM_MUTATION_ACTION_CONTRACTS=[{action:"append",history_scope:"item"},{action:"claim",history_scope:"item"},{action:"close",history_scope:"item"},{action:"close-many",history_scope:"item"},{action:"close-task",history_scope:"item"},{action:"comments",history_scope:"conditional"},{action:"config",history_scope:"conditional"},{action:"copy",history_scope:"item"},{action:"create",history_scope:"item"},{action:"delete",history_scope:"item"},{action:"deps",history_scope:"conditional"},{action:"discover",history_scope:"item"},{action:"docs",history_scope:"conditional"},{action:"files",history_scope:"conditional"},{action:"focus",history_scope:"conditional"},{action:"gc",history_scope:"conditional"},{action:"history-compact",history_scope:"history"},{action:"history-author-acknowledge",history_scope:"workspace"},{action:"history-redact",history_scope:"history"},{action:"history-repair",history_scope:"history"},{action:"init",history_scope:"workspace"},{action:"install",history_scope:"conditional"},{action:"learnings",history_scope:"conditional"},{action:"notes",history_scope:"conditional"},{action:"package",history_scope:"conditional"},{action:"pause-task",history_scope:"item"},{action:"plan",history_scope:"conditional"},{action:"profile",history_scope:"conditional"},{action:"release",history_scope:"item"},{action:"restore",history_scope:"item"},{action:"schema",history_scope:"conditional"},{action:"start-task",history_scope:"item"},{action:"templates",history_scope:"conditional"},{action:"test",history_scope:"conditional"},{action:"test-all",history_scope:"conditional"},{action:"update",history_scope:"item"},{action:"update-many",history_scope:"item"},{action:"upgrade",history_scope:"conditional"}],MUTATION_ACTION_CONTRACTS_BY_NAME=new Map(PM_MUTATION_ACTION_CONTRACTS.map(contract=>[contract.action,contract]));function stringLeaves(value,path="$",seen=new WeakSet){return typeof value=="string"?[{path,value}]:typeof value!="object"||value===null?[]:seen.has(value)?[]:(seen.add(value),Array.isArray(value)?value.flatMap((entry,index)=>stringLeaves(entry,`${path}[${index}]`,seen)):Object.entries(value).flatMap(([key,entry])=>stringLeaves(entry,`${path}.${key}`,seen)))}function scanMutationSecrets(payload){const findings=[];for(const leaf of stringLeaves(payload))for(const rule of SECRET_RULES){rule.regex.lastIndex=0;for(const match of leaf.value.matchAll(rule.regex))if(acceptsSecretMatch(rule,match)){findings.push({rule:rule.mutationRule,path:leaf.path});break}}return findings.filter((finding,index,all)=>all.findIndex(candidate=>candidate.rule===finding.rule&&candidate.path===finding.path)===index)}function evaluateMutationGuard(options){const author=options.author.trim()||"unknown";if(options.settings.require_attributed_author&&author.toLowerCase()==="unknown")throw new PmCliError("Mutation author is required by workspace policy.",EXIT_CODE.USAGE,{code:"mutation_author_required",required:"Pass --author <id>, set PM_AUTHOR, or configure author_default.",nextSteps:["Retry with --author <stable-agent-id>.","Set PM_AUTHOR for this agent session."]});if(options.settings.secret_guard==="off")return{author,findings:[],warnings:[],override_applied:!1};let findings;try{findings=scanMutationSecrets(options.payload)}catch{return{author,findings:[],warnings:["secret_guard_scan_failed_open"],override_applied:!1}}if(findings.length===0)return{author,findings,warnings:[],override_applied:!1};const ruleSummary=[...new Set(findings.map(finding=>finding.rule))].sort().join(",");if(options.settings.secret_guard==="block"&&options.force!==!0)throw new PmCliError(`Mutation blocked by secret guard (${ruleSummary}). No detected value was logged.`,EXIT_CODE.CONFLICT,{code:"mutation_secret_guard_blocked",required:"Remove the credential-shaped content or retry with --force after explicit review.",nextSteps:["Replace the value with a secret-manager reference.","Use pm history-redact if equivalent content already entered history."],recovery:{recovery_mode:"compact",attempted_command:"pm <mutation> [REDACTED]",normalized_args:["[REDACTED]"],suggested_retry:"Remove credential-shaped content, then retry the mutation."}});return{author,findings,warnings:[`secret_guard_detected:${findings.length}:rules=${ruleSummary}`,...options.settings.secret_guard==="block"&&options.force===!0?["secret_guard_force_override"]:[]],override_applied:options.settings.secret_guard==="block"&&options.force===!0}}function isMutationAction(action){return MUTATION_ACTION_CONTRACTS_BY_NAME.has(action.trim().toLowerCase())}function listMutationActions(){return[...MUTATION_ACTION_CONTRACTS_BY_NAME.keys()].sort((left,right)=>left.localeCompare(right))}function getMutationActionContract(action){return MUTATION_ACTION_CONTRACTS_BY_NAME.get(action.trim().toLowerCase())}export{PM_MUTATION_ACTION_CONTRACTS,evaluateMutationGuard,getMutationActionContract,isMutationAction,listMutationActions,scanMutationSecrets};
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d6059407-bf03-5ae8-8b01-e3e7c5b28030")}catch{}})();import{SECRET_RULES,findSecretRuleMatches}from"../core/shared/secret-rules.js";import{EXIT_CODE}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";const PM_MUTATION_ACTION_CONTRACTS=[{action:"append",history_scope:"item"},{action:"claim",history_scope:"item"},{action:"close",history_scope:"item"},{action:"close-many",history_scope:"item"},{action:"close-task",history_scope:"item"},{action:"comments",history_scope:"conditional"},{action:"config",history_scope:"conditional"},{action:"copy",history_scope:"item"},{action:"create",history_scope:"item"},{action:"delete",history_scope:"item"},{action:"deps",history_scope:"conditional"},{action:"discover",history_scope:"item"},{action:"docs",history_scope:"conditional"},{action:"files",history_scope:"conditional"},{action:"focus",history_scope:"conditional"},{action:"gc",history_scope:"conditional"},{action:"history-compact",history_scope:"history"},{action:"history-author-acknowledge",history_scope:"workspace"},{action:"history-redact",history_scope:"history"},{action:"history-repair",history_scope:"history"},{action:"init",history_scope:"workspace"},{action:"install",history_scope:"conditional"},{action:"learnings",history_scope:"conditional"},{action:"notes",history_scope:"conditional"},{action:"package",history_scope:"conditional"},{action:"pause-task",history_scope:"item"},{action:"plan",history_scope:"conditional"},{action:"profile",history_scope:"conditional"},{action:"release",history_scope:"item"},{action:"restore",history_scope:"item"},{action:"schema",history_scope:"conditional"},{action:"start-task",history_scope:"item"},{action:"templates",history_scope:"conditional"},{action:"test",history_scope:"conditional"},{action:"test-all",history_scope:"conditional"},{action:"update",history_scope:"item"},{action:"update-many",history_scope:"item"},{action:"upgrade",history_scope:"conditional"}],MUTATION_ACTION_CONTRACTS_BY_NAME=new Map(PM_MUTATION_ACTION_CONTRACTS.map(contract=>[contract.action,contract]));function stringLeaves(value,path="$",seen=new WeakSet){return typeof value=="string"?[{path,value}]:typeof value!="object"||value===null?[]:seen.has(value)?[]:(seen.add(value),Array.isArray(value)?value.flatMap((entry,index)=>stringLeaves(entry,`${path}[${index}]`,seen)):Object.entries(value).flatMap(([key,entry])=>stringLeaves(entry,`${path}.${key}`,seen)))}function preflightRuleMatch(rule,value){const matches=findSecretRuleMatches(rule,value),first=matches.next().value;if(!first||rule.mutationRule!=="absolute_home_path"||first.normalization_incomplete)return first;for(const later of matches)if(later.normalization_incomplete)return{...first,normalizations:[...new Set([first.normalizations,later.normalizations].flatMap(kinds=>kinds??[]))],normalization_incomplete:!0};return first}function scanMutationSecrets(payload){const findings=[];for(const leaf of stringLeaves(payload))for(const rule of SECRET_RULES){const match=preflightRuleMatch(rule,leaf.value);if(match){const finding={rule:rule.mutationRule,path:leaf.path};match.normalizations&&(finding.normalizations=match.normalizations),match.normalization_incomplete&&(finding.normalization_incomplete=!0),findings.push(finding)}}return findings.filter((finding,index,all)=>all.findIndex(candidate=>candidate.rule===finding.rule&&candidate.path===finding.path)===index)}function evaluateMutationGuard(options){const author=options.author.trim()||"unknown";if(options.settings.require_attributed_author&&author.toLowerCase()==="unknown")throw new PmCliError("Mutation author is required by workspace policy.",EXIT_CODE.USAGE,{code:"mutation_author_required",required:"Pass --author <id>, set PM_AUTHOR, or configure author_default.",nextSteps:["Retry with --author <stable-agent-id>.","Set PM_AUTHOR for this agent session."]});if(options.settings.secret_guard==="off")return{author,findings:[],warnings:[],override_applied:!1};let findings;try{findings=scanMutationSecrets(options.payload)}catch{return{author,findings:[],warnings:["secret_guard_scan_failed_open"],override_applied:!1}}if(findings.length===0)return{author,findings,warnings:[],override_applied:!1};const ruleSummary=[...new Set(findings.map(finding=>finding.rule))].sort().join(",");if(options.settings.secret_guard==="block"&&options.force!==!0)throw new PmCliError(`Mutation blocked by secret guard (${ruleSummary}). No detected value was logged.`,EXIT_CODE.CONFLICT,{code:"mutation_secret_guard_blocked",required:"Remove the credential-shaped content or retry with --force after explicit review.",nextSteps:["Replace the value with a secret-manager reference.","Use pm history-redact if equivalent content already entered history."],recovery:{recovery_mode:"compact",attempted_command:"pm <mutation> [REDACTED]",normalized_args:["[REDACTED]"],suggested_retry:"Remove credential-shaped content, then retry the mutation."}});return{author,findings,warnings:[`secret_guard_detected:${findings.length}:rules=${ruleSummary}`,...options.settings.secret_guard==="block"&&options.force===!0?["secret_guard_force_override"]:[]],override_applied:options.settings.secret_guard==="block"&&options.force===!0}}function isMutationAction(action){return MUTATION_ACTION_CONTRACTS_BY_NAME.has(action.trim().toLowerCase())}function listMutationActions(){return[...MUTATION_ACTION_CONTRACTS_BY_NAME.keys()].sort((left,right)=>left.localeCompare(right))}function getMutationActionContract(action){return MUTATION_ACTION_CONTRACTS_BY_NAME.get(action.trim().toLowerCase())}export{PM_MUTATION_ACTION_CONTRACTS,evaluateMutationGuard,getMutationActionContract,isMutationAction,listMutationActions,scanMutationSecrets};
2
2
  //# sourceMappingURL=mutation-guard.js.map
@@ -0,0 +1,98 @@
1
+ /** Deterministic admission policy; neither policy reorders or splits rows. */
2
+ export type OutputEnvelopeFitPolicy = "prefix" | "skip";
3
+ /** SDK-owned receipt included in every measured envelope as output_fit. */
4
+ export interface OutputEnvelopeFitReceipt {
5
+ /** Receipt schema version. */
6
+ version:1;
7
+ /** Admission policy applied in caller order. */
8
+ policy:OutputEnvelopeFitPolicy;
9
+ /** Number of rows offered before fitting. */
10
+ total_candidates:number;
11
+ /** Original positions of the retained rows. */
12
+ included_indices:readonly number[];
13
+ /** Number of whole rows withheld by the policy. */
14
+ omitted_count:number;
15
+ /** Binding ceiling in the caller's token estimator units. */
16
+ token_budget:number;
17
+ /** Greatest complete-envelope estimate across all required renderers. */
18
+ estimated_tokens:number;
19
+ /** Greatest UTF-8 byte count across all required renderers. */
20
+ output_bytes:number;
21
+ }
22
+ /** Envelope with the reserved, SDK-owned accounting and omission receipt. */
23
+ export type FittedOutputEnvelope<Envelope> = Omit<Envelope,"output_fit"> & {
24
+ /** Self-accounting receipt, including its own bytes and framing. */
25
+ readonly output_fit:Readonly<OutputEnvelopeFitReceipt>;
26
+ };
27
+ /** One complete rendering, ready to emit without reserializing or reframing. */
28
+ export interface OutputEnvelopeRendering {
29
+ /** Caller-selected transport identity. */
30
+ name:string;
31
+ /** Complete output, including any caller-supplied newline or framing. */
32
+ text:string;
33
+ /** UTF-8 bytes of text, including the receipt. */
34
+ bytes:number;
35
+ /** Estimate of text under the selected estimator. */
36
+ estimated_tokens:number;
37
+ }
38
+ /** Actual fitting work, kept outside the measured output to avoid self-counting. */
39
+ export interface OutputEnvelopeFitWork {
40
+ /** Candidate admission trials performed, excluding the minimum envelope. */
41
+ evaluated_candidates:number;
42
+ /** Renderer calls across all fixed-point passes and admission trials. */
43
+ render_calls:number;
44
+ /** Hard ceiling on renderer calls. */
45
+ max_render_calls:number;
46
+ }
47
+ /** Explicit failure to produce a verified packet; no oversized output is returned. */
48
+ export declare class OutputEnvelopeFitError extends RangeError {
49
+ /** Stable refusal reason suitable for package-specific error translation. */
50
+ readonly code:"output_envelope_infeasible" | "output_envelope_work_limit" | "output_envelope_unstable";
51
+ /** Work completed before refusal. */
52
+ readonly work:Readonly<OutputEnvelopeFitWork>;
53
+ /** Measured minimum-envelope estimate when the budget is infeasible. */
54
+ readonly minimum_tokens:number | undefined;
55
+ /** Capture immutable refusal evidence without serializing caller data. */
56
+ constructor(code:OutputEnvelopeFitError["code"],work:OutputEnvelopeFitWork,minimumTokens?:number);
57
+ }
58
+ /** Caller-owned envelope construction, renderers and finite work controls. */
59
+ export interface FitOutputEnvelopeOptions<Row,Envelope extends Record<string,unknown>> {
60
+ /** Maximum complete-envelope estimate accepted by every required renderer. */
61
+ tokenBudget:number;
62
+ /** Preserve a prefix by default; skip may retain later smaller rows. */
63
+ policy?:OutputEnvelopeFitPolicy;
64
+ /** Construct mandatory domain metadata; output_fit is reserved and overwritten by the SDK. Must be pure and deterministic. */
65
+ buildEnvelope:(rows:readonly Row[],receipt:Readonly<OutputEnvelopeFitReceipt>) => Envelope;
66
+ /** Nonempty set of pure deterministic renderers, including all output framing. */
67
+ renderers:Readonly<Record<string,(envelope:FittedOutputEnvelope<Envelope>) => string>>;
68
+ /** Estimate the exact rendered text; defaults to ceil(UTF-8 bytes / 4). Stronger estimators may be supplied. */
69
+ estimateTokens?:(text:string) => number;
70
+ /** Hard renderer-call ceiling; defaults to 10,000. */
71
+ maxRenderCalls?:number;
72
+ /** Fixed-point pass ceiling per admission trial; defaults to 16. */
73
+ maxStabilizationPasses?:number;
74
+ }
75
+ /** Verified envelope and cached renderings; work is diagnostic, not emitted packet data. */
76
+ export interface OutputEnvelopeFitResult<Row,Envelope> {
77
+ /** Whole caller rows retained in original order. */
78
+ rows:readonly Row[];
79
+ /** Domain metadata plus the exact measured receipt. */
80
+ envelope:FittedOutputEnvelope<Envelope>;
81
+ /** Cached complete outputs; every rendering satisfies the budget. */
82
+ renderings:readonly OutputEnvelopeRendering[];
83
+ /** True only when all candidates were retained. */
84
+ complete:boolean;
85
+ /** Actual bounded fitting work. */
86
+ work:Readonly<OutputEnvelopeFitWork>;
87
+ }
88
+ /**
89
+ * Fit whole ordered rows against every required complete rendering. First prove
90
+ * the empty mandatory envelope fits, then admit rows under prefix or skip
91
+ * policy. No monotonic-size assumption or binary search is used. Callbacks
92
+ * must be pure and return promptly; call/pass ceilings bound SDK fitting work,
93
+ * not arbitrary callback execution time. Emit the cached renderings directly.
94
+ *
95
+ * @throws {OutputEnvelopeFitError} When the minimum envelope is too large,
96
+ * accounting fails to stabilize, or the renderer-call ceiling is exhausted.
97
+ */
98
+ export declare function fitOutputEnvelope<Row,Envelope extends Record<string,unknown>>(candidates:readonly Row[],options:FitOutputEnvelopeOptions<Row,Envelope>):OutputEnvelopeFitResult<Row,Envelope>;
@@ -0,0 +1,2 @@
1
+ (function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f698608a-2f39-5972-b24b-93287af6b373")}catch{}})();import{estimatePmOutputTokens}from"../cli-contracts/agent-output-contracts.js";class OutputEnvelopeFitError extends RangeError{code;work;minimum_tokens;constructor(code,work,minimumTokens){super(code),this.name="OutputEnvelopeFitError",this.code=code,this.work=Object.freeze({...work}),this.minimum_tokens=minimumTokens}}function assertPositiveInteger(value,name){if(!Number.isSafeInteger(value)||value<1)throw new RangeError(`${name} must be a positive safe integer`)}function renderEnvelope(envelope,renderers,estimateTokens,work){return renderers.map(([name,render])=>{if(work.render_calls>=work.max_render_calls)throw new OutputEnvelopeFitError("output_envelope_work_limit",work);work.render_calls+=1;const text=render(envelope);if(typeof text!="string")throw new TypeError("Envelope renderers must return strings");const estimatedTokens=estimateTokens(text);if(!Number.isSafeInteger(estimatedTokens)||estimatedTokens<0)throw new RangeError("Envelope token estimates must be non-negative safe integers");return{name,text,bytes:Buffer.byteLength(text,"utf8"),estimated_tokens:estimatedTokens}})}function stabilizeEnvelope(rows,initial,options,work){let receipt=initial;const renderers=Object.entries(options.renderers),estimateTokens=options.estimateTokens??(text=>estimatePmOutputTokens(Buffer.byteLength(text,"utf8")));for(let pass=0;pass<(options.maxStabilizationPasses??16);pass+=1){const frozenReceipt=Object.freeze({...receipt}),envelope={...options.buildEnvelope(rows,frozenReceipt),output_fit:frozenReceipt},renderings=renderEnvelope(envelope,renderers,estimateTokens,work),bytes=Math.max(...renderings.map(rendering=>rendering.bytes)),tokens=Math.max(...renderings.map(rendering=>rendering.estimated_tokens));if(bytes===receipt.output_bytes&&tokens===receipt.estimated_tokens)return{envelope,renderings};receipt={...receipt,output_bytes:bytes,estimated_tokens:tokens}}throw new OutputEnvelopeFitError("output_envelope_unstable",work)}function fitOutputEnvelope(candidates,options){assertPositiveInteger(options.tokenBudget,"tokenBudget"),assertPositiveInteger(options.maxRenderCalls??1e4,"maxRenderCalls"),assertPositiveInteger(options.maxStabilizationPasses??16,"maxStabilizationPasses");const policy=options.policy??"prefix";if(policy!=="prefix"&&policy!=="skip")throw new TypeError("Unknown output envelope fit policy");if(Object.entries(options.renderers).length===0||Object.entries(options.renderers).some(([name,render])=>!name.trim()||typeof render!="function"))throw new TypeError("Envelope fitting requires named callable renderers");const work={evaluated_candidates:0,render_calls:0,max_render_calls:options.maxRenderCalls??1e4};let rows=[],included=[];const receiptFor=indices=>({version:1,policy,total_candidates:candidates.length,included_indices:Object.freeze([...indices]),omitted_count:candidates.length-indices.length,token_budget:options.tokenBudget,estimated_tokens:0,output_bytes:0});let fitted=stabilizeEnvelope(rows,receiptFor(included),options,work);if(fitted.envelope.output_fit.estimated_tokens>options.tokenBudget)throw new OutputEnvelopeFitError("output_envelope_infeasible",work,fitted.envelope.output_fit.estimated_tokens);for(const[index,candidate]of candidates.entries()){work.evaluated_candidates+=1;const trialRows=[...rows,candidate],trialIndices=[...included,index],trial=stabilizeEnvelope(trialRows,receiptFor(trialIndices),options,work);if(trial.envelope.output_fit.estimated_tokens<=options.tokenBudget)rows=trialRows,included=trialIndices,fitted=trial;else if(policy==="prefix")break}return{rows,...fitted,complete:rows.length===candidates.length,work:Object.freeze({...work})}}export{OutputEnvelopeFitError,fitOutputEnvelope};
2
+ //# sourceMappingURL=fit-envelope.js.map