@unbrained/pm-cli 2026.8.12 → 2026.8.13

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 (173) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +22 -0
  3. package/dist/cli/error-guidance.js +4 -8
  4. package/dist/cli/main.js +3 -2
  5. package/dist/cli/register-assurance.js +28 -17
  6. package/dist/cli-bundle/bundle-manifest.json +161 -161
  7. package/dist/cli-bundle/chunks/chunk-3ZMOWAFM.js +2 -0
  8. package/dist/cli-bundle/chunks/chunk-DEDU3DG7.js +196 -0
  9. package/dist/cli-bundle/chunks/chunk-DL5M5J3V.js +13 -0
  10. package/dist/cli-bundle/chunks/chunk-F7FJYGK3.js +8 -0
  11. package/dist/cli-bundle/chunks/{chunk-RBTK5EZV.js → chunk-HIUZVCMN.js} +2 -2
  12. package/dist/cli-bundle/chunks/chunk-IMGIN7R6.js +2 -0
  13. package/dist/cli-bundle/chunks/{chunk-WLAHPSOX.js → chunk-Q47C67TQ.js} +2 -2
  14. package/dist/cli-bundle/chunks/{chunk-BM5YJ2UW.js → chunk-QGTUPEU5.js} +52 -52
  15. package/dist/cli-bundle/chunks/chunk-TF3TPKPR.js +34 -0
  16. package/dist/cli-bundle/chunks/{chunk-KOQMGCTA.js → chunk-YLBAPY22.js} +50 -50
  17. package/dist/cli-bundle/chunks/{register-list-query-XPCJRUUD.js → register-list-query-4HIZ7EA3.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-mutation-4RJCBKRS.js → register-mutation-W64FZ5HN.js} +2 -2
  19. package/dist/cli-bundle/chunks/register-operations-C44ET7R5.js +2 -0
  20. package/dist/cli-bundle/chunks/{register-setup-LLHAKP2V.js → register-setup-634MFRXU.js} +2 -2
  21. package/dist/cli-bundle/focused-chunks/chunk-27N23W6M.js +31 -0
  22. package/dist/cli-bundle/focused-chunks/{chunk-YIAXMC3T.js → chunk-2JP7W7GO.js} +2 -2
  23. package/dist/cli-bundle/focused-chunks/chunk-2RI2FESN.js +4 -0
  24. package/dist/cli-bundle/focused-chunks/chunk-3BNVU6LC.js +8 -0
  25. package/dist/cli-bundle/focused-chunks/{chunk-KHM2CS2C.js → chunk-3DJSGWVB.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/{chunk-2DALMVF7.js → chunk-6MLIRBLV.js} +2 -2
  27. package/dist/cli-bundle/focused-chunks/chunk-6XYLENDV.js +155 -0
  28. package/dist/cli-bundle/focused-chunks/chunk-CP4K4WFS.js +2 -0
  29. package/dist/cli-bundle/focused-chunks/{chunk-RWPAWUOT.js → chunk-E3AVJ655.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/{chunk-5TAGTDRE.js → chunk-EBTNSFYE.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/{chunk-KPSXVBDV.js → chunk-HC2OL2H2.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-GQW4BPUT.js → chunk-I75MANL7.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/{chunk-ZMGWAYH7.js → chunk-IIS2FRKL.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/chunk-NFHFZH2Z.js +2 -0
  35. package/dist/cli-bundle/focused-chunks/chunk-OGC4XSBC.js +17 -0
  36. package/dist/cli-bundle/focused-chunks/{chunk-JAFRIWVF.js → chunk-OOYYKQWX.js} +4 -4
  37. package/dist/cli-bundle/focused-chunks/{chunk-KTLOKKCQ.js → chunk-PKPOZXLL.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-QDZUABWL.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-QQX5TDFT.js +16 -0
  40. package/dist/cli-bundle/focused-chunks/{chunk-NYLOUPOB.js → chunk-TEZIXMTH.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/chunk-WEH6NSL6.js +18 -0
  42. package/dist/cli-bundle/main.js +13 -13
  43. package/dist/cli-bundle/sdk-authoring.js +1 -1
  44. package/dist/cli-bundle/sdk-contracts.js +1 -1
  45. package/dist/cli-bundle/sdk-core.js +42 -41
  46. package/dist/cli-bundle/sdk-governance.js +1 -1
  47. package/dist/cli-bundle/sdk-graph.js +1 -1
  48. package/dist/cli-bundle/sdk-merge.js +1 -1
  49. package/dist/cli-bundle/sdk-query.js +1 -1
  50. package/dist/cli-bundle/sdk-runtime.js +1 -1
  51. package/dist/cli-bundle/sdk-testing.js +1 -1
  52. package/dist/cli-bundle/sdk.js +2 -2
  53. package/dist/core/checkpoint/checkpoint-gc.js +7 -12
  54. package/dist/core/extensions/activation-summary-markdown.js +5 -4
  55. package/dist/core/extensions/activation-summary.d.ts +2 -0
  56. package/dist/core/extensions/activation-summary.js +6 -2
  57. package/dist/core/extensions/capability-usage.d.ts +1 -1
  58. package/dist/core/extensions/capability-usage.js +4 -3
  59. package/dist/core/extensions/contribution-inventory.d.ts +1 -0
  60. package/dist/core/extensions/contribution-inventory.js +6 -2
  61. package/dist/core/extensions/extension-registries.js +3 -2
  62. package/dist/core/extensions/extension-types.d.ts +83 -1
  63. package/dist/core/extensions/extension-types.js +3 -2
  64. package/dist/core/extensions/loader.d.ts +2 -8
  65. package/dist/core/extensions/loader.js +53 -144
  66. package/dist/core/extensions/registration-values.d.ts +43 -0
  67. package/dist/core/extensions/registration-values.js +210 -0
  68. package/dist/core/extensions/runtime-registrations.d.ts +3 -1
  69. package/dist/core/extensions/runtime-registrations.js +12 -2
  70. package/dist/core/fs/fs-utils.d.ts +2 -0
  71. package/dist/core/fs/fs-utils.js +7 -3
  72. package/dist/core/history/drift-cache.js +4 -3
  73. package/dist/core/history/drift-scan.js +5 -11
  74. package/dist/core/lock/lock-gc.js +8 -13
  75. package/dist/core/lock/lock.js +5 -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/schemas/assurance-definition.d.ts +32 -0
  87. package/dist/sdk/cli-contracts/schemas/assurance-definition.js +25 -2
  88. package/dist/sdk/cli-contracts/tool-parameter-tables.js +35 -5
  89. package/dist/sdk/cli-contracts/tool-schema.js +17 -2
  90. package/dist/sdk/comments.js +4 -3
  91. package/dist/sdk/compile-cache.js +4 -3
  92. package/dist/sdk/compose.d.ts +3 -1
  93. package/dist/sdk/compose.js +16 -2
  94. package/dist/sdk/context-signal-store.js +4 -3
  95. package/dist/sdk/extension/install-runtime.js +4 -4
  96. package/dist/sdk/extension/managed-state.js +4 -3
  97. package/dist/sdk/extension-contracts.d.ts +1 -1
  98. package/dist/sdk/flag-invocation-contracts.js +5 -6
  99. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +14 -2
  100. package/dist/sdk/governance/assurance-action.d.ts +27 -3
  101. package/dist/sdk/governance/assurance-action.js +112 -28
  102. package/dist/sdk/governance/assurance-mutation-error.d.ts +7 -0
  103. package/dist/sdk/governance/assurance-mutation-error.js +25 -2
  104. package/dist/sdk/governance/assurance-presets.d.ts +54 -0
  105. package/dist/sdk/governance/assurance-presets.js +136 -0
  106. package/dist/sdk/governance/assurance-runtime.d.ts +4 -0
  107. package/dist/sdk/governance/assurance-runtime.js +109 -8
  108. package/dist/sdk/governance/assurance.d.ts +39 -0
  109. package/dist/sdk/governance/assurance.js +143 -4
  110. package/dist/sdk/governance/gc.js +5 -11
  111. package/dist/sdk/governance/health.js +5 -5
  112. package/dist/sdk/governance/validate.js +4 -4
  113. package/dist/sdk/graph/durable-cache.js +61 -3
  114. package/dist/sdk/graph/governance-contracts.d.ts +12 -0
  115. package/dist/sdk/graph/governance-contracts.js +27 -0
  116. package/dist/sdk/graph/governance.d.ts +30 -2
  117. package/dist/sdk/graph/governance.js +124 -5
  118. package/dist/sdk/guide.js +4 -4
  119. package/dist/sdk/index.d.ts +2 -1
  120. package/dist/sdk/index.js +4 -3
  121. package/dist/sdk/linked-artifacts.d.ts +2 -0
  122. package/dist/sdk/linked-artifacts.js +29 -4
  123. package/dist/sdk/merge/install.js +5 -4
  124. package/dist/sdk/query/activity.js +4 -3
  125. package/dist/sdk/relationship-history.js +5 -5
  126. package/dist/sdk/schema-migration.js +4 -4
  127. package/dist/sdk/test/execution.js +4 -4
  128. package/dist/sdk/workspace-memory.js +4 -4
  129. package/dist/sdk/workspace-snapshot.js +8 -14
  130. package/dist/sdk/workspace-transaction-gc.js +4 -9
  131. package/dist/sdk/workspace-transaction.js +4 -4
  132. package/dist/sdk/workspace.js +4 -3
  133. package/docs/ASSURANCE.md +154 -3
  134. package/docs/CONFIGURATION.md +2 -0
  135. package/docs/EXTENSIONS.md +11 -12
  136. package/docs/RELATIONSHIP_GRAPH.md +15 -0
  137. package/docs/SDK.md +17 -3
  138. package/docs/TESTING.md +24 -15
  139. package/marketplace.json +2 -2
  140. package/package.json +3 -3
  141. package/packages/pm-beads/package.json +1 -1
  142. package/packages/pm-calendar/package.json +1 -1
  143. package/packages/pm-command-kit/package.json +1 -1
  144. package/packages/pm-digital-twin/package.json +1 -1
  145. package/packages/pm-governance-audit/package.json +1 -1
  146. package/packages/pm-guide-shell/package.json +1 -1
  147. package/packages/pm-kanban/package.json +1 -1
  148. package/packages/pm-lifecycle-hooks/package.json +1 -1
  149. package/packages/pm-linked-test-adapters/package.json +1 -1
  150. package/packages/pm-search-advanced/package.json +1 -1
  151. package/packages/pm-templates/package.json +1 -1
  152. package/packages/pm-todos/package.json +1 -1
  153. package/packages/pm-vcs/package.json +1 -1
  154. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  155. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  156. package/sdk/public-surface.json +183 -33
  157. package/dist/cli-bundle/chunks/chunk-3PS7X6GS.js +0 -8
  158. package/dist/cli-bundle/chunks/chunk-7KL6KMWF.js +0 -13
  159. package/dist/cli-bundle/chunks/chunk-D55A5AEN.js +0 -33
  160. package/dist/cli-bundle/chunks/chunk-KIKDWECH.js +0 -196
  161. package/dist/cli-bundle/chunks/chunk-R76342NV.js +0 -2
  162. package/dist/cli-bundle/chunks/chunk-VLUQOIDT.js +0 -2
  163. package/dist/cli-bundle/chunks/register-operations-2OGISOVR.js +0 -2
  164. package/dist/cli-bundle/focused-chunks/chunk-5OQIMELN.js +0 -155
  165. package/dist/cli-bundle/focused-chunks/chunk-ECADZMVC.js +0 -18
  166. package/dist/cli-bundle/focused-chunks/chunk-EJGXRA6B.js +0 -2
  167. package/dist/cli-bundle/focused-chunks/chunk-FFGSZCPK.js +0 -16
  168. package/dist/cli-bundle/focused-chunks/chunk-ISKOBJCM.js +0 -2
  169. package/dist/cli-bundle/focused-chunks/chunk-LGYK3NUF.js +0 -8
  170. package/dist/cli-bundle/focused-chunks/chunk-NBLUKITQ.js +0 -4
  171. package/dist/cli-bundle/focused-chunks/chunk-Q4T463SD.js +0 -31
  172. package/dist/cli-bundle/focused-chunks/chunk-QVZVY53N.js +0 -2
  173. package/dist/cli-bundle/focused-chunks/chunk-UTEWXDEQ.js +0 -17
@@ -8,7 +8,8 @@
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]="94d2a7a8-ce00-5689-b2f6-3d4813234b55")}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]="5e1810a5-04b8-5929-8eee-7a63f1684860")}catch(e){}}();
12
+ import { isFileMissingError } from "../../core/fs/fs-utils.js";
12
13
  import { readFile, realpath, writeFile } from "node:fs/promises";
