@unbrained/pm-cli 2026.10.1 → 2026.10.3

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 +23 -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-structured-mutation.d.ts +2 -0
  7. package/dist/cli/register-structured-mutation.js +1 -1
  8. package/dist/cli/runtime/projection-retry.d.ts +9 -0
  9. package/dist/cli/runtime/projection-retry.js +2 -0
  10. package/dist/cli/runtime/selection.d.ts +5 -1
  11. package/dist/cli/runtime/selection.js +1 -1
  12. package/dist/cli-bundle/bundle-manifest.json +156 -156
  13. package/dist/cli-bundle/chunks/chunk-3AF2R3Y6.js +6 -0
  14. package/dist/cli-bundle/chunks/chunk-44UGMMQ6.js +3 -0
  15. package/dist/cli-bundle/chunks/{chunk-VWVFKPLX.js → chunk-5XKF5SQV.js} +38 -38
  16. package/dist/cli-bundle/chunks/{chunk-AYHLUJKD.js → chunk-6ST3SCQ6.js} +2 -2
  17. package/dist/cli-bundle/chunks/chunk-6Z4IV2MC.js +8 -0
  18. package/dist/cli-bundle/chunks/{chunk-A2UPOCSW.js → chunk-FW5LJXQY.js} +31 -31
  19. package/dist/cli-bundle/chunks/{chunk-SCZMJ5DC.js → chunk-H2DZW6I6.js} +2 -2
  20. package/dist/cli-bundle/chunks/chunk-H7CXQBGQ.js +2 -0
  21. package/dist/cli-bundle/chunks/{chunk-DGOIXJZJ.js → chunk-KP7NBQK3.js} +47 -47
  22. package/dist/cli-bundle/chunks/chunk-MKXBCBAN.js +20 -0
  23. package/dist/cli-bundle/chunks/chunk-QPKQKSQU.js +238 -0
  24. package/dist/cli-bundle/chunks/chunk-YCBGZF3T.js +2 -0
  25. package/dist/cli-bundle/chunks/chunk-YDWKGUQU.js +3 -0
  26. package/dist/cli-bundle/chunks/{register-list-query-L6HAHW7V.js → register-list-query-DYCGUQEW.js} +2 -2
  27. package/dist/cli-bundle/chunks/register-mutation-5PT3NW3A.js +20 -0
  28. package/dist/cli-bundle/chunks/{register-operations-3E5U4TOE.js → register-operations-QDDQOAAV.js} +2 -2
  29. package/dist/cli-bundle/chunks/{register-setup-VUD37N4A.js → register-setup-BBX67YMA.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/{chunk-MOUCTR72.js → chunk-3BIYG72P.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/{chunk-T4PZJQG4.js → chunk-4FFPGNCJ.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-YBQ27KG7.js → chunk-73DA7ISY.js} +47 -47
  33. package/dist/cli-bundle/focused-chunks/{chunk-XUCXRZZZ.js → chunk-DBD4W4QP.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-6275G2AN.js → chunk-E754JDLY.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/{chunk-4AORVUT4.js → chunk-EJN7DY3B.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/{chunk-OMBDUJWZ.js → chunk-FKK2HI3S.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-ZJ3CLTV4.js → chunk-GMYUNHST.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-HIA2ZF64.js +12 -0
  39. package/dist/cli-bundle/focused-chunks/{chunk-RIJQQOEZ.js → chunk-UTRSM3ZK.js} +2 -2
  40. package/dist/cli-bundle/focused-chunks/{chunk-EVMH4SKN.js → chunk-VVWYI2X7.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/chunk-WAJNX4RN.js +20 -0
  42. package/dist/cli-bundle/focused-chunks/chunk-WCY6HRZO.js +30 -0
  43. package/dist/cli-bundle/focused-chunks/{chunk-XB5Q2P7Z.js → chunk-XRSCIYHR.js} +2 -2
  44. package/dist/cli-bundle/focused-chunks/chunk-YC32ZSLS.js +165 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-IQBFYQF2.js → chunk-ZIXFMIUL.js} +2 -2
  46. package/dist/cli-bundle/main.js +22 -22
  47. package/dist/cli-bundle/sdk-authoring.js +1 -1
  48. package/dist/cli-bundle/sdk-contracts.js +2 -2
  49. package/dist/cli-bundle/sdk-core.js +31 -31
  50. package/dist/cli-bundle/sdk-governance.js +1 -1
  51. package/dist/cli-bundle/sdk-graph.js +1 -1
  52. package/dist/cli-bundle/sdk-merge.js +48 -48
  53. package/dist/cli-bundle/sdk-query.js +1 -1
  54. package/dist/cli-bundle/sdk-runtime.js +1 -1
  55. package/dist/cli-bundle/sdk-testing.js +1 -1
  56. package/dist/cli-bundle/sdk.js +7 -7
  57. package/dist/core/fs/fs-utils.d.ts +11 -0
  58. package/dist/core/fs/fs-utils.js +2 -2
  59. package/dist/core/shared/errors.d.ts +9 -0
  60. package/dist/core/shared/errors.js +1 -1
  61. package/dist/core/shared/secret-rules.js +1 -1
  62. package/dist/core/store/settings.d.ts +15 -5
  63. package/dist/core/store/settings.js +3 -3
  64. package/dist/core/telemetry/runtime.js +3 -3
  65. package/dist/mcp/server.js +1 -1
  66. package/dist/sdk/agent/closed-domain-contracts.js +1 -1
  67. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
  68. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  69. package/dist/sdk/item-transaction.d.ts +11 -0
  70. package/dist/sdk/item-transaction.js +1 -1
  71. package/dist/sdk/linked-artifacts.d.ts +1 -1
  72. package/dist/sdk/linked-artifacts.js +1 -1
  73. package/dist/sdk/pagination.js +1 -1
  74. package/dist/sdk/public-surface.json +1 -1
  75. package/dist/sdk/query/context.d.ts +2 -0
  76. package/dist/sdk/query/context.js +1 -1
  77. package/dist/sdk/query/get.js +1 -1
  78. package/dist/sdk/query/list.js +1 -1
  79. package/dist/sdk/query/search-contracts.js +1 -1
  80. package/dist/sdk/query/search-pagination.d.ts +4 -0
  81. package/dist/sdk/query/search-pagination.js +1 -1
  82. package/dist/sdk/query/search.js +1 -1
  83. package/dist/sdk/read-output/options.d.ts +14 -0
  84. package/dist/sdk/read-output/options.js +2 -0
  85. package/dist/sdk/read-output-budget.js +1 -1
  86. package/dist/sdk/read-output-contracts.js +1 -1
  87. package/dist/sdk/read-output-rows.d.ts +4 -0
  88. package/dist/sdk/read-output-rows.js +1 -1
  89. package/dist/sdk/runtime-primitives.d.ts +2 -2
  90. package/dist/sdk/runtime-primitives.js +1 -1
  91. package/dist/sdk/runtime.js +1 -1
  92. package/dist/sdk/test/execution.js +2 -2
  93. package/dist/sdk/test/workspace-snapshot.d.ts +11 -0
  94. package/dist/sdk/test/workspace-snapshot.js +2 -0
  95. package/dist/sdk/workspace-transaction.d.ts +2 -0
  96. package/dist/sdk/workspace-transaction.js +2 -2
  97. package/docs/DEVELOPMENT_DEPENDENCY_SECURITY.md +109 -0
  98. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +27 -0
  99. package/docs/SDK.md +98 -0
  100. package/docs/TESTING.md +32 -5
  101. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +5 -3
  102. package/marketplace.json +2 -2
  103. package/package.json +5 -4
  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-7E2EBJLY.js +0 -3
  123. package/dist/cli-bundle/chunks/chunk-BNAT4EYR.js +0 -20
  124. package/dist/cli-bundle/chunks/chunk-HGJLVN56.js +0 -8
  125. package/dist/cli-bundle/chunks/chunk-JAVO3Z42.js +0 -6
  126. package/dist/cli-bundle/chunks/chunk-TV3YDE36.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-6OCZVTW5.js +0 -20
  131. package/dist/cli-bundle/focused-chunks/chunk-3CAZ7Z4R.js +0 -30
  132. package/dist/cli-bundle/focused-chunks/chunk-DJPP2HAK.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
@@ -0,0 +1,109 @@
1
+ # Development dependency security
2
+
3
+ Tracked by [pm-r61juc](../.agents/pm/issues/pm-r61juc.toon) and
4
+ [pm-fnx3np](../.agents/pm/issues/pm-fnx3np.toon).
5
+
6
+ Development tools execute during tests, benchmarks and release preparation.
7
+ Their dependencies have the same admission requirement as production packages:
8
+ no known advisory may pass the required static gate. Run:
9
+
10
+ ```bash
11
+ pnpm install --frozen-lockfile
12
+ pnpm quality:dependencies
13
+ ```
14
+
15
+ This runs the full `pnpm audit`, with no production-only or severity filter,
16
+ then verifies the installed CodSpeed bundle policy. Network/audit errors,
17
+ missing packages, incomplete policy entries, changed package files, and unreviewed
18
+ upgrades all fail. `quality:static` includes this command, so the existing CI,
19
+ nightly quality and release paths enforce it. The registry-owned local
20
+ preflight also requires the static gate and permits no skip. Trivy separately
21
+ includes development dependencies in its required repository scan.
22
+
23
+ ## File matching dependency removal
24
+
25
+ [GHSA-vfj7-8cjw-p6xm](https://github.com/advisories/GHSA-vfj7-8cjw-p6xm)
26
+ affects `braces` 3.0.3, previously reachable through the production `fast-glob`
27
+ dependency. A pattern under 10,000 characters can exhaust the recursive brace
28
+ expansion stack. No fixed version was published when this change was verified.
29
+ The SDK and repository censuses now use `tinyglobby` 0.2.17, removing `fast-glob`,
30
+ `micromatch` and `braces` from the installed graph. Explicit
31
+ `expandDirectories: false` retains file-only semantics: a directory argument
32
+ does not silently become a recursive match. Dot files, brace and extglob
33
+ patterns, absolute paths and invocation-relative anchoring remain supported.
34
+ The regression uses a real temporary directory and the same deeply nested input
35
+ that failed before the replacement. Audit errors remain mandatory failures.
36
+
37
+ ## CodSpeed 5.7.1 patch
38
+
39
+ CodSpeed's declared dependency previously resolved Axios 1.19.0, and both
40
+ `@codspeed/core` bundles embedded Axios 1.4.0. Updating the declared dependency
41
+ alone leaves those embedded implementations and their source-map contents
42
+ intact. The pnpm override selects Axios 1.20.0 or a newer compatible 1.x
43
+ release that satisfies the normal dependency cooldown. The exact-version
44
+ patch in [the patch file](../patches/@codspeed__core@5.7.1.patch) replaces the
45
+ embedded dependency section in both CJS and ESM entries with imports of the
46
+ declared `axios` and `form-data` packages. Each entry creates its own Axios
47
+ client and retains the cancellation constructor, preserving isolation from
48
+ application request and response interceptors.
49
+
50
+ The replacement boundary starts at `function bind$2(fn, thisArg)` and ends
51
+ immediately before `var __defProp$3 = Object.defineProperty`. Only `axios` and
52
+ `FormData$2` from that section are referenced by the retained code. The patch
53
+ preserves the preceding and following benchmark code, declarations, export
54
+ surface and native binaries. Each entry removes 19,201 embedded lines. Its map
55
+ retains 5,375 mappings, removes 39,536 obsolete mappings, and reduces its source
56
+ inventory from 131 to 21. Remaining generated positions are shifted by the
57
+ replacement's line delta; their original source, line, column and names are
58
+ preserved. Removed Axios, redirects, proxy and form-data sources are absent.
59
+ Both maps end with a newline; the integrity policy records the installed bytes.
60
+
61
+ [The integrity policy](../config/dependency-bundle-integrity.json) pins all twelve
62
+ package-owned files by SHA-256: both entries, both maps, all three native binaries,
63
+ the manifest, declarations and declaration map, license and README. Owned
64
+ artifacts must be regular files; symlinks cannot redirect their module resolution
65
+ to an unreviewed dependency tree even when their target bytes have the same hash. Admission
66
+ also checks the exact installed file inventory, so an added executable, map or
67
+ native payload fails even when the previously approved files are unchanged.
68
+ Package-local dependency overrides also fail inventory admission: an added
69
+ `node_modules/axios` could shadow the audited dependency resolved by the bundle.
70
+ Only the three `node-gyp-build` commands and their CMD/PowerShell variants may
71
+ appear directly in `node_modules/.bin`. Each must be a regular file whose complete
72
+ program matches the reviewed [shim templates](../config/codspeed-shims/), with
73
+ targets and module paths derived from the audited installed dependency. Arbitrary
74
+ names, nested payloads, links, altered programs and forged target markers fail;
75
+ these shims are outside the twelve package-owned hashes but are independently
76
+ validated. The shell template records pnpm 11.10.0's installed output; the CMD
77
+ and PowerShell templates come from `@zkochan/cmd-shim` 9.0.7. Template changes
78
+ require renewed real installation evidence on both POSIX and native Windows.
79
+ The CMD suffix follows the installed `PATHEXT` spelling, including `.CMD`;
80
+ format lookup folds its case while validating the complete program. All three
81
+ shell launchers are required, together with the CMD and PowerShell sets on
82
+ Windows. Any additional format must also contain all three commands. Missing
83
+ or duplicate launchers fail admission. Installation roots and rendered paths
84
+ containing shell interpolation syntax fail before template comparison;
85
+ ordinary paths containing spaces remain supported. Templates are comparison
86
+ data: the checker never rewrites pnpm-generated launchers.
87
+ The locked dependency graph is checked by the full audit. An
88
+ upstream upgrade requires a reviewed patch removal or refresh, renewed runtime
89
+ compatibility evidence, and a matching policy update. Never update hashes
90
+ merely to admit an unexplained difference.
91
+
92
+ ## Verification and scope
93
+
94
+ The admission suite edits real temporary package copies and proves that each
95
+ changed package file and additional unreviewed payload is refused. The runtime
96
+ integration suite
97
+ uses both public entries in fresh processes, calls a real loopback HTTP server,
98
+ and verifies setup, benchmark start/stop, HTTP failure conversion, exports,
99
+ measurement conversions, root-frame wrappers and isolation from real application
100
+ interceptors. The server observes the
101
+ audited Axios transport rather than a mocked client. The existing CodSpeed
102
+ workflow continues to execute the native benchmark integration.
103
+
104
+ These findings establish vulnerable dependency inventory, not a demonstrated
105
+ production exploit. CodSpeed is a development dependency. Its Mongo
106
+ instrumentation transport requires an explicitly configured instrumentation
107
+ server; ordinary benchmarks do not configure one. Native Scorecard analysis
108
+ of the merged commit must confirm alert retirement independently of local
109
+ audits and PR checks.
@@ -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
package/docs/TESTING.md CHANGED
@@ -288,6 +288,8 @@ control and fails if `pm eval --fail-under` stops returning a non-zero exit.
288
288
  Refresh the baseline only with `pnpm quality:retrieval-eval:update` after
289
289
  reviewing query-level ranking changes.
290
290
 
291
+ Development tools must also pass the [development dependency security gate](DEVELOPMENT_DEPENDENCY_SECURITY.md), including the full audit and installed bundle integrity policy.
292
+
291
293
  ## Context Quality Evaluation
292
294
 
293
295
  The required context relevance gate proves that `pm context` and `pm next`
@@ -545,11 +547,36 @@ pm test <item-id> --run --workspace-context snapshot --override-linked-workspace
545
547
  declaration. Use it only for commands that do not require checkout files.
546
548
  - `snapshot` copies the workspace into the linked-test sandbox, runs from that
547
549
  copy, and binds its `.agents/pm` path to the selected temporary tracker.
548
- `.git`, `.agents`, `node_modules`, coverage output, and common cache
549
- directories are excluded at every directory depth; an existing top-level
550
- `node_modules` is linked read-only by convention. Built output remains
551
- available so linked commands such as `node dist/cli.js` keep working. Writes
552
- therefore land in the disposable snapshot rather than the source checkout.
550
+ `.git`, `.agents`, coverage output, and common cache directories are excluded
551
+ at every directory depth, with reserved names matched case-insensitively for
552
+ portable admission. Each existing root or nested `node_modules`
553
+ directory is linked at its original relative path, including installations
554
+ already represented by directory symlinks. This preserves independent
555
+ monorepo dependency versions without copying dependency trees. Dependency
556
+ aliases into excluded trees or included workspace source are refused. Internal
557
+ shared targets must remain within admitted `node_modules` subtrees; external
558
+ installations must be disjoint from the source workspace, rather than
559
+ containing it. They remain available even when an external ancestor shares an
560
+ excluded directory name.
561
+ Links are read-only **by convention**; they do not prevent a trusted command
562
+ from modifying the original installation. Use an independent installation
563
+ when testing package-manager operations or dependency writes. Built output
564
+ remains available so linked commands such as `node dist/cli.js` keep working.
565
+ The source workspace root is resolved before copying. Ordinary file and
566
+ directory symlinks into included workspace source are rebased into the copy,
567
+ including dangling file aliases; writes through those aliases stay in the
568
+ disposable snapshot. Targets are canonicalized through their nearest existing
569
+ ancestor, so absolute aliases through another workspace path spelling and
570
+ missing target suffixes retain the same isolation on supported platforms.
571
+ Source aliases into unadmitted external directories or excluded tracker/build
572
+ trees fail before the linked command runs. An alias into an admitted shared
573
+ dependency namespace retains the dependency-write convention above, including
574
+ packages linked within that installation. Source and snapshot destinations
575
+ must be physically disjoint; overlapping roots fail before copying with a
576
+ diagnostic directing the caller to a temporary root outside the checkout.
577
+
578
+ The real-filesystem regression for this contract is tracked by
579
+ [pm-5iwfkj](../.agents/pm/issues/pm-5iwfkj.toon).
553
580
 
554
581
  Every result reports the requested/effective workspace mode, working
555
582
  directory, exposed source root, and trust decision. Recorded `test_runs` retain
@@ -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: 391
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: 372
12
- - Coverage fraction: 0.048593
11
+ - Uncovered error codes: 374
12
+ - Coverage fraction: 0.048346
13
13
  - Closed-domain probes: 19
14
14
  - Grammar probes: 117
15
15
 
@@ -321,6 +321,8 @@ Every catalog code is listed. An `uncovered` row is an explicit closure obligati
321
321
  | `tracker_root_missing` | `tracker_root_missing` | executable | owned_state | 1 |
322
322
  | `tracker_root_not_directory` | `tracker_root_not_directory` | executable | owned_state | 1 |
323
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 |
324
326
  | `type_duplicate` | `type_duplicate` | uncovered | none | 0 |
325
327
  | `type_invalid` | `type_invalid` | uncovered | none | 0 |
326
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.10.1",
8
+ "version": "2026.10.3",
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.10.1",
14
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
4
4
  "description": "Git-native project management CLI for humans and agents.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.10.0",
@@ -123,8 +123,9 @@
123
123
  "lint:complexity:baseline": "eslint . --suppress-rule complexity --suppress-rule sonarjs/cognitive-complexity",
124
124
  "lint:duplicates": "jscpd --config .jscpd.json && jscpd --config .jscpd.source.json && jscpd --config .jscpd.tables.json",
125
125
  "lint:codefactor": "pnpm quality:static",
126
- "quality:static": "pnpm quality:docstrings && pnpm quality:exports && pnpm lint:eslint && pnpm lint:duplicates && node scripts/check-workflow-permissions.mjs && node scripts/check-dependency-cooldown.mjs && node scripts/gen-plugin-mcp-wrappers.mjs --check && node scripts/gen-agent-plugin-skills.mjs --check && pnpm build && node scripts/contracts-snapshot.mjs --check && node scripts/generate-agent-capability-surfaces.mjs --check && node scripts/generate-error-code-catalog.mjs --check && node scripts/release/repository-assurance.mjs repository-static-quality --trigger ci --json && node scripts/release/audit-package-boundary.mjs && node scripts/release/package-sdk-contract-parity.mjs && node scripts/release/surface-replication-gate.mjs && node scripts/release/command-grammar-gate.mjs && node scripts/release/flag-invocation-parity.mjs && node scripts/release/flag-lexicon-gate.mjs && node scripts/release/refusal-closure-gate.mjs && node scripts/release/agent-task-token-gate.mjs && pnpm quality:mcp-deprecations && node dist/cli.js assurance run tracker-context-quality --trigger ci --dry-run --json --output-budget unbounded && node scripts/release/gate-registry.mjs && node scripts/sdk-surface-snapshot.mjs --check && node scripts/bench/sdk-entrypoint-costs.mjs --check && node scripts/bench/cli-transport-floor.mjs --check && node dist/cli.js assurance run graph-composition --trigger ci --dry-run --json --output-budget unbounded && node dist/cli.js assurance run record-integrity --trigger ci --dry-run --json --output-budget unbounded && pnpm quality:mutation -- --prebuilt",
126
+ "quality:static": "pnpm quality:dependencies && pnpm quality:docstrings && pnpm quality:exports && pnpm lint:eslint && pnpm lint:duplicates && node scripts/check-workflow-permissions.mjs && node scripts/check-dependency-cooldown.mjs && node scripts/gen-plugin-mcp-wrappers.mjs --check && node scripts/gen-agent-plugin-skills.mjs --check && pnpm build && node scripts/contracts-snapshot.mjs --check && node scripts/generate-agent-capability-surfaces.mjs --check && node scripts/generate-error-code-catalog.mjs --check && node scripts/release/repository-assurance.mjs repository-static-quality --trigger ci --json && node scripts/release/audit-package-boundary.mjs && node scripts/release/package-sdk-contract-parity.mjs && node scripts/release/surface-replication-gate.mjs && node scripts/release/command-grammar-gate.mjs && node scripts/release/flag-invocation-parity.mjs && node scripts/release/flag-lexicon-gate.mjs && node scripts/release/refusal-closure-gate.mjs && node scripts/release/agent-task-token-gate.mjs && pnpm quality:mcp-deprecations && node dist/cli.js assurance run tracker-context-quality --trigger ci --dry-run --json --output-budget unbounded && node scripts/release/gate-registry.mjs && node scripts/sdk-surface-snapshot.mjs --check && node scripts/bench/sdk-entrypoint-costs.mjs --check && node scripts/bench/cli-transport-floor.mjs --check && node dist/cli.js assurance run graph-composition --trigger ci --dry-run --json --output-budget unbounded && node dist/cli.js assurance run record-integrity --trigger ci --dry-run --json --output-budget unbounded && pnpm quality:mutation -- --prebuilt",
127
127
  "quality:exports": "knip --no-config-hints && node scripts/run-tests.mjs test -- tests/unit/scripts/release/dead-exports.spec.ts",
128
+ "quality:dependencies": "pnpm audit && node scripts/check-development-dependencies.mjs",
128
129
  "quality:command-grammar": "pnpm build && node scripts/release/command-grammar-gate.mjs && node scripts/release/flag-invocation-parity.mjs && node scripts/release/flag-lexicon-gate.mjs",
129
130
  "quality:recovery-closure": "pnpm build && node scripts/release/refusal-closure-gate.mjs",
130
131
  "quality:token-budget": "node scripts/release/token-budget-gate.mjs",
@@ -216,16 +217,16 @@
216
217
  "@toon-format/toon": "^4.1.1",
217
218
  "@types/node": ">=22",
218
219
  "commander": "^15.0.0",
219
- "fast-glob": "^3.3.3",
220
220
  "fast-json-patch": "^3.1.1",
221
221
  "npm-package-arg": "^13.0.2",
222
222
  "tar": "7.5.22",
223
+ "tinyglobby": "^0.2.17",
223
224
  "yaml": "^2.9.1"
224
225
  },
225
226
  "devDependencies": {
226
227
  "@codspeed/vitest-plugin": "^5.7.1",
227
228
  "@eslint/js": "^10.0.1",
228
- "@modelcontextprotocol/ext-apps": "2.0.0",
229
+ "@modelcontextprotocol/ext-apps": "2.0.3",
229
230
  "@sentry/cli": "^3.8.0",
230
231
  "@stryker-mutator/core": "10.0.0",
231
232
  "@stryker-mutator/vitest-runner": "10.0.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrained/pm-beads",
3
- "version": "2026.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
3
+ "version": "2026.10.3",
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.10.1",
6
+ "version": "2026.10.3",
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.10.1",
5
+ "version": "2026.10.3",
6
6
  "dependencies": {
7
- "@unbrained/pm-cli": "2026.10.1"
7
+ "@unbrained/pm-cli": "2026.10.3"
8
8
  }
9
9
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pm-codex",
3
- "version": "2026.10.1",
3
+ "version": "2026.10.3",
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.10.1",
5
+ "version": "2026.10.3",
6
6
  "dependencies": {
7
- "@unbrained/pm-cli": "2026.10.1"
7
+ "@unbrained/pm-cli": "2026.10.3"
8
8
  }
9
9
  }