@unbrained/pm-cli 2026.8.7 → 2026.8.8

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 (226) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +35 -0
  3. package/dist/cli/main.js +4 -3
  4. package/dist/cli/register-annotations.d.ts +11 -0
  5. package/dist/cli/register-annotations.js +195 -0
  6. package/dist/cli/register-history-author.js +7 -30
  7. package/dist/cli/register-mutation.js +25 -206
  8. package/dist/cli/runtime-compatibility-boundary.d.ts +17 -0
  9. package/dist/cli/runtime-compatibility-boundary.js +43 -0
  10. package/dist/cli-bundle/bundle-manifest.json +155 -691
  11. package/dist/cli-bundle/chunks/chunk-2ZMXX3Q6.js +2068 -0
  12. package/dist/cli-bundle/chunks/chunk-ALDHXLPS.js +3 -0
  13. package/dist/cli-bundle/chunks/chunk-CBLA33YK.js +8 -0
  14. package/dist/cli-bundle/chunks/chunk-GKA54IJM.js +5 -0
  15. package/dist/cli-bundle/chunks/chunk-KRY7SRUO.js +2 -0
  16. package/dist/cli-bundle/chunks/chunk-MNJ4NNSY.js +26 -0
  17. package/dist/cli-bundle/chunks/chunk-NUXRRNAB.js +77 -0
  18. package/dist/cli-bundle/chunks/chunk-OJPCFDDY.js +13 -0
  19. package/dist/cli-bundle/chunks/chunk-PFSD3DAN.js +194 -0
  20. package/dist/cli-bundle/chunks/chunk-QWRRFYK3.js +2 -0
  21. package/dist/cli-bundle/chunks/chunk-SQ7QZFQ6.js +2 -0
  22. package/dist/cli-bundle/chunks/{register-list-query-FJZCJ67O.js → register-list-query-WFZAMJHZ.js} +8 -8
  23. package/dist/cli-bundle/chunks/register-mutation-SOKKOTHQ.js +20 -0
  24. package/dist/cli-bundle/chunks/register-operations-5XQETS6E.js +2 -0
  25. package/dist/cli-bundle/chunks/register-setup-2HL5ZHMK.js +2 -0
  26. package/dist/cli-bundle/focused-chunks/chunk-54EU2UVO.js +2 -0
  27. package/dist/cli-bundle/focused-chunks/{chunk-VZFU2R4M.js → chunk-5FLBTZJQ.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/chunk-5GJ57YKN.js +2 -0
  29. package/dist/cli-bundle/focused-chunks/{chunk-HNL6IFGS.js → chunk-ANTMGYLI.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/chunk-CMDS3KYT.js +14 -0
  31. package/dist/cli-bundle/focused-chunks/{chunk-NBJKQP4S.js → chunk-DMKOMTTY.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-CIXVQPB7.js → chunk-F5B3HBEZ.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/{chunk-R2LEMEV5.js → chunk-FI6DWU2M.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-3K4XV2BF.js → chunk-FNOKJG6N.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/{chunk-RW5IYD4J.js → chunk-FPDYXZBK.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/{chunk-QHRTT7WT.js → chunk-GWI6YIKJ.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-MHMTKV5V.js → chunk-HCUC62AB.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/{chunk-MOTJFQ3F.js → chunk-HGNKPSCB.js} +12 -12
  39. package/dist/cli-bundle/focused-chunks/{chunk-4VJQTS3P.js → chunk-IAS7PMLG.js} +2 -2
  40. package/dist/cli-bundle/focused-chunks/{chunk-TIKDBG4D.js → chunk-PL2EYDJK.js} +27 -27
  41. package/dist/cli-bundle/focused-chunks/chunk-SM2PJGP3.js +2 -0
  42. package/dist/cli-bundle/focused-chunks/{chunk-RPNYG5MO.js → chunk-TCSAPH33.js} +2 -2
  43. package/dist/cli-bundle/focused-chunks/chunk-U646T7Z6.js +18 -0
  44. package/dist/cli-bundle/focused-chunks/chunk-YKPE4PPY.js +2 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-YZEZAPJK.js → chunk-YSAHYUXX.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/{chunk-73JUDYXT.js → chunk-ZOLDTUU7.js} +2 -2
  47. package/dist/cli-bundle/main.js +13 -13
  48. package/dist/cli-bundle/sdk-authoring.js +1 -1
  49. package/dist/cli-bundle/sdk-contracts.js +1 -1
  50. package/dist/cli-bundle/sdk-core.js +38 -38
  51. package/dist/cli-bundle/sdk-governance.js +1 -1
  52. package/dist/cli-bundle/sdk-graph.js +1 -1
  53. package/dist/cli-bundle/sdk-merge.js +1 -1
  54. package/dist/cli-bundle/sdk-query.js +1 -1
  55. package/dist/cli-bundle/sdk-runtime.js +1 -1
  56. package/dist/cli-bundle/sdk-testing.js +1 -1
  57. package/dist/cli-bundle/sdk.js +2 -1
  58. package/dist/cli.js +14 -4
  59. package/dist/core/output/output.js +9 -2
  60. package/dist/core/sentry/helpers.d.ts +10 -0
  61. package/dist/core/sentry/helpers.js +59 -4
  62. package/dist/core/shared/constants.d.ts +2 -0
  63. package/dist/core/shared/constants.js +4 -2
  64. package/dist/sdk/annotations.d.ts +40 -0
  65. package/dist/sdk/annotations.js +61 -9
  66. package/dist/sdk/author-attribution.d.ts +12 -0
  67. package/dist/sdk/author-attribution.js +73 -11
  68. package/dist/sdk/cli-contracts/command-exit-contracts.d.ts +118 -0
  69. package/dist/sdk/cli-contracts/command-exit-contracts.js +152 -0
  70. package/dist/sdk/cli-contracts/completeness.js +5 -3
  71. package/dist/sdk/cli-contracts/flag-contracts.js +5 -2
  72. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +11 -0
  73. package/dist/sdk/cli-contracts/runtime-contracts.js +29 -2
  74. package/dist/sdk/cli-contracts/tool-parameter-tables.js +15 -2
  75. package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
  76. package/dist/sdk/cli-contracts/tool-schema.js +6 -3
  77. package/dist/sdk/comments.d.ts +7 -0
  78. package/dist/sdk/comments.js +2 -2
  79. package/dist/sdk/completion.js +9 -5
  80. package/dist/sdk/contracts.d.ts +1 -0
  81. package/dist/sdk/contracts.js +3 -2
  82. package/dist/sdk/environment/host-environment-errors.d.ts +27 -0
  83. package/dist/sdk/environment/host-environment-errors.js +124 -0
  84. package/dist/sdk/environment/project-runtime-compatibility.d.ts +65 -0
  85. package/dist/sdk/environment/project-runtime-compatibility.js +385 -0
  86. package/dist/sdk/environment/source-context.d.ts +23 -0
  87. package/dist/sdk/environment/source-context.js +29 -0
  88. package/dist/sdk/error-code-catalog.d.ts +6 -0
  89. package/dist/sdk/error-code-catalog.js +62 -8
  90. package/dist/sdk/generated-error-code-catalog.js +629 -3
  91. package/dist/sdk/governance/health.d.ts +11 -3
  92. package/dist/sdk/governance/health.js +5 -5
  93. package/dist/sdk/index.d.ts +7 -3
  94. package/dist/sdk/index.js +9 -5
  95. package/dist/sdk/learnings.d.ts +15 -0
  96. package/dist/sdk/learnings.js +2 -2
  97. package/dist/sdk/lifecycle/close-many.d.ts +6 -1
  98. package/dist/sdk/lifecycle/close-many.js +43 -5
  99. package/dist/sdk/lifecycle/update-many.d.ts +2 -1
  100. package/dist/sdk/lifecycle/update-many.js +29 -5
  101. package/dist/sdk/merge/index.d.ts +1 -1
  102. package/dist/sdk/merge/index.js +3 -3
  103. package/dist/sdk/merge/install.d.ts +21 -0
  104. package/dist/sdk/merge/install.js +50 -20
  105. package/dist/sdk/notes.d.ts +7 -0
  106. package/dist/sdk/notes.js +2 -2
  107. package/dist/sdk/read-output-contracts.js +83 -4
  108. package/dist/sdk/runtime.d.ts +1 -1
  109. package/dist/sdk/runtime.js +9 -25
  110. package/dist/sdk/test/execution.js +4 -2
  111. package/dist/sdk/test/parsers.js +5 -2
  112. package/dist/sdk/workspace-snapshot.d.ts +9 -0
  113. package/dist/sdk/workspace-snapshot.js +48 -16
  114. package/docs/AGENT_RUNTIME_PRIMITIVES.md +11 -4
  115. package/docs/README.md +1 -0
  116. package/docs/RELEASING.md +27 -2
  117. package/docs/SCRIPTING.md +27 -1
  118. package/docs/SDK_CONTEXT_INTEGRITY.md +66 -0
  119. package/docs/SDK_RUNTIME_BOUNDARIES.md +58 -0
  120. package/docs/SELF_DESCRIBING_CONTEXT_CONTRACTS.md +13 -0
  121. package/docs/TESTING.md +17 -0
  122. package/marketplace.json +2 -2
  123. package/package.json +5 -3
  124. package/packages/pm-beads/package.json +1 -1
  125. package/packages/pm-calendar/package.json +1 -1
  126. package/packages/pm-command-kit/package.json +1 -1
  127. package/packages/pm-digital-twin/package.json +1 -1
  128. package/packages/pm-governance-audit/package.json +1 -1
  129. package/packages/pm-guide-shell/package.json +1 -1
  130. package/packages/pm-kanban/package.json +1 -1
  131. package/packages/pm-lifecycle-hooks/package.json +1 -1
  132. package/packages/pm-linked-test-adapters/package.json +1 -1
  133. package/packages/pm-search-advanced/package.json +1 -1
  134. package/packages/pm-templates/package.json +1 -1
  135. package/packages/pm-todos/package.json +1 -1
  136. package/packages/pm-vcs/package.json +1 -1
  137. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  138. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  139. package/sdk/public-surface.json +529 -34
  140. package/dist/cli-bundle/chunks/append-7BXBFDGD.js +0 -2
  141. package/dist/cli-bundle/chunks/chunk-2AXF3VSK.js +0 -2
  142. package/dist/cli-bundle/chunks/chunk-2XS43CCV.js +0 -2
  143. package/dist/cli-bundle/chunks/chunk-3A6KWB72.js +0 -8
  144. package/dist/cli-bundle/chunks/chunk-3ISTDB42.js +0 -8
  145. package/dist/cli-bundle/chunks/chunk-4MTI7XOV.js +0 -5
  146. package/dist/cli-bundle/chunks/chunk-6BX5UDCN.js +0 -3
  147. package/dist/cli-bundle/chunks/chunk-6QPO7KLR.js +0 -2
  148. package/dist/cli-bundle/chunks/chunk-7MXHZHSQ.js +0 -5
  149. package/dist/cli-bundle/chunks/chunk-7ZPMJW4U.js +0 -2
  150. package/dist/cli-bundle/chunks/chunk-A6KGKEWT.js +0 -2
  151. package/dist/cli-bundle/chunks/chunk-AGYNSNCI.js +0 -2
  152. package/dist/cli-bundle/chunks/chunk-B4H7FEFH.js +0 -5
  153. package/dist/cli-bundle/chunks/chunk-B4KLBBMN.js +0 -2
  154. package/dist/cli-bundle/chunks/chunk-BXCSXK4J.js +0 -2
  155. package/dist/cli-bundle/chunks/chunk-CALJHNBL.js +0 -23
  156. package/dist/cli-bundle/chunks/chunk-CFIGP5LY.js +0 -2
  157. package/dist/cli-bundle/chunks/chunk-CHOQ2VAW.js +0 -2
  158. package/dist/cli-bundle/chunks/chunk-CS6MRHG7.js +0 -2
  159. package/dist/cli-bundle/chunks/chunk-CUGNQQKH.js +0 -2
  160. package/dist/cli-bundle/chunks/chunk-D4FPS43D.js +0 -164
  161. package/dist/cli-bundle/chunks/chunk-E2ZSLTOP.js +0 -2
  162. package/dist/cli-bundle/chunks/chunk-FB7G2HY2.js +0 -2
  163. package/dist/cli-bundle/chunks/chunk-H5Y5YE6A.js +0 -2
  164. package/dist/cli-bundle/chunks/chunk-H5Y6UV6E.js +0 -2
  165. package/dist/cli-bundle/chunks/chunk-H7KGWPDF.js +0 -2
  166. package/dist/cli-bundle/chunks/chunk-HFSD77TQ.js +0 -2
  167. package/dist/cli-bundle/chunks/chunk-HX2GTA6L.js +0 -2
  168. package/dist/cli-bundle/chunks/chunk-J2IEKAVR.js +0 -2
  169. package/dist/cli-bundle/chunks/chunk-JC6FKIFM.js +0 -2
  170. package/dist/cli-bundle/chunks/chunk-K44PYFXH.js +0 -2
  171. package/dist/cli-bundle/chunks/chunk-K4KGEEBT.js +0 -2
  172. package/dist/cli-bundle/chunks/chunk-KFLK5TRH.js +0 -21
  173. package/dist/cli-bundle/chunks/chunk-KL6IEBV2.js +0 -2
  174. package/dist/cli-bundle/chunks/chunk-KWQZDZSS.js +0 -2
  175. package/dist/cli-bundle/chunks/chunk-KZ4X3DGU.js +0 -3
  176. package/dist/cli-bundle/chunks/chunk-LD77HJMQ.js +0 -13
  177. package/dist/cli-bundle/chunks/chunk-LEPJJ6QC.js +0 -2
  178. package/dist/cli-bundle/chunks/chunk-ME2JJ4LA.js +0 -3
  179. package/dist/cli-bundle/chunks/chunk-NE5VRDAI.js +0 -2
  180. package/dist/cli-bundle/chunks/chunk-NFLJ3FHD.js +0 -2
  181. package/dist/cli-bundle/chunks/chunk-NG6OXIBR.js +0 -19
  182. package/dist/cli-bundle/chunks/chunk-NYIGHWQY.js +0 -8
  183. package/dist/cli-bundle/chunks/chunk-NZ75GNSA.js +0 -2
  184. package/dist/cli-bundle/chunks/chunk-PCJWJNC2.js +0 -2
  185. package/dist/cli-bundle/chunks/chunk-PD3225AM.js +0 -2
  186. package/dist/cli-bundle/chunks/chunk-PDEGKG7P.js +0 -2
  187. package/dist/cli-bundle/chunks/chunk-PIE5HBNA.js +0 -2
  188. package/dist/cli-bundle/chunks/chunk-PW2H7YJR.js +0 -56
  189. package/dist/cli-bundle/chunks/chunk-RSIC5OPE.js +0 -2
  190. package/dist/cli-bundle/chunks/chunk-TPKN3S7S.js +0 -2
  191. package/dist/cli-bundle/chunks/chunk-TPXAXTCO.js +0 -2
  192. package/dist/cli-bundle/chunks/chunk-UFWUJO4V.js +0 -2
  193. package/dist/cli-bundle/chunks/chunk-UQLZQVFW.js +0 -55
  194. package/dist/cli-bundle/chunks/chunk-VT3Z5G7D.js +0 -2
  195. package/dist/cli-bundle/chunks/chunk-WOD3WWUN.js +0 -2
  196. package/dist/cli-bundle/chunks/chunk-WSJEIGJF.js +0 -3
  197. package/dist/cli-bundle/chunks/chunk-WYNUU7ZW.js +0 -2008
  198. package/dist/cli-bundle/chunks/chunk-YGPNCCXZ.js +0 -2
  199. package/dist/cli-bundle/chunks/close-CMY3BAUG.js +0 -2
  200. package/dist/cli-bundle/chunks/close-many-SA4XZCTK.js +0 -2
  201. package/dist/cli-bundle/chunks/comments-EZ556ZD3.js +0 -2
  202. package/dist/cli-bundle/chunks/copy-ZSGPA52X.js +0 -2
  203. package/dist/cli-bundle/chunks/create-I5DVV4YG.js +0 -2
  204. package/dist/cli-bundle/chunks/delete-RL3JACSW.js +0 -2
  205. package/dist/cli-bundle/chunks/deps-S7UBCECS.js +0 -2
  206. package/dist/cli-bundle/chunks/docs-ZZNVBBYO.js +0 -2
  207. package/dist/cli-bundle/chunks/files-27C337VT.js +0 -2
  208. package/dist/cli-bundle/chunks/focus-5Z2SG7LU.js +0 -2
  209. package/dist/cli-bundle/chunks/history-compact-HJQK67CZ.js +0 -2
  210. package/dist/cli-bundle/chunks/history-redact-PWC6PDWA.js +0 -2
  211. package/dist/cli-bundle/chunks/history-repair-N3CY4WBF.js +0 -2
  212. package/dist/cli-bundle/chunks/learnings-4FH23XDT.js +0 -2
  213. package/dist/cli-bundle/chunks/profile-5Y5XXH5N.js +0 -2
  214. package/dist/cli-bundle/chunks/register-mutation-YGYPW3BL.js +0 -20
  215. package/dist/cli-bundle/chunks/register-operations-WMDSUMQF.js +0 -2
  216. package/dist/cli-bundle/chunks/register-setup-DL7FFABC.js +0 -2
  217. package/dist/cli-bundle/chunks/restore-6KYBV5BY.js +0 -2
  218. package/dist/cli-bundle/chunks/schema-EQGKBYXJ.js +0 -2
  219. package/dist/cli-bundle/chunks/update-QVTYOD6I.js +0 -2
  220. package/dist/cli-bundle/chunks/update-many-DJSBU525.js +0 -2
  221. package/dist/cli-bundle/focused-chunks/chunk-2ECLECMK.js +0 -12
  222. package/dist/cli-bundle/focused-chunks/chunk-4EX25PXM.js +0 -8
  223. package/dist/cli-bundle/focused-chunks/chunk-DLTS3IHM.js +0 -2
  224. package/dist/cli-bundle/focused-chunks/chunk-DQ6FKGL3.js +0 -14
  225. package/dist/cli-bundle/focused-chunks/chunk-JLG2C4EQ.js +0 -2
  226. package/dist/cli-bundle/focused-chunks/chunk-ZJIMJHDB.js +0 -2
@@ -8,7 +8,7 @@
8
8
  * mutations refresh the fenced block automatically once it is installed.
9
9
  */
10
10
 
11
- !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]="a250384a-0279-5d01-ade4-f64be3119ed9")}catch(e){}}();
11
+ !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]="fce8fc1d-b9ca-55bc-9c5a-f4f9728a7158")}catch(e){}}();
12
12
  import { readFile, realpath, writeFile } from "node:fs/promises";
