@unbrained/pm-cli 2026.8.12 → 2026.8.14

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 (218) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +162 -111
  3. package/dist/cli/error-guidance.js +4 -8
  4. package/dist/cli/main.js +40 -3
  5. package/dist/cli/register-assurance.js +28 -17
  6. package/dist/cli-bundle/bundle-manifest.json +141 -173
  7. package/dist/cli-bundle/chunks/chunk-BBOFSVAK.js +197 -0
  8. package/dist/cli-bundle/chunks/chunk-BFALV3YP.js +5 -0
  9. package/dist/cli-bundle/chunks/{chunk-WLAHPSOX.js → chunk-FIW4P64F.js} +2 -2
  10. package/dist/cli-bundle/chunks/{chunk-BM5YJ2UW.js → chunk-G5JUB66K.js} +52 -52
  11. package/dist/cli-bundle/chunks/chunk-SBRKH6OR.js +2 -0
  12. package/dist/cli-bundle/chunks/chunk-TMJ7URWS.js +13 -0
  13. package/dist/cli-bundle/chunks/chunk-TXMEUUUY.js +8 -0
  14. package/dist/cli-bundle/chunks/{chunk-KOQMGCTA.js → chunk-XAVMOK6B.js} +50 -50
  15. package/dist/cli-bundle/chunks/chunk-YAMTJL6Y.js +34 -0
  16. package/dist/cli-bundle/chunks/chunk-YSVSMJ2B.js +2 -0
  17. package/dist/cli-bundle/chunks/{register-list-query-XPCJRUUD.js → register-list-query-T3EHHNNV.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-mutation-4RJCBKRS.js → register-mutation-E27WCLSZ.js} +2 -2
  19. package/dist/cli-bundle/chunks/register-operations-4R6I7CPY.js +2 -0
  20. package/dist/cli-bundle/chunks/{register-setup-LLHAKP2V.js → register-setup-3YXZ444E.js} +2 -2
  21. package/dist/cli-bundle/focused-chunks/{chunk-JAFRIWVF.js → chunk-4DQ2YERC.js} +4 -4
  22. package/dist/cli-bundle/focused-chunks/chunk-F43TT2QQ.js +8 -0
  23. package/dist/cli-bundle/focused-chunks/chunk-K7RWWOAG.js +4 -0
  24. package/dist/cli-bundle/focused-chunks/chunk-KFSZ3BP6.js +16 -0
  25. package/dist/cli-bundle/focused-chunks/{chunk-KHM2CS2C.js → chunk-KSEDUWVZ.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-KYUQGTSB.js +2 -0
  27. package/dist/cli-bundle/focused-chunks/{chunk-ZMGWAYH7.js → chunk-LZ4MV3RH.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/chunk-PGXJQLCT.js +155 -0
  29. package/dist/cli-bundle/focused-chunks/{chunk-RWPAWUOT.js → chunk-PPFGQDSI.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/chunk-Q4YY3XNF.js +2 -0
  31. package/dist/cli-bundle/focused-chunks/chunk-QK753U3U.js +46 -0
  32. package/dist/cli-bundle/focused-chunks/chunk-QUMB2J27.js +2 -0
  33. package/dist/cli-bundle/focused-chunks/chunk-SM7MTG27.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/{chunk-GQW4BPUT.js → chunk-WCVE2NIG.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/chunk-XDNAW5OI.js +18 -0
  36. package/dist/cli-bundle/focused-chunks/{chunk-YIAXMC3T.js → chunk-YOOI3UZX.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/chunk-ZM6TZVHC.js +2 -0
  38. package/dist/cli-bundle/main.js +13 -13
  39. package/dist/cli-bundle/sdk-authoring.js +1 -1
  40. package/dist/cli-bundle/sdk-contracts.js +1 -1
  41. package/dist/cli-bundle/sdk-core.js +45 -41
  42. package/dist/cli-bundle/sdk-governance.js +1 -1
  43. package/dist/cli-bundle/sdk-graph.js +1 -1
  44. package/dist/cli-bundle/sdk-merge.js +109 -1
  45. package/dist/cli-bundle/sdk-query.js +1 -1
  46. package/dist/cli-bundle/sdk-runtime.js +1 -1
  47. package/dist/cli-bundle/sdk-testing.js +1 -1
  48. package/dist/cli-bundle/sdk.js +2 -2
  49. package/dist/core/checkpoint/checkpoint-gc.js +7 -12
  50. package/dist/core/extensions/activation-summary-markdown.js +5 -4
  51. package/dist/core/extensions/activation-summary.d.ts +2 -0
  52. package/dist/core/extensions/activation-summary.js +6 -2
  53. package/dist/core/extensions/capability-usage.d.ts +1 -1
  54. package/dist/core/extensions/capability-usage.js +4 -3
  55. package/dist/core/extensions/contribution-inventory.d.ts +1 -0
  56. package/dist/core/extensions/contribution-inventory.js +6 -2
  57. package/dist/core/extensions/extension-registries.js +3 -2
  58. package/dist/core/extensions/extension-types.d.ts +83 -1
  59. package/dist/core/extensions/extension-types.js +3 -2
  60. package/dist/core/extensions/loader.d.ts +2 -8
  61. package/dist/core/extensions/loader.js +53 -144
  62. package/dist/core/extensions/registration-values.d.ts +43 -0
  63. package/dist/core/extensions/registration-values.js +210 -0
  64. package/dist/core/extensions/runtime-registrations.d.ts +3 -1
  65. package/dist/core/extensions/runtime-registrations.js +12 -2
  66. package/dist/core/fs/fs-utils.d.ts +2 -0
  67. package/dist/core/fs/fs-utils.js +7 -3
  68. package/dist/core/history/drift-cache.js +4 -3
  69. package/dist/core/history/drift-scan.d.ts +6 -0
  70. package/dist/core/history/drift-scan.js +76 -58
  71. package/dist/core/history/workspace-history.d.ts +59 -0
  72. package/dist/core/history/workspace-history.js +197 -10
  73. package/dist/core/lock/lock-gc.js +8 -13
  74. package/dist/core/lock/lock.js +5 -4
  75. package/dist/core/output/output.js +12 -4
  76. package/dist/core/search/vector-stores.js +6 -11
  77. package/dist/core/store/item-metadata-cache.js +4 -4
  78. package/dist/core/telemetry/runtime.d.ts +6 -0
  79. package/dist/core/telemetry/runtime.js +101 -30
  80. package/dist/sdk/agent/command-recovery.d.ts +2 -0
  81. package/dist/sdk/agent/command-recovery.js +10 -2
  82. package/dist/sdk/annotations.js +4 -4
  83. package/dist/sdk/cli-contracts/completeness.js +9 -3
  84. package/dist/sdk/cli-contracts/enum-contracts.d.ts +1 -1
  85. package/dist/sdk/cli-contracts/flag-contracts.js +6 -3
  86. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +6 -1
  87. package/dist/sdk/cli-contracts/runtime-contracts.js +155 -60
  88. package/dist/sdk/cli-contracts/schemas/assurance-definition.d.ts +32 -0
  89. package/dist/sdk/cli-contracts/schemas/assurance-definition.js +25 -2
  90. package/dist/sdk/cli-contracts/tool-parameter-tables.js +35 -5
  91. package/dist/sdk/cli-contracts/tool-schema.js +17 -2
  92. package/dist/sdk/cli-program.js +3 -3
  93. package/dist/sdk/comments.js +4 -3
  94. package/dist/sdk/compile-cache.js +4 -3
  95. package/dist/sdk/compose.d.ts +3 -1
  96. package/dist/sdk/compose.js +16 -2
  97. package/dist/sdk/context-relevance.js +12 -4
  98. package/dist/sdk/context-signal-store.js +4 -3
  99. package/dist/sdk/extension/install-runtime.js +4 -4
  100. package/dist/sdk/extension/managed-state.js +4 -3
  101. package/dist/sdk/extension-contracts.d.ts +1 -1
  102. package/dist/sdk/flag-invocation-contracts.js +5 -6
  103. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +14 -2
  104. package/dist/sdk/governance/assurance-action.d.ts +27 -3
  105. package/dist/sdk/governance/assurance-action.js +112 -28
  106. package/dist/sdk/governance/assurance-mutation-error.d.ts +7 -0
  107. package/dist/sdk/governance/assurance-mutation-error.js +25 -2
  108. package/dist/sdk/governance/assurance-presets.d.ts +54 -0
  109. package/dist/sdk/governance/assurance-presets.js +136 -0
  110. package/dist/sdk/governance/assurance-runtime.d.ts +4 -0
  111. package/dist/sdk/governance/assurance-runtime.js +109 -8
  112. package/dist/sdk/governance/assurance.d.ts +39 -0
  113. package/dist/sdk/governance/assurance.js +145 -6
  114. package/dist/sdk/governance/gc.js +5 -11
  115. package/dist/sdk/governance/health.js +18 -6
  116. package/dist/sdk/governance/validate-history-drift.d.ts +7 -0
  117. package/dist/sdk/governance/validate-history-drift.js +68 -0
  118. package/dist/sdk/governance/validate.js +6 -48
  119. package/dist/sdk/graph/assembly.d.ts +23 -0
  120. package/dist/sdk/graph/assembly.js +54 -2
  121. package/dist/sdk/graph/durable-cache.js +91 -3
  122. package/dist/sdk/graph/governance-contracts.d.ts +12 -0
  123. package/dist/sdk/graph/governance-contracts.js +29 -0
  124. package/dist/sdk/graph/governance.d.ts +51 -3
  125. package/dist/sdk/graph/governance.js +216 -35
  126. package/dist/sdk/graph/index.d.ts +2 -2
  127. package/dist/sdk/graph/index.js +4 -4
  128. package/dist/sdk/graph/mutation-advisory.js +15 -7
  129. package/dist/sdk/graph/remediation.js +12 -2
  130. package/dist/sdk/guide.js +4 -4
  131. package/dist/sdk/history-read.d.ts +8 -0
  132. package/dist/sdk/history-read.js +2 -2
  133. package/dist/sdk/index.d.ts +3 -2
  134. package/dist/sdk/index.js +5 -4
  135. package/dist/sdk/linked-artifacts.d.ts +2 -0
  136. package/dist/sdk/linked-artifacts.js +29 -4
  137. package/dist/sdk/merge/install.js +5 -4
  138. package/dist/sdk/query/activity.js +4 -3
  139. package/dist/sdk/query/history.js +20 -3
  140. package/dist/sdk/read-output-budget.d.ts +2 -0
  141. package/dist/sdk/read-output-budget.js +12 -5
  142. package/dist/sdk/read-output-contracts.d.ts +61 -5
  143. package/dist/sdk/read-output-contracts.js +210 -28
  144. package/dist/sdk/read-output-rows.d.ts +11 -0
  145. package/dist/sdk/read-output-rows.js +47 -2
  146. package/dist/sdk/relationship-history.js +5 -5
  147. package/dist/sdk/runtime-primitives.d.ts +1 -1
  148. package/dist/sdk/runtime-primitives.js +3 -3
  149. package/dist/sdk/runtime.js +5 -3
  150. package/dist/sdk/schema-migration.js +4 -4
  151. package/dist/sdk/test/execution.js +4 -4
  152. package/dist/sdk/workspace-memory.js +4 -4
  153. package/dist/sdk/workspace-snapshot.js +8 -14
  154. package/dist/sdk/workspace-transaction-gc.js +4 -9
  155. package/dist/sdk/workspace-transaction.js +4 -4
  156. package/dist/sdk/workspace.js +4 -3
  157. package/docs/ASSURANCE.md +163 -3
  158. package/docs/COMMANDS.md +31 -2
  159. package/docs/CONFIGURATION.md +2 -0
  160. package/docs/EXTENSIONS.md +11 -12
  161. package/docs/READ_OUTPUT_CONTRACTS.md +44 -8
  162. package/docs/RELATIONSHIP_GRAPH.md +34 -4
  163. package/docs/SDK.md +29 -8
  164. package/docs/SDK_CONTEXT_INTEGRITY.md +32 -1
  165. package/docs/TESTING.md +47 -16
  166. package/docs/performance/sdk-entrypoint-import-costs.md +20 -13
  167. package/marketplace.json +2 -2
  168. package/package.json +6 -6
  169. package/packages/pm-beads/extensions/beads/index.ts +1 -0
  170. package/packages/pm-beads/package.json +1 -1
  171. package/packages/pm-calendar/extensions/calendar/index.ts +1 -0
  172. package/packages/pm-calendar/package.json +1 -1
  173. package/packages/pm-command-kit/package.json +1 -1
  174. package/packages/pm-digital-twin/extensions/digital-twin/index.ts +7 -0
  175. package/packages/pm-digital-twin/package.json +1 -1
  176. package/packages/pm-governance-audit/extensions/governance-audit/index.ts +4 -0
  177. package/packages/pm-governance-audit/package.json +1 -1
  178. package/packages/pm-guide-shell/extensions/guide-shell/index.ts +5 -0
  179. package/packages/pm-guide-shell/package.json +1 -1
  180. package/packages/pm-kanban/package.json +1 -1
  181. package/packages/pm-lifecycle-hooks/package.json +1 -1
  182. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/index.ts +6 -0
  183. package/packages/pm-linked-test-adapters/package.json +1 -1
  184. package/packages/pm-search-advanced/extensions/search-advanced/index.ts +2 -0
  185. package/packages/pm-search-advanced/package.json +1 -1
  186. package/packages/pm-templates/extensions/templates/index.ts +4 -0
  187. package/packages/pm-templates/package.json +1 -1
  188. package/packages/pm-todos/extensions/todos/index.ts +2 -0
  189. package/packages/pm-todos/package.json +1 -1
  190. package/packages/pm-vcs/extensions/vcs/index.ts +7 -0
  191. package/packages/pm-vcs/package.json +1 -1
  192. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  193. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  194. package/scripts/bundle-cli.mjs +17 -4
  195. package/sdk/public-surface.json +390 -60
  196. package/dist/cli-bundle/chunks/chunk-3PS7X6GS.js +0 -8
  197. package/dist/cli-bundle/chunks/chunk-7KL6KMWF.js +0 -13
  198. package/dist/cli-bundle/chunks/chunk-D55A5AEN.js +0 -33
  199. package/dist/cli-bundle/chunks/chunk-KIKDWECH.js +0 -196
  200. package/dist/cli-bundle/chunks/chunk-R76342NV.js +0 -2
  201. package/dist/cli-bundle/chunks/chunk-RBTK5EZV.js +0 -5
  202. package/dist/cli-bundle/chunks/chunk-VLUQOIDT.js +0 -2
  203. package/dist/cli-bundle/chunks/register-operations-2OGISOVR.js +0 -2
  204. package/dist/cli-bundle/focused-chunks/chunk-2DALMVF7.js +0 -5
  205. package/dist/cli-bundle/focused-chunks/chunk-5OQIMELN.js +0 -155
  206. package/dist/cli-bundle/focused-chunks/chunk-5TAGTDRE.js +0 -2
  207. package/dist/cli-bundle/focused-chunks/chunk-ECADZMVC.js +0 -18
  208. package/dist/cli-bundle/focused-chunks/chunk-EJGXRA6B.js +0 -2
  209. package/dist/cli-bundle/focused-chunks/chunk-FFGSZCPK.js +0 -16
  210. package/dist/cli-bundle/focused-chunks/chunk-ISKOBJCM.js +0 -2
  211. package/dist/cli-bundle/focused-chunks/chunk-KPSXVBDV.js +0 -2
  212. package/dist/cli-bundle/focused-chunks/chunk-KTLOKKCQ.js +0 -2
  213. package/dist/cli-bundle/focused-chunks/chunk-LGYK3NUF.js +0 -8
  214. package/dist/cli-bundle/focused-chunks/chunk-NBLUKITQ.js +0 -4
  215. package/dist/cli-bundle/focused-chunks/chunk-NYLOUPOB.js +0 -2
  216. package/dist/cli-bundle/focused-chunks/chunk-Q4T463SD.js +0 -31
  217. package/dist/cli-bundle/focused-chunks/chunk-QVZVY53N.js +0 -2
  218. package/dist/cli-bundle/focused-chunks/chunk-UTEWXDEQ.js +0 -17
@@ -4,13 +4,13 @@
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]="b40f54be-2d3e-5b98-9d49-cb8608a782f5")}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]="76053150-cc55-57e0-80d6-369196284cd0")}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";
11
11
  import path from "node:path";