13
14
  import { execFile } from "node:child_process";
14
15
  import path from "node:path";
@@ -253,7 +254,7 @@ async function reconcileGitattributesBlock(workspaceRoot, patterns, dryRun) {
253
254
  current = await readFile(gitattributesPath, "utf8");
254
255
  }
255
256
  catch (error) {
256
- if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
257
+ if (!(error instanceof Error && "code" in error && isFileMissingError(error))) {
257
258
  throw error;
258
259
  }
259
260
  }
@@ -414,7 +415,7 @@ export async function refreshMergeAttributeFenceIfInstalled(pmRoot) {
414
415
  catch (error) {
415
416
  if (error instanceof Error &&
416
417
  "code" in error &&
417
- error.code === "ENOENT") {
418
+ isFileMissingError(error)) {
418
419
  return { status: "not_installed", path: gitattributesPath };
419
420
  }
420
421
  throw error;
@@ -560,4 +561,4 @@ export async function installMergeFence(options) {
560
561
  };
561
562
  }
562
563
  //# sourceMappingURL=install.js.map
563
- //# debugId=94d2a7a8-ce00-5689-b2f6-3d4813234b55
564
+ //# debugId=5e1810a5-04b8-5929-8eee-7a63f1684860
@@ -4,7 +4,8 @@
4
4
  * Implements the pm activity 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]="27a71d4d-f496-5d0a-9771-caa43b81e1f7")}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]="49304d18-d722-5413-8e4d-4f2c3df4a1ca")}catch(e){}}();
