@unbrained/pm-cli 2026.8.11 → 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 (250) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +74 -21
  3. package/dist/cli/commander-usage.js +37 -11
  4. package/dist/cli/error-guidance.d.ts +2 -0
  5. package/dist/cli/error-guidance.js +82 -19
  6. package/dist/cli/main.js +35 -19
  7. package/dist/cli/register-annotations.js +56 -3
  8. package/dist/cli/register-assurance.js +28 -17
  9. package/dist/cli/register-list-query.js +10 -7
  10. package/dist/cli/register-operations.js +16 -6
  11. package/dist/cli-bundle/bundle-manifest.json +163 -163
  12. package/dist/cli-bundle/chunks/chunk-3ZMOWAFM.js +2 -0
  13. package/dist/cli-bundle/chunks/chunk-DEDU3DG7.js +196 -0
  14. package/dist/cli-bundle/chunks/chunk-DL5M5J3V.js +13 -0
  15. package/dist/cli-bundle/chunks/chunk-F7FJYGK3.js +8 -0
  16. package/dist/cli-bundle/chunks/chunk-HIUZVCMN.js +5 -0
  17. package/dist/cli-bundle/chunks/chunk-IMGIN7R6.js +2 -0
  18. package/dist/cli-bundle/chunks/{chunk-A2SJGHSI.js → chunk-Q47C67TQ.js} +2 -2
  19. package/dist/cli-bundle/chunks/{chunk-WMEZHCFI.js → chunk-QGTUPEU5.js} +62 -54
  20. package/dist/cli-bundle/chunks/chunk-TF3TPKPR.js +34 -0
  21. package/dist/cli-bundle/chunks/{chunk-ZU34C3LH.js → chunk-YLBAPY22.js} +50 -50
  22. package/dist/cli-bundle/chunks/register-list-query-4HIZ7EA3.js +10 -0
  23. package/dist/cli-bundle/chunks/register-mutation-W64FZ5HN.js +20 -0
  24. package/dist/cli-bundle/chunks/register-operations-C44ET7R5.js +2 -0
  25. package/dist/cli-bundle/chunks/{register-setup-NOCOOWQY.js → register-setup-634MFRXU.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-27N23W6M.js +31 -0
  27. package/dist/cli-bundle/focused-chunks/{chunk-4H5IA6AV.js → chunk-2JP7W7GO.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/chunk-2RI2FESN.js +4 -0
  29. package/dist/cli-bundle/focused-chunks/chunk-3BNVU6LC.js +8 -0
  30. package/dist/cli-bundle/focused-chunks/{chunk-2DL3MRTD.js → chunk-3DJSGWVB.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/chunk-6MLIRBLV.js +5 -0
  32. package/dist/cli-bundle/focused-chunks/chunk-6XYLENDV.js +155 -0
  33. package/dist/cli-bundle/focused-chunks/chunk-CP4K4WFS.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/{chunk-TL5M2PAY.js → chunk-E3AVJ655.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/chunk-EBTNSFYE.js +2 -0
  36. package/dist/cli-bundle/focused-chunks/{chunk-J5EKAIVD.js → chunk-HC2OL2H2.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-BKPIY34S.js → chunk-I75MANL7.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-IIS2FRKL.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-NFHFZH2Z.js +2 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-OGC4XSBC.js +17 -0
  41. package/dist/cli-bundle/focused-chunks/{chunk-UDAZWT25.js → chunk-OOYYKQWX.js} +4 -4
  42. package/dist/cli-bundle/focused-chunks/{chunk-23FTGO3W.js → chunk-PKPOZXLL.js} +2 -2
  43. package/dist/cli-bundle/focused-chunks/chunk-QDZUABWL.js +2 -0
  44. package/dist/cli-bundle/focused-chunks/chunk-QQX5TDFT.js +16 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-55BIB6DV.js → chunk-TEZIXMTH.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/chunk-WEH6NSL6.js +18 -0
  47. package/dist/cli-bundle/main.js +13 -13
  48. package/dist/cli-bundle/sdk-authoring.js +1 -1
  49. package/dist/cli-bundle/sdk-contracts.js +1 -1
  50. package/dist/cli-bundle/sdk-core.js +42 -41
  51. package/dist/cli-bundle/sdk-governance.js +1 -1
  52. package/dist/cli-bundle/sdk-graph.js +1 -1
  53. package/dist/cli-bundle/sdk-merge.js +1 -1
  54. package/dist/cli-bundle/sdk-query.js +1 -1
  55. package/dist/cli-bundle/sdk-runtime.js +1 -1
  56. package/dist/cli-bundle/sdk-testing.js +1 -1
  57. package/dist/cli-bundle/sdk.js +2 -2
  58. package/dist/core/checkpoint/checkpoint-gc.js +7 -12
  59. package/dist/core/extensions/activation-summary-markdown.js +5 -4
  60. package/dist/core/extensions/activation-summary.d.ts +2 -0
  61. package/dist/core/extensions/activation-summary.js +6 -2
  62. package/dist/core/extensions/capability-usage.d.ts +1 -1
  63. package/dist/core/extensions/capability-usage.js +4 -3
  64. package/dist/core/extensions/contribution-inventory.d.ts +1 -0
  65. package/dist/core/extensions/contribution-inventory.js +6 -2
  66. package/dist/core/extensions/extension-registries.js +3 -2
  67. package/dist/core/extensions/extension-types.d.ts +83 -1
  68. package/dist/core/extensions/extension-types.js +3 -2
  69. package/dist/core/extensions/loader.d.ts +2 -8
  70. package/dist/core/extensions/loader.js +53 -144
  71. package/dist/core/extensions/registration-values.d.ts +43 -0
  72. package/dist/core/extensions/registration-values.js +210 -0
  73. package/dist/core/extensions/reserved-host-flags.js +3 -2
  74. package/dist/core/extensions/runtime-registrations.d.ts +3 -1
  75. package/dist/core/extensions/runtime-registrations.js +12 -2
  76. package/dist/core/fs/fs-utils.d.ts +2 -0
  77. package/dist/core/fs/fs-utils.js +7 -3
  78. package/dist/core/history/drift-cache.js +4 -3
  79. package/dist/core/history/drift-scan.js +16 -17
  80. package/dist/core/history/history.d.ts +8 -0
  81. package/dist/core/history/history.js +57 -7
  82. package/dist/core/history/replay.d.ts +9 -1
  83. package/dist/core/history/replay.js +48 -13
  84. package/dist/core/lock/lock-gc.js +8 -13
  85. package/dist/core/lock/lock.js +5 -4
  86. package/dist/core/output/output.d.ts +2 -0
  87. package/dist/core/output/output.js +46 -14
  88. package/dist/core/search/vector-stores.js +6 -11
  89. package/dist/core/shared/command-types.d.ts +2 -0
  90. package/dist/core/shared/command-types.js +2 -2
  91. package/dist/core/shared/errors.d.ts +5 -0
  92. package/dist/core/shared/errors.js +2 -2
  93. package/dist/core/store/item-metadata-cache.d.ts +1 -1
  94. package/dist/core/store/item-metadata-cache.js +16 -9
  95. package/dist/core/telemetry/runtime.d.ts +6 -0
  96. package/dist/core/telemetry/runtime.js +101 -30
  97. package/dist/sdk/agent/command-recovery.d.ts +2 -0
  98. package/dist/sdk/agent/command-recovery.js +10 -2
  99. package/dist/sdk/annotations.js +4 -4
  100. package/dist/sdk/cli-bootstrap.d.ts +2 -0
  101. package/dist/sdk/cli-bootstrap.js +6 -2
  102. package/dist/sdk/cli-contracts/completeness.js +10 -3
  103. package/dist/sdk/cli-contracts/enum-contracts.d.ts +1 -1
  104. package/dist/sdk/cli-contracts/flag-contracts.js +9 -3
  105. package/dist/sdk/cli-contracts/registration-helpers.d.ts +20 -3
  106. package/dist/sdk/cli-contracts/registration-helpers.js +18 -12
  107. package/dist/sdk/cli-contracts/schemas/assurance-definition.d.ts +40 -6
  108. package/dist/sdk/cli-contracts/schemas/assurance-definition.js +27 -3
  109. package/dist/sdk/cli-contracts/tool-option-contracts.js +3 -2
  110. package/dist/sdk/cli-contracts/tool-parameter-tables.js +55 -5
  111. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  112. package/dist/sdk/cli-contracts/tool-schema.js +22 -4
  113. package/dist/sdk/cli-program.js +3 -2
  114. package/dist/sdk/comments.js +4 -3
  115. package/dist/sdk/compile-cache.js +4 -3
  116. package/dist/sdk/completion.js +16 -7
  117. package/dist/sdk/compose.d.ts +3 -1
  118. package/dist/sdk/compose.js +16 -2
  119. package/dist/sdk/context-intent-contracts.js +45 -4
  120. package/dist/sdk/context-signal-store.js +4 -3
  121. package/dist/sdk/core.d.ts +1 -0
  122. package/dist/sdk/core.js +3 -2
  123. package/dist/sdk/extension/install-runtime.js +4 -4
  124. package/dist/sdk/extension/managed-state.js +4 -3
  125. package/dist/sdk/extension-contracts.d.ts +1 -1
  126. package/dist/sdk/flag-invocation-contracts.js +5 -6
  127. package/dist/sdk/generated/generated-error-code-catalog-part-1.d.ts +8 -0
  128. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1597 -0
  129. package/dist/sdk/generated/generated-error-code-catalog-part-2.d.ts +8 -0
  130. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1608 -0
  131. package/dist/sdk/generated-error-code-catalog.js +6 -3140
  132. package/dist/sdk/governance/assurance-action.d.ts +27 -3
  133. package/dist/sdk/governance/assurance-action.js +117 -31
  134. package/dist/sdk/governance/assurance-mutation-error.d.ts +14 -0
  135. package/dist/sdk/governance/assurance-mutation-error.js +58 -0
  136. package/dist/sdk/governance/assurance-presets.d.ts +54 -0
  137. package/dist/sdk/governance/assurance-presets.js +136 -0
  138. package/dist/sdk/governance/assurance-runtime.d.ts +4 -0
  139. package/dist/sdk/governance/assurance-runtime.js +118 -14
  140. package/dist/sdk/governance/assurance.d.ts +51 -6
  141. package/dist/sdk/governance/assurance.js +321 -67
  142. package/dist/sdk/governance/gc.js +5 -11
  143. package/dist/sdk/governance/health.d.ts +3 -1
  144. package/dist/sdk/governance/health.js +24 -7
  145. package/dist/sdk/governance/validate-item-reader.js +3 -3
  146. package/dist/sdk/governance/validate.d.ts +2 -0
  147. package/dist/sdk/governance/validate.js +15 -5
  148. package/dist/sdk/graph/durable-cache.d.ts +1 -1
  149. package/dist/sdk/graph/durable-cache.js +156 -29
  150. package/dist/sdk/graph/governance-contracts.d.ts +12 -0
  151. package/dist/sdk/graph/governance-contracts.js +27 -0
  152. package/dist/sdk/graph/governance.d.ts +44 -2
  153. package/dist/sdk/graph/governance.js +156 -12
  154. package/dist/sdk/guide.js +4 -4
  155. package/dist/sdk/index.d.ts +3 -2
  156. package/dist/sdk/index.js +5 -4
  157. package/dist/sdk/lifecycle/claim.js +10 -3
  158. package/dist/sdk/linked-artifacts.d.ts +2 -0
  159. package/dist/sdk/linked-artifacts.js +29 -4
  160. package/dist/sdk/merge/driver.d.ts +2 -2
  161. package/dist/sdk/merge/driver.js +3 -3
  162. package/dist/sdk/merge/install.js +5 -4
  163. package/dist/sdk/merge/receipts.d.ts +6 -4
  164. package/dist/sdk/merge/receipts.js +7 -5
  165. package/dist/sdk/merge/three-way.d.ts +2 -2
  166. package/dist/sdk/merge/three-way.js +3 -3
  167. package/dist/sdk/output-projection.d.ts +9 -1
  168. package/dist/sdk/output-projection.js +45 -9
  169. package/dist/sdk/query/activity.d.ts +49 -2
  170. package/dist/sdk/query/activity.js +185 -38
  171. package/dist/sdk/query/get.js +19 -2
  172. package/dist/sdk/read-output-contracts.d.ts +5 -1
  173. package/dist/sdk/read-output-contracts.js +17 -2
  174. package/dist/sdk/read-output-session.d.ts +2 -0
  175. package/dist/sdk/read-output-session.js +12 -5
  176. package/dist/sdk/relationship-history.js +5 -5
  177. package/dist/sdk/runtime-primitives.d.ts +3 -3
  178. package/dist/sdk/runtime-primitives.js +5 -5
  179. package/dist/sdk/runtime-stats-options.js +3 -2
  180. package/dist/sdk/runtime.js +5 -9
  181. package/dist/sdk/schema-migration.js +4 -4
  182. package/dist/sdk/stats.d.ts +36 -3
  183. package/dist/sdk/stats.js +55 -15
  184. package/dist/sdk/test/execution.d.ts +4 -0
  185. package/dist/sdk/test/execution.js +88 -23
  186. package/dist/sdk/workspace-memory.js +4 -4
  187. package/dist/sdk/workspace-snapshot.js +8 -14
  188. package/dist/sdk/workspace-transaction-gc.js +4 -9
  189. package/dist/sdk/workspace-transaction.js +4 -4
  190. package/dist/sdk/workspace.js +4 -3
  191. package/dist/types.d.ts +2 -0
  192. package/dist/types.js +2 -2
  193. package/docs/ASSURANCE.md +170 -14
  194. package/docs/COMMANDS.md +29 -8
  195. package/docs/CONFIGURATION.md +9 -0
  196. package/docs/DEPENDENCY_KIND_CONTRACT.md +10 -8
  197. package/docs/EXTENSIONS.md +11 -12
  198. package/docs/MERGE_SAFETY.md +3 -1
  199. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +26 -13
  200. package/docs/README.md +2 -0
  201. package/docs/READ_OUTPUT_CONTRACTS.md +17 -2
  202. package/docs/RELATIONSHIP_GRAPH.md +15 -0
  203. package/docs/SDK.md +17 -3
  204. package/docs/SDK_AGENT_SESSION_CONTEXT.md +12 -1
  205. package/docs/SDK_CONTEXT_EVIDENCE_CONTRACTS.md +53 -0
  206. package/docs/SDK_CONTEXT_INTEGRITY.md +28 -1
  207. package/docs/TESTING.md +24 -15
  208. package/docs/TRUSTWORTHY_CONTEXT_EVIDENCE.md +106 -0
  209. package/docs/performance/sdk-entrypoint-import-costs.md +6 -2
  210. package/marketplace.json +2 -2
  211. package/package.json +6 -6
  212. package/packages/pm-beads/package.json +1 -1
  213. package/packages/pm-calendar/package.json +1 -1
  214. package/packages/pm-command-kit/package.json +1 -1
  215. package/packages/pm-digital-twin/package.json +1 -1
  216. package/packages/pm-governance-audit/package.json +1 -1
  217. package/packages/pm-guide-shell/package.json +1 -1
  218. package/packages/pm-kanban/package.json +1 -1
  219. package/packages/pm-lifecycle-hooks/package.json +1 -1
  220. package/packages/pm-linked-test-adapters/package.json +1 -1
  221. package/packages/pm-search-advanced/package.json +1 -1
  222. package/packages/pm-templates/package.json +1 -1
  223. package/packages/pm-todos/package.json +1 -1
  224. package/packages/pm-vcs/package.json +1 -1
  225. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  226. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  227. package/sdk/public-surface.json +384 -83
  228. package/dist/cli-bundle/chunks/chunk-2OOU2AFH.js +0 -8
  229. package/dist/cli-bundle/chunks/chunk-2RA2YTHJ.js +0 -33
  230. package/dist/cli-bundle/chunks/chunk-DSKYD4LG.js +0 -13
  231. package/dist/cli-bundle/chunks/chunk-HC3GM6YY.js +0 -194
  232. package/dist/cli-bundle/chunks/chunk-IUNRVDAI.js +0 -2
  233. package/dist/cli-bundle/chunks/chunk-W7CPZGSB.js +0 -2
  234. package/dist/cli-bundle/chunks/chunk-ZXOC6HG5.js +0 -5
  235. package/dist/cli-bundle/chunks/register-list-query-HAUXVUPY.js +0 -10
  236. package/dist/cli-bundle/chunks/register-mutation-7Y5U7ABT.js +0 -20
  237. package/dist/cli-bundle/chunks/register-operations-EQX3DLAZ.js +0 -2
  238. package/dist/cli-bundle/focused-chunks/chunk-3AYKXMYX.js +0 -2
  239. package/dist/cli-bundle/focused-chunks/chunk-4AHEC74N.js +0 -2
  240. package/dist/cli-bundle/focused-chunks/chunk-4SV3ZKG3.js +0 -16
  241. package/dist/cli-bundle/focused-chunks/chunk-6JW6Z2P5.js +0 -8
  242. package/dist/cli-bundle/focused-chunks/chunk-D4PIWE6D.js +0 -2
  243. package/dist/cli-bundle/focused-chunks/chunk-DPO7TEFX.js +0 -31
  244. package/dist/cli-bundle/focused-chunks/chunk-E3KCL7AY.js +0 -5
  245. package/dist/cli-bundle/focused-chunks/chunk-ERAAFLPS.js +0 -153
  246. package/dist/cli-bundle/focused-chunks/chunk-F6E4JZKG.js +0 -2
  247. package/dist/cli-bundle/focused-chunks/chunk-FPSYO5ZG.js +0 -18
  248. package/dist/cli-bundle/focused-chunks/chunk-KDH5OX3D.js +0 -4
  249. package/dist/cli-bundle/focused-chunks/chunk-OJ3LVVIS.js +0 -2
  250. package/dist/cli-bundle/focused-chunks/chunk-VABB4L5Y.js +0 -17
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)
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
 
@@ -22,20 +22,27 @@ The assurance SDK keeps those semantics independent from presentation. Commander
22
22
 
23
23
  Measurements support these built-in sources:
24
24
 
25
- | Source | Purpose |
26
- | --- | --- |
27
- | `items` | Count items matching status, type, tags, or an exact metadata field. |
28
- | `dependency_kind` | Count typed relationship edges such as `blocked_by` or `verifies`. |
29
- | `graph` | Select a numeric or labelled-set field from a public graph SDK result. |
30
- | `validate` | Select a validator check status or numeric detail. |
31
- | `health` | Select a health check status, numeric detail, or labelled set. |
32
- | `history` | Count immutable events by operation, author, harness, or model. |
33
- | `links` | Count items with present or missing file, test, or documentation evidence. |
34
- | `derived` | Combine numeric measurements with deterministic arithmetic and cycle detection. |
35
- | `provider` | Delegate a measurement to an explicitly supplied host/package resolver. |
25
+ | Source | Purpose |
26
+ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
27
+ | `items` | Count authoritative full item records matching status, type, tags, an exact metadata field, or a field `state` of `present`/`missing`. |
28
+ | `dependency_kind` | Count typed relationship edges such as `blocked_by` or `verifies`; accepted aliases and canonical spellings measure the same population. |
29
+ | `graph` | Select a numeric or labelled-set field from a public graph SDK result. |
30
+ | `validate` | Select a validator check status or numeric detail. |
31
+ | `health` | Select a health check status, numeric detail, or labelled set. |
32
+ | `history` | Count immutable events by operation, author, harness, or model. |
33
+ | `links` | Count items with present or missing file, test, or documentation evidence. |
34
+ | `derived` | Combine numeric measurements with deterministic arithmetic and cycle detection. |
35
+ | `provider` | Delegate a measurement to an explicitly supplied host/package resolver. |
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
+
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.
45
+
39
46
  Assertions require exactly one polarity:
40
47
 
41
48
  - `ceiling`, `floor`, `equals`, or `zero`
@@ -84,6 +91,152 @@ pm assurance remove gate release-readiness
84
91
 
85
92
  Referenced measurements and assertions cannot be removed. Remove the consuming gate or assertion first.
86
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
+
87
240
  ## SDK and MCP
88
241
 
89
242
  The reusable client exposes the same action grammar:
@@ -101,9 +254,9 @@ await pm.assurance({
101
254
  });
102
255
  ```
103
256
 
104
- 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.
105
258
 
106
- 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:
107
260
 
108
261
  ```bash
109
262
  pm contracts --action assurance --schema-only --json
@@ -117,10 +270,13 @@ A gate emits one object containing:
117
270
  - gate id, evaluated tree, trigger, timestamp, and dry-run status;
118
271
  - overall `pass`, `warn`, or `block` plus stable exit code;
119
272
  - every assertion's measurement, scope, population, observed value, structured bound, signed distance, enforcement, negative-control proof, cost, and contributors;
273
+ - every assertion's `measurement_definition_fingerprint`, a SHA-256 identity for the exact declaration that produced the observation;
120
274
  - an aggregate compute receipt.
121
275
 
122
276
  Dry runs never write history. Non-dry verdicts are immutable workspace audit events and remain queryable after ordinary registry changes. Verdict reads return newest entries first and default to a bounded result; use `--limit` to select up to 1,000 matching records. A blocking verdict exits non-zero; warnings and observations remain successful while preserving their failed assertion rows.
123
277
 
278
+ Verdicts persisted before definition fingerprints were introduced remain readable and are identifiable by the absence of `measurement_definition_fingerprint`. Registry mutations and verdict writes use the same explicit-author, configured-author, and detected-harness precedence as other SDK mutations; they do not manufacture an `unknown` author when a harness identity is available.
279
+
124
280
  ## Safety and Evolution
125
281
 
126
282
  - Use stable lowercase ids; prose belongs in descriptions and mutation messages.
package/docs/COMMANDS.md CHANGED
@@ -19,7 +19,9 @@ pm contracts --command <command> --flags-only --json
19
19
  - Use `pm contracts --summary --json` for the cheapest command map, then narrow with command-scoped contracts.
20
20
  - Every mutation writes history.
21
21
 
22
- Tracked documentation work: [pm-u9d0](../.agents/pm/epics/pm-u9d0.toon).
22
+ Tracked documentation work: [pm-u9d0](../.agents/pm/epics/pm-u9d0.toon),
23
+ [pm-7nqo6b](../.agents/pm/issues/pm-7nqo6b.toon), and
24
+ [pm-j1r8gl](../.agents/pm/issues/pm-j1r8gl.toon).
23
25
 
24
26
  ## Command Families
25
27
 
@@ -941,17 +943,25 @@ pm history-repair <id> --dry-run
941
943
  pm history-repair <id> --message "re-anchor legacy drift"
942
944
  pm history-repair --all --dry-run
943
945
  pm history-repair --all --message "bulk re-anchor drifted streams"
944
- pm activity --id <id> --limit 50
946
+ pm activity # item digest for the last 24 hours
947
+ pm activity --raw --id <id> --limit 50
945
948
  pm activity --full --id <id> --limit 50
946
949
  pm activity --full --unbounded
947
950
  pm restore <id> <timestamp-or-version>
948
951
  ```
949
952
 
950
953
  History is append-only. Restore appends a new restore event instead of rewriting old history.
951
- Bare `pm activity` is bounded to 20 compact rows; direct SDK calls default to
952
- five full rows. Every result reports total and omitted counts plus the applied
953
- bound. Use `--limit` for a deliberate cap, or `--unbounded` for an explicitly
954
- unlimited read; the two flags are mutually exclusive.
954
+ Bare `pm activity` is an item-centric digest for the last 24 hours, bounded to
955
+ 15 most-recently-touched items. Each row joins current `id`, `type`, `status`,
956
+ and title with the matching event count, first/last timestamps, and a bounded
957
+ operation histogram. `activity_summary` states the effective window plus event,
958
+ item, author, and operation counts, so an empty digest is distinguishable from
959
+ a bounded one. Use `--raw` for the legacy compact per-event stream, `--compact`
960
+ as its compatibility spelling, `--full` for events with patch payloads, or
961
+ `--provenance` for patch-free provenance rows. Every mode reports total and
962
+ omitted row counts plus the applied bound. Use `--limit` for a deliberate cap,
963
+ or `--unbounded` for an explicitly unlimited read; the two flags are mutually
964
+ exclusive.
955
965
  Workspace-scoped mutations to settings, schema, profiles, init state, and
956
966
  extension/package activation are recorded in
957
967
  `.agents/pm/history/_workspace.jsonl` using the same patch/hash format.
@@ -962,10 +972,21 @@ workspace auditing and is created on the first audited singleton mutation.
962
972
 
963
973
  `--diff` replays the history chain and emits, per entry, a `changes` array of `{ field, before, after }` field-level value transitions (alongside the `changed_fields` name list) — so you can see exactly what each field changed from and to without comparing snapshots. It is independent of the compact/full projection. `--field <name>` narrows the diff to a single field's transitions (implying `--diff`), answering "when did `<field>` change?" — e.g. `pm history <id> --diff --field status`.
964
974
 
965
- `pm stats` reports item and history totals plus per-type/per-status counts. Add `--storage` for aggregate history-stream metrics — `total_streams`, `total_lines`, `total_bytes`, the top streams by size (`largest_by_bytes`) and by depth (`deepest_by_lines`), and the global `oldest_entry`/`newest_entry` — to decide when to compact or redact streams and to plan storage:
975
+ `pm stats` reports item and history totals plus a screen-sized lifecycle table.
976
+ Every non-empty item type is one row with `total`, `open`, `in_progress`,
977
+ `blocked`, `draft`, `closed`, `canceled`, and `other` counts. Empty registered
978
+ types and statuses are suppressed and their combined count is retained in the
979
+ `omitted_zero_buckets` scalar. The default TOON projection is gated at no more
980
+ than 22 lines on the representative release fixture. Add `--include-empty` to
981
+ restore every zero-filled type and status for schema-governance dashboards.
982
+ Add `--storage` for aggregate history-stream metrics — `total_streams`,
983
+ `total_lines`, `total_bytes`, the top streams by size (`largest_by_bytes`) and
984
+ by depth (`deepest_by_lines`), and the global `oldest_entry`/`newest_entry` — to
985
+ decide when to compact or redact streams and to plan storage:
966
986
 
967
987
  ```bash
968
988
  pm stats
989
+ pm stats --include-empty
969
990
  pm stats --storage --json
970
991
  pm stats --metadata-coverage --json
971
992
  pm stats --field-utilization --json
@@ -973,7 +994,7 @@ pm stats --by-assignee --by-priority
973
994
  pm stats --by-tag --tag-prefix domain: --json
974
995
  ```
975
996
 
976
- For governance dashboards, `--metadata-coverage` adds a `metadata_coverage` block reporting per-field `present`/`applicable`/`percent` for `acceptance_criteria`, `estimated_minutes`, `resolution`, `tags`, and `parent` — overall and `by_type` (resolution coverage is scoped to terminal items, its only applicable population). `--field-utilization` adds a `field_utilization` block reporting `present`/`total`/`percent` for each content field (`notes`, `learnings`, `files`, `docs`, `tests`, `comments`, `deps`, `body`, `linked_command`) across all items, so under-documented content dimensions are visible at a glance and pair naturally with the `--has-*`/`--no-*` list filters for drill-down. `--by-assignee`, `--by-tag`, and `--by-priority` add a `breakdowns` block with lifecycle-bucketed rows (`open`/`in_progress`/`blocked`/`draft`/`closed`/`canceled`/`other` + `total`) per group; blank keys render an explicit `(unassigned)`/`(untagged)` label. `--by-tag` accepts `--tag-prefix` to restrict counting to a tag namespace (for example `domain:`). All of these sections are gated behind their flags so the default `pm stats` stays token-light; the per-status/per-type distributions (already in `by_status`/`by_type`) zero-fill every configured state so underutilized lifecycle states and item types are visible at a glance.
997
+ For governance dashboards, `--metadata-coverage` adds a `metadata_coverage` block reporting per-field `present`/`applicable`/`percent` for `acceptance_criteria`, `estimated_minutes`, `resolution`, `tags`, and `parent` — overall and `by_type` (resolution coverage is scoped to terminal items, its only applicable population). `--field-utilization` adds a `field_utilization` block reporting `present`/`total`/`percent` for each content field (`notes`, `learnings`, `files`, `docs`, `tests`, `comments`, `deps`, `body`, `linked_command`) across all items, so under-documented content dimensions are visible at a glance and pair naturally with the `--has-*`/`--no-*` list filters for drill-down. `--by-assignee`, `--by-tag`, and `--by-priority` add a `breakdowns` block with lifecycle-bucketed rows (`open`/`in_progress`/`blocked`/`draft`/`closed`/`canceled`/`other` + `total`) per group; blank keys render an explicit `(unassigned)`/`(untagged)` label. `--by-tag` accepts `--tag-prefix` to restrict counting to a tag namespace (for example `domain:`). All of these sections are gated behind their flags so the default `pm stats` remains readable at first glance.
977
998
  `history-redact` rewrites matching history payloads deterministically, recomputes hash chains, and appends an auditable `history_redact` marker entry when changes are applied. Its result reports only `literal_count`, `regex_count`, `total_count`, and whether the default replacement was selected; literal values, regex source text, and replacement text are never echoed in CLI, SDK, MCP, recovery, profile, or telemetry output. Treat regex text as sensitive input because it can contain the exact material being removed.
978
999
  `history-compact` rewrites long streams into a synthetic checkpoint baseline plus a retained tail (`--before` accepts a 1-based version or ISO timestamp), re-anchors hashes, verifies integrity, and appends an auditable `history_compact` marker when applied.
979
1000
  `history-compact` bulk mode (mutually exclusive with a positional `<id>`) compacts many streams in one audited pass. Select with `--ids <a,b,c>` (an explicit list — used on its own, not combined with the scan selectors below), or a scan: `--all-over <N>` (every stream with more than N entries) and/or a lifecycle filter `--closed` (terminal items only) or `--all-streams` (every stream). `--closed` and `--all-streams` are mutually exclusive. `--min-entries <N>` (default 3) skips already-compact streams; when `history.compact_policy` is enabled and `--all-over` is omitted, the policy's `max_entries` becomes the default threshold. `--before` is single-id only and is rejected in bulk mode. Each selected stream runs the same single-item compaction; one failing stream never aborts the rest — the result reports `totals` (`streams_considered`/`selected`/`items_compacted`/`items_skipped`/`items_errored`) plus one row per stream (`compacted`/`skipped` with a `skip_reason`/`errored`), and the command exits non-zero only if any stream errored.
@@ -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.
@@ -183,6 +185,13 @@ Crash and error diagnostics are reported to Sentry only when telemetry is enable
183
185
 
184
186
  > Sentry is hard-disabled under Vitest (`VITEST` / `VITEST_WORKER_ID`), so these knobs are no-ops inside the test suite.
185
187
 
188
+ Linked commands launched by `pm test --run` default
189
+ `SENTRY_ENVIRONMENT=test`, even when the parent process is classified as
190
+ production. This keeps sandbox failures out of production diagnostics. An
191
+ explicit run-level or per-test `env_set=SENTRY_ENVIRONMENT=<name>` directive
192
+ still overrides the default when a test intentionally targets another
193
+ environment.
194
+
186
195
  ## Item Storage Format
187
196
 
188
197
  TOON is the default:
@@ -1,15 +1,15 @@
1
1
  # Dependency-kind contract
2
2
 
3
- Tracker: [pm-4020c5](../.agents/pm/issues/pm-4020c5.toon), [pm-jkbqt8](../.agents/pm/issues/pm-jkbqt8.toon)
3
+ Tracker: [pm-4020c5](../.agents/pm/issues/pm-4020c5.toon), [pm-jkbqt8](../.agents/pm/issues/pm-jkbqt8.toon), [pm-q6n8sj](../.agents/pm/issues/pm-q6n8sj.toon)
4
4
 
5
5
  Dependency rows have one canonical stored spelling per relationship meaning. Command inputs remain compatibility-friendly: hyphens normalize to underscores and the aliases below are accepted, but `pm create` and `pm update` persist the canonical kind. Existing historical rows are never rewritten implicitly.
6
6
 
7
- | Canonical kind | Accepted legacy aliases |
8
- | --- | --- |
9
- | `blocked_by` | `depends_on`, `depends-on` |
10
- | `related` | `related_to`, `related-to` |
11
- | `parent` | `child_of`, `child-of`, `epic` |
12
- | `child` | `parent_child`, `parent-child`, `task` |
7
+ | Canonical kind | Accepted legacy aliases |
8
+ | -------------- | -------------------------------------- |
9
+ | `blocked_by` | `depends_on`, `depends-on` |
10
+ | `related` | `related_to`, `related-to` |
11
+ | `parent` | `child_of`, `child-of`, `epic` |
12
+ | `child` | `parent_child`, `parent-child`, `task` |
13
13
 
14
14
  `epic` and `task` are compatibility aliases, not item types embedded in the relationship ontology. New integrations should use `parent` or `child` and express the work classification through the item `type` field.
15
15
 
@@ -26,4 +26,6 @@ Readiness, `pm next`, context blocker summaries, `pm list-blocked`, downstream `
26
26
 
27
27
  ## Legacy observability
28
28
 
29
- `pm deps` returns `legacy_alias_counts` for the workspace. `pm graph audit` returns the same field beside the canonical `profile.edges_by_kind` counts. Empty objects mean no stored alias debt. These diagnostics are read-only; terminal history remains untouched until an explicitly governed migration is requested.
29
+ `pm deps` returns `legacy_alias_counts` for the workspace. `pm graph audit` returns the same field beside canonical `profile.edges_by_kind` counts, `profile.edge_share_by_kind` composition ratios, and the `semantic_edges`/`semantic_edge_share` context-preservation census. The semantic census counts `discovered_from`, `incident_from`, `supersedes`, and `verifies` over all deduplicated directed edges. Empty objects and zero shares are explicit, not omitted. These diagnostics are read-only; terminal history remains untouched until an explicitly governed migration is requested.
30
+
31
+ Assurance `dependency_kind` measurements canonicalize both the declaration and stored row before comparing. A declaration using `related` and one using the accepted `related_to` alias therefore measure the same edge population; alias debt remains separately observable through `legacy_alias_counts`.
@@ -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
@@ -61,7 +61,7 @@ pm merge install --dry-run --json
61
61
  | tracker `**/*.jsonl` except the later `history/*.jsonl` override | `pm-relationship` | Covers default and package-owned custom relationship event paths, unions divergent suffixes by `eventId` (timestamp-ordered, ours-first on ties), and renumbers `sequence` consecutively so the strict-sequence store loader accepts the merged stream. |
62
62
  | root `settings.json` and nested `**/*.json` | `pm-json` | Recursively merges objects per key. Arrays compose when both branches preserve the base and add distinct entries, so independent extension installs and evaluation additions merge without weakening edit/removal conflict detection. |
63
63
 
64
- When both sides change the same item scalar differently, the driver writes the same stable value regardless of which branch Git labels ours or theirs, but exits nonzero. JSON leaf conflicts retain the explicit preferred-side policy. Git keeps either path conflicted so a human or coordinating agent must review the discarded value and explicitly `git add` the resolution.
64
+ When both sides change the same item scalar differently, the driver writes the same stable value regardless of which branch Git labels ours or theirs, but exits nonzero. Item results and receipts expose the caller's `requested_preference`; the per-decision `retained` and `discarded` values or hashes are authoritative because stable value order can retain either side. Readers normalize the legacy receipt key `preferred`, while new receipts no longer emit it. JSON leaf conflicts retain the explicit preferred-side policy. Git keeps either path conflicted so a human or coordinating agent must review the discarded value and explicitly `git add` the resolution. This correction is tracked by [pm-qckpnq](../.agents/pm/issues/pm-qckpnq.toon).
65
65
 
66
66
  The driver result's `guidance` always points unresolved conflicts to `pm merge report`. When a clone-local receipt exists, guidance includes its privacy-safe receipt and item ids for exact correlation; discarded values remain confined to the local receipt and never appear in generic logs or tracker history. Tracked by [pm-fbrz7p](../.agents/pm/issues/pm-fbrz7p.toon).
67
67
 
@@ -133,6 +133,8 @@ pm merge reconcile --dry-run --json
133
133
 
134
134
  `history-repair` records the reconciliation patch and classifies its changed fields against the final item. Append-only collection unions and deterministic reordering are reported as preserved context without a data-loss warning. Fields whose replayed values are actually removed or replaced remain loud with discarded event authors/operations and recovery guidance. Re-apply any intended losing mutation as a normal `pm update` so it remains explicit and auditable.
135
135
 
136
+ History events now declare an item-hash epoch. The current epoch preserves linked-test insertion order; unversioned streams are verified against both the legacy sorted-test canonicalization and the order-preserving canonicalization. An unknown explicit epoch is reported as `unsupported_item_hash_version` and repair refuses to guess. This keeps version incompatibility distinct from item corruption and is tracked by [pm-2htk4p](../.agents/pm/issues/pm-2htk4p.toon).
137
+
136
138
  ## Delete versus modify policy
137
139
 
138
140
  A delete on one branch and an edit on another is not safely resolvable by a generic file driver. The merged workspace must not silently resurrect the item. `storage_integrity` reports the live document plus delete-terminated history as a hard finding. The coordinator chooses one policy explicitly:
@@ -12,7 +12,10 @@ cursor-chain amortization are tracked by
12
12
  [pm-yekkvt](../.agents/pm/issues/pm-yekkvt.toon), and
13
13
  [pm-sf31yl](../.agents/pm/issues/pm-sf31yl.toon). Trustworthy collection
14
14
  selectors are tracked by
15
- [pm-x710qm](../.agents/pm/issues/pm-x710qm.toon).
15
+ [pm-x710qm](../.agents/pm/issues/pm-x710qm.toon). Default contract suppression
16
+ and canonical TOON tables are tracked by
17
+ [pm-gjjurs](../.agents/pm/issues/pm-gjjurs.toon) and
18
+ [pm-5y05kq](../.agents/pm/issues/pm-5y05kq.toon).
16
19
 
17
20
  ## Agent Quick Context
18
21
 
@@ -38,12 +41,13 @@ explicit receipt with `has_omissions: false`,
38
41
 
39
42
  Mutually exclusive output modes emit only their active row collection:
40
43
 
41
- | Command mode | Active row key | Withheld group | Restore |
42
- | --------------------------- | ------------------ | -------------- | ---------------- |
43
- | `activity --compact` | `compact_activity` | `provenance` | `--full` |
44
- | `activity --full` | `activity` | none | already complete |
45
- | `history` (compact default) | `compact_history` | `raw_history` | `--full` |
46
- | `history --full` | `history` | none | already complete |
44
+ | Command mode | Active row key | Withheld group | Restore |
45
+ | --------------------------- | --------------------- | -------------- | ---------------- |
46
+ | `activity` (digest default) | `activity_digest` | `event_rows` | `--raw` |
47
+ | `activity --raw/--compact` | `compact_activity` | `provenance` | `--full` |
48
+ | `activity --full` | `activity` | none | already complete |
49
+ | `history` (compact default) | `compact_history` | `raw_history` | `--full` |
50
+ | `history --full` | `history` | none | already complete |
47
51
 
48
52
  Inactive row keys are omitted, not zero-filled. This makes a wrong parser loud:
49
53
  reading `.activity` from compact activity now yields a missing key instead of a
@@ -57,8 +61,11 @@ integrations and built-in output cannot drift independently.
57
61
 
58
62
  ## Universal Read Rows
59
63
 
60
- Core read results expose a `row_contract` whether or not the current page has
61
- rows:
64
+ Core read results expose `row_contract` only when callers request
65
+ `--output-row-contract` / `outputRowContract: true`. Keeping discovery metadata
66
+ off by default makes ordinary reads pay for project data rather than repeating
67
+ the same selector declaration. The explicit contract remains available whether
68
+ or not the current page has rows:
62
69
 
63
70
  ```json
64
71
  {
@@ -67,7 +74,8 @@ rows:
67
74
  "row_kind": "collection",
68
75
  "row_keys": ["items"],
69
76
  "fields": "supported",
70
- "jq_selector": ".row_contract.row_keys[] as $key | getpath($key | split(\".\")) | if type == \"array\" then .[] else if type == \"object\" then to_entries[] else empty end end"
77
+ "jq_selector": ".row_contract.row_keys[] as $key | getpath($key | split(\".\")) | if type == \"array\" then .[] else if type == \"object\" then to_entries[] else empty end end",
78
+ "toon_encoding": "tabular_when_uniform"
71
79
  }
72
80
  }
73
81
  ```
@@ -80,7 +88,12 @@ collections declare every active dot-delimited path. This keeps
80
88
  nested dependency graph and relationship-context rows addressable as
81
89
  `graph.nodes`, `graph.edges`, `context.nodes`, and `context.edges` without
82
90
  duplicating them at the envelope root. Array collections produce their
83
- elements; object maps such as stats counts produce jq `to_entries` rows.
91
+ elements; object maps produce jq `to_entries` rows.
92
+ `toon_encoding: "tabular_when_uniform"` declares that an array of flat objects
93
+ with one shared key set renders as a length-marked TOON table; mixed, nested,
94
+ or heterogeneous arrays retain the expanded representation. Quoted,
95
+ separator-bearing, and multiline values use the canonical TOON encoder and
96
+ round-trip through the strict decoder.
84
97
  Commands without a row collection, including a dependency tree or leaf `get`,
85
98
  declare `row_kind: "none"`, an empty `row_keys` array, and omit `jq_selector`.
86
99
  The absence is therefore distinguishable from a legitimate empty collection.
@@ -94,8 +107,8 @@ publish a row contract.
94
107
  SDK and package authors can import `PM_READ_ROW_CONTRACTS`,
95
108
  `PM_READ_ROW_JQ_SELECTOR`, and `resolveReadRowContract` from
96
109
  `@unbrained/pm-cli/sdk`. Existing package declarations are preserved only
97
- when `command`, `row_kind`, `row_keys`, `fields`, and the conditional
98
- `jq_selector` form a structurally valid row contract; malformed declarations
110
+ when `command`, `row_kind`, `row_keys`, `fields`, the conditional
111
+ `jq_selector`, and any supplied `toon_encoding` form a structurally valid row contract; malformed declarations
99
112
  are replaced by the canonical built-in contract when one applies.
100
113
 
101
114
  ## Self-Describing SDK Projections
package/docs/README.md CHANGED
@@ -54,7 +54,9 @@ pm guide release --json
54
54
  - [SDK Agent Session and Episode Context](SDK_AGENT_SESSION_CONTEXT.md) - inherited role/topic context, cross-process episode identity, and deterministic history grouping.
55
55
  - [Improvement Ledger and History Analytics](IMPROVEMENT_ANALYTICS.md) - audited quantitative observations, live provenance coverage, and bounded observational fleet outcomes.
56
56
  - [Project Assurance Primitives](ASSURANCE.md) - SDK-owned measurements, assertions, lifecycle gates, cost receipts, and durable verdict history shared by CLI and MCP.
57
+ - [Trustworthy Context and Evidence Contracts](TRUSTWORTHY_CONTEXT_EVIDENCE.md) - full-record assurance, graph composition, boolean health rows, lossless linked-test removal, and role-labelled recovery.
57
58
  - [SDK Evidence Traceability and Integrity](SDK_EVIDENCE_TRACEABILITY.md) - reverse source-to-item lookup, atomic evidence replacement, no-op history, linked-test collision classification, and telemetry drain receipts.
59
+ - [SDK Context and Evidence Contracts](SDK_CONTEXT_EVIDENCE_CONTRACTS.md) - material omission receipts, scoped preflight activation, truthful merge preference, claim-race classification, and versioned history hashes.
58
60
  - [Reproducible Workspaces and Snapshots](REPRODUCIBLE_WORKSPACES.md) - deterministic SDK recipes and content-addressed authoritative tracker restore points.
59
61
  - [Portable Corpus Shapes](CORPUS_SHAPES.md) - versioned SDK populations for realistic benchmarks, evaluations, and package tests.
60
62
  - [Agent UX Contracts](AGENT_UX_CONTRACTS.md) - ordering-cycle advisories, graph count units, collision safety, compact context, ownership wording, and recovery behavior.
@@ -1,6 +1,6 @@
1
1
  # Universal Read Output Contracts
2
2
 
3
- Tracker references: [pm-hb7ug8](../.agents/pm/features/pm-hb7ug8.toon), [pm-cxr0jb](../.agents/pm/features/pm-cxr0jb.toon), [pm-hid9g1](../.agents/pm/features/pm-hid9g1.toon), and [pm-sb0tns](../.agents/pm/issues/pm-sb0tns.toon).
3
+ Tracker references: [pm-hb7ug8](../.agents/pm/features/pm-hb7ug8.toon), [pm-cxr0jb](../.agents/pm/features/pm-cxr0jb.toon), [pm-hid9g1](../.agents/pm/features/pm-hid9g1.toon), [pm-sb0tns](../.agents/pm/issues/pm-sb0tns.toon), and [pm-gjjurs](../.agents/pm/issues/pm-gjjurs.toon).
4
4
 
5
5
  ## Agent Quick Context
6
6
 
@@ -19,6 +19,10 @@ Row shaping follows each envelope's `row_contract.row_keys`, including
19
19
  dot-delimited nested arrays and object maps such as `graph.nodes`. Include,
20
20
  amount, repeat suppression, and cost compaction therefore operate on the same
21
21
  machine-declared rows; they do not rely on command-specific top-level keys.
22
+ The runtime uses that declaration internally on every read but omits the
23
+ repeated metadata from results by default. Request
24
+ `--output-row-contract` / `outputRowContract: true` when a consumer needs the
25
+ row paths, jq selector, and active TOON encoding contract.
22
26
 
23
27
  ## Cross-Call Context Sessions
24
28
 
@@ -46,6 +50,15 @@ tokens separately when the remaining group allowance is smaller than the
46
50
  minimum control envelope, plus the accumulated spend, remaining capacity,
47
51
  newly served items, and suppressed repeats.
48
52
 
53
+ The carried served-item set accepts at most 10,000 identities. A receipt always
54
+ preserves identities already present in the supplied state and retains newly
55
+ served identities in deterministic order until that capacity is full. When a
56
+ single read crosses the boundary, `seen_item_overflow_count` reports how many
57
+ new identities were not carried forward; those facts remain in the current
58
+ envelope and may be served in full again on a later read. The emitted
59
+ `next_state` therefore always remains valid input to the next call without
60
+ silently widening the safety bound.
61
+
49
62
  Session state is deliberately caller-carried: CLI processes, SDK clients, MCP
50
63
  hosts, and packages share the same deterministic primitive without a hidden
51
64
  daemon or mutable cache. Validation rejects unknown fields, invalid identifiers,
@@ -69,12 +82,13 @@ tracker content.
69
82
 
70
83
  ## Precedence and Compatibility
71
84
 
72
- Resolution is deterministic: canonical controls win over command-local compatibility options, which win over intent defaults, which win over command defaults. Existing options such as `--fields`, `--limit`, `--token-budget`, `--format`, `--brief`, and `--full` remain accepted. Contract output marks them as hidden compatibility aliases and supplies a migration hint; traversal, cursor, side-effect, and streaming controls instead receive an explicit behavior-preservation hint because a static output control cannot replace their semantics. Callers that omit the canonical controls receive the byte-identical established result.
85
+ Resolution is deterministic: canonical controls win over command-local compatibility options, which win over intent defaults, which win over command defaults. Existing options such as `--fields`, `--limit`, `--token-budget`, `--format`, `--brief`, and `--full` remain accepted. Contract output marks them as hidden compatibility aliases and supplies a migration hint; traversal, cursor, side-effect, and streaming controls instead receive an explicit behavior-preservation hint because a static output control cannot replace their semantics. Callers that omit the four shaping dimensions retain the established data projection; the one intentional envelope correction is that repeated `row_contract` metadata is now opt-in.
73
86
 
74
87
  ```bash
75
88
  pm list-open --output-include id,title,status --output-limit 10
76
89
  pm context --for orient --output-budget 900 --output-format toon
77
90
  pm search "runtime contracts" --output-limit 5 --output-format json
91
+ pm stats --output-row-contract
78
92
  pm contracts --full --json
79
93
  ```
80
94
 
@@ -93,6 +107,7 @@ const result = await pm.list({
93
107
  outputInclude: "id,title,status",
94
108
  outputLimit: 10,
95
109
  outputBudget: 800,
110
+ outputRowContract: true,
96
111
  outputSession: {
97
112
  version: 1,
98
113
  id: "orientation",