12
12
  import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
13
- import { pathExists } from "../../core/fs/fs-utils.js";
13
+ import { isFileMissingError, pathExists } from "../../core/fs/fs-utils.js";
14
14
  import { resolveItemTypeRegistry } from "../../core/item/type-registry.js";
15
15
  import { createStdinTokenResolver, parseCsvKv, parseOptionalNumber, } from "../../core/item/parse.js";
16
16
  import { EXIT_CODE } from "../../core/shared/constants.js";
@@ -989,7 +989,7 @@ async function copyIntoSandboxIfPresent(sourcePath, targetPath, recursive = fals
989
989
  if (typeof error === "object" &&
990
990
  error !== null &&
991
991
  "code" in error &&
992
- error.code === "ENOENT") {
992
+ isFileMissingError(error)) {
993
993
  return;
994
994
  }
995
995
  throw error;
@@ -1939,4 +1939,4 @@ export const _testOnlyTestCommand = {
1939
1939
  summarizeRunResultStatuses,
1940
1940
  };
1941
1941
  //# sourceMappingURL=execution.js.map
1942
- //# debugId=b40f54be-2d3e-5b98-9d49-cb8608a782f5
1942
+ //# debugId=76053150-cc55-57e0-80d6-369196284cd0
@@ -4,10 +4,10 @@
4
4
  * Builds compact, cursor-bound historical rollups for large workspaces.
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]="2e225010-1b53-59e9-8188-dcbe4a4f7eb7")}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]="ad77a79d-54da-55f1-8ac8-935366880d90")}catch(e){}}();
8
8
  import path from "node:path";