8
+ import { isFileMissingError } from "../../core/fs/fs-utils.js";
8
9
  import fs from "node:fs/promises";
9
10
  import path from "node:path";
10
11
  import { getActiveExtensionRegistrations, runActiveOnReadHooks, pathExists, enforceHistoryStreamPolicyForItems, resolveItemTypeRegistry, EXIT_CODE, PmCliError, compareTimestampStrings, nowIso, resolveIsoOrRelative, listAllItemMetadataLight, getSettingsPath, resolvePmRoot, readSettings, } from "../runtime-primitives.js";
@@ -107,7 +108,7 @@ async function listHistoryFiles(historyDir) {
107
108
  }
108
109
  catch (error) {
109
110
  // Activity should degrade gracefully when optional history storage is absent.
110
- if (error.code === "ENOENT") {
111
+ if (isFileMissingError(error)) {
111
112
  return [];
112
113
  }
113
114
  throw error;
@@ -399,4 +400,4 @@ export async function runActivity(options, global) {
399
400
  };
400
401
  }
401
402
  //# sourceMappingURL=activity.js.map
402
- //# debugId=27a71d4d-f496-5d0a-9771-caa43b81e1f7
403
+ //# debugId=49304d18-d722-5413-8e4d-4f2c3df4a1ca
@@ -5,13 +5,13 @@
5
5
  * optimistic concurrency, deterministic replay, snapshots, and cursor pages.
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]="c53bb1b7-12b9-5370-a85b-cdd8bef57936")}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]="bb5dfa6f-b60f-55f2-ae17-3dbcaaff1b47")}catch(e){}}();
9
9
  import { createQueryFingerprint, decodeQueryCursorState, paginateQueryRows, } from "./pagination.js";