13
13
  import { execFile } from "node:child_process";
14
14
  import path from "node:path";
@@ -24,6 +24,7 @@ import { nowIso } from "../../core/shared/time.js";
24
24
  import { getSettingsPath, resolvePmRoot } from "../../core/store/paths.js";
25
25
  import { readSettings, resolveGovernanceKnobs, } from "../../core/store/settings.js";
26
26
  import { isPathOutsideRoot } from "../workspace.js";
27
+ import { resolveSourceContextWritePolicy } from "../environment/source-context.js";
27
28
  const execFileAsync = promisify(execFile);
28
29
  /** Opening marker for the pm-owned merge-driver `.gitattributes` block. */
29
30
  export const PM_GITATTRIBUTES_START = "# pm-cli:merge-drivers:start";
@@ -80,6 +81,45 @@ const MERGE_DRIVER_DEFINITIONS = [
80
81
  itemPath: undefined,
81
82
  },
82
83
  ];
84
+ /** Resolve explicit, inherited-source, and current-workspace precedence without I/O. */
85
+ export function resolveMergeInstallContext(options) {
86
+ const explicitPath = options.explicitPath?.trim();
87
+ const sourceWorkspaceRoot = options.environment.PM_SOURCE_WORKSPACE_ROOT?.trim();
88
+ const sourcePmRoot = options.environment.PM_SOURCE_PM_PATH?.trim();
89
+ const sourcePolicy = resolveSourceContextWritePolicy(options.environment);
90
+ const sourceCoordinatesAvailable = Boolean(sourceWorkspaceRoot && sourcePmRoot);
91
+ const sourceContextSelected = explicitPath === undefined &&
92
+ sourceCoordinatesAvailable &&
93
+ (options.dryRun || sourcePolicy.source_writes_allowed);
94
+ if (explicitPath !== undefined) {
95
+ const pmRoot = resolvePmRoot(options.cwd, explicitPath);
96
+ return {
97
+ pm_root: pmRoot,
98
+ workspace_start: pmRoot,
99
+ source_coordinates_available: sourceCoordinatesAvailable,
100
+ source_context_selected: false,
101
+ source_policy: sourcePolicy,
102
+ };
103
+ }
104
+ if (sourceContextSelected) {
105
+ return {
106
+ pm_root: path.resolve(sourcePmRoot),
107
+ workspace_start: path.resolve(sourceWorkspaceRoot),
108
+ source_coordinates_available: true,
109
+ source_context_selected: true,
110
+ source_policy: sourcePolicy,
111
+ };
112
+ }
113
+ return {
114
+ pm_root: sourceCoordinatesAvailable
115
+ ? resolvePmRoot(options.cwd, ".agents/pm")
116
+ : resolvePmRoot(options.cwd),
117
+ workspace_start: options.cwd,
118
+ source_coordinates_available: sourceCoordinatesAvailable,
119
+ source_context_selected: false,
120
+ source_policy: sourcePolicy,
121
+ };
122
+ }
83
123
  /** Resolve the enclosing Git worktree root or return null outside Git. */
