@unbrained/pm-cli 2026.10.18 → 2026.10.19
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +10 -0
- package/dist/cli/help-content.js +2 -2
- package/dist/cli/register-mutation.js +2 -2
- package/dist/cli/register-setup.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +1 -1
- package/dist/cli-bundle/chunks/chunk-4CXQSRSP.js +126 -0
- package/dist/cli-bundle/chunks/{chunk-AETP4JEM.js → chunk-4ICKT4GH.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-7WIEAGDS.js → chunk-4MJ6WBMA.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-4XFVAXA4.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-FIJOSX2C.js → chunk-5GZSX5HU.js} +72 -66
- package/dist/cli-bundle/chunks/{chunk-4GA4MUTE.js → chunk-6DS5CAN2.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-6VZKYJW7.js → chunk-D3EDNAN7.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-DMFM5R4G.js +3 -0
- package/dist/cli-bundle/chunks/chunk-H3XYP24A.js +70 -0
- package/dist/cli-bundle/chunks/chunk-LBEMB3BW.js +6 -0
- package/dist/cli-bundle/chunks/chunk-NX7Q4LGN.js +8 -0
- package/dist/cli-bundle/chunks/chunk-O2ZRRLAP.js +8 -0
- package/dist/cli-bundle/chunks/chunk-OEJ3G7XT.js +20 -0
- package/dist/cli-bundle/chunks/{chunk-PYRREEUB.js → chunk-QBNEZII5.js} +28 -28
- package/dist/cli-bundle/chunks/chunk-QG4LMU7A.js +112 -0
- package/dist/cli-bundle/chunks/chunk-QKAAZWGC.js +3 -0
- package/dist/cli-bundle/chunks/chunk-RUKSEZE7.js +2 -0
- package/dist/cli-bundle/chunks/chunk-TK3G3GAP.js +2 -0
- package/dist/cli-bundle/chunks/chunk-VS565KBX.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-ZZED447K.js → chunk-XH7C3A5N.js} +2 -2
- package/dist/cli-bundle/chunks/indexed-receipt-blobs-BVSHK4WQ.js +4 -0
- package/dist/cli-bundle/chunks/{register-list-query-PUCVYYBD.js → register-list-query-WQUVDJIH.js} +2 -2
- package/dist/cli-bundle/chunks/register-mutation-IWIBQIO3.js +20 -0
- package/dist/cli-bundle/chunks/register-operations-SUWOZNDU.js +2 -0
- package/dist/cli-bundle/chunks/register-setup-QGM5YJW7.js +2 -0
- package/dist/cli-bundle/chunks/settlement-hook-UNFMCKKQ.js +7 -0
- package/dist/cli-bundle/chunks/stage-7DELI4Z5.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-2MPQUMTF.js → chunk-2IEWB6MB.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-3TEIACFU.js +3 -0
- package/dist/cli-bundle/focused-chunks/chunk-5YTOYBTZ.js +65 -0
- package/dist/cli-bundle/focused-chunks/chunk-AK2CI2GI.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-BJMPRB7A.js +91 -0
- package/dist/cli-bundle/focused-chunks/{chunk-BFEMT5U3.js → chunk-ENZMATOY.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-EOQ6W25I.js +8 -0
- package/dist/cli-bundle/focused-chunks/{chunk-A4ZHHTEX.js → chunk-FEN7WOOP.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-FOA7XKWP.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-GWTV45SB.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-ZDBS2DCT.js → chunk-H6BVQ63U.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-IRVL4UXR.js → chunk-HYMYAGSY.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-JBJJW2WL.js +6 -0
- package/dist/cli-bundle/focused-chunks/chunk-JCLC6HKT.js +8 -0
- package/dist/cli-bundle/focused-chunks/{chunk-TB7F4FCR.js → chunk-JDBO7SMR.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-TZUIBG3P.js → chunk-KLJN44VY.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-L4PL3GZR.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-OV2WVLUR.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-PXDR77TO.js +78 -0
- package/dist/cli-bundle/focused-chunks/chunk-RLRMNFLF.js +3 -0
- package/dist/cli-bundle/focused-chunks/{chunk-Z7YIFCNE.js → chunk-SKYLOPU2.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-VVRXF4JO.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-WZTEFFQI.js +3 -0
- package/dist/cli-bundle/focused-chunks/chunk-Y54ONBEX.js +25 -0
- package/dist/cli-bundle/focused-chunks/indexed-receipt-blobs-QW2VWWJE.js +4 -0
- package/dist/cli-bundle/focused-chunks/settlement-hook-X4FNHTOD.js +7 -0
- package/dist/cli-bundle/focused-chunks/stage-MZSVQYO4.js +2 -0
- package/dist/cli-bundle/main.js +3 -3
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +28 -22
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +66 -53
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +2 -2
- package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +1 -1
- package/dist/sdk/cli-contracts/flag-contracts.js +1 -1
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -1
- package/dist/sdk/cli-contracts/runtime-contracts.d.ts +1 -0
- package/dist/sdk/cli-contracts/runtime-contracts.js +1 -1
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -1
- package/dist/sdk/cli-contracts/tool-schema.js +1 -1
- package/dist/sdk/extension/install-sources.js +3 -3
- package/dist/sdk/extension/shared.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
- package/dist/sdk/governance/health.js +1 -1
- package/dist/sdk/history-repair.js +1 -1
- package/dist/sdk/merge/driver.js +7 -1
- package/dist/sdk/merge/indexed-receipt-blobs.d.ts +2 -0
- package/dist/sdk/merge/indexed-receipt-blobs.js +4 -0
- package/dist/sdk/merge/install.d.ts +8 -0
- package/dist/sdk/merge/install.js +3 -3
- package/dist/sdk/merge/receipts.d.ts +6 -0
- package/dist/sdk/merge/receipts.js +2 -2
- package/dist/sdk/merge/reconcile.d.ts +6 -0
- package/dist/sdk/merge/reconcile.js +1 -1
- package/dist/sdk/merge/settlement-hook.d.ts +12 -0
- package/dist/sdk/merge/settlement-hook.js +7 -0
- package/dist/sdk/merge/stage.d.ts +13 -0
- package/dist/sdk/merge/stage.js +2 -0
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/list.js +1 -1
- package/dist/sdk/runtime-extended-actions.js +1 -1
- package/docs/COMMANDS.md +13 -0
- package/docs/CONFIGURATION.md +14 -0
- package/docs/MERGE_SAFETY.md +88 -5
- package/docs/SDK.md +26 -0
- package/docs/TESTING.md +7 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +6 -3
- package/marketplace.json +1 -1
- package/package.json +1 -1
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/package.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/package.json +1 -1
- package/plugins/pm-codex/plugin.json +1 -1
- package/runtime-dependencies.json +1 -1
- package/dist/cli-bundle/chunks/chunk-4V6ONLI6.js +0 -3
- package/dist/cli-bundle/chunks/chunk-7PATQWTH.js +0 -20
- package/dist/cli-bundle/chunks/chunk-LUKVNL72.js +0 -2
- package/dist/cli-bundle/chunks/chunk-OJ77ZQN3.js +0 -8
- package/dist/cli-bundle/chunks/chunk-RHQJ6VWC.js +0 -237
- package/dist/cli-bundle/chunks/chunk-RNABUEFB.js +0 -80
- package/dist/cli-bundle/chunks/chunk-TO6CIV4C.js +0 -2
- package/dist/cli-bundle/chunks/register-mutation-GGGVBXD5.js +0 -20
- package/dist/cli-bundle/chunks/register-operations-NRZ6Q4QE.js +0 -2
- package/dist/cli-bundle/chunks/register-setup-MV3P3YSF.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-5UKCSZWT.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-B4YLP5ZB.js +0 -3
- package/dist/cli-bundle/focused-chunks/chunk-FW7CSSKL.js +0 -75
- package/dist/cli-bundle/focused-chunks/chunk-HYPRPSYD.js +0 -3
- package/dist/cli-bundle/focused-chunks/chunk-L55KQ2KT.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-MASIOFPG.js +0 -8
- package/dist/cli-bundle/focused-chunks/chunk-RJ3EIHDU.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-SEL4Z2OO.js +0 -167
|
@@ -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]="
|
|
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)}
|
|
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 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))}return await retainAuditedDurableSettlements(receipts,durable.receipts,trackerRoot),{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 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,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"),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};
|
|
5
5
|
//# sourceMappingURL=receipts.js.map
|
|
@@ -12,6 +12,8 @@ author?:string;
|
|
|
12
12
|
message?:string;
|
|
13
13
|
/** Permit ownership overrides already supported by history repair. */
|
|
14
14
|
force?:boolean;
|
|
15
|
+
/** Stage only authenticated lossless audit artifacts, refusing force, conflicts and unrelated drift. */
|
|
16
|
+
stage?:boolean;
|
|
15
17
|
}
|
|
16
18
|
/** Structured post-merge reconciliation and verification result. */
|
|
17
19
|
export interface MergeReconcileResult {
|
|
@@ -32,6 +34,8 @@ reconciled:number;
|
|
|
32
34
|
/** Receipts proven to restore their original Git state; these do not represent applied merges. */
|
|
33
35
|
abandoned:number;
|
|
34
36
|
summaries:MergeDecisionReceiptSummary[];
|
|
37
|
+
/** Valid sidecars awaiting matching Git index evidence; a preview never authorizes audit writes. */
|
|
38
|
+
unstaged_durable_receipt_ids?:string[];
|
|
35
39
|
/** Missing durable references discovered before this reconciliation. */
|
|
36
40
|
missing_history_references_before:number;
|
|
37
41
|
/** Pre-durable references eligible for explicit audited disposition. */
|
|
@@ -47,6 +51,8 @@ validation:ValidateResult;
|
|
|
47
51
|
guidance:string[];
|
|
48
52
|
/** ISO timestamp copied from the validation pass. */
|
|
49
53
|
generated_at:string;
|
|
54
|
+
/** Repository-relative artifacts staged by an explicit stage request; dry runs return an empty list. */
|
|
55
|
+
staged_paths?:string[];
|
|
50
56
|
}
|
|
51
57
|
/**
|
|
52
58
|
* Preview or apply post-merge history reconciliation and immediately validate
|
|
@@ -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]="7b1ffef4-9e2b-5ff9-8b8c-030ea89ea27e")}catch{}})();import{settleAbandonedMergeReceipts}from"./abandoned-receipts.js";import{readFile}from"node:fs/promises";import path from"node:path";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{compareTimestampStrings}from"../../core/shared/time.js";import{resolvePmRoot}from"../../core/store/paths.js";import{mapWithFixedConcurrency}from"../extension/concurrency.js";import{runHealth}from"../governance/health.js";import{DURABLE_MERGE_RECEIPT_INTRODUCED_AT,extractHistoryMergeReceiptReferences,isPreDurableDispositionEligibleReference}from"../governance/merge-receipt-history.js";import{runValidate}from"../governance/validate.js";import{runHistoryRepair,runHistoryRepairAll}from"../history-repair.js";import{findGitWorkspaceRoot}from"./install.js";import{inspectMergeReceiptEvidence,markMergeReceiptReconciled,summarizeMergeReceipt}from"./receipts.js";const RECEIPT_ONLY_REPAIR_CONCURRENCY=4;function missingReceiptCoordinates(health){const integrity=health.checks.find(check=>check.name==="integrity"),counts=integrity?.details.counts,count=typeof counts=="object"&&counts!==null&&!Array.isArray(counts)&&typeof counts.missing_merge_receipt_history_references=="number"?counts.missing_merge_receipt_history_references:0,details=integrity?.details.missing_merge_receipt_history_reference_details,truncated=integrity?.details.missing_merge_receipt_history_reference_details_truncated===!0,coordinates=Array.isArray(details)?details.flatMap(detail=>{if(typeof detail!="object"||detail===null||Array.isArray(detail))return[];const record=detail;return typeof record.item_id=="string"&&typeof record.history_line=="number"&&Number.isSafeInteger(record.history_line)&&typeof record.receipt_id=="string"?[{item_id:record.item_id,history_line:record.history_line,receipt_id:record.receipt_id}]:[]}):[];return{count,truncated,coordinates}}async function identifyLegacyDispositionCandidates(pmRoot,coordinates){const candidates=[];for(const coordinate of coordinates){let raw;try{raw=await readFile(path.join(pmRoot,"history",`${coordinate.item_id}.jsonl`),"utf8")}catch{continue}const line=raw.split(/\r?\n/)[coordinate.history_line-1];if(line===void 0||line.trim().length===0)continue;let parsed;try{parsed=JSON.parse(line)}catch{continue}const reference=extractHistoryMergeReceiptReferences(parsed,coordinate.item_id).find(entry=>entry.receiptId===coordinate.receipt_id);reference===void 0||!isPreDurableDispositionEligibleReference(reference)||reference.eventTimestamp===void 0||compareTimestampStrings(reference.eventTimestamp,DURABLE_MERGE_RECEIPT_INTRODUCED_AT)>=0||candidates.push({...coordinate,original_event_ts:reference.eventTimestamp})}return candidates}async function recordLegacyReceiptDispositions(params){if(params.options.dryRun===!0||params.options.force!==!0||params.candidates.length===0)return 0;const byItem=new Map;for(const candidate of params.candidates){const itemCandidates=byItem.get(candidate.item_id)??[];itemCandidates.push(candidate),byItem.set(candidate.item_id,itemCandidates)}for(const[itemId,itemCandidates]of byItem)await runHistoryRepair(itemId,{author:params.options.author??params.global.author,message:params.options.message??"accept unrecoverable pre-durable merge receipt references",force:!0,auditOperation:"merge_reconcile",forceAuditEntry:!0,auditContext:{merge:{missing_receipt_dispositions:itemCandidates.map(candidate=>({receipt_id:candidate.receipt_id,original_history_line:candidate.history_line,original_event_ts:candidate.original_event_ts,reason:"legacy_clone_local_only"}))}}},params.global);return params.candidates.length}async function runReceiptOnlyRepair(params){try{return{status:"fulfilled",value:await runHistoryRepair(params.id,{dryRun:params.dryRun,author:params.options.author??params.global.author,message:params.options.message??"record field-aware branch merge provenance",force:params.options.force,auditOperation:"merge_reconcile",auditContext:params.auditContext,forceAuditEntry:!0,mergeReceiptProof:params.mergeReceiptProof},params.global)}}catch(reason){return{status:"rejected",reason}}}function appendReceiptOnlyResults(params){params.results.forEach((settled,index)=>{const id=params.receiptOnlyIds[index];if(settled.status==="fulfilled"){const outcome=settled.value.changed?"repaired":"skipped_clean";params.repair.streams.push({id,outcome,entries_rehashed:settled.value.history.entries_rehashed,entries_patch_repaired:settled.value.history.entries_patch_repaired,reconciled_with_item:settled.value.history.reconciled_with_item,...settled.value.merge_receipt_proof?{merge_receipt_proof:settled.value.merge_receipt_proof}:{},warnings:settled.value.warnings}),params.repair.totals[outcome]+=1;return}params.repair.streams.push({id,outcome:"failed",error:settled.reason instanceof Error?settled.reason.message:String(settled.reason)}),params.repair.totals.failed+=1})}async function settleProvenReceipts(params){if(params.dryRun||params.repair.totals.failed>0)return 0;const trustedReceiptIds=new Set(params.repair.streams.flatMap(stream=>stream.merge_receipt_proof?.trusted?stream.merge_receipt_proof.receipt_ids:[])),receiptsToSettle=params.pendingReceipts.filter(receipt=>trustedReceiptIds.has(receipt.id));return await Promise.all(receiptsToSettle.map(receipt=>markMergeReceiptReconciled(params.receiptWorkspaceRoot,receipt,{requireExisting:!0}))),receiptsToSettle.length}function receiptReviewGuidance(pendingCount,complete){if(!(pendingCount===0||complete))return'If receipt proof is unavailable or competing values were discarded, first review receipts.summaries and repair.streams and re-apply intended values. Only with explicit authority to accept that review, run pm merge reconcile --force --message "<reviewed receipt disposition and rationale>". This appends an audit event; it does not restore missing evidence or discarded values.'}async function runMergeReconcile(options,global2){const dryRun=options.dryRun===!0,pmRoot=resolvePmRoot(process.cwd(),global2.path),preflightHealth=await runHealth(global2,{checkOnly:!0,full:!0,skipVectors:!0,skipDrift:!0}),missingBefore=missingReceiptCoordinates(preflightHealth),legacyDispositionCandidates=await identifyLegacyDispositionCandidates(pmRoot,missingBefore.coordinates),gitWorkspaceRoot=await findGitWorkspaceRoot(pmRoot),receiptWorkspaceRoot=gitWorkspaceRoot??pmRoot,receiptEvidence=await inspectMergeReceiptEvidence(receiptWorkspaceRoot,{includeLossless:!0,pmRoot});if(receiptEvidence.invalid_evidence_count>0)throw new PmCliError(`Merge reconciliation refused ${receiptEvidence.invalid_evidence_count} invalid receipt evidence file(s).`,EXIT_CODE.CONFLICT,{code:"merge_receipt_evidence_invalid",required:"Every clone-local and durable receipt candidate must pass bounded-file, schema, and identity validation.",recovery:{suggested_retry:"pm health --check-only --full"}});const recordedLegacyDispositionCount=await recordLegacyReceiptDispositions({candidates:legacyDispositionCandidates,options,global:global2}),pendingReceipts=receiptEvidence.receipts,abandonedReceiptIds=await settleAbandonedMergeReceipts({cwd:gitWorkspaceRoot,receipts:pendingReceipts,dryRun,author:options.author,message:options.message,global:global2}),abandonedIds=new Set(abandonedReceiptIds),receiptsByItem=new Map;for(const receipt of pendingReceipts.filter(entry=>!abandonedIds.has(entry.id))){const receipts=receiptsByItem.get(receipt.item_id)??[];receipts.push(receipt),receiptsByItem.set(receipt.item_id,receipts)}const auditContextById=Object.fromEntries([...receiptsByItem].map(([id,receipts])=>[id,{merge:{receipts:receipts.map(summarizeMergeReceipt)}}])),mergeReceiptProofById=Object.fromEntries([...receiptsByItem].map(([id,receipts])=>[id,{gitWorkspaceRoot,receipts}])),repair=await runHistoryRepairAll({dryRun,author:options.author??global2.author,message:options.message??"post-merge reconciliation of field-aware tracker history",force:options.force,auditOperation:"merge_reconcile",auditContextById,...pendingReceipts.length>0?{mergeReceiptProofById}:{}},global2),representedIds=new Set(repair.streams.map(stream=>stream.id)),receiptOnlyIds=[...receiptsByItem.keys()].filter(id=>!representedIds.has(id)),receiptOnlyResults=await mapWithFixedConcurrency(receiptOnlyIds,RECEIPT_ONLY_REPAIR_CONCURRENCY,id=>runReceiptOnlyRepair({id,dryRun,options,global:global2,auditContext:auditContextById[id],mergeReceiptProof:mergeReceiptProofById[id]}));appendReceiptOnlyResults({receiptOnlyIds,results:receiptOnlyResults,repair});const reconciledReceiptCount=await settleProvenReceipts({dryRun,pendingReceipts,repair,receiptWorkspaceRoot}),validation=await runValidate({checkHistoryDrift:!0,checkStorageIntegrity:!0},global2),postflightHealth=recordedLegacyDispositionCount===0?preflightHealth:await runHealth(global2,{checkOnly:!0,full:!0,skipVectors:!0,skipDrift:!0}),missingAfter=missingReceiptCoordinates(postflightHealth),mergeChecksGreen=validation.checks.every(check=>check.status==="ok"),receiptSettlementComplete=dryRun?pendingReceipts.length===0:reconciledReceiptCount+abandonedReceiptIds.length===pendingReceipts.length,ok=[repair.totals.failed===0,mergeChecksGreen,receiptSettlementComplete,missingAfter.count===0,!missingAfter.truncated].every(Boolean),boundedBatchGuidance=missingBefore.truncated||missingAfter.truncated?"Missing receipt coordinates exceed the bounded detail response; each reviewed --force pass can disposition only the reported eligible batch. Repeat dry-run and --force passes until receipts.missing_history_references_after=0 and pm merge reconcile returns ok=true.":void 0,explicitReviewGuidance=receiptReviewGuidance(pendingReceipts.length,ok),guidance=(dryRun?["Review repair.streams, then rerun pm merge reconcile without --dry-run to apply audited repairs.",boundedBatchGuidance,explicitReviewGuidance,"No Git hook is installed automatically; invoke this command from an explicit post-merge hook only when your repository policy opts in."]:ok?["Reconciliation is complete; commit changed history streams with the merge result.","Rerun pm merge reconcile --dry-run after future tracker-data merges as a non-mutating integrity check."]:["Reconciliation remains incomplete; inspect repair failures and non-green validation checks before retrying.",boundedBatchGuidance,explicitReviewGuidance,...legacyDispositionCandidates.length>0&&recordedLegacyDispositionCount===0?["Pre-durable clone-local-only receipt references are eligible for an audited disposition; rerun with --force after reviewing receipts.legacy_disposition_eligible."]:[],"Do not commit repaired history streams as reconciled until pm merge reconcile returns ok=true."]).filter(message=>message!==void 0);return{ok,dry_run:dryRun,repair,receipts:{pending_before:pendingReceipts.length,reconciled:reconciledReceiptCount,abandoned:abandonedReceiptIds.length,summaries:pendingReceipts.map(summarizeMergeReceipt),missing_history_references_before:missingBefore.count,legacy_disposition_eligible:legacyDispositionCandidates.length,legacy_disposition_recorded:recordedLegacyDispositionCount,missing_history_references_after:missingAfter.count},validation,guidance,generated_at:validation.generated_at}}export{runMergeReconcile};
|
|
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]="4d506b3c-f1c1-5642-a258-45d93ba64d81")}catch{}})();import{settleAbandonedMergeReceipts}from"./abandoned-receipts.js";import{readFile}from"node:fs/promises";import path from"node:path";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{compareTimestampStrings}from"../../core/shared/time.js";import{resolvePmRoot}from"../../core/store/paths.js";import{mapWithFixedConcurrency}from"../extension/concurrency.js";import{runHealth}from"../governance/health.js";import{DURABLE_MERGE_RECEIPT_INTRODUCED_AT,extractHistoryMergeReceiptReferences,isPreDurableDispositionEligibleReference}from"../governance/merge-receipt-history.js";import{runValidate}from"../governance/validate.js";import{runHistoryRepair,runHistoryRepairAll}from"../history-repair.js";import{findGitWorkspaceRoot}from"./install.js";import{assertPortableMergeReceiptReferences,inspectMergeReceiptEvidence,markMergeReceiptReconciled,summarizeMergeReceipt}from"./receipts.js";const RECEIPT_ONLY_REPAIR_CONCURRENCY=4;function missingReceiptCoordinates(health){const integrity=health.checks.find(check=>check.name==="integrity"),counts=integrity?.details.counts,count=typeof counts=="object"&&counts!==null&&!Array.isArray(counts)&&typeof counts.missing_merge_receipt_history_references=="number"?counts.missing_merge_receipt_history_references:0,details=integrity?.details.missing_merge_receipt_history_reference_details,truncated=integrity?.details.missing_merge_receipt_history_reference_details_truncated===!0,coordinates=Array.isArray(details)?details.flatMap(detail=>{if(typeof detail!="object"||detail===null||Array.isArray(detail))return[];const record=detail;return typeof record.item_id=="string"&&typeof record.history_line=="number"&&Number.isSafeInteger(record.history_line)&&typeof record.receipt_id=="string"?[{item_id:record.item_id,history_line:record.history_line,receipt_id:record.receipt_id}]:[]}):[];return{count,truncated,coordinates}}async function identifyLegacyDispositionCandidates(pmRoot,coordinates){const candidates=[];for(const coordinate of coordinates){let raw;try{raw=await readFile(path.join(pmRoot,"history",`${coordinate.item_id}.jsonl`),"utf8")}catch{continue}const line=raw.split(/\r?\n/)[coordinate.history_line-1];if(line===void 0||line.trim().length===0)continue;let parsed;try{parsed=JSON.parse(line)}catch{continue}const reference=extractHistoryMergeReceiptReferences(parsed,coordinate.item_id).find(entry=>entry.receiptId===coordinate.receipt_id);reference===void 0||!isPreDurableDispositionEligibleReference(reference)||reference.eventTimestamp===void 0||compareTimestampStrings(reference.eventTimestamp,DURABLE_MERGE_RECEIPT_INTRODUCED_AT)>=0||candidates.push({...coordinate,original_event_ts:reference.eventTimestamp})}return candidates}async function recordLegacyReceiptDispositions(params){if(params.options.dryRun===!0||params.options.force!==!0||params.candidates.length===0)return 0;const byItem=new Map;for(const candidate of params.candidates){const itemCandidates=byItem.get(candidate.item_id)??[];itemCandidates.push(candidate),byItem.set(candidate.item_id,itemCandidates)}for(const[itemId,itemCandidates]of byItem)await runHistoryRepair(itemId,{author:params.options.author??params.global.author,message:params.options.message??"accept unrecoverable pre-durable merge receipt references",force:!0,auditOperation:"merge_reconcile",forceAuditEntry:!0,auditContext:{merge:{missing_receipt_dispositions:itemCandidates.map(candidate=>({receipt_id:candidate.receipt_id,original_history_line:candidate.history_line,original_event_ts:candidate.original_event_ts,reason:"legacy_clone_local_only"}))}}},params.global);return params.candidates.length}async function runReceiptOnlyRepair(params){try{return{status:"fulfilled",value:await runHistoryRepair(params.id,{dryRun:params.dryRun,author:params.options.author??params.global.author,message:params.options.message??"record field-aware branch merge provenance",force:params.options.force,auditOperation:"merge_reconcile",auditContext:params.auditContext,forceAuditEntry:!0,mergeReceiptProof:params.mergeReceiptProof},params.global)}}catch(reason){return{status:"rejected",reason}}}function appendReceiptOnlyResults(params){params.results.forEach((settled,index)=>{const id=params.receiptOnlyIds[index];if(settled.status==="fulfilled"){const outcome=settled.value.changed?"repaired":"skipped_clean";params.repair.streams.push({id,outcome,entries_rehashed:settled.value.history.entries_rehashed,entries_patch_repaired:settled.value.history.entries_patch_repaired,reconciled_with_item:settled.value.history.reconciled_with_item,...settled.value.merge_receipt_proof?{merge_receipt_proof:settled.value.merge_receipt_proof}:{},warnings:settled.value.warnings}),params.repair.totals[outcome]+=1;return}params.repair.streams.push({id,outcome:"failed",error:settled.reason instanceof Error?settled.reason.message:String(settled.reason)}),params.repair.totals.failed+=1})}async function settleProvenReceipts(params){if(params.dryRun||params.repair.totals.failed>0)return 0;const trustedReceiptIds=new Set(params.repair.streams.flatMap(stream=>stream.merge_receipt_proof?.trusted?stream.merge_receipt_proof.receipt_ids:[])),receiptsToSettle=params.pendingReceipts.filter(receipt=>trustedReceiptIds.has(receipt.id));return await Promise.all(receiptsToSettle.map(receipt=>markMergeReceiptReconciled(params.receiptWorkspaceRoot,receipt,{requireExisting:!0}))),receiptsToSettle.length}function receiptReviewGuidance(pendingCount,complete){if(!(pendingCount===0||complete))return'If receipt proof is unavailable or competing values were discarded, first review receipts.summaries and repair.streams and re-apply intended values. Only with explicit authority to accept that review, run pm merge reconcile --force --message "<reviewed receipt disposition and rationale>". This appends an audit event; it does not restore missing evidence or discarded values.'}async function runStagedMergeReconcile(options,global2){if(options.force===!0)throw new PmCliError("Automatic staging cannot accept forced reconciliation.",EXIT_CODE.CONFLICT,{code:"merge_settlement_review_required",recovery:{suggested_retry:"pm merge reconcile --dry-run"}});const unstagedOptions={...options,stage:!1},{stageLosslessMergeReconciliation,resolveMergeSettlementGlobal}=await import("./stage.js"),settledGlobal=await resolveMergeSettlementGlobal(global2),preview=await runMergeReconcile({...unstagedOptions,dryRun:!0},settledGlobal);return stageLosslessMergeReconciliation({preview,dryRun:options.dryRun===!0,global:settledGlobal,apply:()=>runMergeReconcile({...unstagedOptions,dryRun:!1},settledGlobal)})}async function runMergeReconcile(options,global2){return options.stage===!0?runStagedMergeReconcile(options,global2):reconcileMergeHistory(options,global2)}async function reconcileMergeHistory(options,global2){const dryRun=options.dryRun===!0,pmRoot=resolvePmRoot(process.cwd(),global2.path),preflightHealth=await runHealth(global2,{checkOnly:!0,full:!0,skipVectors:!0,skipDrift:!0}),missingBefore=missingReceiptCoordinates(preflightHealth),legacyDispositionCandidates=await identifyLegacyDispositionCandidates(pmRoot,missingBefore.coordinates),gitWorkspaceRoot=await findGitWorkspaceRoot(pmRoot),receiptWorkspaceRoot=gitWorkspaceRoot??pmRoot,receiptEvidence=await inspectMergeReceiptEvidence(receiptWorkspaceRoot,{includeLossless:!0,pmRoot});if(receiptEvidence.invalid_evidence_count>0)throw new PmCliError(`Merge reconciliation refused ${receiptEvidence.invalid_evidence_count} invalid receipt evidence file(s).`,EXIT_CODE.CONFLICT,{code:"merge_receipt_evidence_invalid",required:"Every clone-local and durable receipt candidate must pass bounded-file, schema, and identity validation.",recovery:{suggested_retry:"pm health --check-only --full"}});assertPortableMergeReceiptReferences(receiptEvidence.receipts.map(receipt=>receipt.id),receiptEvidence,dryRun);const recordedLegacyDispositionCount=await recordLegacyReceiptDispositions({candidates:legacyDispositionCandidates,options,global:global2}),pendingReceipts=receiptEvidence.receipts,abandonedReceiptIds=await settleAbandonedMergeReceipts({cwd:gitWorkspaceRoot,receipts:pendingReceipts,dryRun,author:options.author,message:options.message,global:global2}),abandonedIds=new Set(abandonedReceiptIds),receiptsByItem=new Map;for(const receipt of pendingReceipts.filter(entry=>!abandonedIds.has(entry.id))){const receipts=receiptsByItem.get(receipt.item_id)??[];receipts.push(receipt),receiptsByItem.set(receipt.item_id,receipts)}const auditContextById=Object.fromEntries([...receiptsByItem].map(([id,receipts])=>[id,{merge:{receipts:receipts.map(summarizeMergeReceipt)}}])),mergeReceiptProofById=Object.fromEntries([...receiptsByItem].map(([id,receipts])=>[id,{gitWorkspaceRoot,receipts}])),repair=await runHistoryRepairAll({dryRun,author:options.author??global2.author,message:options.message??"post-merge reconciliation of field-aware tracker history",force:options.force,auditOperation:"merge_reconcile",auditContextById,...pendingReceipts.length>0?{mergeReceiptProofById}:{}},global2),representedIds=new Set(repair.streams.map(stream=>stream.id)),receiptOnlyIds=[...receiptsByItem.keys()].filter(id=>!representedIds.has(id)),receiptOnlyResults=await mapWithFixedConcurrency(receiptOnlyIds,RECEIPT_ONLY_REPAIR_CONCURRENCY,id=>runReceiptOnlyRepair({id,dryRun,options,global:global2,auditContext:auditContextById[id],mergeReceiptProof:mergeReceiptProofById[id]}));appendReceiptOnlyResults({receiptOnlyIds,results:receiptOnlyResults,repair});const reconciledReceiptCount=await settleProvenReceipts({dryRun,pendingReceipts,repair,receiptWorkspaceRoot}),validation=await runValidate({checkHistoryDrift:!0,checkStorageIntegrity:!0},global2),postflightHealth=recordedLegacyDispositionCount===0?preflightHealth:await runHealth(global2,{checkOnly:!0,full:!0,skipVectors:!0,skipDrift:!0}),missingAfter=missingReceiptCoordinates(postflightHealth),mergeChecksGreen=validation.checks.every(check=>check.status==="ok"),receiptSettlementComplete=dryRun?pendingReceipts.length===0:reconciledReceiptCount+abandonedReceiptIds.length===pendingReceipts.length,ok=[repair.totals.failed===0,mergeChecksGreen,receiptSettlementComplete,missingAfter.count===0,!missingAfter.truncated].every(Boolean),boundedBatchGuidance=missingBefore.truncated||missingAfter.truncated?"Missing receipt coordinates exceed the bounded detail response; each reviewed --force pass can disposition only the reported eligible batch. Repeat dry-run and --force passes until receipts.missing_history_references_after=0 and pm merge reconcile returns ok=true.":void 0,explicitReviewGuidance=receiptReviewGuidance(pendingReceipts.length,ok),guidance=(dryRun?["Review repair.streams, then rerun pm merge reconcile without --dry-run to apply audited repairs.",boundedBatchGuidance,explicitReviewGuidance,"No Git hook is installed automatically; invoke this command from an explicit post-merge hook only when your repository policy opts in."]:ok?["Reconciliation is complete; commit changed history streams with the merge result.","Rerun pm merge reconcile --dry-run after future tracker-data merges as a non-mutating integrity check."]:["Reconciliation remains incomplete; inspect repair failures and non-green validation checks before retrying.",boundedBatchGuidance,explicitReviewGuidance,...legacyDispositionCandidates.length>0&&recordedLegacyDispositionCount===0?["Pre-durable clone-local-only receipt references are eligible for an audited disposition; rerun with --force after reviewing receipts.legacy_disposition_eligible."]:[],"Do not commit repaired history streams as reconciled until pm merge reconcile returns ok=true."]).filter(message=>message!==void 0);return{ok,dry_run:dryRun,repair,receipts:{pending_before:pendingReceipts.length,reconciled:reconciledReceiptCount,abandoned:abandonedReceiptIds.length,summaries:pendingReceipts.map(summarizeMergeReceipt),unstaged_durable_receipt_ids:receiptEvidence.unstaged_durable_receipt_ids,missing_history_references_before:missingBefore.count,legacy_disposition_eligible:legacyDispositionCandidates.length,legacy_disposition_recorded:recordedLegacyDispositionCount,missing_history_references_after:missingAfter.count},validation,guidance,generated_at:validation.generated_at}}export{runMergeReconcile};
|
|
2
2
|
//# sourceMappingURL=reconcile.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Preview an owned hook without replacing existing user policy or following file links. */
|
|
2
|
+
export declare function prepareMergeSettlementHook(workspaceRoot:string,command:string):Promise<{
|
|
3
|
+
path:string;
|
|
4
|
+
changed:boolean;
|
|
5
|
+
content:string;
|
|
6
|
+
}>;
|
|
7
|
+
/** Publish an explicitly requested hook after its command and merge fence are installed. */
|
|
8
|
+
export declare function writeMergeSettlementHook(hook:{
|
|
9
|
+
path:string;
|
|
10
|
+
changed:boolean;
|
|
11
|
+
content:string;
|
|
12
|
+
}):Promise<void>;
|
|
@@ -0,0 +1,7 @@
|
|
|
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]="5b6eab08-f707-586f-bb05-484bd9160551")}catch{}})();import{execFile}from"node:child_process";import{chmod,lstat}from"node:fs/promises";import{promisify}from"node:util";import{isFileMissingError,writeFileAtomic}from"../../core/fs/fs-utils.js";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{readBoundedRegularFile}from"./receipt-file-boundary.js";import{gitWorkspaceEnvironment}from"./worktree-config.js";const execFileAsync=promisify(execFile),HOOK_HEADER=`#!/bin/sh
|
|
2
|
+
# pm-cli:lossless-merge-settlement:v1
|
|
3
|
+
`;async function prepareMergeSettlementHook(workspaceRoot,command){const hookPath=(await execFileAsync("git",["rev-parse","--path-format=absolute","--git-path","hooks/pre-commit"],{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4})).stdout.trim();let content,executable=!1;try{content=await readBoundedRegularFile(hookPath,65536)??"",executable=process.platform==="win32"||((await lstat(hookPath)).mode&73)!==0}catch(error){if(!isFileMissingError(error))throw error}if(content!==void 0&&!content.startsWith(HOOK_HEADER))throw new PmCliError("The pre-commit hook already exists and is not owned by pm; it was preserved.",EXIT_CODE.CONFLICT,{code:"merge_settlement_hook_conflict",recovery:{suggested_retry:"pm merge install --dry-run"},nextSteps:["Integrate pm merge reconcile --stage into your existing hook after reviewing its policy, or keep manual settlement. pm never replaces an existing unowned hook."]});const expected=`${HOOK_HEADER}merge_head=$(git rev-parse --git-path MERGE_HEAD) || exit 1
|
|
4
|
+
[ -f "$merge_head" ] || exit 0
|
|
5
|
+
exec ${command} merge reconcile --stage
|
|
6
|
+
`;return{path:hookPath,changed:content!==expected||!executable,content:expected}}async function writeMergeSettlementHook(hook){hook.changed&&(await writeFileAtomic(hook.path,hook.content),await chmod(hook.path,493))}export{prepareMergeSettlementHook,writeMergeSettlementHook};
|
|
7
|
+
//# sourceMappingURL=settlement-hook.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { GlobalOptions } from "../../core/shared/command-types.js";
|
|
2
|
+
import type { MergeReconcileResult } from "./reconcile.js";
|
|
3
|
+
/** Bind preview, proof verification and staging to one physical tracker location. */
|
|
4
|
+
export declare function resolveMergeSettlementGlobal(global:GlobalOptions):Promise<GlobalOptions & {
|
|
5
|
+
path:string;
|
|
6
|
+
}>;
|
|
7
|
+
/** Stage only the verified history and receipt paths, preserving all unrelated index entries. */
|
|
8
|
+
export declare function stageLosslessMergeReconciliation(params:{
|
|
9
|
+
preview:MergeReconcileResult;
|
|
10
|
+
dryRun:boolean;
|
|
11
|
+
global:GlobalOptions;
|
|
12
|
+
apply:() => Promise<MergeReconcileResult>;
|
|
13
|
+
}):Promise<MergeReconcileResult>;
|
|
@@ -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]="fe5937fc-def3-5ed4-9663-70a6e9d258a1")}catch{}})();import{execFile}from"node:child_process";import{realpath}from"node:fs/promises";import path from"node:path";import{promisify}from"node:util";import{EXIT_CODE}from"../../core/shared/constants.js";import{PmCliError}from"../../core/shared/errors.js";import{resolvePmRoot}from"../../core/store/paths.js";import{findGitWorkspaceRoot}from"./install.js";import{gitWorkspaceEnvironment}from"./worktree-config.js";import{isPathOutsideRoot}from"../workspace.js";const execFileAsync=promisify(execFile);function stagingRefusal(reason){return new PmCliError(`Automatic merge settlement refused: ${reason}`,EXIT_CODE.CONFLICT,{code:"merge_settlement_review_required",recovery:{suggested_retry:"pm merge reconcile --dry-run"},nextSteps:["Inspect receipts, repair streams and Git changes. Resolve competing values or unrelated drift explicitly, then run pm merge reconcile and stage the reviewed artifacts manually."]})}async function assertNormalGitIndex(git){const indexOverride=process.env.GIT_INDEX_FILE;if(indexOverride===void 0)return;const indexPath=(await git(["rev-parse","--path-format=absolute","--git-path","index"])).stdout.trim();if(path.resolve(indexOverride)!==path.resolve(indexPath))throw stagingRefusal("an alternate Git index requires explicit manual settlement")}async function newReceiptPaths(git,receiptPaths){if(receiptPaths.length===0)return[];const pathspecs=receiptPaths.map(target=>`:(literal)${target}`),indexed=new Set((await git(["ls-files","--cached","-z","--",...pathspecs])).stdout.split("\0")),missing=receiptPaths.filter(target=>!indexed.has(target)).map(target=>`:(literal)${target}`);if(missing.length>0&&(await git(["diff","--cached","--name-only","--diff-filter=D","-z","--",...missing])).stdout.length>0)throw stagingRefusal("a receipt has a staged deletion that requires explicit review");return missing}async function applyAndStage(params){const{git,preview,paths,newReceiptPathspecs}=params;let preparedReceipts=!1;try{newReceiptPathspecs.length>0&&(await git(["add","--",...newReceiptPathspecs]),preparedReceipts=!0);const result=await params.apply();if(!result.ok||JSON.stringify(result.receipts.summaries)!==JSON.stringify(preview.receipts.summaries)||result.repair.streams.some(stream=>!preview.repair.streams.some(prior=>prior.id===stream.id)))throw stagingRefusal("reconciliation changed or did not finish; inspect the audit artifacts before staging");return paths.length>0&&await git(["add","--",...paths.map(target=>`:(literal)${target}`)]),{...result,staged_paths:paths,guidance:["Lossless reconciliation artifacts are staged for the merge commit. Review git diff --cached before committing."]}}catch(error){throw preparedReceipts&&await git(["reset","--quiet","--",...newReceiptPathspecs]),error}}function assertLosslessPreview(preview){if(preview.repair.totals.failed>0||preview.receipts.missing_history_references_after>0||preview.receipts.summaries.some(receipt=>receipt.conflict_fields.length>0)||preview.repair.streams.some(stream=>stream.merge_receipt_proof?.trusted!==!0)||preview.validation.checks.some(check=>check.name!=="history_drift"&&check.status!=="ok"))throw stagingRefusal("only fully authenticated receipt-backed repairs without discarded values can be staged")}async function resolveMergeSettlementGlobal(global2){try{return{...global2,path:await realpath(resolvePmRoot(process.cwd(),global2.path))}}catch{throw stagingRefusal("the tracker root cannot be resolved")}}async function stageLosslessMergeReconciliation(params){const{preview}=params;assertLosslessPreview(preview);const{path:pmRoot}=await resolveMergeSettlementGlobal(params.global),workspaceRoot=await findGitWorkspaceRoot(pmRoot);if(workspaceRoot===null)throw stagingRefusal("a Git workspace is required");const git=args=>execFileAsync("git",args,{cwd:workspaceRoot,env:gitWorkspaceEnvironment(),timeout:1e4});if(await assertNormalGitIndex(git),(await git(["ls-files","--unmerged","-z"])).stdout.length>0)throw stagingRefusal("Git still has unresolved paths");const paths=[...new Set([...[...new Set([...preview.repair.streams.map(stream=>stream.id),...preview.receipts.summaries.map(receipt=>receipt.item_id)])].map(id=>path.join(pmRoot,"history",`${id}.jsonl`)),...preview.receipts.summaries.map(receipt=>path.join(pmRoot,"merge-receipts",`${receipt.receipt_id}.json`))])].map(target=>path.relative(workspaceRoot,target).replaceAll("\\","/"));if(paths.some(isPathOutsideRoot))throw stagingRefusal("reconciliation artifacts must belong to this Git workspace");const pathspecs=paths.map(target=>`:(literal)${target}`),receiptPaths=preview.receipts.summaries.map(receipt=>path.relative(workspaceRoot,path.join(pmRoot,"merge-receipts",`${receipt.receipt_id}.json`)).replaceAll("\\","/"));if(pathspecs.length>0)try{await git(["diff","--quiet","--",...pathspecs])}catch{throw stagingRefusal("reconciliation paths have unstaged edits or Git cannot verify their index state")}const newReceiptPathspecs=await newReceiptPaths(git,receiptPaths);return params.dryRun?{...preview,staged_paths:[]}:applyAndStage({...params,git,paths,newReceiptPathspecs})}export{resolveMergeSettlementGlobal,stageLosslessMergeReconciliation};
|
|
2
|
+
//# sourceMappingURL=stage.js.map
|