10
10
  import { constants } from "node:fs";
11
11
  import { lstat, mkdir, open } from "node:fs/promises";
12
12
  import path from "node:path";
13
13
  import { acquireLock } from "../core/lock/lock.js";
14
- import { writeFileAtomic } from "../core/fs/fs-utils.js";
14
+ import { isFileMissingError, writeFileAtomic } from "../core/fs/fs-utils.js";
15
15
  import { stableStringify } from "../core/shared/serialization.js";
16
16
  import { RelationshipGraph, createRelationshipKindRegistry, } from "./relationships.js";
17
17
  function requiredText(value, field) {
@@ -298,7 +298,7 @@ async function resolveRelationshipEventStorePath(pmRoot, relativePath = "relatio
298
298
  throw new TypeError("Relationship event path must not contain symbolic links");
299
299
  }
300
300
  catch (error) {
301
- if (error.code === "ENOENT")
301
+ if (isFileMissingError(error))
302
302
  break;
303
303
  throw error;
304
304
  }
@@ -314,7 +314,7 @@ async function loadRelationshipEventLog(target, nodes, registry) {
314
314
  raw = await handle.readFile("utf8");
315
315
  }
316
316
  catch (error) {
317
- if (error.code === "ENOENT")
317
+ if (isFileMissingError(error))
318
318
  return { log, raw: "" };
319
319
  throw error;
320
320
  }
@@ -517,4 +517,4 @@ export class RelationshipEventStore {
517
517
  }
518
518
  }