9
9
  import { createHash } from "node:crypto";
10
- import { writeFileAtomic } from "../core/fs/fs-utils.js";
10
+ import { isFileAbsentError, writeFileAtomic } from "../core/fs/fs-utils.js";
11
11
  import { normalizeStatusForRegistry } from "../core/item/status.js";
12
12
  import { stableStringify } from "../core/shared/serialization.js";
13
13
  import { readItemMetadataDerivedIndexState } from "../core/store/item-metadata-cache.js";
@@ -194,7 +194,7 @@ async function readPersistedWorkspaceMemory(filePath) {
194
194
  const missing = typeof error === "object" &&
195
195
  error !== null &&
196
196
  "code" in error &&
197
- error.code === "ENOENT";
197
+ isFileAbsentError(error);
198
198
  return { snapshot: null, invalid: !missing };
199
199
  }
200
200
  }
@@ -316,4 +316,4 @@ export function searchWorkspaceMemoryReadResult(memory, query, limit = 5) {
316
316
  };
317
317
  }
318
318
  //# sourceMappingURL=workspace-memory.js.map
319
- //# debugId=2e225010-1b53-59e9-8188-dcbe4a4f7eb7
319
+ //# debugId=ad77a79d-54da-55f1-8ac8-935366880d90
@@ -5,11 +5,11 @@
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]="470bb466-4619-5c54-a154-186b7ef4a049")}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]="36aacc71-c5f5-56f1-85f4-28b2be5b02e2")}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";
12
- import { writeFileAtomic } from "../core/fs/fs-utils.js";
12
+ import { isFileMissingError, writeFileAtomic } from "../core/fs/fs-utils.js";
13
13
  import { appendWorkspaceAuditEvent } from "../core/history/workspace-history.js";
14
14
  import { acquireLock } from "../core/lock/lock.js";
15
15
  import { getLockPath } from "../core/store/paths.js";
@@ -205,12 +205,6 @@ function validateSnapshotTarget(target) {
205
205
  });