84
124
  export async function findGitWorkspaceRoot(cwd) {
85
125
  try {
@@ -393,27 +433,17 @@ export async function refreshMergeAttributeFenceIfInstalled(pmRoot) {
393
433
  * per checkout). Requires an initialized tracker and a git repository.
394
434
  */
395
435
  export async function runMergeInstall(options, global) {
396
- const explicitPath = global.path?.trim();
397
- const sourceWorkspaceRoot = process.env.PM_SOURCE_WORKSPACE_ROOT?.trim();
398
- const sourcePmRoot = process.env.PM_SOURCE_PM_PATH?.trim();
399
- const usesLinkedTestSourceContext = explicitPath === undefined &&
400
- sourceWorkspaceRoot !== undefined &&
401
- sourceWorkspaceRoot.length > 0 &&
402
- sourcePmRoot !== undefined &&
403
- sourcePmRoot.length > 0;
404
- const pmRoot = explicitPath !== undefined
405
- ? resolvePmRoot(process.cwd(), explicitPath)
406
- : usesLinkedTestSourceContext
407
- ? path.resolve(sourcePmRoot)
408
- : resolvePmRoot(process.cwd());
436
+ const context = resolveMergeInstallContext({
437
+ cwd: process.cwd(),
438
+ explicitPath: global.path,
439
+ dryRun: options.dryRun === true,
440
+ environment: process.env,
441
+ });
442
+ const pmRoot = context.pm_root;
409
443
  if (!(await pathExists(getSettingsPath(pmRoot)))) {
410
444
  throw new PmCliError(`Tracker is not initialized at ${pmRoot}. Run pm init first.`, EXIT_CODE.NOT_FOUND);
411
445
  }
412
- const workspaceRoot = await resolveGitWorkspaceRoot(explicitPath !== undefined
413
- ? pmRoot
414
- : usesLinkedTestSourceContext
415
- ? path.resolve(sourceWorkspaceRoot)
416
- : process.cwd());
446
+ const workspaceRoot = await resolveGitWorkspaceRoot(context.workspace_start);
417
447
  return installMergeFence({
418
448
  pmRoot,
419
449
  workspaceRoot,
@@ -518,4 +548,4 @@ export async function installMergeFence(options) {
518
548
  };
519
549
  }
520
550
  //# sourceMappingURL=install.js.map
521
- //# debugId=a250384a-0279-5d01-ade4-f64be3119ed9
551
+ //# debugId=fce8fc1d-b9ca-55bc-9c5a-f4f9728a7158
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { type GlobalOptions } from "./runtime-primitives.js";
7
7
  import type { LogNote } from "../types/index.js";
8
+ import { type AnnotationMutationReceipt, type AnnotationOmissionReceipt } from "./annotations.js";
8
9
  /** Documents the notes command options payload exchanged by command, SDK, and package integrations. */
9
10
  export interface NotesCommandOptions {
10
11
  /** Value that configures or reports add for this contract. */
@@ -27,6 +28,8 @@ export interface NotesCommandOptions {
27
28
  eventType?: string;
28
29
  /** Include total, returned, and truncation metadata. */
29
30
  includeMeta?: boolean;
31
+ /** Return complete note history after a mutation instead of a bounded receipt. */
32
+ fullHistory?: boolean;
30
33
  /** Value that configures or reports author for this contract. */
31
34
  author?: string;
32
35
  /** Human-readable explanation suitable for logs and agent-facing output. */
@@ -52,6 +55,10 @@ export interface NotesResult {
52
55
  limit?: number;
53
56
  /** Number of structured context events in the returned projection. */
54
57
  structured_event_count?: number;
58
+ /** Mutation identity when this result changed the collection. */
59
+ mutation_receipt?: AnnotationMutationReceipt;
60
+ /** Declares whether older notes were withheld from a mutation response. */
61
+ omission_receipt?: AnnotationOmissionReceipt;
55
62
  }
56
63
  /** Implements run notes for the public runtime surface of this module. */
57
64
  export declare function runNotes(id: string, options: NotesCommandOptions, global: GlobalOptions): Promise<NotesResult>;
package/dist/sdk/notes.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Implements the pm notes command surface and its agent-facing runtime behavior.
5
5
  */
6
6
 
7
- !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]="973fe868-d08e-5a45-83c5-b7ecb959f680")}catch(e){}}();
7
+ !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]="ebd9f151-593f-5081-97da-d78f83de4914")}catch(e){}}();
8
8
  import { EXIT_CODE, PmCliError, parseLimit, stableStringify, } from "./runtime-primitives.js";
