@unbrained/pm-cli 2026.8.8 → 2026.8.10

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 (222) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +59 -0
  3. package/dist/cli/commander-usage.js +17 -12
  4. package/dist/cli/help-json-payload.js +51 -16
  5. package/dist/cli/main.js +7 -6
  6. package/dist/cli-bundle/bundle-manifest.json +162 -162
  7. package/dist/cli-bundle/chunks/chunk-2QYH4QMI.js +30 -0
  8. package/dist/cli-bundle/chunks/{chunk-2ZMXX3Q6.js → chunk-EBO7OUUE.js} +70 -70
  9. package/dist/cli-bundle/chunks/{chunk-NUXRRNAB.js → chunk-ETML5C6H.js} +48 -48
  10. package/dist/cli-bundle/chunks/chunk-JUDU4RO4.js +194 -0
  11. package/dist/cli-bundle/chunks/chunk-JZSNADRR.js +5 -0
  12. package/dist/cli-bundle/chunks/chunk-MENWVGJW.js +2 -0
  13. package/dist/cli-bundle/chunks/{chunk-ALDHXLPS.js → chunk-S4EITGLA.js} +2 -2
  14. package/dist/cli-bundle/chunks/chunk-TIGKWVLA.js +13 -0
  15. package/dist/cli-bundle/chunks/{chunk-CBLA33YK.js → chunk-TRWXF476.js} +2 -2
  16. package/dist/cli-bundle/chunks/chunk-TSLDTJLQ.js +2 -0
  17. package/dist/cli-bundle/chunks/{register-list-query-WFZAMJHZ.js → register-list-query-OJ6ZXKV4.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-mutation-SOKKOTHQ.js → register-mutation-DKI7QC7O.js} +2 -2
  19. package/dist/cli-bundle/chunks/register-operations-P5OGMT6I.js +2 -0
  20. package/dist/cli-bundle/chunks/{register-setup-2HL5ZHMK.js → register-setup-S3KT2LTH.js} +2 -2
  21. package/dist/cli-bundle/focused-chunks/{chunk-ANTMGYLI.js → chunk-2COZ67XT.js} +2 -2
  22. package/dist/cli-bundle/focused-chunks/chunk-2EXI2DKR.js +4 -0
  23. package/dist/cli-bundle/focused-chunks/{chunk-YSAHYUXX.js → chunk-34SX654S.js} +2 -2
  24. package/dist/cli-bundle/focused-chunks/chunk-36FTZ2NT.js +2 -0
  25. package/dist/cli-bundle/focused-chunks/chunk-3CQECMKR.js +16 -0
  26. package/dist/cli-bundle/focused-chunks/chunk-75774JWP.js +6 -0
  27. package/dist/cli-bundle/focused-chunks/chunk-7UN6BHHO.js +2 -0
  28. package/dist/cli-bundle/focused-chunks/{chunk-TCSAPH33.js → chunk-CHIBMEZR.js} +6 -6
  29. package/dist/cli-bundle/focused-chunks/chunk-DZGAHZZV.js +3 -0
  30. package/dist/cli-bundle/focused-chunks/chunk-FF4MNAYB.js +153 -0
  31. package/dist/cli-bundle/focused-chunks/chunk-GHTN2ILR.js +2 -0
  32. package/dist/cli-bundle/focused-chunks/chunk-I4YM2I7S.js +2 -0
  33. package/dist/cli-bundle/focused-chunks/chunk-IMDTEN7K.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/chunk-IW43E4XL.js +2 -0
  35. package/dist/cli-bundle/focused-chunks/chunk-JMVA4HKR.js +5 -0
  36. package/dist/cli-bundle/focused-chunks/{chunk-PL2EYDJK.js → chunk-KR5RVXLQ.js} +31 -29
  37. package/dist/cli-bundle/focused-chunks/chunk-KSNJG4KI.js +16 -0
  38. package/dist/cli-bundle/focused-chunks/chunk-M6RPIXKC.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-MXYSIQ56.js +18 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-RIIN5V4N.js +2 -0
  41. package/dist/cli-bundle/focused-chunks/chunk-RKWSCYYN.js +2 -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 +38 -38
  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/diagnostics/remediation.js +19 -9
  54. package/dist/core/fs/fs-utils.d.ts +10 -0
  55. package/dist/core/fs/fs-utils.js +16 -4
  56. package/dist/core/history/history.js +13 -4
  57. package/dist/core/item/actionability.d.ts +7 -1
  58. package/dist/core/item/actionability.js +71 -15
  59. package/dist/core/item/item-format.js +3 -32
  60. package/dist/core/schema/fields-file.d.ts +1 -2
  61. package/dist/core/schema/fields-file.js +4 -11
  62. package/dist/core/schema/runtime-schema.js +3 -10
  63. package/dist/core/schema/status-defs-file.d.ts +1 -2
  64. package/dist/core/schema/status-defs-file.js +4 -32
  65. package/dist/core/schema/status-token.d.ts +2 -0
  66. package/dist/core/schema/status-token.js +32 -0
  67. package/dist/core/schema/type-workflows.d.ts +1 -2
  68. package/dist/core/schema/type-workflows.js +4 -11
  69. package/dist/core/shared/author.d.ts +25 -0
  70. package/dist/core/shared/author.js +128 -10
  71. package/dist/core/shared/errors.d.ts +4 -0
  72. package/dist/core/shared/errors.js +2 -2
  73. package/dist/core/shared/text-normalization.d.ts +1 -1
  74. package/dist/core/shared/text-normalization.js +2 -2
  75. package/dist/sdk/actionability.d.ts +1 -1
  76. package/dist/sdk/actionability.js +3 -3
  77. package/dist/sdk/agent/command-recovery.d.ts +4 -0
  78. package/dist/sdk/agent/command-recovery.js +52 -0
  79. package/dist/sdk/agent/command-suggestions.d.ts +4 -0
  80. package/dist/sdk/agent/command-suggestions.js +67 -0
  81. package/dist/sdk/agent/create-option-policy.d.ts +12 -0
  82. package/dist/sdk/agent/create-option-policy.js +32 -0
  83. package/dist/sdk/agent/item-addressing.d.ts +19 -0
  84. package/dist/sdk/agent/item-addressing.js +208 -0
  85. package/dist/sdk/cli-bootstrap.d.ts +1 -1
  86. package/dist/sdk/cli-bootstrap.js +20 -5
  87. package/dist/sdk/cli-contracts/commander-mutation-options.js +16 -4
  88. package/dist/sdk/cli-contracts/flag-contracts.js +62 -21
  89. package/dist/sdk/cli-contracts/registration-helpers.js +10 -2
  90. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +8 -0
  91. package/dist/sdk/cli-contracts/runtime-contracts.js +20 -3
  92. package/dist/sdk/cli-contracts/tool-option-contracts.d.ts +6 -0
  93. package/dist/sdk/cli-contracts/tool-option-contracts.js +4 -2
  94. package/dist/sdk/cli-contracts/tool-parameter-tables.js +19 -4
  95. package/dist/sdk/cli-contracts/tool-schema.js +17 -4
  96. package/dist/sdk/context-intent-runtime.js +8 -5
  97. package/dist/sdk/context-signal-store.d.ts +34 -0
  98. package/dist/sdk/context-signal-store.js +24 -2
  99. package/dist/sdk/core-governance.d.ts +1 -1
  100. package/dist/sdk/core-governance.js +3 -3
  101. package/dist/sdk/dependencies.d.ts +2 -0
  102. package/dist/sdk/dependencies.js +7 -6
  103. package/dist/sdk/dependency-flag-validation.d.ts +15 -1
  104. package/dist/sdk/dependency-flag-validation.js +54 -2
  105. package/dist/sdk/extension/bundled-catalog.js +19 -6
  106. package/dist/sdk/generated-error-code-catalog.js +110 -2
  107. package/dist/sdk/governance/health.js +38 -12
  108. package/dist/sdk/governance/provenance-health.d.ts +39 -0
  109. package/dist/sdk/governance/provenance-health.js +185 -0
  110. package/dist/sdk/governance/validate-item-reader.d.ts +13 -0
  111. package/dist/sdk/governance/validate-item-reader.js +18 -0
  112. package/dist/sdk/governance/validate.d.ts +2 -2
  113. package/dist/sdk/governance/validate.js +25 -11
  114. package/dist/sdk/governance.d.ts +1 -0
  115. package/dist/sdk/governance.js +3 -2
  116. package/dist/sdk/graph/assembly.d.ts +4 -0
  117. package/dist/sdk/graph/assembly.js +22 -2
  118. package/dist/sdk/graph/run.d.ts +2 -0
  119. package/dist/sdk/graph/run.js +3 -2
  120. package/dist/sdk/index.d.ts +4 -0
  121. package/dist/sdk/index.js +6 -2
  122. package/dist/sdk/lifecycle/close.js +27 -19
  123. package/dist/sdk/lifecycle/create.d.ts +2 -0
  124. package/dist/sdk/lifecycle/create.js +66 -19
  125. package/dist/sdk/lifecycle/plan.js +6 -3
  126. package/dist/sdk/lifecycle/update-many.js +39 -27
  127. package/dist/sdk/lifecycle/update.d.ts +6 -1
  128. package/dist/sdk/lifecycle/update.js +51 -28
  129. package/dist/sdk/merge/driver.d.ts +2 -1
  130. package/dist/sdk/merge/driver.js +7 -4
  131. package/dist/sdk/merge/index.d.ts +1 -1
  132. package/dist/sdk/merge/index.js +3 -3
  133. package/dist/sdk/merge/install.d.ts +4 -0
  134. package/dist/sdk/merge/install.js +29 -17
  135. package/dist/sdk/merge/receipts.d.ts +17 -1
  136. package/dist/sdk/merge/receipts.js +112 -20
  137. package/dist/sdk/merge/reconcile.js +21 -4
  138. package/dist/sdk/merge/three-way.d.ts +6 -2
  139. package/dist/sdk/merge/three-way.js +24 -10
  140. package/dist/sdk/output-projection.js +24 -4
  141. package/dist/sdk/query/context.d.ts +2 -0
  142. package/dist/sdk/query/context.js +7 -4
  143. package/dist/sdk/query/get.d.ts +11 -0
  144. package/dist/sdk/query/get.js +37 -3
  145. package/dist/sdk/query/light-metadata.d.ts +3 -0
  146. package/dist/sdk/query/light-metadata.js +19 -0
  147. package/dist/sdk/read-output-contracts.d.ts +1 -1
  148. package/dist/sdk/read-output-contracts.js +23 -4
  149. package/dist/sdk/relationship-kinds/contract.d.ts +48 -0
  150. package/dist/sdk/relationship-kinds/contract.js +22 -0
  151. package/dist/sdk/relationships.d.ts +6 -41
  152. package/dist/sdk/relationships.js +12 -126
  153. package/dist/sdk/runtime-primitives.d.ts +1 -1
  154. package/dist/sdk/runtime-primitives.js +3 -3
  155. package/dist/sdk/runtime.d.ts +4 -1
  156. package/dist/sdk/runtime.js +8 -3
  157. package/dist/sdk/schema-migration.js +7 -9
  158. package/dist/sdk/similarity-scoring.d.ts +1 -2
  159. package/dist/sdk/similarity-scoring.js +4 -16
  160. package/dist/sdk/similarity.d.ts +5 -0
  161. package/dist/sdk/similarity.js +40 -10
  162. package/dist/sdk/structured-mutations.js +5 -2
  163. package/dist/sdk/test/linked-command-detection.d.ts +2 -7
  164. package/dist/sdk/test/linked-command-detection.js +22 -9
  165. package/dist/sdk/workspace-snapshot.d.ts +4 -0
  166. package/dist/sdk/workspace-snapshot.js +24 -11
  167. package/docs/AGENT_GUIDE.md +5 -2
  168. package/docs/COMMANDS.md +27 -6
  169. package/docs/DEPENDENCY_KIND_CONTRACT.md +29 -0
  170. package/docs/ITEM_READ_PROJECTIONS.md +26 -0
  171. package/docs/MERGE_SAFETY.md +17 -8
  172. package/docs/README.md +2 -5
  173. package/docs/READ_OUTPUT_CONTRACTS.md +2 -2
  174. package/docs/RELATIONSHIP_GRAPH.md +1 -1
  175. package/docs/SDK.md +33 -8
  176. package/docs/SDK_AGENT_SESSION_CONTEXT.md +36 -2
  177. package/docs/SDK_CONTEXT.md +97 -0
  178. package/docs/SDK_CONTEXT_INTEGRITY.md +33 -3
  179. package/docs/SENTRY_CONTRACT_EPOCHS.md +20 -0
  180. package/docs/agent-task-token-baseline.json +2 -2
  181. package/marketplace.json +2 -2
  182. package/package.json +5 -4
  183. package/packages/pm-beads/package.json +1 -1
  184. package/packages/pm-calendar/package.json +1 -1
  185. package/packages/pm-command-kit/package.json +1 -1
  186. package/packages/pm-digital-twin/package.json +1 -1
  187. package/packages/pm-governance-audit/package.json +1 -1
  188. package/packages/pm-guide-shell/package.json +1 -1
  189. package/packages/pm-kanban/package.json +1 -1
  190. package/packages/pm-lifecycle-hooks/package.json +1 -1
  191. package/packages/pm-linked-test-adapters/package.json +1 -1
  192. package/packages/pm-search-advanced/package.json +1 -1
  193. package/packages/pm-templates/package.json +1 -1
  194. package/packages/pm-todos/package.json +1 -1
  195. package/packages/pm-vcs/package.json +1 -1
  196. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  197. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  198. package/sdk/public-surface.json +415 -43
  199. package/dist/cli-bundle/chunks/chunk-GKA54IJM.js +0 -5
  200. package/dist/cli-bundle/chunks/chunk-KRY7SRUO.js +0 -2
  201. package/dist/cli-bundle/chunks/chunk-MNJ4NNSY.js +0 -26
  202. package/dist/cli-bundle/chunks/chunk-OJPCFDDY.js +0 -13
  203. package/dist/cli-bundle/chunks/chunk-PFSD3DAN.js +0 -194
  204. package/dist/cli-bundle/chunks/chunk-SQ7QZFQ6.js +0 -2
  205. package/dist/cli-bundle/chunks/register-operations-5XQETS6E.js +0 -2
  206. package/dist/cli-bundle/focused-chunks/chunk-54EU2UVO.js +0 -2
  207. package/dist/cli-bundle/focused-chunks/chunk-5FLBTZJQ.js +0 -2
  208. package/dist/cli-bundle/focused-chunks/chunk-5GJ57YKN.js +0 -2
  209. package/dist/cli-bundle/focused-chunks/chunk-CMDS3KYT.js +0 -14
  210. package/dist/cli-bundle/focused-chunks/chunk-DMKOMTTY.js +0 -6
  211. package/dist/cli-bundle/focused-chunks/chunk-F5B3HBEZ.js +0 -5
  212. package/dist/cli-bundle/focused-chunks/chunk-FI6DWU2M.js +0 -2
  213. package/dist/cli-bundle/focused-chunks/chunk-FNOKJG6N.js +0 -16
  214. package/dist/cli-bundle/focused-chunks/chunk-FPDYXZBK.js +0 -4
  215. package/dist/cli-bundle/focused-chunks/chunk-GWI6YIKJ.js +0 -3
  216. package/dist/cli-bundle/focused-chunks/chunk-HCUC62AB.js +0 -2
  217. package/dist/cli-bundle/focused-chunks/chunk-HGNKPSCB.js +0 -153
  218. package/dist/cli-bundle/focused-chunks/chunk-IAS7PMLG.js +0 -2
  219. package/dist/cli-bundle/focused-chunks/chunk-SM2PJGP3.js +0 -2
  220. package/dist/cli-bundle/focused-chunks/chunk-U646T7Z6.js +0 -18
  221. package/dist/cli-bundle/focused-chunks/chunk-YKPE4PPY.js +0 -2
  222. package/dist/cli-bundle/focused-chunks/chunk-ZOLDTUU7.js +0 -2