206
206
  }
207
207
  }
208
- function isErrno(error, code) {
209
- return (typeof error === "object" &&
210
- error !== null &&
211
- "code" in error &&
212
- error.code === code);
213
- }
214
208
  /**
215
209
  * Runs a bounded snapshot filesystem stage, converting host environment faults
216
210
  * into declared refusals.
@@ -250,7 +244,7 @@ async function readSnapshotJson(file, target) {
250
244
  return JSON.parse(await readFile(file, "utf8"));
251
245
  }
252
246
  catch (error) {
253
- if (isErrno(error, "ENOENT")) {
247
+ if (isFileMissingError(error)) {
254
248
  throw workspaceSnapshotNotFound(target);
255
249
  }
256
250
  throw error;
@@ -261,7 +255,7 @@ async function removeSnapshotEntry(entry, target, recursive) {
261
255
  await rm(entry, { recursive });
262
256
  }
263
257
  catch (error) {
264
- if (isErrno(error, "ENOENT")) {
258
+ if (isFileMissingError(error)) {
265
259
  throw workspaceSnapshotNotFound(target);
266
260
  }
267
261
  throw error;
@@ -335,7 +329,7 @@ export async function createWorkspaceSnapshot(pmRoot, options = {}) {
335
329
  deduplicated = objectStat.isDirectory();
336
330
  }
337
331
  catch (error) {
338
- if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
332
+ if (!(error instanceof Error && "code" in error && isFileMissingError(error))) {
339
333
  throw error;
340
334
  }
341
335
  }
@@ -392,7 +386,7 @@ export async function listWorkspaceSnapshots(pmRoot) {
392
386
  const objectNames = await readdir(path.join(store, "objects")).catch((error) => {
393
387
  if (error instanceof Error &&
394
388
  "code" in error &&
395
- error.code === "ENOENT") {
389
+ isFileMissingError(error)) {
396
390
  return [];
397
391
  }
398
392
  throw error;
@@ -400,7 +394,7 @@ export async function listWorkspaceSnapshots(pmRoot) {
400
394
  const referenceNames = await readdir(path.join(store, "refs")).catch((error) => {
401
395
  if (error instanceof Error &&
402
396
  "code" in error &&
403
- error.code === "ENOENT") {
397
+ isFileMissingError(error)) {
404
398
  return [];
405
399
  }
406
400
  throw error;
@@ -609,4 +603,4 @@ export async function deleteWorkspaceSnapshot(pmRoot, target) {
609
603
  return { deleted: "object", target };
610
604
  }
611
605
  //# sourceMappingURL=workspace-snapshot.js.map
612
- //# debugId=470bb466-4619-5c54-a154-186b7ef4a049
606
+ //# debugId=36aacc71-c5f5-56f1-85f4-28b2be5b02e2
@@ -13,17 +13,12 @@
13
13
  * never breaks correctness.
14
14
  */
15
15
 
16
- !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]="a6c0aa24-501a-5e76-a669-91d125634cf1")}catch(e){}}();
16
+ !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]="7df80849-51e1-5efa-b40a-b44088f941a9")}catch(e){}}();
17
+ import { isFileMissingError } from "../core/fs/fs-utils.js";
17
18
  import fs from "node:fs/promises";
18
19
  import path from "node:path";
19
20
  const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
20
21
  const TERMINAL_JOURNAL_STATUSES = new Set(["committed", "compensated"]);
21
- function isErrno(error, code) {
22
- return (typeof error === "object" &&
23
- error !== null &&
24
- "code" in error &&
25
- error.code === code);
26
- }
27
22
  function readJournalStatusAndTimestamp(raw) {
28
23
  let parsed;
29
24
  try {
@@ -129,7 +124,7 @@ export async function runWorkspaceTransactionGc(pmRoot, options) {
129
124
  .sort((left, right) => left.localeCompare(right));
130
125
  }
131
126
  catch (error) {
132
- if (isErrno(error, "ENOENT")) {
127
+ if (isFileMissingError(error)) {
133
128
  return result;
134
129
  }
135
130
  throw error;
@@ -152,4 +147,4 @@ export async function runWorkspaceTransactionGc(pmRoot, options) {
152
147
  return result;
153
148
  }
154
149
  //# sourceMappingURL=workspace-transaction-gc.js.map
155
- //# debugId=a6c0aa24-501a-5e76-a669-91d125634cf1
150
+ //# debugId=7df80849-51e1-5efa-b40a-b44088f941a9
@@ -5,11 +5,11 @@
5
5
  * mutation primitives without rewriting immutable item or relationship history.
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]="d49ec604-c10c-5706-9d87-510845e55e1a")}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]="6ed4a26f-2ae2-556a-87db-56844108757f")}catch(e){}}();
9
9
  import { mkdir, readFile } from "node:fs/promises";
10
10
  import path from "node:path";
11
11
  import { acquireLock } from "../core/lock/lock.js";
12
- import { writeFileAtomic } from "../core/fs/fs-utils.js";
12
+ import { isFileMissingError, writeFileAtomic } from "../core/fs/fs-utils.js";
13
13
  import { nowIso } from "../core/shared/time.js";