9
9
  import { limitAnnotationEntries, parseAnnotationTextInput, resolveAnnotationInput, runAnnotationCommand, } from "./annotations.js";
10
10
  function parseStructuredEventPayload(raw) {
@@ -137,4 +137,4 @@ export async function runNotes(id, options, global) {
137
137
  };
138
138
  }
139
139
  //# sourceMappingURL=notes.js.map
140
- //# debugId=973fe868-d08e-5a45-83c5-b7ecb959f680
140
+ //# debugId=ebd9f151-593f-5081-97da-d78f83de4914
@@ -5,7 +5,7 @@
5
5
  * surface without coupling package authors to command-specific option names.
6
6
  */
7
7
 
8
- !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]="0bccb953-bd78-5eee-a78d-8af3e250fa55")}catch(e){}}();
8
+ !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]="27f3a09b-9b90-5c85-9e41-21cfed5dc0ce")}catch(e){}}();
9
9
  import { EXIT_CODE } from "../core/shared/constants.js";
10
10
  import { PmCliError } from "../core/shared/errors.js";
11
11
  import { compactReadOutputToBudget, updateReadOutputReceiptEstimate, } from "./read-output-budget.js";
@@ -427,7 +427,86 @@ function projectRecordFields(value, selectors) {
427
427
  const selected = new Set(selectors.map((selector) => selector.split(".")[0]));
428
428
  return Object.fromEntries(Object.entries(value).filter(([key]) => selected.has(key)));
429
429
  }