519
519
  //# sourceMappingURL=relationship-history.js.map
520
- //# debugId=c53bb1b7-12b9-5370-a85b-cdd8bef57936
520
+ //# debugId=bb5dfa6f-b60f-55f2-ae17-3dbcaaff1b47
@@ -7,11 +7,11 @@
7
7
  * guarantees.
8
8
  */
9
9
 
10
- !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]="7d175791-016d-59b6-a44d-a70371f36751")}catch(e){}}();
10
+ !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]="cd777de3-665f-5e01-ac98-8986f2a5a4c2")}catch(e){}}();
11
11
  import { createHash } from "node:crypto";
12
12
  import fs from "node:fs/promises";
13
13
  import path from "node:path";
14
- import { pathExists, readFileIfExists, writeFileAtomic, } from "../core/fs/fs-utils.js";
14
+ import { isFileMissingError, pathExists, readFileIfExists, writeFileAtomic, } from "../core/fs/fs-utils.js";
15
15
  import { appendWorkspaceHistoryChange } from "../core/history/workspace-history.js";
16
16
  import { resolveItemTypeRegistry } from "../core/item/type-registry.js";
17
17
  import { parseFieldsFile, serializeFieldsFile, } from "../core/schema/fields-file.js";
@@ -681,7 +681,7 @@ async function collectSchemaReferenceState(params) {
681
681
  if (typeof error !== "object" ||
682
682
  error === null ||
683
683
  !("code" in error) ||
684
- error.code !== "ENOENT") {
684
+ !isFileMissingError(error)) {
685
685
  throw error;
686
686
  }
687
687
  }
@@ -855,4 +855,4 @@ export const schemaMigrationTestOnly = {
855
855
  validateAndStoreMigrationPlan,
856
856
  };
857
857
  //# sourceMappingURL=schema-migration.js.map
858
- //# debugId=7d175791-016d-59b6-a44d-a70371f36751
858
+ //# debugId=cd777de3-665f-5e01-ac98-8986f2a5a4c2
@@ -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)
4
4
 
5
5
  ## Agent Quick Context
6
6
 
@@ -36,6 +36,11 @@ Measurements support these built-in sources:
36
36
 
37
37
  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
38
 
39
+ One workspace context memoizes identical graph operations and the shared
40
+ validate and health reports for its lifetime. A gate may therefore project many
41
+ fields from one authoritative audit snapshot without multiplying full-workspace
42
+ I/O or allowing concurrent assertions to observe different tracker states.
43
+
39
44
  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
45
 
41
46
  Assertions require exactly one polarity:
@@ -86,6 +91,152 @@ pm assurance remove gate release-readiness
86
91
 
87
92
  Referenced measurements and assertions cannot be removed. Remove the consuming gate or assertion first.
88
93
 