@@ -5,10 +5,12 @@
5
5
  * governance and package-owned duplicate analysis.
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]="078456b8-ed61-5c47-afc7-7553833934e1")}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]="a2fad90a-3575-5f3e-8cbe-ac380ac45591")}catch(e){}}();
9
9
  import { EXIT_CODE } from "../core/shared/constants.js";
10
10
  import { PmCliError } from "../core/shared/errors.js";
11
11
  import { resolveItemTypeRegistry } from "../core/item/type-registry.js";
12
+ import { normalizeStatusInput } from "../core/item/status.js";
13
+ import { resolveRuntimeStatusRegistry } from "../core/schema/runtime-schema.js";
12
14
  import { listAllItemMetadataLight } from "../core/store/item-store.js";
13
15
  import { resolvePmRoot } from "../core/store/paths.js";
14
16
  import { readSettings } from "../core/store/settings.js";
@@ -62,14 +64,38 @@ async function loadPreparedDuplicateItems(options, since) {
62
64
  const pmRoot = resolvePmRoot(options.cwd ?? process.cwd(), options.pmRoot);
63
65
  const settings = await readSettings(pmRoot);
64
66
  const typeRegistry = resolveItemTypeRegistry(settings);
65
- const allowedStatuses = options.statuses
66
- ? new Set(options.statuses.map((status) => status.trim()).filter(Boolean))
67
+ const requestedStatuses = options.statuses
68
+ ?.map((status) => status.trim().toLowerCase())
69
+ .filter(Boolean);
70
+ if (options.statuses !== undefined && requestedStatuses?.length === 0) {
71
+ throw new PmCliError("Duplicate cluster statuses must include at least one lifecycle status or all.", EXIT_CODE.USAGE);
72
+ }
73
+ const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);
74
+ const normalizedStatuses = requestedStatuses?.map((status) => {
75
+ if (status === "all")
76
+ return status;
77
+ const normalized = normalizeStatusInput(status, statusRegistry);
78
+ if (!normalized) {
79
+ throw new PmCliError(`Unknown duplicate-cluster status "${status}". Allowed: all, ${statusRegistry.definitions.map((entry) => entry.id).join(", ")}.`, EXIT_CODE.USAGE);
80
+ }
81
+ return normalized;
82
+ });
83
+ const hasAll = normalizedStatuses?.includes("all") === true;
84
+ if (hasAll && normalizedStatuses?.some((status) => status !== "all")) {
85
+ throw new PmCliError('The "all" status cannot be combined with other statuses.', EXIT_CODE.USAGE);
86
+ }
87
+ const effectiveStatuses = hasAll ? undefined : normalizedStatuses;
88
+ const allowedStatuses = effectiveStatuses
89
+ ? new Set(effectiveStatuses)
67
90
  : undefined;