430
- function applyIncludeProjection(result, selectors) {
430
+ function getProjectionVocabulary(result) {
431
+ const item = isRecord(result.item) ? result.item : {};
432
+ const itemFields = Object.keys(item).sort((left, right) => left.localeCompare(right));
433
+ const sections = Object.keys(result)
434
+ .filter((key) => key !== "item" && key !== "read_output" && !ENVELOPE_KEYS.has(key))
435
+ .sort((left, right) => left.localeCompare(right));
436
+ return {
437
+ item,
438
+ itemFields,
439
+ sections,
440
+ valid: [
441
+ ...itemFields,
442
+ "item",
443
+ ...itemFields.map((field) => `item.${field}`),
444
+ ...sections,
445
+ ],
446
+ };
447
+ }
448
+ function applyGetIncludeProjection(result, selectors) {
449
+ const vocabulary = getProjectionVocabulary(result);
450
+ const unknown = selectors.filter((selector) => !vocabulary.valid.includes(selector));
451
+ if (unknown.length > 0) {
452
+ throw new PmCliError(`Unknown --output-include selector(s) for get: ${unknown.join(", ")}. Valid selectors: ${vocabulary.valid.join(", ")}.`, EXIT_CODE.USAGE);
453
+ }
454
+ const fullItem = selectors.includes("item");
455
+ const itemSelectors = selectors
456
+ .flatMap((selector) => selector.startsWith("item.")
457
+ ? [selector.slice("item.".length)]
458
+ : vocabulary.itemFields.includes(selector)
459
+ ? [selector]
460
+ : [])
461
+ .filter((selector, index, values) => values.indexOf(selector) === index);
462
+ if (fullItem && itemSelectors.length > 0) {
463
+ throw new PmCliError("--output-include cannot mix a full item with projected item fields; use item or item.<field>, not both.", EXIT_CODE.USAGE);
464
+ }
465
+ const selectedSections = new Set(selectors.filter((selector) => vocabulary.sections.includes(selector)));
466
+ const projected = Object.fromEntries(Object.entries(result).filter(([key]) => ENVELOPE_KEYS.has(key) || selectedSections.has(key)));
467
+ if (fullItem) {
468
+ projected.item = vocabulary.item;
469
+ }
470
+ else if (itemSelectors.length > 0) {
471
+ projected.item = projectRecordFields(vocabulary.item, itemSelectors);
472
+ }
473
+ const omittedFieldGroups = [
474
+ ...(fullItem
475
+ ? []
476
+ : itemSelectors.length > 0
477
+ ? vocabulary.itemFields
478
+ .filter((field) => !itemSelectors.includes(field))
479
+ .map((field) => ({
480
+ name: `item.${field}`,
481
+ restore_with: `--output-include item.${field}`,
482
+ }))
483
+ : [{ name: "item", restore_with: "--output-include item" }]),
484
+ ...vocabulary.sections
485
+ .filter((section) => !selectedSections.has(section))
486
+ .map((section) => ({
487
+ name: section,
488
+ restore_with: `--output-include ${section}`,
489
+ })),
490
+ ];
491
+ const inheritedOmittedFieldGroups = isRecord(projected.omission_receipt) &&
492
+ Array.isArray(projected.omission_receipt.omitted_field_groups)
493
+ ? projected.omission_receipt.omitted_field_groups
494
+ : [];
495
+ const combinedOmittedFieldGroups = [
496
+ ...inheritedOmittedFieldGroups,
497
+ ...omittedFieldGroups,
498
+ ];
499
+ projected.omission_receipt = {
500
+ has_omissions: combinedOmittedFieldGroups.length > 0,
501
+ omitted_field_group_count: combinedOmittedFieldGroups.length,
502
+ omitted_field_groups: combinedOmittedFieldGroups,
503
+ };
504
+ return projected;
505
+ }
506
+ function applyIncludeProjection(command, result, selectors) {
507
+ if (command === "get") {
508
+ return applyGetIncludeProjection(result, selectors);
509
+ }
431
510
  const rows = readOutputRowPaths(result);
432
511
  const qualifiedRowSelectors = selectors.flatMap((selector) => rows.flatMap((rowPath) => selector.startsWith(`${rowPath}.`)
433
512
  ? [selector.slice(rowPath.length + 1)]
@@ -486,7 +565,7 @@ function attachReadOutputSessionContracts(result, state, receipt) {
486
565
  function projectReadOutputRows(result, resolved, session) {
487
566
  let projected = { ...result };
488
567
  if (resolved.include?.source === "canonical") {
489
- projected = applyIncludeProjection(projected, resolved.include.value);
568
+ projected = applyIncludeProjection(resolved.command, projected, resolved.include.value);
490
569
  }
491
570
  if (resolved.amount?.source === "canonical") {
492
571
  projected = applyAmountBound(projected, resolved.amount.value);
@@ -597,4 +676,4 @@ export function resolveReadOutputEncoding(command, options) {
597
676
  : undefined;
598
677
  }
599
678
  //# sourceMappingURL=read-output-contracts.js.map
600
- //# debugId=0bccb953-bd78-5eee-a78d-8af3e250fa55
679
+ //# debugId=27f3a09b-9b90-5c85-9e41-21cfed5dc0ce
@@ -10,7 +10,7 @@ import { EXIT_CODE } from "../core/shared/constants.js";
10
10
  export type { WorkspaceExtensionCommandContract, WorkspaceFieldContract, } from "./workspace-contracts.js";
11
11
  import { type PmReadOutputOptions, type PmReadOutputResultFor } from "./read-output-contracts.js";
12
12
  export type { PmReadOutputBudgetExceeded, PmReadOutputOptions, PmReadOutputResult, PmReadOutputResultFor, PmReadOutputSurfaceContract, } from "./read-output-contracts.js";
13
- export type { PmReadOutputSessionReceipt, PmReadOutputSessionState } from "./read-output-session.js";
13
+ export type { PmReadOutputSessionReceipt, PmReadOutputSessionState, } from "./read-output-session.js";
14
14
  export type { PmContextIntentContract } from "./context-intent-contracts.js";
15
15
  export type { PmErrorCodeContract } from "./error-code-catalog.js";
16
16
  export { clearWorkspaceContractsCache } from "./workspace-contracts-cache.js";
@@ -4,7 +4,7 @@
4
4
  * Defines public SDK APIs and package-author helpers for Runtime.
5
5
  */
6
6
 
7
- !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]="42339e0d-ca3c-505f-8b98-acec81158b6e")}catch(e){}}();
7
+ !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]="c8f2b88e-1883-5744-868b-3a16f3814813")}catch(e){}}();
8
8
  export { PM_GITIGNORE_END, PM_GITIGNORE_START, ensurePmGitignore, getPmGitignoreBlock, } from "./workspace.js";
9
9
  export { SEARCH_EXTENSION_FLAG_DEFINITIONS } from "./extension-contracts.js";
10
10
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -36,6 +36,7 @@ import { runAggregate, } from "./query/aggregate.js";
36
36
  import { runAppend } from "./lifecycle/append.js";
37
37
  import { runClaim, runClaimNext, runRelease } from "./lifecycle/claim.js";
38
38
  import { runCloseMany } from "./lifecycle/close-many.js";
39
+ import { normalizeAnnotationTransportOptions } from "./annotations.js";
39
40
  import { runComments } from "./comments.js";
40
41
  import { runHistory } from "./query/history.js";
41
42
  import { runLearnings } from "./learnings.js";
@@ -60,7 +61,7 @@ import { runExtension, } from "./extension.js";
60
61
  import { runConfig } from "./config.js";
61
62
  import { runInit } from "./init.js";
62
63
  import { runRuntimeEvalAction, runRuntimeEventsAction, runRuntimeMergeAction, runRuntimeSchedulingAction, runRuntimeWorkspaceAction, } from "./runtime-extended-actions.js";
