@unbrained/pm-cli 2026.9.30 → 2026.10.2

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 (134) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +20 -0
  3. package/dist/cli/error-guidance.d.ts +5 -0
  4. package/dist/cli/error-guidance.js +1 -1
  5. package/dist/cli/main.js +2 -2
  6. package/dist/cli/register-list-query.d.ts +8 -5
  7. package/dist/cli/register-list-query.js +2 -2
  8. package/dist/cli/register-structured-mutation.d.ts +2 -0
  9. package/dist/cli/register-structured-mutation.js +1 -1
  10. package/dist/cli/runtime/projection-retry.d.ts +9 -0
  11. package/dist/cli/runtime/projection-retry.js +2 -0
  12. package/dist/cli/runtime/selection.d.ts +5 -1
  13. package/dist/cli/runtime/selection.js +1 -1
  14. package/dist/cli-bundle/bundle-manifest.json +157 -157
  15. package/dist/cli-bundle/chunks/{chunk-EGAIIWI3.js → chunk-7FUEQOYG.js} +2 -2
  16. package/dist/cli-bundle/chunks/chunk-7NL3A2BY.js +2 -0
  17. package/dist/cli-bundle/chunks/chunk-CZLAWXDR.js +8 -0
  18. package/dist/cli-bundle/chunks/chunk-HY6M34B4.js +2 -0
  19. package/dist/cli-bundle/chunks/{chunk-O23CQ5T4.js → chunk-MTJMOI4I.js} +14 -14
  20. package/dist/cli-bundle/chunks/chunk-R4TUD355.js +6 -0
  21. package/dist/cli-bundle/chunks/chunk-R7ULWMBG.js +238 -0
  22. package/dist/cli-bundle/chunks/chunk-SV7CZEVV.js +20 -0
  23. package/dist/cli-bundle/chunks/chunk-TRYXVQ7W.js +3 -0
  24. package/dist/cli-bundle/chunks/{chunk-BTCE36JS.js → chunk-WP6NRH72.js} +2 -2
  25. package/dist/cli-bundle/chunks/{chunk-XRN3JYNN.js → chunk-XOUBIF6Z.js} +38 -38
  26. package/dist/cli-bundle/chunks/chunk-Z2GYDSAO.js +3 -0
  27. package/dist/cli-bundle/chunks/{chunk-6YEET7XI.js → chunk-ZI7F3FVE.js} +47 -47
  28. package/dist/cli-bundle/chunks/{register-list-query-SYNBVMNG.js → register-list-query-R4PHNB7W.js} +6 -6
  29. package/dist/cli-bundle/chunks/register-mutation-PDDAUDYV.js +20 -0
  30. package/dist/cli-bundle/chunks/{register-operations-7FCJUC4Z.js → register-operations-A5HJT46K.js} +2 -2
  31. package/dist/cli-bundle/chunks/{register-setup-YTWW2KL5.js → register-setup-EZ2F25JI.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-MOUCTR72.js → chunk-3BIYG72P.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/{chunk-T4PZJQG4.js → chunk-4FFPGNCJ.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-AKB3JN57.js → chunk-BLCTBSGT.js} +45 -45
  35. package/dist/cli-bundle/focused-chunks/{chunk-XUCXRZZZ.js → chunk-DBD4W4QP.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/{chunk-KZULQIAG.js → chunk-E754JDLY.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-4AORVUT4.js → chunk-EJN7DY3B.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/{chunk-OMBDUJWZ.js → chunk-FKK2HI3S.js} +2 -2
  39. package/dist/cli-bundle/focused-chunks/chunk-GBD4EN6S.js +12 -0
  40. package/dist/cli-bundle/focused-chunks/{chunk-ZJ3CLTV4.js → chunk-GMYUNHST.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/chunk-IR4UXLRP.js +20 -0
  42. package/dist/cli-bundle/focused-chunks/{chunk-RIJQQOEZ.js → chunk-UTRSM3ZK.js} +2 -2
  43. package/dist/cli-bundle/focused-chunks/{chunk-EVMH4SKN.js → chunk-VVWYI2X7.js} +2 -2
  44. package/dist/cli-bundle/focused-chunks/chunk-WCY6HRZO.js +30 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-XB5Q2P7Z.js → chunk-XRSCIYHR.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/chunk-YC32ZSLS.js +165 -0
  47. package/dist/cli-bundle/focused-chunks/{chunk-IQBFYQF2.js → chunk-ZIXFMIUL.js} +2 -2
  48. package/dist/cli-bundle/main.js +22 -22
  49. package/dist/cli-bundle/sdk-authoring.js +1 -1
  50. package/dist/cli-bundle/sdk-contracts.js +2 -2
  51. package/dist/cli-bundle/sdk-core.js +2 -2
  52. package/dist/cli-bundle/sdk-governance.js +1 -1
  53. package/dist/cli-bundle/sdk-graph.js +1 -1
  54. package/dist/cli-bundle/sdk-merge.js +48 -48
  55. package/dist/cli-bundle/sdk-query.js +1 -1
  56. package/dist/cli-bundle/sdk-runtime.js +1 -1
  57. package/dist/cli-bundle/sdk-testing.js +1 -1
  58. package/dist/cli-bundle/sdk.js +7 -7
  59. package/dist/core/fs/fs-utils.d.ts +11 -0
  60. package/dist/core/fs/fs-utils.js +2 -2
  61. package/dist/core/shared/errors.d.ts +9 -0
  62. package/dist/core/shared/errors.js +1 -1
  63. package/dist/core/store/settings.d.ts +15 -5
  64. package/dist/core/store/settings.js +3 -3
  65. package/dist/core/telemetry/runtime.js +3 -3
  66. package/dist/mcp/server.js +1 -1
  67. package/dist/sdk/agent/closed-domain-contracts.js +1 -1
  68. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
  69. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  70. package/dist/sdk/item-transaction.d.ts +11 -0
  71. package/dist/sdk/item-transaction.js +1 -1
  72. package/dist/sdk/merge/install.d.ts +2 -2
  73. package/dist/sdk/merge/install.js +1 -1
  74. package/dist/sdk/pagination.js +1 -1
  75. package/dist/sdk/public-surface.json +1 -1
  76. package/dist/sdk/query/context.d.ts +2 -0
  77. package/dist/sdk/query/context.js +1 -1
  78. package/dist/sdk/query/get.d.ts +19 -15
  79. package/dist/sdk/query/get.js +1 -1
  80. package/dist/sdk/query/list.js +1 -1
  81. package/dist/sdk/query/search-contracts.js +1 -1
  82. package/dist/sdk/query/search-pagination.d.ts +4 -0
  83. package/dist/sdk/query/search-pagination.js +1 -1
  84. package/dist/sdk/query/search.js +1 -1
  85. package/dist/sdk/read-output/options.d.ts +14 -0
  86. package/dist/sdk/read-output/options.js +2 -0
  87. package/dist/sdk/read-output-budget.js +1 -1
  88. package/dist/sdk/read-output-contracts.js +1 -1
  89. package/dist/sdk/read-output-rows.d.ts +4 -0
  90. package/dist/sdk/read-output-rows.js +1 -1
  91. package/dist/sdk/runtime-primitives.d.ts +2 -2
  92. package/dist/sdk/runtime-primitives.js +1 -1
  93. package/dist/sdk/runtime.js +1 -1
  94. package/dist/sdk/workspace-transaction.d.ts +2 -0
  95. package/dist/sdk/workspace-transaction.js +2 -2
  96. package/docs/BOUNDED_CONTEXT_READS.md +10 -7
  97. package/docs/COMMANDS.md +1 -1
  98. package/docs/MERGE_SAFETY.md +12 -6
  99. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +27 -0
  100. package/docs/SDK.md +98 -0
  101. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +6 -3
  102. package/marketplace.json +2 -2
  103. package/package.json +1 -1
  104. package/packages/pm-beads/package.json +1 -1
  105. package/packages/pm-calendar/package.json +1 -1
  106. package/packages/pm-command-kit/package.json +1 -1
  107. package/packages/pm-digital-twin/package.json +1 -1
  108. package/packages/pm-governance-audit/package.json +1 -1
  109. package/packages/pm-guide-shell/package.json +1 -1
  110. package/packages/pm-kanban/package.json +1 -1
  111. package/packages/pm-lifecycle-hooks/package.json +1 -1
  112. package/packages/pm-linked-test-adapters/package.json +1 -1
  113. package/packages/pm-search-advanced/package.json +1 -1
  114. package/packages/pm-templates/package.json +1 -1
  115. package/packages/pm-todos/package.json +1 -1
  116. package/packages/pm-vcs/package.json +1 -1
  117. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  118. package/plugins/pm-claude/package.json +2 -2
  119. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  120. package/plugins/pm-codex/package.json +2 -2
  121. package/plugins/pm-codex/plugin.json +1 -1
  122. package/dist/cli-bundle/chunks/chunk-6XTJLOKW.js +0 -3
  123. package/dist/cli-bundle/chunks/chunk-A6NDNRCK.js +0 -6
  124. package/dist/cli-bundle/chunks/chunk-BNAT4EYR.js +0 -20
  125. package/dist/cli-bundle/chunks/chunk-BX4LEJX4.js +0 -8
  126. package/dist/cli-bundle/chunks/chunk-FYTXVVZG.js +0 -2
  127. package/dist/cli-bundle/chunks/chunk-UIAH7UFK.js +0 -238
  128. package/dist/cli-bundle/chunks/chunk-USTAFHIM.js +0 -2
  129. package/dist/cli-bundle/chunks/chunk-YM342ELM.js +0 -3
  130. package/dist/cli-bundle/chunks/register-mutation-4GSUK23D.js +0 -20
  131. package/dist/cli-bundle/focused-chunks/chunk-3CAZ7Z4R.js +0 -30
  132. package/dist/cli-bundle/focused-chunks/chunk-EFD4ATS7.js +0 -12
  133. package/dist/cli-bundle/focused-chunks/chunk-IDB6E3ZV.js +0 -165
  134. package/dist/cli-bundle/focused-chunks/chunk-UPOMLSW7.js +0 -20
@@ -1,3 +1,3 @@
1
- !(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4d7f490a-03a3-512c-ba3c-b19fcc9a1270")}catch(e2){}})();import{mkdir,readFile}from"node:fs/promises";import path from"node:path";import{acquireLock}from"../core/lock/lock.js";import{isFileMissingError,writeFileAtomic}from"../core/fs/fs-utils.js";import{nowIso}from"../core/shared/time.js";class WorkspaceTransactionInterruptedError extends Error{constructor(message="Workspace transaction interrupted"){super(message);this.name="WorkspaceTransactionInterruptedError"}}const WORKSPACE_TRANSACTION_LOCK_ID="sdk-workspace-transaction";const TRANSACTION_ID_PATTERN=/^[a-zA-Z0-9._-]+$/;const DEFAULT_LOCK_TTL_SECONDS=30;const DEFAULT_LOCK_WAIT_MS=3e3;function requiredIdentifier(value,label){if(typeof value!=="string")throw new TypeError(`${label} must be a string`);const normalized=value.trim();if(!normalized||!TRANSACTION_ID_PATTERN.test(normalized))throw new TypeError(`${label} must match [a-zA-Z0-9._-]+`);return normalized}function requiredText(value,label){if(typeof value!=="string")throw new TypeError(`${label} must be a string`);const normalized=value.trim();if(!normalized)throw new TypeError(`${label} must be non-empty`);return normalized}function positiveInteger(value,fallback,label){const resolved=value??fallback;if(!Number.isInteger(resolved)||resolved<=0)throw new TypeError(`${label} must be a positive integer`);return resolved}function journalPath(pmRoot,transactionId){return path.join(pmRoot,"transactions","sdk",`${transactionId}.json`)}async function writeJournal(pmRoot,journal){journal.updatedAt=nowIso();const target=journalPath(pmRoot,journal.transactionId);await mkdir(path.dirname(target),{recursive:true});await writeFileAtomic(target,`${JSON.stringify(journal,null,2)}
2
- `)}function hasValidJournalMetadata(journal){return Number.isInteger(journal.attempt)&&typeof journal.author==="string"&&typeof journal.createdAt==="string"&&typeof journal.updatedAt==="string"}function createResultMap(source){return Object.assign(Object.create(null),source)}function cloneJournalValue(value,label){try{const serialized=JSON.stringify(value,(_key,nestedValue)=>{if(typeof nestedValue==="number"&&!Number.isFinite(nestedValue))throw new TypeError(`${label} contains a non-finite number`);if(nestedValue===void 0||typeof nestedValue==="function"||typeof nestedValue==="symbol")throw new TypeError(`${label} contains a non-JSON value`);return nestedValue});return JSON.parse(serialized)}catch(error){throw new TypeError(`${label} must be JSON serializable`,{cause:error})}}function cloneOptionalJournalValue(value,label){return value===void 0?void 0:cloneJournalValue(value,label)}function setJournalEntry(target,stepId,value,label){const cloned=cloneOptionalJournalValue(value,label);if(cloned===void 0)delete target[stepId];else target[stepId]=cloned}function isJournalMap(value){return value!==null&&typeof value==="object"&&!Array.isArray(value)}function hasUniqueKnownStepIds(value,stepIds){return Array.isArray(value)&&new Set(value).size===value.length&&value.every(stepId=>typeof stepId==="string"&&stepIds.includes(stepId))}function hasValidJournalLifecycle(journal,stepIds){if(!hasUniqueKnownStepIds(journal.startedStepIds,stepIds)||!hasUniqueKnownStepIds(journal.completedStepIds,stepIds)||!isJournalMap(journal.results)||!isJournalMap(journal.compensationData))return false;const startedStepIds=journal.startedStepIds;const started=new Set(startedStepIds);const completedStepIds=journal.completedStepIds;const completed=new Set(completedStepIds);if(Object.keys(journal.compensationData).some(stepId=>!started.has(stepId))||Object.keys(journal.results).some(stepId=>!completed.has(stepId)))return false;if(journal.status==="committed")return stepIds.every(stepId=>completed.has(stepId));return journal.status!=="compensated"||completed.size===0&&started.size===0}function parseJournal(raw,transactionId,stepIds){let parsed;try{parsed=JSON.parse(raw)}catch{throw new TypeError(`Workspace transaction ${transactionId} journal is invalid JSON`)}if(!parsed||typeof parsed!=="object"||Array.isArray(parsed))throw new TypeError(`Workspace transaction ${transactionId} journal is invalid`);const journal=parsed;const identityMatches=journal.schemaVersion===1&&journal.transactionId===transactionId;const stepsMatch=Array.isArray(journal.stepIds)&&journal.stepIds.length===stepIds.length&&journal.stepIds.every((stepId,index)=>stepId===stepIds[index]);const lifecycleMatches=hasValidJournalLifecycle(journal,stepIds);const metadataMatches=hasValidJournalMetadata(journal);const statusMatches=["applying","compensating","compensated","committed"].includes(String(journal.status));if(!identityMatches||!stepsMatch||!lifecycleMatches||!metadataMatches||!statusMatches)throw new TypeError(`Workspace transaction ${transactionId} journal does not match the supplied plan`);return{...journal,results:createResultMap(journal.results),compensationData:createResultMap(journal.compensationData)}}async function prepareJournalForApply(options,transactionId,author,stepIds,existing){let journal=existing;const recovered=journal!==void 0;if(journal?.status==="compensating")await compensateAppliedSteps(options,journal);if(journal===void 0||journal.status==="compensated"){const timestamp=nowIso();journal={schemaVersion:1,transactionId,author,status:"applying",attempt:(journal?.attempt??0)+1,createdAt:journal?.createdAt??timestamp,updatedAt:timestamp,stepIds,startedStepIds:[],completedStepIds:[],results:createResultMap(),compensationData:createResultMap()};await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"prepared")}return{journal,recovered}}async function applyPreparedTransaction(options,journal,recovered){try{await discoverAppliedSteps(journal,options.steps);await writeJournal(options.pmRoot,journal);const completed=new Set(journal.completedStepIds);const started=new Set(journal.startedStepIds);for(const step of options.steps){if(completed.has(step.id))continue;const inspection=await step.inspect();let result=cloneOptionalJournalValue(inspection.result,`Step ${step.id} result`);if(inspection.state!=="applied"){const compensationData=await step.prepareCompensation?.();setJournalEntry(journal.compensationData,step.id,compensationData,`Step ${step.id} compensation data`);started.add(step.id);journal.startedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>started.has(stepId));await writeJournal(options.pmRoot,journal);const appliedResult=await step.apply();result=cloneOptionalJournalValue(appliedResult,`Step ${step.id} result`)}await emitTransition(options,journal,"step_applied",step.id);completed.add(step.id);journal.completedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>completed.has(stepId));setJournalEntry(journal.results,step.id,result,`Step ${step.id} result`);await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"step_recorded",step.id)}await emitTransition(options,journal,"committing");journal.status="committed";await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"committed");return{transactionId:journal.transactionId,status:"committed",recovered,results:createResultMap(journal.results)}}catch(error){if(error instanceof WorkspaceTransactionInterruptedError||journal.status==="committed")throw error;try{await compensateAppliedSteps(options,journal)}catch(compensationError){throw new AggregateError([error,compensationError],`Workspace transaction failed and compensation failed: ${String(compensationError)}`,{cause:compensationError})}throw error}}async function loadJournal(pmRoot,transactionId,stepIds){try{return parseJournal(await readFile(journalPath(pmRoot,transactionId),"utf8"),transactionId,stepIds)}catch(error){if(error!==null&&typeof error==="object"&&"code"in error&&isFileMissingError(error))return void 0;throw error}}async function emitTransition(options,journal,transition,stepId){await options.onTransition?.({transactionId:journal.transactionId,attempt:journal.attempt,transition,...stepId===void 0?{}:{stepId}})}async function discoverAppliedSteps(journal,steps){const completed=new Set;const results=createResultMap();for(const step of steps){const inspection=await step.inspect();if(inspection.state==="applied"){completed.add(step.id);setJournalEntry(results,step.id,inspection.result,`Step ${step.id} result`)}}journal.results=results;journal.completedStepIds=steps.map(step=>step.id).filter(stepId=>completed.has(stepId))}async function compensateAppliedSteps(options,journal){journal.status="compensating";await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"compensating");const completed=new Set(journal.completedStepIds);const started=new Set(journal.startedStepIds);for(const step of[...options.steps].reverse()){if(!started.has(step.id)&&!completed.has(step.id))continue;let shouldCompensate;try{shouldCompensate=started.has(step.id)&&(await step.inspect()).state==="applied"}catch{shouldCompensate=started.has(step.id)}if(shouldCompensate){await emitTransition(options,journal,"step_compensating",step.id);await step.compensate(journal.compensationData[step.id])}completed.delete(step.id);started.delete(step.id);journal.completedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>completed.has(stepId));journal.startedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>started.has(stepId));delete journal.results[step.id];delete journal.compensationData[step.id];await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"step_compensated",step.id)}journal.status="compensated";await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"compensated")}async function commitWorkspaceTransaction(options){const pmRoot=requiredText(options.pmRoot,"Transaction pmRoot");const transactionId=requiredIdentifier(options.transactionId,"Transaction id");const author=requiredText(options.author,"Transaction author");const lockTtlSeconds=positiveInteger(options.lockTtlSeconds,DEFAULT_LOCK_TTL_SECONDS,"Transaction lock TTL");const lockWaitMs=positiveInteger(options.lockWaitMs,DEFAULT_LOCK_WAIT_MS,"Transaction lock wait");const steps=[...options.steps];if(steps.length===0)throw new TypeError("Workspace transaction requires at least one step");const stepIds=steps.map(step=>{const stepId=requiredIdentifier(step.id,"Transaction step id");if(stepId!==step.id)throw new TypeError("Transaction step id must not contain surrounding whitespace");return stepId});if(new Set(stepIds).size!==stepIds.length)throw new TypeError("Workspace transaction step ids must be unique");const stableOptions={...options,pmRoot,transactionId,author,steps,lockTtlSeconds,lockWaitMs};const release=await acquireLock(pmRoot,WORKSPACE_TRANSACTION_LOCK_ID,lockTtlSeconds,author,false,false,lockWaitMs);try{const existing=await loadJournal(pmRoot,transactionId,stepIds);const recovered=existing!==void 0;if(existing?.status==="committed")return{transactionId,status:"committed",recovered:true,results:createResultMap(existing.results)};const prepared=await prepareJournalForApply(stableOptions,transactionId,author,stepIds,existing);return await applyPreparedTransaction(stableOptions,prepared.journal,recovered||prepared.recovered)}finally{await release()}}export{WorkspaceTransactionInterruptedError,commitWorkspaceTransaction};
1
+ !(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d9ef3878-afce-5983-8fe9-8a896f82d0a7")}catch(e2){}})();import{mkdir,readFile}from"node:fs/promises";import path from"node:path";import{acquireLock}from"../core/lock/lock.js";import{isFileMissingError,writeFileAtomic}from"../core/fs/fs-utils.js";import{nowIso}from"../core/shared/time.js";class WorkspaceTransactionInterruptedError extends Error{constructor(message="Workspace transaction interrupted"){super(message);this.name="WorkspaceTransactionInterruptedError"}}const WORKSPACE_TRANSACTION_LOCK_ID="sdk-workspace-transaction";const TRANSACTION_ID_PATTERN=/^[a-zA-Z0-9._-]+$/;const DEFAULT_LOCK_TTL_SECONDS=30;const DEFAULT_LOCK_WAIT_MS=3e3;function requiredIdentifier(value,label){if(typeof value!=="string")throw new TypeError(`${label} must be a string`);const normalized=value.trim();if(!normalized||!TRANSACTION_ID_PATTERN.test(normalized))throw new TypeError(`${label} must match [a-zA-Z0-9._-]+`);return normalized}function requiredText(value,label){if(typeof value!=="string")throw new TypeError(`${label} must be a string`);const normalized=value.trim();if(!normalized)throw new TypeError(`${label} must be non-empty`);return normalized}function validateWorkspaceTransactionIdentity(options){return{pmRoot:requiredText(options.pmRoot,"Transaction pmRoot"),transactionId:requiredIdentifier(options.transactionId,"Transaction id"),author:requiredText(options.author,"Transaction author")}}function positiveInteger(value,fallback,label){const resolved=value??fallback;if(!Number.isInteger(resolved)||resolved<=0)throw new TypeError(`${label} must be a positive integer`);return resolved}function journalPath(pmRoot,transactionId){return path.join(pmRoot,"transactions","sdk",`${transactionId}.json`)}async function writeJournal(pmRoot,journal){journal.updatedAt=nowIso();const target=journalPath(pmRoot,journal.transactionId);await mkdir(path.dirname(target),{recursive:true});await writeFileAtomic(target,`${JSON.stringify(journal,null,2)}
2
+ `)}function hasValidJournalMetadata(journal){return Number.isInteger(journal.attempt)&&typeof journal.author==="string"&&typeof journal.createdAt==="string"&&typeof journal.updatedAt==="string"}function createResultMap(source){return Object.assign(Object.create(null),source)}function cloneJournalValue(value,label){try{const serialized=JSON.stringify(value,(_key,nestedValue)=>{if(typeof nestedValue==="number"&&!Number.isFinite(nestedValue))throw new TypeError(`${label} contains a non-finite number`);if(nestedValue===void 0||typeof nestedValue==="function"||typeof nestedValue==="symbol")throw new TypeError(`${label} contains a non-JSON value`);return nestedValue});return JSON.parse(serialized)}catch(error){throw new TypeError(`${label} must be JSON serializable`,{cause:error})}}function cloneOptionalJournalValue(value,label){return value===void 0?void 0:cloneJournalValue(value,label)}function setJournalEntry(target,stepId,value,label){const cloned=cloneOptionalJournalValue(value,label);if(cloned===void 0)delete target[stepId];else target[stepId]=cloned}function isJournalMap(value){return value!==null&&typeof value==="object"&&!Array.isArray(value)}function hasUniqueKnownStepIds(value,stepIds){return Array.isArray(value)&&new Set(value).size===value.length&&value.every(stepId=>typeof stepId==="string"&&stepIds.includes(stepId))}function hasValidJournalLifecycle(journal,stepIds){if(!hasUniqueKnownStepIds(journal.startedStepIds,stepIds)||!hasUniqueKnownStepIds(journal.completedStepIds,stepIds)||!isJournalMap(journal.results)||!isJournalMap(journal.compensationData))return false;const startedStepIds=journal.startedStepIds;const started=new Set(startedStepIds);const completedStepIds=journal.completedStepIds;const completed=new Set(completedStepIds);if(Object.keys(journal.compensationData).some(stepId=>!started.has(stepId))||Object.keys(journal.results).some(stepId=>!completed.has(stepId)))return false;if(journal.status==="committed")return stepIds.every(stepId=>completed.has(stepId));return journal.status!=="compensated"||completed.size===0&&started.size===0}function parseJournal(raw,transactionId,stepIds){let parsed;try{parsed=JSON.parse(raw)}catch{throw new TypeError(`Workspace transaction ${transactionId} journal is invalid JSON`)}if(!parsed||typeof parsed!=="object"||Array.isArray(parsed))throw new TypeError(`Workspace transaction ${transactionId} journal is invalid`);const journal=parsed;const identityMatches=journal.schemaVersion===1&&journal.transactionId===transactionId;const stepsMatch=Array.isArray(journal.stepIds)&&journal.stepIds.length===stepIds.length&&journal.stepIds.every((stepId,index)=>stepId===stepIds[index]);const lifecycleMatches=hasValidJournalLifecycle(journal,stepIds);const metadataMatches=hasValidJournalMetadata(journal);const statusMatches=["applying","compensating","compensated","committed"].includes(String(journal.status));if(!identityMatches||!stepsMatch||!lifecycleMatches||!metadataMatches||!statusMatches)throw new TypeError(`Workspace transaction ${transactionId} journal does not match the supplied plan`);return{...journal,results:createResultMap(journal.results),compensationData:createResultMap(journal.compensationData)}}async function prepareJournalForApply(options,transactionId,author,stepIds,existing){let journal=existing;const recovered=journal!==void 0;if(journal?.status==="compensating")await compensateAppliedSteps(options,journal);if(journal===void 0||journal.status==="compensated"){const timestamp=nowIso();journal={schemaVersion:1,transactionId,author,status:"applying",attempt:(journal?.attempt??0)+1,createdAt:journal?.createdAt??timestamp,updatedAt:timestamp,stepIds,startedStepIds:[],completedStepIds:[],results:createResultMap(),compensationData:createResultMap()};await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"prepared")}return{journal,recovered}}async function applyPreparedTransaction(options,journal,recovered){try{await discoverAppliedSteps(journal,options.steps);await writeJournal(options.pmRoot,journal);const completed=new Set(journal.completedStepIds);const started=new Set(journal.startedStepIds);for(const step of options.steps){if(completed.has(step.id))continue;const inspection=await step.inspect();let result=cloneOptionalJournalValue(inspection.result,`Step ${step.id} result`);if(inspection.state!=="applied"){const compensationData=await step.prepareCompensation?.();setJournalEntry(journal.compensationData,step.id,compensationData,`Step ${step.id} compensation data`);started.add(step.id);journal.startedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>started.has(stepId));await writeJournal(options.pmRoot,journal);const appliedResult=await step.apply();result=cloneOptionalJournalValue(appliedResult,`Step ${step.id} result`)}await emitTransition(options,journal,"step_applied",step.id);completed.add(step.id);journal.completedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>completed.has(stepId));setJournalEntry(journal.results,step.id,result,`Step ${step.id} result`);await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"step_recorded",step.id)}await emitTransition(options,journal,"committing");journal.status="committed";await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"committed");return{transactionId:journal.transactionId,status:"committed",recovered,results:createResultMap(journal.results)}}catch(error){if(error instanceof WorkspaceTransactionInterruptedError||journal.status==="committed")throw error;try{await compensateAppliedSteps(options,journal)}catch(compensationError){throw new AggregateError([error,compensationError],`Workspace transaction failed and compensation failed: ${String(compensationError)}`,{cause:compensationError})}throw error}}async function loadJournal(pmRoot,transactionId,stepIds){try{return parseJournal(await readFile(journalPath(pmRoot,transactionId),"utf8"),transactionId,stepIds)}catch(error){if(error!==null&&typeof error==="object"&&"code"in error&&isFileMissingError(error))return void 0;throw error}}async function emitTransition(options,journal,transition,stepId){await options.onTransition?.({transactionId:journal.transactionId,attempt:journal.attempt,transition,...stepId===void 0?{}:{stepId}})}async function discoverAppliedSteps(journal,steps){const completed=new Set;const results=createResultMap();for(const step of steps){const inspection=await step.inspect();if(inspection.state==="applied"){completed.add(step.id);setJournalEntry(results,step.id,inspection.result,`Step ${step.id} result`)}}journal.results=results;journal.completedStepIds=steps.map(step=>step.id).filter(stepId=>completed.has(stepId))}async function compensateAppliedSteps(options,journal){journal.status="compensating";await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"compensating");const completed=new Set(journal.completedStepIds);const started=new Set(journal.startedStepIds);for(const step of[...options.steps].reverse()){if(!started.has(step.id)&&!completed.has(step.id))continue;let shouldCompensate;try{shouldCompensate=started.has(step.id)&&(await step.inspect()).state==="applied"}catch{shouldCompensate=started.has(step.id)}if(shouldCompensate){await emitTransition(options,journal,"step_compensating",step.id);await step.compensate(journal.compensationData[step.id])}completed.delete(step.id);started.delete(step.id);journal.completedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>completed.has(stepId));journal.startedStepIds=options.steps.map(candidate=>candidate.id).filter(stepId=>started.has(stepId));delete journal.results[step.id];delete journal.compensationData[step.id];await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"step_compensated",step.id)}journal.status="compensated";await writeJournal(options.pmRoot,journal);await emitTransition(options,journal,"compensated")}async function commitWorkspaceTransaction(options){const{pmRoot,transactionId,author}=validateWorkspaceTransactionIdentity(options);const lockTtlSeconds=positiveInteger(options.lockTtlSeconds,DEFAULT_LOCK_TTL_SECONDS,"Transaction lock TTL");const lockWaitMs=positiveInteger(options.lockWaitMs,DEFAULT_LOCK_WAIT_MS,"Transaction lock wait");const steps=[...options.steps];if(steps.length===0)throw new TypeError("Workspace transaction requires at least one step");const stepIds=steps.map(step=>{const stepId=requiredIdentifier(step.id,"Transaction step id");if(stepId!==step.id)throw new TypeError("Transaction step id must not contain surrounding whitespace");return stepId});if(new Set(stepIds).size!==stepIds.length)throw new TypeError("Workspace transaction step ids must be unique");const stableOptions={...options,pmRoot,transactionId,author,steps,lockTtlSeconds,lockWaitMs};const release=await acquireLock(pmRoot,WORKSPACE_TRANSACTION_LOCK_ID,lockTtlSeconds,author,false,false,lockWaitMs);try{const existing=await loadJournal(pmRoot,transactionId,stepIds);const recovered=existing!==void 0;if(existing?.status==="committed")return{transactionId,status:"committed",recovered:true,results:createResultMap(existing.results)};const prepared=await prepareJournalForApply(stableOptions,transactionId,author,stepIds,existing);return await applyPreparedTransaction(stableOptions,prepared.journal,recovered||prepared.recovered)}finally{await release()}}export{WorkspaceTransactionInterruptedError,commitWorkspaceTransaction,validateWorkspaceTransactionIdentity};
3
3
  //# sourceMappingURL=workspace-transaction.js.map
@@ -4,8 +4,9 @@ Tracked by [pm-pshhry](../.agents/pm/issues/pm-pshhry.toon),
4
4
  [pm-ydshl9](../.agents/pm/issues/pm-ydshl9.toon),
5
5
  [pm-bab3gb](../.agents/pm/issues/pm-bab3gb.toon),
6
6
  [pm-fx80w2](../.agents/pm/issues/pm-fx80w2.toon),
7
- [pm-gtw5zh](../.agents/pm/issues/pm-gtw5zh.toon), and
8
- [pm-s8ybl9](../.agents/pm/issues/pm-s8ybl9.toon).
7
+ [pm-gtw5zh](../.agents/pm/issues/pm-gtw5zh.toon),
8
+ [pm-s8ybl9](../.agents/pm/issues/pm-s8ybl9.toon), and
9
+ [pm-x8jdt8](../.agents/pm/issues/pm-x8jdt8.toon).
9
10
 
10
11
  Project management is context management. Routine reads should pay for the
11
12
  requested evidence, and derived feedback should not grow with every candidate
@@ -21,12 +22,14 @@ The cost still depends on the addressed record, registered type directories,
21
22
  and extension activation; this is not a constant-latency guarantee for arbitrary
22
23
  extensions or unbounded individual history streams.
23
24
 
24
- Request hierarchy work explicitly with `--fields id,children`, `--tree`, or a
25
- deep/full container read. These paths preserve registered hierarchy semantics,
25
+ Request hierarchy work explicitly with `--fields id,children`, `--tree`, a
26
+ deep read of a container or custom type, or a full read of any type. These
27
+ paths preserve registered hierarchy semantics,
26
28
  including inverse and custom relationship kinds, rather than treating only the
27
- scalar `parent` field as authoritative. An omitted `children` facet carries a
28
- restore selector in the omission receipt. Explicit deep/full and child-field
29
- projections retain a zero-count rollup for an empty container.
29
+ scalar `parent` field as authoritative. An uncomputed `children` facet carries a
30
+ restore selector in the omission receipt even for Task items, which may have
31
+ children. Explicit full and child-field projections retain a zero-count rollup
32
+ for an empty item; a deep container read does the same.
30
33
 
31
34
  Caller-carried output sessions retain only portable item identities in
32
35
  `next_state.seen_item_ids`. Workspace activity such as `_workspace` remains
package/docs/COMMANDS.md CHANGED
@@ -184,7 +184,7 @@ As a read-only structured surface, `duplicates` accepts the universal output
184
184
  controls, including `--output-format json`, `--lean`, projection/amount
185
185
  controls, and token accounting, while its default TOON output remains bounded.
186
186
  Tracked by [pm-gh1076](../.agents/pm/issues/pm-gh1076.toon).
187
- Use `pm get <id>` to read a single item by ID — the single-item read primitive used throughout the agent loop. It accepts `--fields <list>` and `--depth brief|standard|deep|full` for token-minimal projections, and `--tree`/`--tree-depth <n>` to include descendants. The universal `--output-include` selector can request stored collections directly, for example `pm get <id> --output-include comments,learnings,tests`; the same bare and `item.<field>` grammar works through CLI, SDK, and MCP transports. Standard/deep reads expose a normalized `schedule` facet (`deadline`, `start_at`, `end_at`, `location`, reminders, and events) when scheduling metadata exists. Standard reads of every item type avoid a workspace child scan. For container-oriented built-ins (Epic, Feature, Milestone, and Plan) and custom types, `--depth deep|full` adds type-agnostic child counts, a bounded sample, and continuation metadata. An explicit `--fields id,children` requests that rollup for any type. See [bounded context reads](BOUNDED_CONTEXT_READS.md) for the cost contract. `pm get <id> --json` returns the `body` inside the `item` object (`.item.body`); see [Full results, totals, and bodies](#full-results-totals-and-bodies). To duplicate an existing item as a starting point, `pm copy <id> --title "New title"` clones it into a fresh id with lifecycle fields reset.
187
+ Use `pm get <id>` to read a single item by ID — the single-item read primitive used throughout the agent loop. It accepts `--fields <list>` and `--depth brief|standard|deep|full` for token-minimal projections, and `--tree`/`--tree-depth <n>` to include descendants. The universal `--output-include` selector can request stored collections directly, for example `pm get <id> --output-include comments,learnings,tests`; the same bare and `item.<field>` grammar works through CLI, SDK, and MCP transports. Standard/deep reads expose a normalized `schedule` facet (`deadline`, `start_at`, `end_at`, `location`, reminders, and events) when scheduling metadata exists. Standard reads of every item type avoid a workspace child scan. For container-oriented built-ins (Epic, Feature, Milestone, and Plan) and custom types, `--depth deep` adds type-agnostic child counts, a bounded sample, and continuation metadata. Explicit `--full` and `--depth full` reads compute that rollup for every type, including Task parents. An explicit `--fields id,children` requests that rollup without the other deep fields. A read without a child rollup retains a `children` omission hint until a rollup proves the item has no children. See [bounded context reads](BOUNDED_CONTEXT_READS.md) for the cost contract. `pm get <id> --json` returns the `body` inside the `item` object (`.item.body`); see [Full results, totals, and bodies](#full-results-totals-and-bodies). To duplicate an existing item as a starting point, `pm copy <id> --title "New title"` clones it into a fresh id with lifecycle fields reset.
188
188
 
189
189
  When the strongest duplicate match is terminal because the same work recurred,
190
190
  reuse its lineage instead of creating or copying another item:
@@ -14,7 +14,9 @@ compatibility is tracked by
14
14
  Lossless concurrent acceptance-criteria composition is tracked by
15
15
  [pm-inn5y5](../.agents/pm/issues/pm-inn5y5.toon).
16
16
  The Bun launcher and strict-health repair are tracked by
17
- [pm-vks66s](../.agents/pm/issues/pm-vks66s.toon).
17
+ [pm-vks66s](../.agents/pm/issues/pm-vks66s.toon), with the Bun 1.3.5
18
+ package-runner recurrence tracked by
19
+ [pm-aaxq4n](../.agents/pm/issues/pm-aaxq4n.toon).
18
20
  Recency-aware scalar convergence, bounded fresh-clone decision evidence, and
19
21
  audited pre-durable receipt dispositions are tracked by
20
22
  [pm-7wzb6d](../.agents/pm/issues/pm-7wzb6d.toon),
@@ -44,11 +46,15 @@ The clone-local driver values record the absolute runtime launcher and bundled
44
46
  `dist/cli.js` path resolved by the installing SDK. With Node, the installer
45
47
  preserves an absolute PATH symlink when it resolves to the executing runtime, so
46
48
  a runtime manager can retarget that launcher during upgrades without breaking
47
- every clone. With Bun, it resolves an executable `bun` on an absolute PATH entry,
48
- checks that its version matches the running Bun version, and records its stable
49
- absolute target. Relative PATH entries, inaccessible launchers, and different
50
- runtimes are ignored; without a matching launcher, the installer retains the
51
- executing binary path.
49
+ every clone. With Bun, the installer checks the absolute executable published
50
+ by the package runner, absolute `PATH` entries, and the directly invoked binary
51
+ for a matching Bun version, then records its stable target. Known temporary
52
+ `bun-node-*` launcher copies are rejected even when their version matches.
53
+ Relative paths, inaccessible launchers, and
54
+ different runtimes are ignored. If no durable matching Bun launcher is available,
55
+ installation fails with recovery guidance before it writes Git configuration or
56
+ the shared fence; a temporary `bunx` runtime is never recorded as a successful
57
+ driver installation.
52
58
  Git therefore does not depend
53
59
  on a bare `pm` command or the caller's later `PATH` when it merges tracker data.
54
60
  The item-path placeholder is stored as bare `%P`: Git performs the required
@@ -244,6 +244,24 @@ Explicit overrides below 256 tokens are rejected because the minimum
244
244
  machine-readable receipt cannot fit; malformed or absent overrides retain the
245
245
  declared intent budget.
246
246
 
247
+ Projection refusals repair the original CLI invocation: tracker selection,
248
+ operands, output encoding, filters and unrelated controls survive the retry.
249
+ Unknown field selectors are removed while valid requested selectors remain.
250
+ Full/projection conflicts retain the selected full or brief mode and remove the
251
+ conflicting projection and intent controls. If that correction cannot be
252
+ constructed safely, the error does not advertise an automatic retry.
253
+ Tracked by [pm-gh1364](../.agents/pm/issues/pm-gh1364.toon).
254
+
255
+ For a budget-compacted producer page, follow
256
+ `output_budget_truncation.recovery.cursor` with `--output-cursor`, keeping the
257
+ original arguments and any `--after` value. Once that cursor is absent, use the
258
+ producer `next_cursor` with `--after` and remove `--output-cursor`. Universal
259
+ output shaping and continuation controls do not change producer query identity;
260
+ actual filter and scope changes still invalidate its cursor. Emitted `count`
261
+ and `summary.returned_focus` describe delivered rows; matching-population totals
262
+ remain separate, including explicit count-only aggregates. Context hierarchy companions may repeat while continuing a
263
+ focus collection. Tracked by [pm-gh1371](../.agents/pm/issues/pm-gh1371.toon).
264
+
247
265
  The first cursor page carries the complete projection, filtering, sorting,
248
266
  completeness, row, and omission contracts. Continuation pages replace those
249
267
  chain-invariant blocks with `continuation_contract`, which carries the cursor's
@@ -380,3 +398,12 @@ key continue to receive the same rows.
380
398
  Dependency token accounting includes the projection declaration and derived
381
399
  receipt. Reported `usedTokens` and truncation estimates therefore describe the
382
400
  final serialized result, not a pre-receipt intermediate.
401
+
402
+ When context field selection hides focus roles or blocker flags, optional
403
+ `summary.focus_row_states` strings retain those classifications across JSON
404
+ transport. Each character corresponds to a delivered record in its named focus
405
+ section: `i`/`I` means in progress, `o`/`O` means open; uppercase means blocked.
406
+ Continuation and compaction slice these receipts with the rows. Self-describing
407
+ focus rows need no extra receipt. Semantic receipts are preserved during
408
+ explanatory-string compaction, and corrupt alphabet or row-count mismatches are
409
+ rejected.
package/docs/SDK.md CHANGED
@@ -1449,6 +1449,104 @@ the same durable coordinator, restoring annotations, linked artifacts,
1449
1449
  lifecycle fields, and claim ownership on failure. It is the SDK primitive
1450
1450
  behind `pm item complete`.
1451
1451
 
1452
+ `previewItemMutations` validates an ordered plan with the same transaction
1453
+ coordinator and lifecycle primitives as apply, using a disposable copy of the
1454
+ tracker. Earlier staged creates, evidence updates and closures participate in
1455
+ later validation. Existing journals retain the coordinator's replay and
1456
+ plan-identity behavior. The preview coordinator writes no source item, history,
1457
+ claim, lock or journal data. Active declarative extension registrations are preserved;
1458
+ executable extension guards, hooks, service overrides, and package command
1459
+ handlers remain disabled. Package-backed `--template` create input cannot be
1460
+ semantically previewed through its package handler; expand it into explicit core
1461
+ mutation options before preview.
1462
+
1463
+ Staging excludes derived `search` and `locks` content and unrelated runtime state,
1464
+ retaining registered item folders, explicitly configured schema files, and
1465
+ `runtime/session.json` for core focus and inherited-parent semantics. Root-layout
1466
+ trackers copy only tracker-owned files and directories, including standard and
1467
+ custom item folders and configured schema files; unrelated project content is
1468
+ excluded. Preview accepts regular files and directories. Entry checks reject
1469
+ existing interior symbolic links and special files; an explicitly selected root
1470
+ link is resolved once to establish the tracker boundary. Absolute configured
1471
+ schema paths inside that resolved boundary are
1472
+ remapped only in the staged settings. Schema files configured outside the
1473
+ tracker fail with `transaction_preview_external_schema`; relocate them inside
1474
+ the selected tracker root before retrying.
1475
+
1476
+ Preview requires trusted, stable directory entries and ancestors. Where Node
1477
+ exposes `O_NOFOLLOW` and `O_NONBLOCK`, descriptor opens request those POSIX guards.
1478
+ Windows retains entry checks, regular-file descriptor checks and snapshot
1479
+ comparisons without these flags. Portable Node APIs do not pin directory entries
1480
+ or ancestors, and later fingerprints cannot prevent hostile path redirection
1481
+ before a read. Use a caller-owned isolated copy when an untrusted actor can
1482
+ concurrently replace workspace paths.
1483
+
1484
+ Durable source bytes are fingerprinted before and after copying and compared
1485
+ with the staged copy. An inconsistent copy or a file disappearing during the
1486
+ scan fails with `transaction_preview_snapshot_changed` and can be retried.
1487
+ File contents are hashed in bounded streams so large history files do not
1488
+ require whole-file buffers. `PmCliError` failures from staged step inspection,
1489
+ compensation preparation and apply preserve their lifecycle classification and
1490
+ include a structured `transaction_operation` with the zero-based index,
1491
+ operation and requested target ID from the mutation plan, after any batch-local
1492
+ reference resolution. That ID need not be prefix-normalized. Native errors and
1493
+ compensation or recovery failures retain the coordinator's error semantics; if
1494
+ compensation also fails, its `AggregateError` preserves the decorated primary
1495
+ error.
1496
+
1497
+ CLI `item mutate --dry-run`, `item complete --dry-run`, and MCP `pm_mutate`
1498
+ with `dryRun: true` share this validation. CLI structured previews use
1499
+ `runWithConfigurationOnlySettings(pmRoot, run, isEnabled?)` across bootstrap,
1500
+ command execution and error cleanup. The optional predicate defaults to enabled.
1501
+ Scoped settings reads validate `settings.json` and
1502
+ merge inline configuration with defaults, without loading or scaffolding optional
1503
+ schema files, running settings-read hooks, or consulting or populating hydrated
1504
+ settings caches. Only selected bootstrap/action tracker roots, the actual global
1505
+ tracker root, and their resolved aliases are scoped.
1506
+
1507
+ Nested async-local scopes compose through new policy layers without modifying
1508
+ their ancestors. Matching-root reads evaluate live predicates, so returning
1509
+ `false` disables that layer while an enabled caller-owned ancestor can still
1510
+ protect the same root. Concurrent invocations keep separate policies, and scopes
1511
+ restore when the callback returns or throws. Staged and unrelated roots retain
1512
+ normal settings behavior, including schema-file hydration.
1513
+
1514
+ CLI bootstrap conservatively protects normalized invocations in the `item`
1515
+ namespace that contain a literal `--dry-run`, before extension option contracts
1516
+ are available. These settings policies cover command registration, invocation
1517
+ base-settings reads and parser-error cleanup. The earliest root `preAction` hook
1518
+ then settles invocation-owned policies from the actual selected command path and
1519
+ effective `optsWithGlobals()` values, before host-owned consent, migration and
1520
+ telemetry preparation. The real parser owns required-option values, `--`
1521
+ boundaries, positional child selection and inherited option precedence. For
1522
+ example, an extension's required `--package-note --dry-run` consumes `--dry-run`
1523
+ as its value. Without a separate preview flag, ordinary apply disables its own
1524
+ policy layers and resumes normal source/global settings hydration, consent,
1525
+ identity initialization and migration preparation. Explicit caller-owned layers
1526
+ remain effective. Genuine previews remain scoped when extension options are
1527
+ present.
1528
+ Structured CLI previews skip host-owned extension
1529
+ migration execution, first-use telemetry consent persistence and installation-ID
1530
+ initialization. Pending migration effects on a later commit remain unvalidated.
1531
+ Existing consented telemetry with a persisted identity still
1532
+ enters derived local queues; preview schedules no flush worker, and later normal
1533
+ commands retain delivery. Normal CLI extension discovery and activation happen
1534
+ before staged execution and retain their existing policy. CLI parser/preflight
1535
+ overrides and before/after-command hooks are also outside staged
1536
+ suppression; `--no-extensions` disables those executable paths.
1537
+
1538
+ `ItemMutationPreviewValidation`
1539
+ reports `validated: true, state: "staged_snapshot"` when the coordinator
1540
+ executes the staged plan. An already committed journal instead returns
1541
+ `validated: false, state: "replayed_committed_plan"`, matching idempotent apply
1542
+ without freshly executing or validating the supplied lifecycle steps. Both
1543
+ results disclose
1544
+ `unresolved_commit_constraints: ["concurrent_tracker_changes",
1545
+ "extension_mutation_guards_and_hooks"]`. Newly staged validation proves core
1546
+ semantic validity at that snapshot; commit still checks current state and runs
1547
+ extension guards and hooks. Preview cost is proportional to the tracker data
1548
+ staged, rather than unrelated root-layout project content.
1549
+
1452
1550
  `itemDocumentToMutationOptions` is the companion full-document adapter. It
1453
1551
  accepts either a direct `ItemDocument` or the envelope returned by
1454
1552
  `pm get <id> --json`, strips read-only metadata, maps canonical snake-case item
@@ -4,12 +4,12 @@ Tracker: `pm-f05lsg`.
4
4
 
5
5
  Every catalog code is listed. An `uncovered` row is an explicit closure obligation, never an omission or implied approval.
6
6
 
7
- - Catalog error codes: 390
7
+ - Catalog error codes: 393
8
8
  - Executable error codes: 19
9
9
  - Executable-code ratchet floor: 18
10
10
  - Required executable canonical codes: `bulk_ids_input_empty`, `bulk_ids_input_missing_path`, `bulk_ids_input_unreadable`, `invalid_argument_value`, `manifest_unknown_key`, `missing_lifecycle_target`, `missing_required_argument`, `no_version_bounds_declared`, `projection_options_mutually_exclusive`, `tracker_not_initialized`, `tracker_root_missing`, `tracker_root_not_directory`, `tracker_root_unreadable`, `unknown_context_intent`, `unknown_field_projection`, `unknown_option`, `unknown_subcommand`
11
- - Uncovered error codes: 371
12
- - Coverage fraction: 0.048718
11
+ - Uncovered error codes: 374
12
+ - Coverage fraction: 0.048346
13
13
  - Closed-domain probes: 19
14
14
  - Grammar probes: 117
15
15
 
@@ -200,6 +200,7 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati
200
200
  | `mcp_annotation_file_unavailable` | `mcp_annotation_file_unavailable` | uncovered | none | 0 |
201
201
  | `mcp_stdin_unavailable` | `mcp_stdin_unavailable` | uncovered | none | 0 |
202
202
  | `mcp_task_not_found_or_not_authorized` | `mcp_task_not_found_or_not_authorized` | uncovered | none | 0 |
203
+ | `merge_bun_launcher_unavailable` | `merge_bun_launcher_unavailable` | uncovered | none | 0 |
203
204
  | `merge_conflict_markers_detected` | `merge_conflict_markers_detected` | uncovered | none | 0 |
204
205
  | `merge_decisions_unreviewed` | `merge_decisions_unreviewed` | uncovered | none | 0 |
205
206
  | `merge_driver_configuration_drift` | `merge_driver_configuration_drift` | uncovered | none | 0 |
@@ -320,6 +321,8 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati
320
321
  | `tracker_root_missing` | `tracker_root_missing` | executable | owned_state | 1 |
321
322
  | `tracker_root_not_directory` | `tracker_root_not_directory` | executable | owned_state | 1 |
322
323
  | `tracker_root_unreadable` | `tracker_root_unreadable` | executable | owned_state | 1 |
324
+ | `transaction_preview_external_schema` | `transaction_preview_external_schema` | uncovered | none | 0 |
325
+ | `transaction_preview_snapshot_changed` | `transaction_preview_snapshot_changed` | uncovered | none | 0 |
323
326
  | `type_duplicate` | `type_duplicate` | uncovered | none | 0 |
324
327
  | `type_invalid` | `type_invalid` | uncovered | none | 0 |
325
328
  | `undeclared_observation` | `undeclared_observation` | uncovered | none | 0 |
package/marketplace.json CHANGED
@@ -5,13 +5,13 @@
5
5
  "url": "https://github.com/unbraind/pm-cli"
6
6
  },
7
7
  "description": "Official marketplace for pm CLI — native git-based project management for Claude Code and AI coding agents.",
8
- "version": "2026.9.30",
8
+ "version": "2026.10.2",
9
9
  "plugins": [
10
10
  {
11
11
  "name": "pm-claude",
12
12
  "source": "./plugins/pm-claude",
13
13
  "description": "Native pm CLI integration for Claude Code — 32 MCP tools, 14 workflow skills with slash invocations, 4 subagents, hybrid TUI task tracking, session context injection, and coordination subagents for git-based project management without leaving Claude Code.",
14
- "version": "2026.9.30",
14
+ "version": "2026.10.2",
15
15
  "author": {
16
16
  "name": "unbrained",
17
17
  "url": "https://github.com/unbraind/pm-cli"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-cli",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "description": "Git-native project management CLI for humans and agents.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.10.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-beads",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for importing Beads JSONL records.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-calendar",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for calendar and agenda views.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-command-kit",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm exemplar package for the commands capability: registerCommand, registerFlags, and registerParser.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-digital-twin",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Beyond-PM exemplar: a temporal production-facility digital twin built entirely on public pm SDK and extension primitives.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-governance-audit",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for governance audit and normalization workflows.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-guide-shell",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for guide and shell completion workflows.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-kanban",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm archetype package: a complete Kanban continuous-flow profile built on public SDK primitives.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-lifecycle-hooks",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package demonstrating safe lifecycle hook registration.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-linked-test-adapters",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for optional linked-test run management adapters and reporters.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-search-advanced",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for semantic, hybrid, and reindex search workflows.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-templates",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for reusable create templates.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-todos",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "First-party pm package for Todo markdown import and export.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-vcs-sdk-exemplar",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Beyond-PM exemplar: a VCS-style changeset workflow built entirely on public pm SDK and extension primitives.",
@@ -3,7 +3,7 @@
3
3
  "name": "pm-claude",
4
4
  "displayName": "pm CLI",
5
5
  "description": "Native pm CLI integration for Claude Code — 32 MCP tools, 14 skills with slash invocations, 4 subagents (coordinator, delivery-chain, triage, verification), hybrid TUI task tracking (pm as persistent store + Claude Code task panel as live view), session context injection, and full git-based project management without leaving Claude Code.",
6
- "version": "2026.9.30",
6
+ "version": "2026.10.2",
7
7
  "author": {
8
8
  "name": "unbrained",
9
9
  "url": "https://github.com/unbraind/pm-cli"
@@ -2,8 +2,8 @@
2
2
  "name": "pm-claude-runtime",
3
3
  "private": true,
4
4
  "type": "module",
5
- "version": "2026.9.30",
5
+ "version": "2026.10.2",
6
6
  "dependencies": {
7
- "@unbrained/pm-cli": "2026.9.30"
7
+ "@unbrained/pm-cli": "2026.10.2"
8
8
  }
9
9
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pm-codex",
3
- "version": "2026.9.30",
3
+ "version": "2026.10.2",
4
4
  "description": "Native Codex integration for pm-cli with bundled MCP tools, skills, commands, and agent workflows.",
5
5
  "author": {
6
6
  "name": "unbrained",
@@ -2,8 +2,8 @@
2
2
  "name": "pm-codex-runtime",
3
3
  "private": true,
4
4
  "type": "module",
5
- "version": "2026.9.30",
5
+ "version": "2026.10.2",
6
6
  "dependencies": {
7
- "@unbrained/pm-cli": "2026.9.30"
7
+ "@unbrained/pm-cli": "2026.10.2"
8
8
  }
9
9
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "pm-codex",
4
- "version": "2026.9.30",
4
+ "version": "2026.10.2",
5
5
  "description": "Repository-native project context workflows and a version-pinned pm MCP server for Codex.",
6
6
  "author": {
7
7
  "name": "unbrained",