68
- return (await listAllItemMetadataLight(pmRoot, settings.item_format, typeRegistry.type_to_folder, undefined, settings.schema))
69
- .filter((item) => (!allowedStatuses || allowedStatuses.has(item.status)) &&
70
- (!since || new Date(item.created_at).getTime() >= since.getTime()))
71
- .sort((left, right) => left.id.localeCompare(right.id))
72
- .map((item) => ({ item, prepared: prepareSimilarityText(item.title) }));
91
+ return {
92
+ items: (await listAllItemMetadataLight(pmRoot, settings.item_format, typeRegistry.type_to_folder, undefined, settings.schema))
93
+ .filter((item) => (!allowedStatuses || allowedStatuses.has(item.status)) &&
94
+ (!since || new Date(item.created_at).getTime() >= since.getTime()))
95
+ .sort((left, right) => left.id.localeCompare(right.id))
96
+ .map((item) => ({ item, prepared: prepareSimilarityText(item.title) })),
97
+ statuses: effectiveStatuses,
98
+ };
73
99
  }
74
100
  function collectDuplicateCandidatePairs(items, maxPairEvaluations = MAX_BATCH_PAIR_EVALUATIONS) {
75
101
  const candidates = new Set();
@@ -233,7 +259,7 @@ export async function findDuplicateClusters(options = {}) {
233
259
  limit: undefined,
234
260
  });
235
261
  const { limit, since } = validateDuplicateClusterOptions(options);
236
- const items = await loadPreparedDuplicateItems(options, since);
262
+ const { items, statuses } = await loadPreparedDuplicateItems(options, since);
237
263
  const candidates = collectDuplicateCandidatePairs(items);
238
264
  const union = createDuplicateUnionFind(items.length);
239
265
  const matches = scoreDuplicateCandidates(items, candidates, threshold, union);
@@ -243,6 +269,10 @@ export async function findDuplicateClusters(options = {}) {
243
269
  count: clusters.length,
244
270
  threshold,
245
271
  source: "metadata_scan",
272
+ filters: {
273
+ statuses: statuses ?? null,
274
+ since: options.since ?? null,
275
+ },
246
276
  cost: {
247
277
  item_count: items.length,
248
278
  candidate_pairs: candidates.size,
@@ -273,4 +303,4 @@ export async function evaluateSimilarityGovernance(candidate, options) {
273
303
  return { mode: options.mode, bypassed, result };
274
304
  }
275
305
  //# sourceMappingURL=similarity.js.map
276
- //# debugId=078456b8-ed61-5c47-afc7-7553833934e1
306
+ //# debugId=a2fad90a-3575-5f3e-8cbe-ac380ac45591
@@ -5,7 +5,7 @@
5
5
  * transaction contract and normalizes full item documents for CLI round trips.
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]="91be2c5d-c495-54bc-ac79-b4c645ad2c6e")}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]="07876c03-63a8-5c69-9c6c-c3446076a382")}catch(e){}}();
9
9
  import crypto from "node:crypto";
10
10
  import { normalizeItemId, normalizePrefix } from "../core/item/id.js";
11
11
  import { EXIT_CODE, ITEM_PROJECT_CONTEXT_KEYS, } from "../core/shared/constants.js";
@@ -43,6 +43,9 @@ const READ_ONLY_ITEM_KEYS = new Set([
43
43
  "format_version",
44
44
  "path",
45
45
  "author",
46
+ "collection_counts",
47
+ "notes_count",
48
+ "tests_count",
46
49
  ]);