63
- import { acknowledgeUnknownAuthorHistoryEvents, } from "./author-attribution.js";
64
+ import { acknowledgeUnknownAuthorHistoryEventsFromTransport, } from "./author-attribution.js";
64
65
  import { runProfileApply, runProfileLint, runProfileList, runProfileShow, } from "./profile.js";
65
66
  import { runHistoryRedact, } from "./history-redact.js";
66
67
  import { runMcpHistoryCompactAction, runMcpHistoryRepairAction, } from "./history-mcp.js";
@@ -1387,7 +1388,7 @@ async function runMcpCloseAction(ctx) {
1387
1388
  });
1388
1389
  }
1389
1390
  function runMcpCommentsAction(ctx) {
1390
- const commentOptions = { ...ctx.options };
1391
+ const commentOptions = normalizeAnnotationTransportOptions(ctx.options);
1391
1392
  const isListing = commentOptions.add === undefined &&
1392
1393
  commentOptions.stdin === undefined &&
1393
1394
  commentOptions.file === undefined &&
@@ -1395,11 +1396,10 @@ function runMcpCommentsAction(ctx) {
1395
1396
  commentOptions.delete === undefined;
1396
1397
  if (isListing) {
1397
1398
  commentOptions.includeMeta = true;
1398
- if (commentOptions.limit === undefined && commentOptions.full !== true) {
1399
+ if (commentOptions.limit === undefined && commentOptions.fullHistory !== true) {
1399
1400
  commentOptions.limit = "20";
1400
1401
  }
1401
1402
  }
1402
- delete commentOptions.full;
1403
1403
  return runComments(requireMcpItemId(ctx), commentOptions, ctx.global);
1404
1404
  }
1405
1405
  function runMcpFilesAction(ctx) {
@@ -1752,23 +1752,7 @@ function runMcpGraphAction(ctx) {
1752
1752
  return runGraph(readRequiredString(merged, "subcommand"), readString(merged, "id") ?? ctx.id, readString(merged, "target"), graphOptionsFromFlat(merged), ctx.global);
1753
1753
  }
1754
1754
  function runMcpHistoryAuthorAcknowledgeAction(ctx) {
1755
- const merged = { ...ctx.args, ...ctx.options };
1756
- const events = readStringArray(merged.historyEvent).map((value) => {
1757
- const separator = value.lastIndexOf(":");
1758
- return {
1759
- item_id: value.slice(0, separator).trim(),
1760
- line: Number(value.slice(separator + 1)),
1761
- };
1762
- });
1763
- return acknowledgeUnknownAuthorHistoryEvents(resolvePmRoot(process.cwd(), ctx.global.path), {
1764
- events,
1765
- all_actionable: merged.allActionable === true || merged.all_actionable === true,
1766
- attributed_author: readString(merged, "attributedAuthor") ??
1767
- readString(merged, "attributed_author") ??
1768
- "",
1769
- reviewer: readString(merged, "reviewer") ?? "",
1770
- reason: readString(merged, "reason") ?? "",
1771
- });
1755
+ return acknowledgeUnknownAuthorHistoryEventsFromTransport(resolvePmRoot(process.cwd(), ctx.global.path), { ...ctx.args, ...ctx.options });
1772
1756
  }
1773
1757
  const SDK_ACTION_HANDLERS = {
1774
1758
  init: (ctx) => runInit(readString(ctx.args, "prefix"), ctx.global, ctx.options),
@@ -1813,8 +1797,8 @@ const SDK_ACTION_HANDLERS = {
1813
1797
  "close-task": runMcpCloseTaskAction,
1814
1798
  close: runMcpCloseAction,
1815
1799
  comments: runMcpCommentsAction,
1816
- notes: (ctx) => runNotes(requireMcpItemId(ctx), ctx.options, ctx.global),
1817
- learnings: (ctx) => runLearnings(requireMcpItemId(ctx), ctx.options, ctx.global),
1800
+ notes: (ctx) => runNotes(requireMcpItemId(ctx), normalizeAnnotationTransportOptions(ctx.options), ctx.global),
1801
+ learnings: (ctx) => runLearnings(requireMcpItemId(ctx), normalizeAnnotationTransportOptions(ctx.options), ctx.global),
1818
1802
  files: runMcpFilesAction,
1819
1803
  docs: (ctx) => runDocs(requireMcpItemId(ctx), withAddNoteOption(ctx.options), ctx.global),
1820
1804
  test: (ctx) => runTest(requireMcpItemId(ctx), ctx.options, ctx.global),
@@ -1941,4 +1925,4 @@ async function loadWorkspaceExtensionRegistrations(pmRoot, settings, cwd) {
1941
1925
  }
1942
1926
  }
1943
1927
  //# sourceMappingURL=runtime.js.map
1944
- //# debugId=42339e0d-ca3c-505f-8b98-acec81158b6e
1928
+ //# debugId=c8f2b88e-1883-5744-868b-3a16f3814813
@@ -4,7 +4,7 @@
4
4
  * Implements the pm test command surface and its agent-facing runtime behavior.
5
5
  */
6
6
 
7
- !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]="7d05534b-3cf7-506c-8626-4bd4a42cbc80")}catch(e){}}();
7
+ !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]="2c0646bf-44d7-521f-aea1-b0683d88a8fa")}catch(e){}}();
8
8
  import { spawn } from "node:child_process";
9
9
  import { cp, mkdir, mkdtemp, open, readdir, rm } from "node:fs/promises";
10
10
  import { tmpdir } from "node:os";
@@ -28,6 +28,7 @@ import { LINKED_TEST_PM_CONTEXT_MODE_VALUES as PM_CONTEXT_MODE_VALUES, LINKED_TE
28
28
  import { parseOnlyIndexValue, resolveLinkedTestRunSelection, } from "../../core/test/run-selectors.js";
29
29
  import { SCOPE_VALUES } from "../../types/index.js";
30
30
  import { diffTestRunMeasurements, parseTestRunMeasurements, queryTestRunMeasurementsBelow, } from "./measurements.js";
31
+ import { SOURCE_CONTEXT_ACCESS_ENV } from "../environment/source-context.js";
31
32
  const TEST_OUTPUT_MAX_BUFFER_BYTES = 20 * 1024 * 1024;
32
33
  const DEFAULT_LINKED_TEST_TIMEOUT_FORCE_KILL_DELAY_MS = 3000;
33
34
  const DEFAULT_LINKED_TEST_HEARTBEAT_INTERVAL_MS = 10000;
@@ -1398,6 +1399,7 @@ function buildLinkedTestExecutionEnv(params) {
1398
1399
  process.env.PM_SOURCE_WORKSPACE_ROOT ?? process.cwd();
1399
1400
  executionEnv.PM_SOURCE_PM_PATH =
1400
1401
  params.executionContext.source_project_pm_path;
1402
+ executionEnv[SOURCE_CONTEXT_ACCESS_ENV] = "read_only";
1401
1403
  return executionEnv;
1402
1404
  }
1403
1405
  function buildLinkedTestPassedResult(linkedTest, executionContext, execution) {
@@ -1872,4 +1874,4 @@ export const _testOnlyTestCommand = {
1872
1874
  summarizeRunResultStatuses,
1873
1875
  };
1874
1876
  //# sourceMappingURL=execution.js.map
1875
- //# debugId=7d05534b-3cf7-506c-8626-4bd4a42cbc80
1877
+ //# debugId=2c0646bf-44d7-521f-aea1-b0683d88a8fa
@@ -4,13 +4,14 @@
4
4
  * Implements the pm linked test parsers command surface and its agent-facing runtime behavior.
5
5
  */
6
6
 
7
- !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]="f592ba2b-fc8d-5930-a2a5-d02835c457a3")}catch(e){}}();
7
+ !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]="74163bed-a473-543e-ae6a-0d5091b3b3a9")}catch(e){}}();
8
8
  import { parseOptionalNumber } from "../../core/item/parse.js";