14
14
  /**
15
15
  * Deliberate process-boundary interruption used by deterministic crash tests.
@@ -244,7 +244,7 @@ async function loadJournal(pmRoot, transactionId, stepIds) {
244
244
  if (error !== null &&
245
245
  typeof error === "object" &&
246
246
  "code" in error &&
247
- error.code === "ENOENT")
247
+ isFileMissingError(error))
248
248
  return undefined;
249
249
  throw error;
250
250
  }
@@ -361,4 +361,4 @@ export async function commitWorkspaceTransaction(options) {
361
361
  }
362
362
  }
363
363
  //# sourceMappingURL=workspace-transaction.js.map
364
- //# debugId=d49ec604-c10c-5706-9d87-510845e55e1a
364
+ //# debugId=6ed4a26f-2ae2-556a-87db-56844108757f
@@ -4,7 +4,8 @@
4
4
  * Maintains repository-scaffold contracts shared by the public SDK and CLI.
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]="bbfa5b19-d801-582a-98e8-0c4925dd57c0")}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]="89261a49-97e5-5ffc-a81c-2409f0b529cf")}catch(e){}}();
8
+ import { isFileMissingError } from "../core/fs/fs-utils.js";
8
9
  import { readFile, writeFile } from "node:fs/promises";
9
10
  import path from "node:path";
10
11
  import { EXIT_CODE } from "../core/shared/constants.js";
@@ -107,7 +108,7 @@ export async function ensurePmGitignore(workspaceRoot, options = {}) {
107
108
  current = await withGitignorePermissionRecovery(() => readFile(gitignorePath, "utf8"));
108
109
  }
109
110
  catch (error) {
110
- if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
111
+ if (!(error instanceof Error && "code" in error && isFileMissingError(error))) {
111
112
  throw error;
112
113
  }
113
114
  }
@@ -125,4 +126,4 @@ export async function ensurePmGitignore(workspaceRoot, options = {}) {
125
126
  return { path: gitignorePath, changed: true };
126
127
  }
127
128
  //# sourceMappingURL=workspace.js.map
128
- //# debugId=bbfa5b19-d801-582a-98e8-0c4925dd57c0
129
+ //# debugId=89261a49-97e5-5ffc-a81c-2409f0b529cf
package/docs/ASSURANCE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Project Assurance Primitives
2
2
 
3
- Tracker: [pm-2lex4r](../.agents/pm/features/pm-2lex4r.toon), [pm-lyfu7b](../.agents/pm/features/pm-lyfu7b.toon), [pm-wn6wot](../.agents/pm/features/pm-wn6wot.toon), [pm-91xeam](../.agents/pm/features/pm-91xeam.toon), [pm-py7qv2](../.agents/pm/issues/pm-py7qv2.toon), [pm-33mjrw](../.agents/pm/issues/pm-33mjrw.toon), [pm-q6n8sj](../.agents/pm/issues/pm-q6n8sj.toon)
3
+ Tracker: [pm-2lex4r](../.agents/pm/features/pm-2lex4r.toon), [pm-lyfu7b](../.agents/pm/features/pm-lyfu7b.toon), [pm-wn6wot](../.agents/pm/features/pm-wn6wot.toon), [pm-91xeam](../.agents/pm/features/pm-91xeam.toon), [pm-uhv1m5](../.agents/pm/features/pm-uhv1m5.toon), [pm-m7bb7r](../.agents/pm/features/pm-m7bb7r.toon), [pm-py7qv2](../.agents/pm/issues/pm-py7qv2.toon), [pm-33mjrw](../.agents/pm/issues/pm-33mjrw.toon), [pm-q6n8sj](../.agents/pm/issues/pm-q6n8sj.toon), [pm-h06944](../.agents/pm/issues/pm-h06944.toon), [pm-dwj33e](../.agents/pm/decisions/pm-dwj33e.toon)
4
4
 
5
5
  ## Agent Quick Context
6
6
 
@@ -12,6 +12,15 @@ Assurance turns project policy into three reusable SDK-owned declarations:
12
12
 
13
13
  Declarations live in `.agents/pm/assurance.json`. Every registry mutation and non-dry gate verdict is appended through the verified workspace history stream; never edit either file directly.
14
14
 
15
+ `pm history _workspace --verify`, `pm validate`, and `pm health` verify both
16
+ the hash chain and replay-to-disk agreement for every governed singleton. A
17
+ valid chain paired with a different, missing, or unreadable singleton is still
18
+ drift. SDK hosts can inspect that state with `inspectWorkspaceHistoryState`,
19
+ adopt a reviewed out-of-band value only through
20
+ `reconcileWorkspaceJsonHistory` with a terminal authorizing Decision, or
21
+ replace it from a verified version with `restoreWorkspaceJsonFromHistory`.
22
+ Both recovery paths append forward; neither rewrites the existing stream.
23
+
15
24
  ## Why Assurance Exists
16
25
 
17
26
  Project management is context management. A useful quality gate therefore needs more than a shell exit code: it must preserve what was measured, which population was judged, why a bound exists, who owns it, what changed the result, how expensive the evaluation was, and which immutable tree received the verdict.
@@ -36,6 +45,11 @@ Measurements support these built-in sources:
36
45
 
37
46
  Each measurement may declare `max_cost`. Evaluation fails closed when the total abstract compute units exceed that ceiling. Every result reports units, scanned items, scanned history rows, provider calls, duration, population size, and contributors.
38
47
 
48
+ One workspace context memoizes identical graph operations and the shared
49
+ validate and health reports for its lifetime. A gate may therefore project many
50
+ fields from one authoritative audit snapshot without multiplying full-workspace
51
+ I/O or allowing concurrent assertions to observe different tracker states.
52
+
39
53
  An `items` source with `field` must declare exactly one predicate: `equals` (including an explicit `null`) or `state`. `state: missing` treats an absent property, `null`, an empty string, or an empty array as missing; `state: present` selects the complement. Workspace evaluation loads full item metadata, so `files`, `tests`, and `docs` selectors measure stored evidence rather than a light projection that omitted those collections.
40
54
 
41
55
  Assertions require exactly one polarity:
@@ -86,6 +100,152 @@ pm assurance remove gate release-readiness
86
100
 
87
101
  Referenced measurements and assertions cannot be removed. Remove the consuming gate or assertion first.
88
102
 
103
+ ### Presets, derivation, and promotion
104
+
105
+ List the four built-in project shapes, preview one as ordinary declarations, or apply it atomically:
106
+
107
+ ```bash
108
+ pm assurance presets
109
+ pm assurance presets software-delivery --owner pm-example
110
+ pm assurance apply software-delivery --owner pm-example \
111
+ --message "Adopt the initial delivery evidence contract"
112
+ ```
113
+
114
+ The preset creates measurements, assertions, and a gate in one audited transaction. Reapplying the same bundle is idempotent; an existing divergent id is refused rather than overwritten. The available shapes are `software-delivery`, `research`, `agent-evaluation`, and `operations`.
115
+
116
+ Self-derivation observes active items without writing anything. Each proposal reports its active scope, population size, and observed missing-evidence ceiling. Persistence requires the explicit `--apply` flag:
117
+
118
+ ```bash
119
+ pm assurance derive --owner pm-example
120
+ pm assurance derive --owner pm-example --apply \
121
+ --message "Accept the observed evidence baseline"
122
+ pm assurance promote derived-active-missing-tests-ceiling --enforcement warn
123
+ pm assurance promote derived-active-missing-tests-ceiling --enforcement block
124
+ ```
125
+
126
+ Derived assertions start at `observe`. Promotion is exactly one step (`observe` to `warn`, then `warn` to `block`) and each transition is an ordinary audited declaration mutation. There is no automatic promotion and no privileged preset execution path.
127
+
128
+ ## Extension Measurement Providers
129
+
130
+ An extension opens the measurement vocabulary through `api.registerAssuranceMeasurementProvider`. The registration declares stable keys, parameter types, a coarse `low`/`medium`/`high` cost class, network use, a host timeout, and a resolver. It requires the `services` capability; a network provider must also declare `permissions.network: true` in `manifest.json`.
131
+
132
+ This code-quality provider measures a local report without changing assertion or gate semantics:
133
+
134
+ ```ts
135
+ import { readFile } from "node:fs/promises";
136
+ import { defineExtension } from "@unbrained/pm-cli/sdk";
137
+
138
+ export default defineExtension({
139
+ activate(api) {
140
+ api.registerAssuranceMeasurementProvider({
141
+ id: "coverage",
142
+ keys: {
143
+ lines: {
144
+ value_type: "number",
145
+ parameters: { report: { type: "string", required: true } },
146
+ },
147
+ },
148
+ cost_class: "low",
149
+ network: false,
150
+ timeout_ms: 2_000,
151
+ async resolve({ parameters }) {
152
+ const report = JSON.parse(
153
+ await readFile(String(parameters.report), "utf8"),
154
+ ) as {
155
+ total: { lines: { pct: number } };
156
+ };
157
+ return { value: report.total.lines.pct, population_size: 1, cost: 1 };
158
+ },
159
+ });
160
+ },
161
+ });
162
+ ```
163
+
164
+ An evaluation package can expose episode reward on the same surface:
165
+
166
+ ```ts
167
+ api.registerAssuranceMeasurementProvider({
168
+ id: "agent-eval",
169
+ keys: {
170
+ "mean-reward": {
171
+ value_type: "number",
172
+ parameters: { suite: { type: "string", required: true } },
173
+ },
174
+ },
175
+ cost_class: "high",
176
+ network: false,
177
+ timeout_ms: 120_000,
178
+ async resolve({ parameters }) {
179
+ const result = await runFrozenEvaluation(String(parameters.suite));
180
+ return {
181
+ value: result.meanReward,
182
+ population_size: result.episodes,
183
+ cost: result.steps,
184
+ contributors: result.regressedScenarioIds,
185
+ };
186
+ },
187
+ });
188
+ ```
189
+
190
+ An external registry provider declares its network dependency explicitly:
191
+
192
+ ```ts
193
+ api.registerAssuranceMeasurementProvider({
194
+ id: "npm-registry",
195
+ keys: {
196
+ "dist-tag-count": {
197
+ value_type: "number",
198
+ parameters: { package: { type: "string", required: true } },
199
+ },
200
+ },
201
+ cost_class: "medium",
202
+ network: true,
203
+ timeout_ms: 5_000,
204
+ async resolve({ parameters }) {
205
+ const name = encodeURIComponent(String(parameters.package));
206
+ const response = await fetch(
207
+ `https://registry.npmjs.org/-/package/${name}/dist-tags`,
208
+ );
209
+ if (!response.ok) throw new Error(`registry returned ${response.status}`);
210
+ const tags = (await response.json()) as Record<string, string>;
211
+ return { value: Object.keys(tags).length, population_size: 1, cost: 10 };
212
+ },
213
+ });
214
+ ```
215
+
216
+ The corresponding measurement is ordinary registry data:
217
+
218
+ ```json
219
+ {
220
+ "id": "published-tag-count",
221
+ "source": {
222
+ "kind": "provider",
223
+ "provider": "npm-registry",
224
+ "key": "dist-tag-count",
225
+ "parameters": { "package": "@example/tool" }
226
+ }
227
+ }
228
+ ```
229
+
230
+ A provider-backed gate must opt into every provider and each trigger's execution envelope. Omission refuses provider execution:
231
+
232
+ ```json
233
+ {
234
+ "id": "release-readiness",
235
+ "assertion_ids": ["published-tag-count-ceiling"],
236
+ "triggers": ["ci", "scheduled"],
237
+ "provider_policy": {
238
+ "allowed_providers": ["npm-registry"],
239
+ "triggers": {
240
+ "ci": { "max_cost_class": "low", "allow_network": false },
241
+ "scheduled": { "max_cost_class": "medium", "allow_network": true }
242
+ }
243
+ }
244
+ }
245
+ ```
246
+
247
+ Before invocation the host verifies the provider allow-list, declared cost class, and network capability for the active trigger. It then validates key parameters and result shape, enforces the registered timeout, and charges the returned cost through the existing measurement ceiling. Extension tests must bind `PM_PATH` and `PM_GLOBAL_PATH` to temporary roots; never point provider fixtures at the repository tracker or a live service.
248
+
89
249
  ## SDK and MCP
90
250
 
91
251
  The reusable client exposes the same action grammar:
@@ -103,9 +263,9 @@ await pm.assurance({
103
263
  });
104
264
  ```
105
265
 
106
- For direct host composition, use `evaluateMeasurement`, `evaluateAssuranceGate`, `createAssuranceWorkspaceContext`, and the audited declaration/verdict helpers exported from `@unbrained/pm-cli/sdk`. A host contributes provider measurements by passing stable resolver ids to `createAssuranceWorkspaceContext`; an absent resolver fails loudly. External adapters must enforce an appropriate timeout. The core evaluator bounds concurrent assertions and expression operands, and workspace history loading uses bounded concurrency; item-only callers can explicitly skip history and Git identity resolution.
266
+ For direct host composition, use `evaluateMeasurement`, `evaluateAssuranceGate`, `createAssuranceWorkspaceContext`, the preset/derivation helpers, and the audited declaration/verdict helpers exported from `@unbrained/pm-cli/sdk`. Active extension registrations are discovered automatically. Embedding hosts may additionally pass stable resolver ids and matching `provider_capabilities` to `createAssuranceWorkspaceContext`; an absent resolver or capability fails loudly. The core evaluator bounds concurrent assertions and expression operands, and workspace history loading uses bounded concurrency; item-only callers can explicitly skip history and Git identity resolution.
107
267
 
108
- Generic SDK and MCP dispatch use `action: "assurance"` with `subcommand` set to `list`, `show`, `put`, `remove`, `run`, or `verdicts`. Discover the current machine contract instead of copying parameter lists:
268
+ Generic SDK and MCP dispatch use `action: "assurance"` with `subcommand` set to `list`, `show`, `put`, `remove`, `run`, `verdicts`, `presets`, `apply`, `derive`, or `promote`. Discover the current machine contract instead of copying parameter lists:
109
269
 
110
270
  ```bash
111
271
  pm contracts --action assurance --schema-only --json
package/docs/COMMANDS.md CHANGED
@@ -21,7 +21,29 @@ pm contracts --command <command> --flags-only --json
21
21
 
22
22
  Tracked documentation work: [pm-u9d0](../.agents/pm/epics/pm-u9d0.toon),
23
23
  [pm-7nqo6b](../.agents/pm/issues/pm-7nqo6b.toon), and
24
- [pm-j1r8gl](../.agents/pm/issues/pm-j1r8gl.toon).
24
+ [pm-j1r8gl](../.agents/pm/issues/pm-j1r8gl.toon). Universal command intent and
25
+ default output-budget enforcement are tracked by
26
+ [pm-5t33or](../.agents/pm/features/pm-5t33or.toon),
27
+ [pm-s2h0mq](../.agents/pm/issues/pm-s2h0mq.toon),
28
+ [pm-pmrae8](../.agents/pm/issues/pm-pmrae8.toon), and
29
+ [pm-7i97c3](../.agents/pm/issues/pm-7i97c3.toon).
30
+
31
+ ### Universal read-output ceiling
32
+
33
+ Every SDK-backed read has a format-aware default token ceiling discovered from
34
+ `pm contracts`. Results already within that ceiling are returned unchanged, so
35
+ the default adds no receipt overhead to ordinary reads. An oversized result is
36
+ degraded deterministically through string compaction, row compaction, and a
37
+ truthful omission envelope; automatic degradation adds `read_output` with
38
+ `budget_source: default`, `budget_tokens`, the final estimate, and the applied
39
+ compaction flags.
40
+
41
+ Use `--output-budget <tokens>` to request a tighter per-call ceiling. Use
42
+ `--output-budget unbounded` only when the caller deliberately accepts the full
43
+ cost. This cost opt-out is distinct from command-local row controls such as
44
+ `activity --unbounded`, which disable a row limit but do not disable the shared
45
+ token ceiling. `--output-format json` and global `--json` select the declared
46
+ JSON ceiling; TOON uses the smaller default agent ceiling.
25
47
 
26
48
  ## Command Families
27
49
 
@@ -1161,7 +1183,14 @@ Extension policy tokens are explicitly scoped by
1161
1183
  Project-governance setters use the separate `governance_contracts` value
1162
1184
  domains; in particular, workflow enforcement is `off|warn|strict`, not the
1163
1185
  extension policy token `enforce`.
1164
- Use `pm contracts --summary --json` first when bootstrapping in a tight context window; it emits one command and terse intent per row before the agent requests heavier command-specific flags or schemas.
1186
+ Use `pm contracts --summary --json` first when bootstrapping in a tight context
1187
+ window. `command_summaries` contains exactly one row per advertised command
1188
+ path—including aliases and namespace children—with a meaningful intent plus
1189
+ `default_max_estimated_tokens` and the format-specific TOON/JSON ceilings.
1190
+ Package commands contribute their intent through command registration (falling
1191
+ back to the registered description), so agents never need a generic
1192
+ `Inspect flags.` placeholder. Request heavier command-specific flags or schemas
1193
+ only after selecting the exact path.
1165
1194
 
1166
1195
  ## Completion
1167
1196
 
@@ -156,6 +156,7 @@ Telemetry is opt-in via `pm config set telemetry-tracking on` (see [Common Setti
156
156
  | `PM_TELEMETRY_OTEL_DISABLED` | boolean | Disable only OTLP trace-span export; the event queue still flushes. |
157
157
  | `PM_TELEMETRY_INLINE_FLUSH` | boolean | Flush the queue and OTLP spans inline instead of dispatching the detached worker. Mainly for tests; normal use relies on the background worker. |
158
158
  | `PM_TELEMETRY_SOURCE_CONTEXT` | `user` \| `automation` \| `test` \| `dogfood` | Override the inferred source context recorded on each event. Any other value is ignored and the context is inferred. |
159
+ | `PM_TELEMETRY_HTTP_TIMEOUT_MS` | integer milliseconds | Bound each background event or OTLP request (default `20000`, clamped to `1000`–`25000` to stay below the worker lock TTL). |
159
160
  | `PM_TELEMETRY_INGEST_KEY` | string | Sent as the `x-pm-telemetry-key` header on queue flushes; never logged. |
160
161
  | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | URL | OTLP/HTTP traces endpoint for command spans. Takes precedence over the base endpoint. |
161
162
  | `OTEL_EXPORTER_OTLP_ENDPOINT` | URL | Base OTLP endpoint; the traces endpoint is derived by appending `/v1/traces`. |
@@ -164,6 +165,7 @@ Telemetry is opt-in via `pm config set telemetry-tracking on` (see [Common Setti
164
165
  Interaction rules:
165
166
 
166
167
  - `PM_TELEMETRY_DISABLED` / `PM_NO_TELEMETRY` short-circuit everything, including OTLP export, regardless of the other knobs.
168
+ - `PM_TELEMETRY_HTTP_TIMEOUT_MS` applies only to the detached flush worker in normal use. It covers DNS, each IPv4/IPv6 connection attempt, TLS, and response completion. The upper bound preserves the 60-second worker-lock safety margin across the sequential event and OTLP phases; it does not make foreground commands wait for the network.
167
169
  - OTLP span export only happens when telemetry is enabled, `PM_TELEMETRY_OTEL_DISABLED` is off, and a traces endpoint is configured. By default spans are persisted to a bounded queue and exported by the detached, unref'd flush worker so commands exit promptly even when the traces endpoint is unreachable. `PM_TELEMETRY_INLINE_FLUSH=1` is the explicit test-oriented exception that performs the flush inline.
168
170
  - `pm health --check-telemetry --json` surfaces flush and OTLP export diagnostics (`pending_otel_spans`, `last_otel_attempt_at`, `last_otel_success_at`, `last_otel_failure_at`, `last_otel_failure_error`) and the active `env_overrides` (including `telemetry_inline_flush` and `telemetry_source_context`) so agents can self-diagnose a stalled endpoint.
169
171
  - An explicit `PM_AUTHOR` override adds a privacy-preserving agent-identity dimension to `command_start`/`command_finish` events so overridden invocations can be segmented in dashboards without leaking the raw author string. At `redacted`/`max` capture the events carry `author_context_hash` — the same installation-id-keyed one-way SHA-256 used for `pm_root_hash`/`cwd_hash`, so the same author hashes consistently within an installation but differently across installations. At `minimal` capture only a boolean `has_author_context` is emitted. The raw `PM_AUTHOR` value is never exported.
@@ -29,6 +29,7 @@ pm install npm:package@1.2.3 --project
29
29
  pm install https://github.com/org/repo --project
30
30
  pm install --github org/repo/path --ref main --project
31
31
  ```
32
+
32
33
  Bundled first-party packages live under `packages/pm-*`:
33
34
 
34
35
  ```bash
@@ -38,6 +39,7 @@ pm install calendar --project
38
39
  pm install search-advanced --project
39
40
  pm install kanban --project
40
41
  ```
42
+
41
43
  `pm install '*'`, `pm install all`, and shell-expanded `pm install *` are normalized to the same bundled install-all request. First-party package aliases come from each package manifest, with a fallback derived from the `packages/pm-*` directory name. A bare bundled alias that also names an installed npm package reports both explicit choices in `source_resolution`; see [Extension Lifecycle Contracts](EXTENSION_LIFECYCLE.md).
42
44
 
43
45
  External registry packages are installed by exact package name. If `npm:<name>` returns a registry 404, JSON error output includes `fallback_candidates` and `next_best_command`; unpublished first-party packages fall back to `pm install --project github.com/unbraind/<name>`. Install results include package-owned `command_paths`, `action_paths`, `contributions`, `command_discovery`, and a light `verification` block covering the target tracker, activation status, registered commands/actions/item types, and health verdict. Agents should consume those fields instead of guessing from the package name or immediately spending another invocation on doctor. A successful activation persists the versioned contribution inventory in `.managed-extensions.json`; subsequent discovery can enumerate command handlers, hooks, parser/renderer targets, schema names, and the other registered surfaces without importing the package module. A failed runtime activation returns `ok: false`, `activated: false`, a non-zero CLI exit, and actionable diagnostics; missing SDK resolution adds an explicit dependency recovery step. Local installs are containment-safe when the extension destination is nested inside the source checkout: pm stages the package outside the source and prunes the destination, `.agents`, `node_modules`, and install-backup directories before copying, so reinstalling cannot recursively copy tracker history, host dependencies, or prior backups.
@@ -52,6 +54,7 @@ pm install npm:pm-github --project
52
54
  pm package doctor --project --detail deep --trace
53
55
  pm github validate --repo owner/repo
54
56
  ```
57
+
55
58
  For `pm-github`, run `pm github validate --repo owner/repo` before mutating commands; write paths require `GITHUB_TOKEN`/`GH_TOKEN` or `gh auth login`.
56
59
 
57
60
  For ecosystem maintenance, use the reusable external package smoke harness after building `dist/`:
@@ -132,7 +135,11 @@ Extensions are authored **and loaded** as TypeScript (ADR [pm-2c28](../.agents/p
132
135
  "process_spawn": false
133
136
  },
134
137
  "capabilities": ["commands"],
135
- "contributions": { "schema_version": 1, "commands": ["hello"], "command_handlers": ["hello"] },
138
+ "contributions": {
139
+ "schema_version": 1,
140
+ "commands": ["hello"],
141
+ "command_handlers": ["hello"]
142
+ },
136
143
  "activation": { "commands": ["hello"] }
137
144
  }
138
145
  ```
@@ -286,11 +293,9 @@ Doctor JSON also includes `triage.collision_plan` with grouped surfaces, ranked
286
293
  ## Runtime APIs
287
294
 
288
295
  Use the public SDK barrel. Do not deep-import from `src/core` or `dist/core`.
289
-
290
296
  ```ts
291
297
  import { defineExtension } from "@unbrained/pm-cli/sdk";
292
298
  ```
293
-
294
299
  Common APIs:
295
300
 
296
301
  - `api.extension` is a read-only identity (`name`, `layer`, `version`, `capabilities`, `pm_min_version?`, `pm_max_version?`, `source_package?`) for self-identifying logs and version gating without re-reading the manifest.
@@ -302,6 +307,7 @@ Common APIs:
302
307
  - `api.registerRelationshipKinds(definitions)` adds validated graph semantics. Definitions declare direction, inverse spelling, ordering/precedence, hierarchy, cardinality, lifecycle, aliases, payload schema, compatibility version, and self-edge policy. Active definitions are merged into native CLI, MCP, and SDK workspace graph assembly. Requires the `schema` capability and is governed by the `schema.relationshipkinds` policy surface.
303
308
  - `api.registerMigration(definition)` adds schema migrations.
304
309
  - `api.registerProfile(profile)` contributes a project profile — a declarative archetype bundling item types, statuses, fields, per-type workflows, config, templates, and package recommendations. Once active it resolves by name through `pm profile list/show/apply` alongside the core `agile`/`ops`/`research` archetypes (built-in names are reserved; a colliding registration is ignored with a warning). Requires the `schema` capability.
310
+ - `api.registerAssuranceMeasurementProvider(provider)` contributes typed measurements to assurance. It requires `services`; network providers also require manifest `permissions.network: true`. Gates allow providers and cost/network limits per trigger. See [Project Assurance Primitives](ASSURANCE.md#extension-measurement-providers) for examples.
305
311
  - `api.registerService("output_format", handler)` customizes output formatting through the service override API. Use `handleServiceOverride(result)` to claim a payload and return the scaffold-compatible literal `{ handled: false }` (or `declineServiceOverride()`) for commands the extension does not own. Legacy `null`/`undefined` declines remain supported; returning the original payload is now an unambiguous handled result.
306
312
  - `api.registerRenderer("toon" | "json", renderer, ownership?)` adds format-specific renderers. Scope ownership with `commands` and/or a `resultDiscriminator`; the host checks both before invoking the renderer and falls back to native rendering for unrelated output. The legacy unscoped callback remains supported, but doctor warns because package ownership cannot be proven statically.
307
313
  - `suppressHostOutput(result?)` from `@unbrained/pm-cli/sdk` marks commands that already wrote output, preventing a second CLI payload while retaining the optional result for hooks, telemetry, and embedded hosts.
@@ -312,15 +318,8 @@ Common APIs:
312
318
  - Registered command, importer, and exporter handlers receive `context.sdk`, a host-bound service bundle containing a native-action `PmClient`, `getItemAt`, and `openRelationshipEventStore`, alongside portable workspace coordinates. The client reuses the already-active extension schema context without recursively loading extensions, so package commands and data adapters can compose core lifecycle operations safely in CLI and SDK hosts.
313
319
  - An optional module-level `deactivate()` export (VS Code-style) is invoked by the host on shutdown/reload — including by the long-running MCP server between native-action requests — to close connections, clear timers, and release resources opened during `activate`. Teardown is best-effort and timeout-bounded by default so it does not block other extensions, except when a host explicitly disables waiting limits with `deactivate_timeout_ms: 0` or `Infinity`, which can wait indefinitely for a hanging `deactivate()` hook.
314
320
 
315
- The bundled `pm-lifecycle-hooks` package is the hook exemplar: it declares only
316
- `hooks` and registers a default-inert `afterCommand` hook so authors can copy a
317
- safe lifecycle pattern without changing command output.
318
-
319
- If a package calls a `register*` API without declaring the required manifest
320
- capability, `pm package doctor --project --detail deep --trace` reports
321
- `extension_capability_missing:<name>:<capability>` and shows the exact capability
322
- to add before publishing.
323
-
321
+ The bundled `pm-lifecycle-hooks` package is the hook exemplar: it declares only `hooks` and registers a default-inert `afterCommand` hook so authors can copy a safe lifecycle pattern without changing command output.
322
+ If a package calls a `register*` API without declaring the required manifest capability, `pm package doctor --project --detail deep --trace` reports `extension_capability_missing:<name>:<capability>` and shows the exact capability to add before publishing.
324
323
  Inline command flags require both `commands` and `schema` capabilities. Runtime schema changes should be verified with:
325
324
 
326
325
  ```bash