47
50
  const UPDATE_READ_ONLY_ITEM_KEYS = new Set([
48
51
  ...READ_ONLY_ITEM_KEYS,
@@ -568,4 +571,4 @@ export function validateItemMutationRows(value) {
568
571
  return parseItemMutationBatch(JSON.stringify(value));
569
572
  }
570
573
  //# sourceMappingURL=structured-mutations.js.map
571
- //# debugId=91be2c5d-c495-54bc-ac79-b4c645ad2c6e
574
+ //# debugId=07876c03-63a8-5c69-9c6c-c3446076a382
@@ -1,8 +1,3 @@
1
- /**
2
- * @module sdk/test/linked-command-detection
3
- *
4
- * Parses linked-test command invocations for sandbox and context-safety checks.
5
- */
6
1
  /** Public contract for pm global flags with value, shared by SDK and presentation-layer consumers. */
7
2
  export declare const PM_GLOBAL_FLAGS_WITH_VALUE: Set<string>;
8
3
  /** Value-bearing flags accepted before item positionals by item-referencing commands. */
@@ -28,7 +23,7 @@ export declare const SCRIPT_RUN_FLAGS_WITH_VALUE: Set<string>;
28
23
  /** Implements split normalized command segments for the public runtime surface of this module. */
29
24
  export declare function splitNormalizedCommandSegments(normalizedCommand: string): string[];
30
25
  /** Extract pm CLI arguments from one normalized shell-command segment. */
31
- export declare function extractPmInvocationArgsFromSegment(segment: string): string[] | null;
26
+ export declare function extractPmInvocationArgsFromSegment(segment: string, cwd?: string): string[] | null;
32
27
  /** Extract referenced item IDs from pm CLI invocations embedded in one command. */
33
28
  export declare const extractReferencedPmItemIdsFromCommand: (command: string, idPrefix?: string) => string[];
34
29
  /** Implements strip leading env assignments for the public runtime surface of this module. */
@@ -42,7 +37,7 @@ export declare function normalizePackageSpecifier(token: string): string;
42
37
  /** Implements check whether pm cli package token for the public runtime surface of this module. */
43
38
  export declare function isPmCliPackageToken(token: string): boolean;
44
39
  /** Implements check whether pm cli script token for the public runtime surface of this module. */
45
- export declare function isPmCliScriptToken(token: string): boolean;
40
+ export declare function isPmCliScriptToken(token: string, cwd?: string): boolean;
46
41
  /** Implements parse npx command for the public runtime surface of this module. */
47
42
  export declare function parseNpxCommand(tokens: string[]): {
48
43
  command: string;
@@ -9,7 +9,9 @@
9
9
  // normalized command string. Kept in core so SDK governance and CLI test
10
10
  // execution share one dependency-direction-safe parser.
11
11
 
12
- !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]="8c4d45cf-6b97-5151-a325-db09d31962a7")}catch(e){}}();
12
+ !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]="673a5a26-6214-58d6-af19-baee5a8359f0")}catch(e){}}();
13
+ import { readFileSync } from "node:fs";
14
+ import path from "node:path";
13
15
  const PM_SUBCOMMANDS_WITH_ITEM_REFERENCE = new Set([
14
16
  "get",
15
17
  "history",
@@ -277,15 +279,15 @@ export function splitNormalizedCommandSegments(normalizedCommand) {
277
279
  .filter((segment) => segment.length > 0);
278
280
  }
279
281
  /** Extract pm CLI arguments from one normalized shell-command segment. */
280
- export function extractPmInvocationArgsFromSegment(segment) {
282
+ export function extractPmInvocationArgsFromSegment(segment, cwd = process.cwd()) {
281
283
  const tokens = stripLeadingEnvAssignments(segment.split(" ").filter((token) => token.length > 0));
282
284
  if (tokens.length === 0)
283
285
  return null;
284
286
  const [executable, ...args] = tokens;
285
- if (isPmExecutableToken(executable) || isPmCliScriptToken(executable)) {
287
+ if (isPmExecutableToken(executable) || isPmCliScriptToken(executable, cwd)) {
286
288
  return args;
287
289
  }
288
- if (executable === "node" && args[0] && isPmCliScriptToken(args[0])) {
290
+ if (executable === "node" && args[0] && isPmCliScriptToken(args[0], cwd)) {
289
291
  return args.slice(1);
290
292
  }
291
293
  const invocation = executable === "npx" || executable === "bunx"
@@ -408,10 +410,21 @@ export function isPmCliPackageToken(token) {
408
410
  token.endsWith("/pm-cli"));
409
411
  }
410
412
  /** Implements check whether pm cli script token for the public runtime surface of this module. */
411
- export function isPmCliScriptToken(token) {
412
- return (token === "dist/cli.js" ||
413
- token === "./dist/cli.js" ||
414
- token.endsWith("/dist/cli.js"));
413
+ export function isPmCliScriptToken(token, cwd = process.cwd()) {
414
+ const normalized = token.replaceAll("\\", "/");
415
+ if (normalized !== "dist/cli.js" &&
416
+ normalized !== "./dist/cli.js" &&
417
+ !normalized.endsWith("/dist/cli.js")) {
418
+ return false;
419
+ }
420
+ const scriptPath = path.resolve(cwd, token);
421
+ try {
422
+ const packageJson = JSON.parse(readFileSync(path.join(path.dirname(path.dirname(scriptPath)), "package.json"), "utf8"));
423
+ return packageJson.name === "@unbrained/pm-cli";
424
+ }
425
+ catch {
426
+ return false;
427
+ }
415
428
  }
416
429
  /** Implements parse npx command for the public runtime surface of this module. */
417
430
  export function parseNpxCommand(tokens) {
@@ -531,4 +544,4 @@ export function firstPositionalToken(tokens, flagsWithValue = new Set()) {
531
544
  return undefined;
532
545
  }
533
546
  //# sourceMappingURL=linked-command-detection.js.map
534
- //# debugId=8c4d45cf-6b97-5151-a325-db09d31962a7
547
+ //# debugId=673a5a26-6214-58d6-af19-baee5a8359f0
@@ -19,10 +19,14 @@ declare class WorkspaceLockHeartbeat {
19
19
  constructor(lockPath: string, owner: string, ttlSeconds: number);
20
20
  /** Begin renewing the lock without keeping the process alive on its own. */
21
21
  start(): void;
22
+ /** Force and await one owned renewal so callers can establish a fresh lease boundary. */
23
+ refreshNow(): Promise<void>;
22
24
  /** Fail the restore before activation when lease ownership was lost. */
23
25
  assertHealthy(): void;
24
26
  /** Stop future renewals and await the last in-flight atomic write. */
25
27
  stop(): Promise<void>;
28
+ /** Serialize renewal attempts and retain the first ownership failure. */
29
+ private scheduleRenewal;
26
30
  /** Atomically refresh only the lock still owned by this process and actor. */
27
31
  private renew;
28
32
  }
@@ -5,7 +5,7 @@
5
5
  * state while excluding clone-local caches, locks, and recovery journals.
6
6
  */
7
7
 
8
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="cdde844a-a14a-501d-a45b-18cbd1dd9008")}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]="470bb466-4619-5c54-a154-186b7ef4a049")}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";
@@ -55,19 +55,18 @@ class WorkspaceLockHeartbeat {
55
55
  }
56
56
  /** Begin renewing the lock without keeping the process alive on its own. */
57
57
  start() {
58
+ this.scheduleRenewal();
58
59
  this.timer = setInterval(() => {
59
- if (this.refresh !== undefined)
60
- return;
61
- this.refresh = this.renew()
62
- .catch((error) => {
63
- this.failure = error;
64
- })
65
- .finally(() => {
66
- this.refresh = undefined;
67
- });
60
+ this.scheduleRenewal();
68
61
  }, this.intervalMs);
69
62
  this.timer.unref();
70
63
  }
64
+ /** Force and await one owned renewal so callers can establish a fresh lease boundary. */
65
+ async refreshNow() {
66
+ this.scheduleRenewal();
67
+ await this.refresh;
68
+ this.assertHealthy();
69
+ }
71
70
  /** Fail the restore before activation when lease ownership was lost. */
72
71
  assertHealthy() {
73
72
  if (this.failure !== undefined) {
@@ -83,6 +82,18 @@ class WorkspaceLockHeartbeat {
83
82
  await this.refresh;
84
83
  this.assertHealthy();
85
84
  }
85
+ /** Serialize renewal attempts and retain the first ownership failure. */
86
+ scheduleRenewal() {
87
+ if (this.refresh !== undefined || this.failure !== undefined)
88
+ return;
89
+ this.refresh = this.renew()
90
+ .catch((error) => {
91
+ this.failure = error;
92
+ })
93
+ .finally(() => {
94
+ this.refresh = undefined;
95
+ });
96
+ }
86
97
  /** Atomically refresh only the lock still owned by this process and actor. */
87
98
  async renew() {
88
99
  const parsed = JSON.parse(await readFile(this.lockPath, "utf8"));
@@ -483,6 +494,7 @@ export async function restoreWorkspaceSnapshotWithRecovery(pmRoot, target, optio
483
494
  const releaseWorkspaceLock = await acquireLock(pmRoot, WORKSPACE_WRITER_LOCK_ID, lockTtlSeconds, author, false, false, lockWaitMs);
484
495
  const heartbeat = new WorkspaceLockHeartbeat(getLockPath(pmRoot, WORKSPACE_WRITER_LOCK_ID), author, lockTtlSeconds);
485
496
  heartbeat.start();
497
+ await heartbeat.refreshNow();
486
498
  let staging;
487
499
  let swapStarted = false;
488
500
  let heartbeatStopped = false;
@@ -536,6 +548,7 @@ export async function restoreWorkspaceSnapshotWithRecovery(pmRoot, target, optio
536
548
  await swapWorkspaceSnapshotRootWithActivation(staging, pmRoot, backup, DEFAULT_ATOMIC_OPERATIONS, async () => {
537
549
  heartbeat.start();
538
550
  heartbeatStopped = false;
551
+ await heartbeat.refreshNow();
539
552
  });
540
553
  await heartbeat.stop();
541
554
  heartbeatStopped = true;
@@ -596,4 +609,4 @@ export async function deleteWorkspaceSnapshot(pmRoot, target) {
596
609
  return { deleted: "object", target };
597
610
  }
598
611
  //# sourceMappingURL=workspace-snapshot.js.map
599
- //# debugId=cdde844a-a14a-501d-a45b-18cbd1dd9008
612
+ //# debugId=470bb466-4619-5c54-a154-186b7ef4a049
@@ -50,7 +50,9 @@ Repeated singular/plural list flags accumulate, so `--tag a --tag b` is equivale
50
50
 
51
51
  `--tags` REPLACES the whole tag list. To edit tags without restating the full set, prefer `--add-tags <value>` (adds without replacing) and `--remove-tags <value>` (prunes) on `create`/`update`/`update-many` (both repeatable; CSV or JSON-array). `--remove-tags` is `update`/`update-many` only. Also note `--expected`/`--actual` are short aliases for `--expected-result`/`--actual-result` on these commands, matching `pm close`.
52
52
 
53
- `--acceptance-criteria`/`--ac` likewise REPLACES the whole criteria value. Prefer `--add-ac <text>` (appends one criterion; deduped on exact text) and `--remove-ac <text>` (removes one criterion by exact text; unmatched selectors surface a `remove_ac_unmatched` warning) on `update`/`update-many` so concurrent branch edits with disjoint additions merge instead of clobbering. Acceptance criteria use semicolons as storage boundaries, so one criterion cannot itself contain a semicolon.
53
+ `--acceptance-criteria`/`--ac` likewise explicitly REPLACES the whole criteria value and cannot be combined with `--add-ac` or `--remove-ac`. Prefer `--add-ac <text>` (appends one criterion; deduped on exact text) and `--remove-ac <text>` (removes one criterion by exact text) on `update`/`update-many` so concurrent branch edits with disjoint additions merge instead of clobbering. Every removal must match or the complete mutation fails atomically with structured unmatched-selector recovery. Acceptance criteria use semicolons as storage boundaries, so one criterion cannot itself contain a semicolon.
54
+
55
+ Local dependency targets supplied to `create`, `update`, or `update-many` must already exist. Mark cross-workspace targets with `source_kind=external`; reserve `--allow-unresolved-deps` for deliberate staged imports and retain its structured warning as evidence.
54
56
 
55
57
  ```bash
56
58
  pm update <item-id> --add-tags urgent,backend # keep existing tags, add two
@@ -189,6 +191,7 @@ Use these defaults unless the task requires otherwise:
189
191
  - `pm validate --check-resolution --check-history-drift` before closing broad work.
190
192
  - `pm history-redact <id> --dry-run` before rewriting sensitive history payloads, then rerun without `--dry-run` once scope is confirmed.
191
193
  - `pm history-repair <id> --dry-run` when `pm health` or `pm validate --check-history-drift` report drifted streams; it re-anchors the hash chain and reconciles with the on-disk item without touching item content. Rerun without `--dry-run` to apply.
194
+ - `pm merge reconcile --dry-run --json` after a branch merge that touched tracker data. Review every discarded-value receipt before applying; a lossy reconciliation requires the explicit `--force` acceptance flag. `history-repair` alone does not settle merge receipts.
192
195
  - `pm schema list` and `pm schema show <Type>` before creating custom-domain work; they show built-in, persisted custom, and extension-provided item types without reading schema files by hand.
193
196
  - `pm schema add-type <Name>` when `pm create`/`pm update` reject a project-specific type as invalid; it registers the type in `.agents/pm/schema/types.json` so `pm create <Name> "..."` works. Built-in types are reserved; the upsert is idempotent. `pm schema remove-type <Name>` removes a custom type (warns, non-blocking, if items still use it).
194
197
  - `pm schema add-status <id> --role <role>` / `pm schema remove-status <id>` manage custom lifecycle statuses in `.agents/pm/schema/statuses.json`; roles come from the runtime status-role vocabulary, the upsert is idempotent, and built-in default statuses cannot be removed. `pm schema list` now reports statuses (builtin vs custom) alongside types.
@@ -204,7 +207,7 @@ Use these defaults unless the task requires otherwise:
204
207
  Concurrent agents work on ordinary Git branches/worktrees; tracker artifacts need the semantic merge contract from [Merge Safety](MERGE_SAFETY.md). The short loop:
205
208
 
206
209
  - After a fresh clone or new worktree, run `pm merge install` once so the field-aware merge drivers back the committed `.gitattributes` fence.
207
- - After every merge that touches `.agents/pm`, run `pm validate --check-storage-integrity` and `pm history --verify --strict-exit`; follow the remediation output (`pm history-repair` wiring included).
210
+ - After every merge that touches `.agents/pm`, run `pm merge reconcile --dry-run --json`, review the receipt classification, then apply `pm merge reconcile`. Add `--force` only after explicitly accepting or re-applying every discarded scalar value. Finish with `pm validate --check-storage-integrity` and `pm history --verify --strict-exit`.
208
211
  - Repositories fanning out many branches between merges should raise id entropy: `pm config project set ids_token_length 6`.
209
212
  - Use `--add-ac`/`--remove-ac` (not `--ac`) so concurrent acceptance-criteria edits merge instead of clobbering.
210
213
 
package/docs/COMMANDS.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  This is a task-oriented command guide. For exact flags, use runtime help because extensions and settings can change the active surface:
4
4
 
5
- Tracked implementation updates: [pm-52eh](../.agents/pm/features/pm-52eh.toon), [pm-mcxr](../.agents/pm/issues/pm-mcxr.toon), [pm-qd3woa](../.agents/pm/issues/pm-qd3woa.toon), [pm-ypuc39](../.agents/pm/issues/pm-ypuc39.toon), [pm-tz2ikr](../.agents/pm/issues/pm-tz2ikr.toon), and the schema-migration recovery contract [pm-s79kel](../.agents/pm/issues/pm-s79kel.toon).
5
+ Tracked implementation updates: [pm-52eh](../.agents/pm/features/pm-52eh.toon), [pm-mcxr](../.agents/pm/issues/pm-mcxr.toon), [pm-qd3woa](../.agents/pm/issues/pm-qd3woa.toon), [pm-ypuc39](../.agents/pm/issues/pm-ypuc39.toon), [pm-tz2ikr](../.agents/pm/issues/pm-tz2ikr.toon), the schema-migration recovery contract [pm-s79kel](../.agents/pm/issues/pm-s79kel.toon), the lossless mutation contracts [pm-x3dq0l](../.agents/pm/issues/pm-x3dq0l.toon), [pm-lppm6y](../.agents/pm/issues/pm-lppm6y.toon), and [pm-embm6t](../.agents/pm/issues/pm-embm6t.toon), and the SDK-first agent grammar tranche [pm-p316vn](../.agents/pm/issues/pm-p316vn.toon), [pm-st7wgu](../.agents/pm/issues/pm-st7wgu.toon), [pm-mkinft](../.agents/pm/issues/pm-mkinft.toon), [pm-ulqu](../.agents/pm/issues/pm-ulqu.toon), [pm-qmjx](../.agents/pm/issues/pm-qmjx.toon), [pm-4bzq](../.agents/pm/features/pm-4bzq.toon), [pm-x2vx](../.agents/pm/issues/pm-x2vx.toon), and [pm-g543](../.agents/pm/issues/pm-g543.toon).
6
6
 
7
7
  ```bash
8
8
  pm <command> --help
@@ -78,6 +78,7 @@ Use `--with-packages` for one-step agent setup when bundled package commands sho
78
78
  pm package # bare command defaults to --explore (list installed)
79
79
  pm install '*' --project
80
80
  pm package catalog --project
81
+ pm package manage --project --output-format json
81
82
  pm install npm:@scope/pm-package --project
82
83
  pm package describe --project # by-name surface map of every loaded package
83
84
  pm package describe my-package --markdown --output docs/my-package-reference.md
@@ -90,6 +91,11 @@ pm upgrade --cli-only --repair
90
91
  ```
91
92
 
92
93
  `pm install` and `pm package` are the preferred package-first workflow. `pm package` and `pm extension` bare invocations default to `--explore` so agents can list installed packages without remembering an action flag. `pm install '*'`, shell-expanded `pm install *`, and `pm install all` install bundled first-party packages. `pm extension` remains as a compatibility command for direct extension lifecycle operations.
94
+ `pm package catalog` emits one row per package, with every resolvable bundled
95
+ alias preserved in `aliases`; totals therefore measure packages rather than
96
+ alias-index entries. `pm package manage` participates in the universal read
97
+ output contract, so `--output-include`, `--output-limit`, `--output-budget`, and
98
+ `--output-format` behave like the same controls on other SDK-backed reads.
93
99
  Install output includes a light `verification` summary with target tracker root, activation state, registered commands/actions/item types, and an `ok|degraded` health verdict. Runtime activation failure sets the command result and process exit status to failure; inspect `activation_diagnostics` and `command_discovery.next_steps` for the exact recovery path.
94
100
  Bare install names use bundled aliases before installed npm packages. Every
95
101
  install result reports `source_resolution`; when both candidates exist it marks
@@ -113,6 +119,7 @@ pm list-open --type Task --priority 1 --limit 20
113
119
  pm list-in-progress --limit 20
114
120
  pm aggregate --group-by parent,type --status open
115
121
  pm aggregate --group-by parent,type --completion --include-unparented
122
+ pm duplicates --status all --threshold 0.8
116
123
  ```
117
124
 
118
125
  Use `pm next` when the only question is "what should I work on now?" It computes dependency-aware readiness, keeps dangling dependency ids blocked until repaired, and combines lifecycle-blocked plus graph-blocked work in the companion queue. The `recommended` projection is excluded from the `ready` tail; every actionable row carries its one-based `rank`, and the tail preserves the exact recommendation order. Foreign-owned in-progress work is summarized under `held_by_others`. Human-gated `Decision` records remain visible under `decision_needed` but are excluded from agent recommendations by default.
@@ -126,6 +133,10 @@ pm next --ready-only --limit 3 --json # tightest agent-loop projection
126
133
  ```
127
134
 
128
135
  Use `context` first for a compact active-work snapshot. Use `search` when the request names a concept, component, or prior issue.
136
+ Use `duplicates --status all` for a true whole-history duplicate check. The
137
+ result echoes `filters.statuses: null` for the unrestricted corpus; named or
138
+ custom statuses are normalized through the runtime status registry and an
139
+ unknown status fails instead of returning a false-clean result.
129
140
  Use `pm get <id>` to read a single item by ID — the single-item read primitive used throughout the agent loop. It accepts `--fields <list>` and `--depth brief|standard|deep|full` for token-minimal projections, and `--tree`/`--tree-depth <n>` to include descendants. Standard/deep reads expose a normalized `schedule` facet (`deadline`, `start_at`, `end_at`, `location`, reminders, and events) when scheduling metadata exists. Container-oriented built-ins (Epic, Feature, Milestone, and Plan) plus custom types automatically expose type-agnostic child counts and continuation metadata. Standard depth keeps that rollup counts-only; `--depth deep|full` or an explicit `--fields id,children` request adds the deterministic bounded child sample. Built-in leaf reads avoid a workspace scan unless children are explicitly requested. `pm get <id> --json` returns the `body` inside the `item` object (`.item.body`); see [Full results, totals, and bodies](#full-results-totals-and-bodies). To duplicate an existing item as a starting point, `pm copy <id> --title "New title"` clones it into a fresh id with lifecycle fields reset.
130
141
 
131
142
  Add `--at <version|ISO-timestamp>` for a verified point-in-time read. It replays the same hash-checked history kernel used by restore but never acquires a lock, writes the item, or appends history. The result always includes `reconstructed: true`, `as_of_version`, and `as_of_timestamp`. Future/out-of-range targets fail with structured `valid_range` metadata. `--at` cannot be combined with `--tree`; workspace-wide historical graph projections require a future indexed primitive.
@@ -347,7 +358,9 @@ pm close-many --rollback close-many-20260604-abc123 # restore the batch
347
358
 
348
359
  `close-many` skips already-terminal matches by default (pass `--force` to re-close), accepts `--completed-at <timestamp>` to preserve one shared actual-completion time across the batch, reports a per-item plan (`close`/`skip`, plus `active_child_ids` for parents that would be orphaned) under `--dry-run`, and writes a checkpoint by default (`--no-checkpoint` to disable). Checkpoints for both commands live under `.agents/pm/checkpoints/<command>/` and are restored with `--rollback <checkpoint-id>`.
349
360
 
350
- When a flag is rejected with `Unknown option`, the error guidance now suggests the nearest supported flag (including abbreviations like `--desc` → `--description`) and notes when the flag is valid on a different command (for example `--type` on `test-all` points to `create`/`list`).
361
+ When a flag is rejected with `Unknown option`, the error guidance now suggests the nearest supported flag (including abbreviations like `--desc` → `--description`) and notes when the flag is valid on a different command (for example `--type` on `test-all` points to `create`/`list`). Unknown-command guidance ranks agent verb synonyms first, bounded edit distance second, and substring matches last; `pm log` therefore points to `history`, `comments`, and `notes`, never a `catalog` command.
362
+
363
+ Commands addressed to one item retain their canonical positional form and also accept `--id <item-id>` consistently. For example, `pm get --id pm-a1`, `pm comments --id pm-a1 --add "..."`, and `pm test --id pm-a1 --run` are equivalent to their positional forms. Supplying both forms is a usage error. Command-scoped `pm contracts --flags-only`, completion, and help expose the same alias.
351
364
 
352
365
  ## Create and Update
353
366
 
@@ -368,6 +381,7 @@ pm create "Fix login bug" --type Issue --priority high
368
381
  `pm create` defaults `--type` to `settings.governance.create_default_type` (falling back to `Task`).
369
382
  Set it with `pm config project set governance-create-default-type <Type>` (must resolve to a known item type).
370
383
  Pass `--create-mode strict` to require an explicit `--type` flag for governance-controlled flows.
384
+ Type-aware help reports requirements for the selected mode and lists stricter requirements separately. A strict required repeatable collection can be deliberately empty: for example, `--clear-deps` satisfies a required dependency consideration while writing no relationship edge and recording `explicit_unset=dependencies` in history. Required runtime-schema fields are named with their metadata key and expected input format in structured recovery.
371
385
  `pm update --status` can be constrained per item type via `schema.type_workflows` plus
372
386
  `pm config project set governance-workflow-enforcement <off|warn|strict>` (see CONFIGURATION.md → Per-Type Workflows).
373
387
  Priority accepts either `0..4` or the equivalent names `critical`, `high`, `medium`, `low`, and `minimal`.
@@ -439,7 +453,7 @@ pm update pm-abc1 --remove-tags stale # drops "stale", keeps the rest
439
453
  pm create "New backend task" --add-tags backend,p1
440
454
  ```
441
455
 
442
- Acceptance criteria get the same additive treatment on `update`/`update-many`: `--acceptance-criteria`/`--ac` REPLACES the whole value, while `--add-ac <text>` appends one criterion (repeatable; deduped on exact text) and `--remove-ac <text>` removes one criterion by exact text match (repeatable; a non-matching selector adds a `remove_ac_unmatched:<text>` warning instead of silently no-oping). Criteria are stored with semicolon-space separators, so one criterion cannot contain a semicolon. Disjoint `--add-ac` edits from concurrent agents/branches merge cleanly instead of clobbering each other.
456
+ Acceptance criteria get the same additive treatment on `update`/`update-many`: `--acceptance-criteria`/`--ac` explicitly REPLACES the whole value and returns an `acceptance_criteria_replaced:<before-count>:<after-count>` warning when it changes existing criteria. `--add-ac <text>` appends one criterion (repeatable; deduped on exact text), while `--remove-ac <text>` removes one criterion by exact text match. Every requested removal must match: otherwise the mutation fails atomically with `acceptance_criteria_remove_unmatched` and reports the unmatched selectors, without applying valid removals or additions. Whole-value replacement cannot be combined with additive flags in one mutation. Criteria are stored with semicolon-space separators, so one criterion cannot contain a semicolon. Disjoint `--add-ac` edits from concurrent agents/branches merge cleanly instead of clobbering each other.
443
457
 
444
458
  ```bash
445
459
  pm update pm-abc1 --add-ac "error path covered by a regression test"
@@ -478,7 +492,7 @@ pm update <id> \
478
492
  pm update <id> --expected "Retry succeeds after backoff" --actual "Retry threw on first attempt"
479
493
  ```
480
494
 
481
- Repeat `--ac`/`--acceptance-criteria` to build multi-part criteria; values are stored in order joined by `; `. Dependency inputs accept either a bare existing item id or the explicit `id=<id>,kind=<kind>` form, and malformed shorthand such as `related:pm-abcd` fails before it can create a dangling graph edge.
495
+ Repeat `--ac`/`--acceptance-criteria` to build multi-part criteria; values are stored in order joined by `; `. Dependency inputs accept either a bare existing item id or the explicit `id=<id>,kind=<kind>` form, and malformed shorthand such as `related:pm-abcd` fails before it can create a dangling graph edge. New local dependency targets must already exist. Cross-workspace targets must declare `source_kind=external` (canonicalized to `global`); deliberately staged local edges require `--allow-unresolved-deps` and return one structured `dependency_target_unresolved:<id>` warning per missing target. The same validation runs during `update-many --dry-run`, so preview and apply cannot disagree.
482
496
 
483
497
  Mutation commands (`create`/`update`/`close`/`append`/...) default to an
484
498
  agent-efficient `id`/`status`/`changed_field_count` envelope. Use
@@ -736,7 +750,7 @@ pm test <id> --run --only-last
736
750
  pm test-all --status in_progress --progress
737
751
  ```
738
752
 
739
- Linked test commands should be sandbox-safe. Prefer `node scripts/run-tests.mjs ...` for repo-local test suites; normal package-manager scripts such as `pnpm test` and `npm run test` are accepted because linked-test execution injects temporary `PM_PATH` and `PM_GLOBAL_PATH`. Direct runner binaries such as `vitest` or `node --test` still need the wrapper or explicit inline sandbox env. The two-token form `--add command "npm test -- parser"` (and `--add path "..."` / `--remove command "..."`) is accepted when the value is quoted into a single shell argument; it is normalized to `--add command=...` before parsing. Use `--add-json` when command strings contain commas, nested quotes, shell variables, or `--` separators that are awkward to preserve through CSV-style `--add` parsing. `--match`, `--only-index`, and `--only-last` select which linked tests execute without mutating the stored linked-test list.
753
+ Linked test commands should be sandbox-safe. Prefer `node scripts/run-tests.mjs ...` for repo-local test suites; normal package-manager scripts such as `pnpm test` and `npm run test` are accepted because linked-test execution injects temporary `PM_PATH` and `PM_GLOBAL_PATH`. Direct runner binaries such as `vitest` or `node --test` still need the wrapper or explicit inline sandbox env. The two-token form `--add command "npm test -- parser"` (and `--add path "..."` / `--remove command "..."`) is accepted when the value is quoted into a single shell argument; it is normalized to `--add command=...` before parsing. Use `--add-json` when command strings contain commas, nested quotes, shell variables, or `--` separators that are awkward to preserve through CSV-style `--add` parsing. Linked tests retain append order; consequently `--only-last` always selects the most recently added stored test. `--match`, `--only-index`, and `--only-last` select which linked tests execute without mutating the stored linked-test list.
740
754
 
741
755
  Strict linked-test guards:
742
756
 
@@ -891,12 +905,19 @@ one-command reconciliation gate:
891
905
  ```bash
892
906
  pm merge reconcile --dry-run --json
893
907
  pm merge reconcile --message "Reconcile branch histories" --json
908
+ # Only after reviewing every discarded-value receipt:
909
+ pm merge reconcile --force --message "Accept reviewed merge decisions" --json
894
910
  ```
895
911
 
896
912
  The command scans every history stream, delegates repairs to the audited
897
913
  `history-repair --all` engine, then runs the history-drift and storage-integrity
898
914
  validation checks. Dry-run leaves drift intact and reports it in `validation`;
899
- apply exits nonzero if any stream fails or either invariant stays red. No Git
915
+ apply exits nonzero if any stream fails or either invariant stays red. Lossless
916
+ receipts remain visible in `pm merge report` but do not count as discarded-value
917
+ decisions. When a receipt contains a discarded scalar value, apply refuses with
918
+ `merge_reconcile_discards_require_acceptance` until the coordinator reviews or
919
+ re-applies the value and passes `--force`. `pm history-repair` is the narrower
920
+ drift-only tool and never substitutes for receipt reconciliation. No Git
900
921
  hook is installed automatically—teams may call this command from an explicit
901
922
  post-merge hook after opting into that policy.
902
923
 
@@ -0,0 +1,29 @@
1
+ # Dependency-kind contract
2
+
3
+ Tracker: [pm-4020c5](../.agents/pm/issues/pm-4020c5.toon), [pm-jkbqt8](../.agents/pm/issues/pm-jkbqt8.toon)
4
+
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
+
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
+
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
+
16
+ The SDK relationship registry is authoritative. `canonicalizeRelationshipKind()` rejects unknown spellings, while `resolveCanonicalRelationshipKind()` supports validation flows that need an undefined result. `pm contracts` publishes `relationship_kind_contracts` with canonical names, aliases, inverses, and ordering/hierarchy semantics.
17
+
18
+ ## Direction and actionability
19
+
20
+ `blocked_by` and `blocks` are inverse storage directions with identical scheduling meaning:
21
+
22
+ - `A --blocked_by--> B` means A waits for B.
23
+ - `B --blocks--> A` means A waits for B.
24
+
25
+ Readiness, `pm next`, context blocker summaries, `pm list-blocked`, downstream `unblocks` projections, and close-time auto-unblock use that shared interpretation. This applies to mixed-direction corpora without migrating existing rows.
26
+
27
+ ## Legacy observability
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.
@@ -0,0 +1,26 @@
1
+ # Stable Item Read Projections
2
+
3
+ Tracker reference: [pm-b1w8vr](../.agents/pm/issues/pm-b1w8vr.toon).
4
+
5
+ Item mutation and read-back surfaces share one collection vocabulary:
6
+ `comments`, `notes`, `learnings`, `files`, `tests`, `docs`, `reminders`, and
7
+ `events`. Every normal `pm get` and SDK `runGet` result carries
8
+ `item.collection_counts` with all eight keys, including zero values. This gives
9
+ agents a stable, low-token way to verify a write without requesting complete
10
+ collection payloads.
11
+
12
+ `brief` and `standard` omit the collection arrays but retain the stable counts.
13
+ `deep`, `full`, and `--full` include every collection array, normalizing absent
14
+ collections to `[]`, and retain `notes_count`, `tests_count`, and
15
+ `collection_counts`. A full projection is therefore a structural superset of
16
+ the standard item projection rather than a different shape.
17
+
18
+ Field projections can request `collection_counts` directly:
19
+
20
+ ```bash
21
+ pm get pm-example --fields id,updated_at,collection_counts
22
+ ```
23
+
24
+ Package authors receive the same contract from `runGet`. Do not infer whether
25
+ a collection exists by checking for an omitted key; use the count projection,
26
+ or request a full projection when the entries themselves are needed.
@@ -1,6 +1,6 @@
1
1
  # Multi-Branch Tracker Merge Safety
2
2
 
3
- Tracked by [pm-wc1r](../.agents/pm/features/pm-wc1r.toon), with the integrity and concurrency fixes [pm-9q2t](../.agents/pm/issues/pm-9q2t.toon), [pm-cxyv](../.agents/pm/issues/pm-cxyv.toon), [pm-gpo7](../.agents/pm/issues/pm-gpo7.toon), [pm-m3nl](../.agents/pm/issues/pm-m3nl.toon), [pm-wwfd](../.agents/pm/issues/pm-wwfd.toon), and [pm-xdn6](../.agents/pm/issues/pm-xdn6.toon). Fresh-init fence ownership is tracked by [pm-1w3ljt](../.agents/pm/issues/pm-1w3ljt.toon); runtime-cache index governance by [pm-hous](../.agents/pm/issues/pm-hous.toon); local allocation safety by [pm-khdq](../.agents/pm/issues/pm-khdq.toon); fence-coverage completeness and drift detection by [pm-i4fx](../.agents/pm/issues/pm-i4fx.toon); package-defined item coverage by [pm-5rexki](../.agents/pm/issues/pm-5rexki.toon); non-item JSON coverage by [pm-gjicmx](../.agents/pm/issues/pm-gjicmx.toon); portable driver identity by [pm-w91mvg](../.agents/pm/issues/pm-w91mvg.toon); pending receipt validation by [pm-ysqb6n](../.agents/pm/issues/pm-ysqb6n.toon); cross-branch id collision safety by [pm-pibw](../.agents/pm/issues/pm-pibw.toon); auditable merge history by [pm-9j2r3b](../.agents/pm/tasks/pm-9j2r3b.toon); durable conflict decisions by [pm-rh98vo](../.agents/pm/issues/pm-rh98vo.toon); continuous conformance by [pm-76dnfg](../.agents/pm/tasks/pm-76dnfg.toon); workspace-wide CI enforcement by [pm-pdr8t1](../.agents/pm/tasks/pm-pdr8t1.toon); post-merge reconciliation by [pm-mfkv92](../.agents/pm/issues/pm-mfkv92.toon); this repository's own adoption by [pm-iwsj](../.agents/pm/chores/pm-iwsj.toon).
3
+ Tracked by [pm-wc1r](../.agents/pm/features/pm-wc1r.toon), with the integrity and concurrency fixes [pm-9q2t](../.agents/pm/issues/pm-9q2t.toon), [pm-cxyv](../.agents/pm/issues/pm-cxyv.toon), [pm-gpo7](../.agents/pm/issues/pm-gpo7.toon), [pm-m3nl](../.agents/pm/issues/pm-m3nl.toon), [pm-wwfd](../.agents/pm/issues/pm-wwfd.toon), and [pm-xdn6](../.agents/pm/issues/pm-xdn6.toon). Fresh-init fence ownership is tracked by [pm-1w3ljt](../.agents/pm/issues/pm-1w3ljt.toon); runtime-cache index governance by [pm-hous](../.agents/pm/issues/pm-hous.toon); local allocation safety by [pm-khdq](../.agents/pm/issues/pm-khdq.toon); fence-coverage completeness and drift detection by [pm-i4fx](../.agents/pm/issues/pm-i4fx.toon); package-defined item coverage by [pm-5rexki](../.agents/pm/issues/pm-5rexki.toon); non-item JSON coverage by [pm-gjicmx](../.agents/pm/issues/pm-gjicmx.toon); portable driver identity by [pm-w91mvg](../.agents/pm/issues/pm-w91mvg.toon); pending receipt validation by [pm-ysqb6n](../.agents/pm/issues/pm-ysqb6n.toon); receipt classification by [pm-jtwsct](../.agents/pm/issues/pm-jtwsct.toon); direction-independent item conflict selection by [pm-dlx7v7](../.agents/pm/issues/pm-dlx7v7.toon); corrected reconciliation guidance by [pm-lwmstb](../.agents/pm/issues/pm-lwmstb.toon); cross-branch id collision safety by [pm-pibw](../.agents/pm/issues/pm-pibw.toon); auditable merge history by [pm-9j2r3b](../.agents/pm/tasks/pm-9j2r3b.toon); durable conflict decisions by [pm-rh98vo](../.agents/pm/issues/pm-rh98vo.toon); continuous conformance by [pm-76dnfg](../.agents/pm/tasks/pm-76dnfg.toon); workspace-wide CI enforcement by [pm-pdr8t1](../.agents/pm/tasks/pm-pdr8t1.toon); post-merge reconciliation by [pm-mfkv92](../.agents/pm/issues/pm-mfkv92.toon); this repository's own adoption by [pm-iwsj](../.agents/pm/chores/pm-iwsj.toon).
4
4
 
5
5
  pm stores project context as reviewable repository files. Concurrent agents can therefore use ordinary branches and worktrees, but tracker artifacts need semantic merge behavior: raw line merging cannot preserve TOON collection counts, JSON object structure, or append-only history hash chains.
6
6
 
@@ -56,20 +56,22 @@ pm merge install --dry-run --json
56
56
 
57
57
  | Artifact | Driver | Merge behavior |
58
58
  | ---------------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
59
- | Item `.toon` / `.md` | `pm-item-toon` / `pm-item-markdown` | Three-way field merge; format-specific drivers avoid passing the repository path through Git's shell command, append-like collections use set union, `updated_at` uses latest timestamp, and canonical serialization recomputes TOON counts. |
59
+ | Item `.toon` / `.md` | `pm-item-toon` / `pm-item-markdown` | Three-way field merge; append-like collections use set union, `updated_at` uses latest timestamp, scalar conflicts select a stable direction-independent value, and canonical serialization recomputes TOON counts. |
60
60
  | `history/*.jsonl` | `pm-history` | Preserves the common prefix and both divergent suffixes, orders deterministically, then re-anchors the resulting hash chain. |
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 scalar or JSON leaf differently, the driver writes a parseable preferred-side result but exits nonzero. Git keeps the path conflicted so a human or coordinating agent must review the losing value and explicitly `git add` the resolution. Use `--prefer theirs` only when that is the intended resolution policy.
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.
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
 
68
- For item conflicts, the driver also writes a clone-local receipt below the Git
69
- directory. It contains retained and discarded values so recovery does not
70
- depend on a reflog. Raw values never enter public tracker history:
71
- reconciliation records field names and value hashes, while the explicit local
72
- report is the only command that shows recoverable values:
68
+ For item conflicts, the driver writes a clone-local receipt below the Git
69
+ directory and a durable privacy-safe sidecar below `merge-receipts/` in the
70
+ tracker. The local receipt contains retained and discarded values so recovery
71
+ does not depend on a reflog. The tracked sidecar contains only field names and
72
+ value hashes, so fresh clones and CI can still fail closed on an unreviewed
73
+ decision without publishing either value. When both copies exist the SDK
74
+ deduplicates them and prefers the locally recoverable copy:
73
75
 
74
76
  ```bash
75
77
  pm merge report
@@ -97,9 +99,16 @@ After every branch merge that touches `.agents/pm`, run:
97
99
  ```bash
98
100
  pm merge reconcile --dry-run --json
99
101
  pm merge reconcile --message "Reconcile merged tracker histories" --json
102
+ # Required only after every discarded field has been reviewed or re-applied:
103
+ pm merge reconcile --force --message "Accept reviewed merge decisions" --json
100
104
  ```
101
105
 
102
106
  The preview reports every drifted stream and pending receipt without mutation.
107
+ Lossless receipts are reported as provenance but do not become blocking merge
108
+ decisions. Receipts with discarded scalar values remain blocking and the apply
109
+ pass refuses them unless the coordinator explicitly supplies `--force` after
110
+ review. This prevents a routine history repair from silently accepting data
111
+ loss while preserving a zero-noise path for merges that retained both changes.
103
112
  It exits nonzero while either merge-critical validation check is non-green, so
104
113
  CI and explicit post-merge hooks cannot approve unresolved receipts or drift.
105
114
  The apply pass uses the audited history rewrite boundary to append a
package/docs/README.md CHANGED
@@ -46,17 +46,14 @@ pm guide release --json
46
46
  - [Context Relevance and Packing](CONTEXT_RELEVANCE.md) - shared CLI/SDK signals, derived-store provenance, ranking explanations, and token budgets.
47
47
  - [Output Projection and Omission Contracts](OUTPUT_PROJECTION_CONTRACTS.md) - explicit withheld-field receipts, mode-paired row keys, and completion resolver outcomes.
48
48
  - [Output Token Accounting](OUTPUT_TOKEN_ACCOUNTING.md) - opt-in CLI/MCP byte attribution, bounded receipt overhead, and release-level tokens-per-task baselines.
49
- - [SDK Context Truth Contracts](SDK_CONTEXT_TRUTH_CONTRACTS.md) - typed tracker-root diagnostics and explicit merge-driver enforcement policy.
49
+ - [SDK Context Platform](SDK_CONTEXT.md) - task-oriented entry point for authoritative reads, ranking, package workflows, diagnostics, recovery, and verification.
50
50
  - [Self-Describing Context Contracts](SELF_DESCRIBING_CONTEXT_CONTRACTS.md) - intent-scoped reads, semantic flag invocation metadata, visibility parity, and generated error vocabulary.
51
51
  - [Universal Read Output Contracts](READ_OUTPUT_CONTRACTS.md) - cross-command include, amount, cost, and encoding controls for CLI, SDK, MCP, and packages.
52
52
  - [Mutation Integrity](MUTATION_INTEGRITY.md) - shared CLI/SDK/MCP author, secret, append-only disposition, and stale-work guardrails.
53
53
  - [Agent Provenance ADR Amendment](AGENT_PROVENANCE_ADR.md) - extensible model, effort, role, and host provenance with privacy and compatibility boundaries.
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
- - [SDK Context Coordination](SDK_CONTEXT_COORDINATION.md) - durable mutation events, bounded duplicate governance, and scale-safe package primitives.
56
55
  - [Improvement Ledger and History Analytics](IMPROVEMENT_ANALYTICS.md) - audited quantitative observations, live provenance coverage, and bounded observational fleet outcomes.
57
56
  - [SDK Evidence Traceability and Integrity](SDK_EVIDENCE_TRACEABILITY.md) - reverse source-to-item lookup, atomic evidence replacement, no-op history, linked-test collision classification, and telemetry drain receipts.
58
- - [SDK Context Integrity Primitives](SDK_CONTEXT_INTEGRITY_PRIMITIVES.md) - batch duplicate discovery, structured errors, Plan evidence/lifecycle, sparse settings, tombstones, linked-test output, relocation diagnostics, and scoped output services.
59
- - [SDK Context Integrity](SDK_CONTEXT_INTEGRITY.md) - truthful `get` projections, bounded annotation mutations, shared author coordinates, health provider boundaries, and changeset-aware replication gates.
60
57
  - [Reproducible Workspaces and Snapshots](REPRODUCIBLE_WORKSPACES.md) - deterministic SDK recipes and content-addressed authoritative tracker restore points.
61
58
  - [Portable Corpus Shapes](CORPUS_SHAPES.md) - versioned SDK populations for realistic benchmarks, evaluations, and package tests.
62
59
  - [Agent UX Contracts](AGENT_UX_CONTRACTS.md) - ordering-cycle advisories, graph count units, collision safety, compact context, ownership wording, and recovery behavior.
@@ -80,7 +77,7 @@ pm guide release --json
80
77
  | `quickstart` | [Quickstart](QUICKSTART.md), [Command Reference](COMMANDS.md) |
81
78
  | `commands` | [Command Reference](COMMANDS.md), [Configuration](CONFIGURATION.md) |
82
79
  | `workflows` | [Agent Guide](AGENT_GUIDE.md), [Testing](TESTING.md) |
83
- | `sdk` | [SDK](SDK.md), [Architecture](ARCHITECTURE.md) |
80
+ | `sdk` | [SDK](SDK.md), [SDK context contracts](SDK_CONTEXT.md), [Architecture](ARCHITECTURE.md) |
84
81
  | `extensions`, `packages` | [Packages and Extensions](EXTENSIONS.md), [starter extension](examples/starter-extension/README.md) |
85
82
  | `skills` | [Agent Guide](AGENT_GUIDE.md) plus `.agents/skills/*` |
86
83
  | `harnesses` | [Agent Guide](AGENT_GUIDE.md) plus `.agents/skills/HARNESS_COMPATIBILITY.md` |