9
9
  import { EXIT_CODE } from "../../core/shared/constants.js";
10
10
  import { PmCliError } from "../../core/shared/errors.js";
11
11
  import { splitCommaList } from "../../core/shared/split-comma-list.js";
12
12
  import { STRUCTURED_LINKED_TEST_KEYS } from "./entry.js";
13
13
  import { SCOPE_VALUES } from "../../types/index.js";
14
+ import { SOURCE_CONTEXT_ACCESS_ENV, SOURCE_CONTEXT_WRITE_OVERRIDE_ENV, } from "../environment/source-context.js";
14
15
  /**
15
16
  * Shared linked-test field parsers used by the `create` and `test` commands.
16
17
  *
@@ -30,6 +31,8 @@ export const LINKED_TEST_PROTECTED_ENV_KEYS = new Set([
30
31
  "PM_GLOBAL_PATH",
31
32
  "PM_SOURCE_PM_PATH",
32
33
  "PM_SOURCE_WORKSPACE_ROOT",
34
+ SOURCE_CONTEXT_ACCESS_ENV,
35
+ SOURCE_CONTEXT_WRITE_OVERRIDE_ENV,
33
36
  "FORCE_COLOR",
34
37
  ]);
35
38
  /** Public contract for linked test env name pattern, shared by SDK and presentation-layer consumers. */
@@ -521,4 +524,4 @@ export const _testOnlyLinkedTestParsers = {
521
524
  parseLinkedTestJsonEntry,
522
525
  };
523
526
  //# sourceMappingURL=parsers.js.map
524
- //# debugId=f592ba2b-fc8d-5930-a2a5-d02835c457a3
527
+ //# debugId=74163bed-a473-543e-ae6a-0d5091b3b3a9
@@ -29,6 +29,7 @@ declare class WorkspaceLockHeartbeat {
29
29
  /** Internal heartbeat constructor exposed only for deterministic lock tests. */
30
30
  export declare const _testOnlyWorkspaceSnapshot: {
31
31
  WorkspaceLockHeartbeat: typeof WorkspaceLockHeartbeat;
32
+ withSnapshotFilesystemGuard: typeof withSnapshotFilesystemGuard;
32
33
  };
33
34
  /** Immutable manifest stored with every content-addressed snapshot object. */
34
35
  export interface WorkspaceSnapshotManifest {
@@ -117,6 +118,14 @@ export interface RestoreWorkspaceSnapshotResult {
117
118
  /** Stable audit operation appended after staging and before activation. */
118
119
  audit_operation: "workspace_snapshot_restore";
119
120
  }
121
+ /**
122
+ * Runs a bounded snapshot filesystem stage, converting host environment faults
123
+ * into declared refusals.
124
+ *
125
+ * The operation label is a fixed identifier, never a path: snapshot failures
126
+ * are reported without disclosing workspace topology.
127
+ */
128
+ declare function withSnapshotFilesystemGuard<T>(operation: string, run: () => Promise<T>): Promise<T>;
120
129
  /**
121
130
  * Publish a prepared immutable object, treating a concurrent winning publish
122
131
  * as successful deduplication.
@@ -5,7 +5,7 @@
5
5
  * state while excluding clone-local caches, locks, and recovery journals.
6
6
  */
7
7
 
8
- !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]="3e32be7c-f8a4-547d-8c19-456b4567982e")}catch(e){}}();
8
+ !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]="cdde844a-a14a-501d-a45b-18cbd1dd9008")}catch(e){}}();
9
9
  import crypto from "node:crypto";
10
10
  import { cp, lstat, mkdir, readFile, readdir, rename, rm, writeFile, } from "node:fs/promises";
11
11
  import path from "node:path";
@@ -15,6 +15,7 @@ import { acquireLock } from "../core/lock/lock.js";
15
15
  import { getLockPath } from "../core/store/paths.js";
16
16
  import { EXIT_CODE } from "../core/shared/constants.js";
17
17
  import { PmCliError } from "../core/shared/errors.js";
18
+ import { withHostEnvironmentBoundary } from "./environment/host-environment-errors.js";
18
19
  /** Current content-addressed workspace snapshot manifest schema identifier. */
19
20
  export const SNAPSHOT_SCHEMA = "https://schema.unbrained.dev/pm/workspace-snapshot/v1";
20
21
  const SNAPSHOT_RUNTIME_PATH = path.join("runtime", "workspace-snapshots");
@@ -27,6 +28,11 @@ const EXCLUDED_ROOT_NAMES = new Set([
27
28
  ]);
28
29
  const SNAPSHOT_TARGET_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
29
30
  const WORKSPACE_WRITER_LOCK_ID = "sdk-workspace-transaction";