94
+ ### Presets, derivation, and promotion
95
+
96
+ List the four built-in project shapes, preview one as ordinary declarations, or apply it atomically:
97
+
98
+ ```bash
99
+ pm assurance presets
100
+ pm assurance presets software-delivery --owner pm-example
101
+ pm assurance apply software-delivery --owner pm-example \
102
+ --message "Adopt the initial delivery evidence contract"
103
+ ```
104
+
105
+ 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`.
106
+
107
+ 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:
108
+
109
+ ```bash
110
+ pm assurance derive --owner pm-example
111
+ pm assurance derive --owner pm-example --apply \
112
+ --message "Accept the observed evidence baseline"
113
+ pm assurance promote derived-active-missing-tests-ceiling --enforcement warn
114
+ pm assurance promote derived-active-missing-tests-ceiling --enforcement block
115
+ ```
116
+
117
+ 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.
118
+
119
+ ## Extension Measurement Providers
120
+
121
+ 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`.
122
+
123
+ This code-quality provider measures a local report without changing assertion or gate semantics:
124
+
125
+ ```ts
126
+ import { readFile } from "node:fs/promises";
127
+ import { defineExtension } from "@unbrained/pm-cli/sdk";
128
+
129
+ export default defineExtension({
130
+ activate(api) {
131
+ api.registerAssuranceMeasurementProvider({
132
+ id: "coverage",
133
+ keys: {
134
+ lines: {
135
+ value_type: "number",
136
+ parameters: { report: { type: "string", required: true } },
137
+ },
138
+ },
139
+ cost_class: "low",
140
+ network: false,
141
+ timeout_ms: 2_000,
142
+ async resolve({ parameters }) {
143
+ const report = JSON.parse(
144
+ await readFile(String(parameters.report), "utf8"),
145
+ ) as {
146
+ total: { lines: { pct: number } };
147
+ };
148
+ return { value: report.total.lines.pct, population_size: 1, cost: 1 };
149
+ },
150
+ });
151
+ },
152
+ });
153
+ ```
154
+
155
+ An evaluation package can expose episode reward on the same surface:
156
+
157
+ ```ts
158
+ api.registerAssuranceMeasurementProvider({
159
+ id: "agent-eval",
160
+ keys: {
161
+ "mean-reward": {
162
+ value_type: "number",
163
+ parameters: { suite: { type: "string", required: true } },
164
+ },
165
+ },
166
+ cost_class: "high",
167
+ network: false,
168
+ timeout_ms: 120_000,
169
+ async resolve({ parameters }) {
170
+ const result = await runFrozenEvaluation(String(parameters.suite));
171
+ return {
172
+ value: result.meanReward,
173
+ population_size: result.episodes,
174
+ cost: result.steps,
175
+ contributors: result.regressedScenarioIds,
176
+ };
177
+ },
178
+ });
179
+ ```
180
+
181
+ An external registry provider declares its network dependency explicitly:
182
+
183
+ ```ts
184
+ api.registerAssuranceMeasurementProvider({
185
+ id: "npm-registry",
186
+ keys: {
187
+ "dist-tag-count": {
188
+ value_type: "number",
189
+ parameters: { package: { type: "string", required: true } },
190
+ },
191
+ },
192
+ cost_class: "medium",
193
+ network: true,
194
+ timeout_ms: 5_000,
195
+ async resolve({ parameters }) {
196
+ const name = encodeURIComponent(String(parameters.package));
197
+ const response = await fetch(
198
+ `https://registry.npmjs.org/-/package/${name}/dist-tags`,
199
+ );
200
+ if (!response.ok) throw new Error(`registry returned ${response.status}`);
201
+ const tags = (await response.json()) as Record<string, string>;
202
+ return { value: Object.keys(tags).length, population_size: 1, cost: 10 };
203
+ },
204
+ });
205
+ ```
206
+
207
+ The corresponding measurement is ordinary registry data:
208
+
209
+ ```json
210
+ {
211
+ "id": "published-tag-count",
212
+ "source": {
213
+ "kind": "provider",
214
+ "provider": "npm-registry",
215
+ "key": "dist-tag-count",
216
+ "parameters": { "package": "@example/tool" }
217
+ }
218
+ }
219
+ ```
220
+
221
+ A provider-backed gate must opt into every provider and each trigger's execution envelope. Omission refuses provider execution:
222
+
223
+ ```json
224
+ {
225
+ "id": "release-readiness",
226
+ "assertion_ids": ["published-tag-count-ceiling"],
227
+ "triggers": ["ci", "scheduled"],
228
+ "provider_policy": {
229
+ "allowed_providers": ["npm-registry"],
230
+ "triggers": {
231
+ "ci": { "max_cost_class": "low", "allow_network": false },
232
+ "scheduled": { "max_cost_class": "medium", "allow_network": true }
233
+ }
234
+ }
235
+ }
236
+ ```
237
+
238
+ 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.
239
+
89
240
  ## SDK and MCP
90
241
 
91
242
  The reusable client exposes the same action grammar:
@@ -103,9 +254,9 @@ await pm.assurance({
103
254
  });
104
255
  ```
105
256
 
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.
257
+ 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
258
 
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:
259
+ 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
260
 
110
261
  ```bash
111
262
  pm contracts --action assurance --schema-only --json
@@ -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.