31
+ const SNAPSHOT_HOST_FAULT_CODES = {
32
+ capacity: { code: "workspace_snapshot_storage_exhausted" },
33
+ permission: { code: "workspace_snapshot_permission_denied" },
34
+ resource: { code: "workspace_snapshot_resource_exhausted" },
35
+ };
30
36
  const DEFAULT_ATOMIC_OPERATIONS = {
31
37
  renameEntry: rename,
32
38
  removeEntry: async (target) => {
@@ -97,6 +103,7 @@ class WorkspaceLockHeartbeat {
97
103
  /** Internal heartbeat constructor exposed only for deterministic lock tests. */
98
104
  export const _testOnlyWorkspaceSnapshot = {
99
105
  WorkspaceLockHeartbeat,
106
+ withSnapshotFilesystemGuard,
100
107
  };
101
108
  async function collectAuthoritativeFiles(root, relative = "") {
102
109
  const entries = await readdir(path.join(root, relative), {
@@ -193,6 +200,27 @@ function isErrno(error, code) {
193
200
  "code" in error &&
194
201
  error.code === code);
195
202
  }
203
+ /**
204
+ * Runs a bounded snapshot filesystem stage, converting host environment faults
205
+ * into declared refusals.
206
+ *
207
+ * The operation label is a fixed identifier, never a path: snapshot failures
208
+ * are reported without disclosing workspace topology.
209
+ */
210
+ async function withSnapshotFilesystemGuard(operation, run) {
211
+ return withHostEnvironmentBoundary(`workspace_snapshot_${operation}`, run, {
212
+ codes: {
213
+ capacity: SNAPSHOT_HOST_FAULT_CODES.capacity.code,
214
+ permission: SNAPSHOT_HOST_FAULT_CODES.permission.code,
215
+ resource: SNAPSHOT_HOST_FAULT_CODES.resource.code,
216
+ },
217
+ why: "Snapshot create and restore copy authoritative tracker state before activation, so the stage needs capacity and write access up front.",
218
+ nextSteps: [
219
+ "Reclaim rebuildable tracker cache space with pm gc, then retry.",
220
+ ],
221
+ suggestedRetry: "pm gc --json",
222
+ });
223
+ }
196
224
  function snapshotStore(pmRoot) {
197
225
  return path.join(pmRoot, SNAPSHOT_RUNTIME_PATH);
198
226
  }
@@ -302,14 +330,16 @@ export async function createWorkspaceSnapshot(pmRoot, options = {}) {
302
330
  }
303
331
  if (!deduplicated) {
304
332
  const temporaryRoot = path.join(store, "objects", `.create-${process.pid}-${crypto.randomUUID()}`);
305
- await mkdir(path.join(temporaryRoot, "files"), { recursive: true });
306
- for (const [index, file] of manifest.files.entries()) {
307
- const target = path.join(temporaryRoot, "files", file);
308
- await mkdir(path.dirname(target), { recursive: true });
309
- await writeFile(target, contents[index]);
310
- }
311
- await writeFile(path.join(temporaryRoot, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
312
- await mkdir(path.dirname(objectRoot), { recursive: true });
333
+ await withSnapshotFilesystemGuard("create_object", async () => {
334
+ await mkdir(path.join(temporaryRoot, "files"), { recursive: true });
335
+ for (const [index, file] of manifest.files.entries()) {
336
+ const target = path.join(temporaryRoot, "files", file);
337
+ await mkdir(path.dirname(target), { recursive: true });
338
+ await writeFile(target, contents[index]);
339
+ }
340
+ await writeFile(path.join(temporaryRoot, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
341
+ await mkdir(path.dirname(objectRoot), { recursive: true });
342
+ });
313
343
  deduplicated = await publishWorkspaceSnapshotObject(temporaryRoot, objectRoot);
314
344
  }
315
345
  if (options.name !== undefined) {
@@ -467,12 +497,14 @@ export async function restoreWorkspaceSnapshotWithRecovery(pmRoot, target, optio
467
497
  const base = path.basename(pmRoot);
468
498
  staging = path.join(parent, `.${base}.restore-${crypto.randomUUID()}`);
469
499
  const backup = path.join(parent, `.${base}.backup-${crypto.randomUUID()}`);
470
- await mkdir(staging, { recursive: true });
471
- await cp(source, staging, { recursive: true, force: false });
472
- await mkdir(path.join(staging, "runtime"), { recursive: true });
473
- await cp(store, path.join(staging, SNAPSHOT_RUNTIME_PATH), {
474
- recursive: true,
475
- force: false,
500
+ await withSnapshotFilesystemGuard("restore_stage", async () => {
501
+ await mkdir(staging, { recursive: true });
502
+ await cp(source, staging, { recursive: true, force: false });
503
+ await mkdir(path.join(staging, "runtime"), { recursive: true });
504
+ await cp(store, path.join(staging, SNAPSHOT_RUNTIME_PATH), {
505
+ recursive: true,
506
+ force: false,
507
+ });
476
508
  });
477
509
  const audit = await appendWorkspaceAuditEvent({
478
510
  pmRoot: staging,
@@ -564,4 +596,4 @@ export async function deleteWorkspaceSnapshot(pmRoot, target) {
564
596
  return { deleted: "object", target };
565
597
  }
566
598
  //# sourceMappingURL=workspace-snapshot.js.map
567
- //# debugId=3e32be7c-f8a4-547d-8c19-456b4567982e
599
+ //# debugId=cdde844a-a14a-501d-a45b-18cbd1dd9008
@@ -25,6 +25,8 @@ historical projections, and versioned legacy identity are tracked by
25
25
  [pm-ffz0a9](../.agents/pm/issues/pm-ffz0a9.toon),
26
26
  [pm-v8gfi7](../.agents/pm/issues/pm-v8gfi7.toon), and
27
27
  [pm-3yxwv5](../.agents/pm/issues/pm-3yxwv5.toon).
28
+ [pm-alhqbz](../.agents/pm/issues/pm-alhqbz.toon) governs the nested source-write
29
+ boundary retained by linked-test package lifecycles.
28
30
 
29
31
  `pm` treats project management as context management. These primitives keep
30
32
  mutation provenance, source-workspace identity, extension flags, and bounded
@@ -198,10 +200,15 @@ Every extension command, parser, preflight hook, renderer, and service receives:
198
200
  Linked tests set `PM_SOURCE_WORKSPACE_ROOT` before replacing `PM_PATH` and
199
201
  `PM_GLOBAL_PATH`, and protect `PM_SOURCE_PM_PATH` as the read-only source
200
202
  tracker coordinate for source-repository lifecycle commands such as
201
- `pm merge install`. Extensions can therefore inspect source VCS metadata
202
- without writing real tracker state. Package code should prefer `pm_root_rel`
203
- in output and persisted evidence to avoid leaking host-specific absolute
204
- paths.
203
+ `pm merge install`. They also set `PM_SOURCE_CONTEXT_ACCESS=read_only`.
204
+ Validation may continue to inspect the source coordinates, but a nested
205
+ non-dry-run `pm merge install` selects the current package repository instead
206
+ of the source workspace. A maintainer can deliberately opt into the legacy
207
+ source mutation for one invocation with `PM_ALLOW_SOURCE_CONTEXT_WRITES=1`;
208
+ `resolveMergeInstallContext()` reports whether that override selects the source.
209
+ Both variables are protected from linked-test `env_set` and `env_clear`
210
+ metadata. Package code should prefer `pm_root_rel` in output and persisted
211
+ evidence to avoid leaking host-specific absolute paths.
205
212
 
206
213
  ## Strict extension flag descriptors
207
214
 
package/docs/README.md CHANGED
@@ -56,6 +56,7 @@ pm guide release --json
56
56
  - [Improvement Ledger and History Analytics](IMPROVEMENT_ANALYTICS.md) - audited quantitative observations, live provenance coverage, and bounded observational fleet outcomes.
57
57
  - [SDK Evidence Traceability and Integrity](SDK_EVIDENCE_TRACEABILITY.md) - reverse source-to-item lookup, atomic evidence replacement, no-op history, linked-test collision classification, and telemetry drain receipts.
58
58
  - [SDK Context Integrity Primitives](SDK_CONTEXT_INTEGRITY_PRIMITIVES.md) - batch duplicate discovery, structured errors, Plan evidence/lifecycle, sparse settings, tombstones, linked-test output, relocation diagnostics, and scoped output services.
59
+ - [SDK Context Integrity](SDK_CONTEXT_INTEGRITY.md) - truthful `get` projections, bounded annotation mutations, shared author coordinates, health provider boundaries, and changeset-aware replication gates.
59
60
  - [Reproducible Workspaces and Snapshots](REPRODUCIBLE_WORKSPACES.md) - deterministic SDK recipes and content-addressed authoritative tracker restore points.
60
61
  - [Portable Corpus Shapes](CORPUS_SHAPES.md) - versioned SDK populations for realistic benchmarks, evaluations, and package tests.
61
62
  - [Agent UX Contracts](AGENT_UX_CONTRACTS.md) - ordering-cycle advisories, graph count units, collision safety, compact context, ownership wording, and recovery behavior.