@unbrained/pm-cli 2026.9.4 → 2026.9.6

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 (277) hide show
  1. package/.agents/skills/HARNESS_COMPATIBILITY.md +1 -1
  2. package/.agents/skills/README.md +2 -2
  3. package/.agents/skills/pm-developer/SKILL.md +4 -1
  4. package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +10 -0
  5. package/.agents/skills/pm-developer/references/GRAPH_AND_RELATIONSHIPS.md +3 -1
  6. package/.agents/skills/pm-extensions/SKILL.md +2 -2
  7. package/.agents/skills/pm-extensions/references/LIFECYCLE.md +3 -3
  8. package/.agents/skills/pm-extensions/references/TROUBLESHOOTING.md +3 -3
  9. package/.agents/skills/pm-user/SKILL.md +2 -0
  10. package/.agents/skills/pm-user/references/BACKLOG_SHAPING.md +5 -0
  11. package/.agents/skills/pm-user/references/WORKFLOWS.md +20 -5
  12. package/.claude-plugin/marketplace.json +2 -2
  13. package/CHANGELOG.md +58 -3
  14. package/dist/cli/commander-usage.d.ts +2 -0
  15. package/dist/cli/commander-usage.js +15 -12
  16. package/dist/cli/help-json-payload.js +12 -7
  17. package/dist/cli/main.js +22 -11
  18. package/dist/cli/register-list-query.js +4 -2
  19. package/dist/cli/register-mutation.js +4 -2
  20. package/dist/cli/register-operations.js +17 -8
  21. package/dist/cli-bundle/bundle-manifest.json +153 -161
  22. package/dist/cli-bundle/chunks/chunk-AZDFJGH6.js +9 -0
  23. package/dist/cli-bundle/chunks/{chunk-V72UV7QS.js → chunk-BAX2BYUW.js} +73 -73
  24. package/dist/cli-bundle/chunks/{chunk-HI6BHPCU.js → chunk-CKOL5U7J.js} +51 -51
  25. package/dist/cli-bundle/chunks/{chunk-A24WAVE6.js → chunk-JT6XF6GE.js} +2 -2
  26. package/dist/cli-bundle/chunks/{chunk-I2AS367G.js → chunk-JX4DH4WC.js} +2 -2
  27. package/dist/cli-bundle/chunks/{chunk-YW4QK2G7.js → chunk-NPN5I67J.js} +2 -2
  28. package/dist/cli-bundle/chunks/chunk-OF76PSDQ.js +5 -0
  29. package/dist/cli-bundle/chunks/chunk-QUWFABDM.js +232 -0
  30. package/dist/cli-bundle/chunks/{chunk-RI4XNOYS.js → chunk-RWWBOWJ5.js} +2 -2
  31. package/dist/cli-bundle/chunks/chunk-WGAOYSYH.js +40 -0
  32. package/dist/cli-bundle/chunks/{chunk-X6CMGV6L.js → chunk-WI2AYPA7.js} +2 -2
  33. package/dist/cli-bundle/chunks/{register-list-query-Z5YYSJJG.js → register-list-query-5JL52D33.js} +3 -3
  34. package/dist/cli-bundle/chunks/{register-mutation-ELSP6I4I.js → register-mutation-ZPG7KMVP.js} +4 -4
  35. package/dist/cli-bundle/chunks/register-operations-I5W3KMGB.js +2 -0
  36. package/dist/cli-bundle/chunks/{register-setup-E2AE72QG.js → register-setup-H4SVAVX7.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-3646BZ4W.js → chunk-3BZEOXLT.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-AL5ON6K6.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-CHC2IDXA.js +6 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-CK6QFAPZ.js +20 -0
  41. package/dist/cli-bundle/focused-chunks/{chunk-NBMJD6MK.js → chunk-EZR3GHFE.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/chunk-H2WJWK7L.js +56 -0
  43. package/dist/cli-bundle/focused-chunks/{chunk-53IHPICX.js → chunk-LIJIA4TY.js} +2 -2
  44. package/dist/cli-bundle/focused-chunks/{chunk-25UJBH4M.js → chunk-M6PBRFSH.js} +2 -2
  45. package/dist/cli-bundle/focused-chunks/{chunk-AJR3OP2E.js → chunk-NTPY5GSO.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/chunk-P5O7MP5Y.js +2 -0
  47. package/dist/cli-bundle/focused-chunks/chunk-PDMW2E36.js +177 -0
  48. package/dist/cli-bundle/focused-chunks/{chunk-MHKUIPJW.js → chunk-PEFABF7E.js} +2 -2
  49. package/dist/cli-bundle/focused-chunks/{chunk-6PWPQKDN.js → chunk-QTVCLMSZ.js} +2 -2
  50. package/dist/cli-bundle/focused-chunks/{chunk-7N2TV7WZ.js → chunk-QTZRBUYA.js} +2 -2
  51. package/dist/cli-bundle/focused-chunks/chunk-SGQST3PQ.js +12 -0
  52. package/dist/cli-bundle/focused-chunks/chunk-UKHASMMP.js +10 -0
  53. package/dist/cli-bundle/focused-chunks/chunk-ZPEKQHOC.js +2 -0
  54. package/dist/cli-bundle/main.js +22 -15
  55. package/dist/cli-bundle/sdk-authoring.js +1 -1
  56. package/dist/cli-bundle/sdk-contracts.js +2 -2
  57. package/dist/cli-bundle/sdk-core.js +37 -37
  58. package/dist/cli-bundle/sdk-governance.js +1 -1
  59. package/dist/cli-bundle/sdk-graph.js +1 -1
  60. package/dist/cli-bundle/sdk-merge.js +42 -39
  61. package/dist/cli-bundle/sdk-query.js +1 -1
  62. package/dist/cli-bundle/sdk-runtime.js +1 -1
  63. package/dist/cli-bundle/sdk-testing.js +1 -1
  64. package/dist/cli-bundle/sdk.js +9 -9
  65. package/dist/core/extensions/extension-types.d.ts +2 -0
  66. package/dist/core/extensions/extension-types.js +2 -2
  67. package/dist/core/extensions/module-graph-snapshot.js +5 -4
  68. package/dist/core/fs/host-environment-errors.d.ts +27 -0
  69. package/dist/core/fs/host-environment-errors.js +142 -0
  70. package/dist/core/history/drift-scan.d.ts +5 -0
  71. package/dist/core/history/drift-scan.js +16 -6
  72. package/dist/core/history/event-classification.d.ts +1 -1
  73. package/dist/core/history/event-classification.js +3 -2
  74. package/dist/core/history/history.js +37 -5
  75. package/dist/core/history/identity.d.ts +15 -0
  76. package/dist/core/history/identity.js +31 -0
  77. package/dist/core/history/projection.js +50 -17
  78. package/dist/core/history/read.js +6 -3
  79. package/dist/core/history/replay.d.ts +1 -2
  80. package/dist/core/history/replay.js +4 -4
  81. package/dist/core/history/version-address.d.ts +13 -0
  82. package/dist/core/history/version-address.js +33 -0
  83. package/dist/core/item/actionability.d.ts +21 -1
  84. package/dist/core/item/actionability.js +45 -10
  85. package/dist/core/item/id.js +8 -4
  86. package/dist/core/item/item-type-definition.d.ts +2 -0
  87. package/dist/core/item/item-type-definition.js +9 -2
  88. package/dist/core/item/type-registry.d.ts +5 -1
  89. package/dist/core/item/type-registry.js +9 -4
  90. package/dist/core/output/mutation-projection.js +12 -5
  91. package/dist/core/shared/errors.d.ts +8 -0
  92. package/dist/core/shared/errors.js +2 -2
  93. package/dist/core/store/item-metadata-query-index.js +13 -10
  94. package/dist/core/store/item-metadata-read-work.d.ts +19 -0
  95. package/dist/core/store/item-metadata-read-work.js +44 -0
  96. package/dist/core/store/item-store.d.ts +14 -0
  97. package/dist/core/store/item-store.js +52 -10
  98. package/dist/core/store/settings-validator.js +3 -2
  99. package/dist/core/validate/fix-planning.d.ts +1 -1
  100. package/dist/core/validate/fix-planning.js +2 -2
  101. package/dist/core/validate/missing-link-owners.d.ts +1 -1
  102. package/dist/core/validate/missing-link-owners.js +5 -3
  103. package/dist/core/validate/stale-file-classification.d.ts +6 -4
  104. package/dist/core/validate/stale-file-classification.js +20 -5
  105. package/dist/mcp/server.js +12 -5
  106. package/dist/sdk/actionability.d.ts +1 -1
  107. package/dist/sdk/actionability.js +3 -3
  108. package/dist/sdk/cli-contracts/command-aliases.js +11 -2
  109. package/dist/sdk/cli-contracts/completeness.js +4 -3
  110. package/dist/sdk/cli-contracts/flag-contracts.d.ts +2 -0
  111. package/dist/sdk/cli-contracts/flag-contracts.js +31 -5
  112. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +21 -13
  113. package/dist/sdk/cli-contracts/grammar-contracts.js +11 -2
  114. package/dist/sdk/cli-contracts/runtime-contracts.js +5 -2
  115. package/dist/sdk/cli-contracts/schemas/assurance-definition.d.ts +254 -5
  116. package/dist/sdk/cli-contracts/schemas/assurance-definition.js +52 -2
  117. package/dist/sdk/cli-contracts/tool-parameter-tables.js +30 -6
  118. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  119. package/dist/sdk/cli-contracts/tool-schema.js +32 -7
  120. package/dist/sdk/context/usage-ledger.d.ts +41 -0
  121. package/dist/sdk/context/usage-ledger.js +186 -0
  122. package/dist/sdk/context-intent-contracts.js +3 -3
  123. package/dist/sdk/context-usage.d.ts +18 -9
  124. package/dist/sdk/context-usage.js +44 -121
  125. package/dist/sdk/define.d.ts +1 -1
  126. package/dist/sdk/define.js +2 -2
  127. package/dist/sdk/dependency-provenance.d.ts +7 -0
  128. package/dist/sdk/dependency-provenance.js +14 -4
  129. package/dist/sdk/duplicates.d.ts +2 -0
  130. package/dist/sdk/duplicates.js +3 -2
  131. package/dist/sdk/environment/host-environment-errors.d.ts +5 -27
  132. package/dist/sdk/environment/host-environment-errors.js +4 -137
  133. package/dist/sdk/extension/install-runtime.js +9 -6
  134. package/dist/sdk/extension/install-snapshot.js +7 -6
  135. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +104 -56
  136. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +50 -2
  137. package/dist/sdk/governance/assurance-action.d.ts +3 -2
  138. package/dist/sdk/governance/assurance-action.js +23 -11
  139. package/dist/sdk/governance/assurance-runtime.d.ts +2 -0
  140. package/dist/sdk/governance/assurance-runtime.js +6 -3
  141. package/dist/sdk/governance/defect-recurrence-coverage.d.ts +93 -0
  142. package/dist/sdk/governance/defect-recurrence-coverage.js +189 -0
  143. package/dist/sdk/governance/defect-recurrence-graph.d.ts +33 -0
  144. package/dist/sdk/governance/defect-recurrence-graph.js +121 -0
  145. package/dist/sdk/governance/defect-recurrence.d.ts +8 -0
  146. package/dist/sdk/governance/defect-recurrence.js +118 -17
  147. package/dist/sdk/governance/health.d.ts +17 -2
  148. package/dist/sdk/governance/health.js +31 -10
  149. package/dist/sdk/governance/linked-file-report.d.ts +41 -0
  150. package/dist/sdk/governance/linked-file-report.js +72 -0
  151. package/dist/sdk/governance/validate-history-drift.js +16 -5
  152. package/dist/sdk/governance/validate.d.ts +5 -2
  153. package/dist/sdk/governance/validate.js +21 -37
  154. package/dist/sdk/governance.d.ts +2 -0
  155. package/dist/sdk/governance.js +4 -2
  156. package/dist/sdk/guide-topics.js +116 -2
  157. package/dist/sdk/history/salvage.d.ts +35 -0
  158. package/dist/sdk/history/salvage.js +248 -0
  159. package/dist/sdk/history-compact.js +32 -37
  160. package/dist/sdk/history-read.d.ts +3 -2
  161. package/dist/sdk/history-read.js +6 -3
  162. package/dist/sdk/history-repair.d.ts +12 -0
  163. package/dist/sdk/history-repair.js +60 -13
  164. package/dist/sdk/lifecycle/claim.d.ts +4 -0
  165. package/dist/sdk/lifecycle/claim.js +9 -3
  166. package/dist/sdk/lifecycle/close.d.ts +8 -0
  167. package/dist/sdk/lifecycle/close.js +16 -8
  168. package/dist/sdk/lifecycle/create.js +9 -12
  169. package/dist/sdk/lifecycle/restore.d.ts +1 -0
  170. package/dist/sdk/lifecycle/restore.js +19 -7
  171. package/dist/sdk/lifecycle/update.js +12 -12
  172. package/dist/sdk/lifecycle-policy.d.ts +3 -0
  173. package/dist/sdk/lifecycle-policy.js +2 -2
  174. package/dist/sdk/merge/abandoned-receipts.d.ts +15 -0
  175. package/dist/sdk/merge/abandoned-receipts.js +47 -0
  176. package/dist/sdk/merge/driver.js +4 -2
  177. package/dist/sdk/merge/index.d.ts +1 -0
  178. package/dist/sdk/merge/index.js +2 -2
  179. package/dist/sdk/merge/receipt-operation.d.ts +15 -0
  180. package/dist/sdk/merge/receipt-operation.js +97 -0
  181. package/dist/sdk/merge/receipt-schema.d.ts +20 -0
  182. package/dist/sdk/merge/receipt-schema.js +398 -0
  183. package/dist/sdk/merge/receipts.d.ts +16 -7
  184. package/dist/sdk/merge/receipts.js +54 -340
  185. package/dist/sdk/merge/reconcile.d.ts +2 -0
  186. package/dist/sdk/merge/reconcile.js +11 -4
  187. package/dist/sdk/output-projection.d.ts +3 -1
  188. package/dist/sdk/output-projection.js +11 -3
  189. package/dist/sdk/public-surface.json +1 -0
  190. package/dist/sdk/query/context.d.ts +9 -0
  191. package/dist/sdk/query/context.js +33 -21
  192. package/dist/sdk/query/duplicate-candidates.d.ts +7 -0
  193. package/dist/sdk/query/duplicate-candidates.js +71 -0
  194. package/dist/sdk/query/get.d.ts +2 -2
  195. package/dist/sdk/query/get.js +9 -8
  196. package/dist/sdk/query/history.d.ts +11 -0
  197. package/dist/sdk/query/history.js +34 -10
  198. package/dist/sdk/query/next.d.ts +15 -1
  199. package/dist/sdk/query/next.js +43 -20
  200. package/dist/sdk/query.d.ts +3 -0
  201. package/dist/sdk/query.js +5 -2
  202. package/dist/sdk/read-output-session.js +5 -3
  203. package/dist/sdk/runtime-primitives.d.ts +1 -1
  204. package/dist/sdk/runtime-primitives.js +3 -3
  205. package/dist/sdk/runtime.js +4 -2
  206. package/dist/sdk/similarity.d.ts +24 -3
  207. package/dist/sdk/similarity.js +50 -55
  208. package/dist/types.d.ts +2 -0
  209. package/dist/types.js +2 -2
  210. package/docs/AGENT_GUIDE.md +58 -1
  211. package/docs/BOUNDED_CONTEXT_READS.md +185 -0
  212. package/docs/CLOSE_EVIDENCE_RECOVERY.md +35 -0
  213. package/docs/COMMANDS.md +30 -8
  214. package/docs/CONTEXT_RELEVANCE.md +16 -4
  215. package/docs/DEFECT_RECURRENCE.md +80 -6
  216. package/docs/EXTENSIONS.md +18 -19
  217. package/docs/HISTORY_RECOVERY.md +146 -0
  218. package/docs/MERGE_SAFETY.md +50 -0
  219. package/docs/OUTPUT_PROJECTION_CONTRACTS.md +66 -0
  220. package/docs/PR_REVIEW_LOOP.md +18 -3
  221. package/docs/README.md +19 -1
  222. package/docs/RELEASING.md +31 -13
  223. package/docs/SDK_CONTEXT_TRUTH_CONTRACTS.md +24 -1
  224. package/docs/SDK_EXECUTION_CONTRACTS.md +105 -0
  225. package/docs/SDK_LIFECYCLE.md +27 -0
  226. package/docs/SDK_RUNTIME_BOUNDARIES.md +11 -0
  227. package/docs/TESTING.md +5 -0
  228. package/docs/agent-task-token-baseline.json +17 -17
  229. package/docs/agent-task-transcripts.json +2 -1
  230. package/docs/generated/FLAG_LEXICON_BUDGETS.md +11 -11
  231. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +11 -3
  232. package/marketplace.json +2 -2
  233. package/package.json +8 -6
  234. package/packages/pm-beads/package.json +1 -1
  235. package/packages/pm-calendar/package.json +1 -1
  236. package/packages/pm-command-kit/package.json +1 -1
  237. package/packages/pm-digital-twin/package.json +1 -1
  238. package/packages/pm-governance-audit/README.md +10 -1
  239. package/packages/pm-governance-audit/extensions/governance-audit/dedupe-audit.ts +50 -65
  240. package/packages/pm-governance-audit/extensions/governance-audit/index.ts +2 -2
  241. package/packages/pm-governance-audit/extensions/governance-audit/sdk.ts +1 -0
  242. package/packages/pm-governance-audit/package.json +1 -1
  243. package/packages/pm-guide-shell/package.json +1 -1
  244. package/packages/pm-kanban/package.json +1 -1
  245. package/packages/pm-lifecycle-hooks/package.json +1 -1
  246. package/packages/pm-linked-test-adapters/package.json +1 -1
  247. package/packages/pm-search-advanced/package.json +1 -1
  248. package/packages/pm-templates/package.json +1 -1
  249. package/packages/pm-todos/package.json +1 -1
  250. package/packages/pm-vcs/package.json +1 -1
  251. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  252. package/plugins/pm-claude/skills/pm-audit/SKILL.md +2 -2
  253. package/plugins/pm-claude/skills/pm-developer/SKILL.md +2 -2
  254. package/plugins/pm-claude/skills/pm-planner/SKILL.md +2 -2
  255. package/plugins/pm-claude/skills/pm-release/SKILL.md +2 -2
  256. package/plugins/pm-claude/skills/pm-workflow/SKILL.md +3 -3
  257. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  258. package/plugins/pm-codex/skills/pm-auditor/SKILL.md +2 -2
  259. package/plugins/pm-codex/skills/pm-native/SKILL.md +3 -4
  260. package/plugins/pm-codex/skills/pm-release/SKILL.md +2 -2
  261. package/scripts/finalize-build.mjs +13 -1
  262. package/dist/cli-bundle/chunks/chunk-6XSKPNCF.js +0 -230
  263. package/dist/cli-bundle/chunks/chunk-OGBVU67G.js +0 -5
  264. package/dist/cli-bundle/chunks/chunk-TXW6VL33.js +0 -38
  265. package/dist/cli-bundle/chunks/chunk-VGLHECAP.js +0 -9
  266. package/dist/cli-bundle/chunks/register-operations-OH7R3ZT4.js +0 -2
  267. package/dist/cli-bundle/focused-chunks/chunk-4YRAVI43.js +0 -12
  268. package/dist/cli-bundle/focused-chunks/chunk-5P4HVOWG.js +0 -176
  269. package/dist/cli-bundle/focused-chunks/chunk-636VVQ7F.js +0 -54
  270. package/dist/cli-bundle/focused-chunks/chunk-B3A6MJYE.js +0 -2
  271. package/dist/cli-bundle/focused-chunks/chunk-BFDDJ2T2.js +0 -2
  272. package/dist/cli-bundle/focused-chunks/chunk-BHUIRZWQ.js +0 -9
  273. package/dist/cli-bundle/focused-chunks/chunk-JADHHPY6.js +0 -6
  274. package/dist/cli-bundle/focused-chunks/chunk-NSKBLHVI.js +0 -20
  275. package/dist/cli-bundle/focused-chunks/chunk-P2GOAOQN.js +0 -2
  276. package/dist/cli-bundle/focused-chunks/chunk-YARWJM2B.js +0 -2
  277. package/sdk/public-surface.json +0 -33034
@@ -9,9 +9,10 @@
9
9
  * re-deriving readiness from a full `pm context` snapshot every turn.
10
10
  */
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]="b0f3d72c-ff0b-59bb-a6bc-8476eb9fb6c1")}catch(e){}}();
13
- import { collectBlockedByIds, computeActionabilityReport, } from "../actionability.js";
14
- import { resolveRuntimeStatusRegistry, EXIT_CODE, PmCliError, nowIso, normalizeStatusInput, normalizeStatusForRegistry, resolvePmRoot, readSettings, resolveAuthor, } from "../runtime-primitives.js";
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]="6035e85b-81ed-512e-b291-b0d86af1848b")}catch(e){}}();
13
+ import { collectBlockedByIds, computeActionabilityReport, resolveItemExecutionRole, selectActionableEntries, } from "../actionability.js";
14
+ import { resolveRuntimeStatusRegistry, EXIT_CODE, PmCliError, nowIso, normalizeStatusInput, normalizeStatusForRegistry, resolvePmRoot, readSettings, resolveAuthor, resolveItemTypeRegistry, } from "../runtime-primitives.js";
15
+ import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
15
16
  import { parseIntegerLimit } from "./parsers.js";
16
17
  import { buildChildrenByParent, collectSubtreeIds, compareCriticalItems, mergeSortedWarnings, packRankedContextItems, resolveContextTokenBudget, toContextPackingSummary, toContextRankingSummary, toContextFocusItem, } from "./context.js";
17
18
  import { runList } from "./list.js";
@@ -94,7 +95,7 @@ function buildRecommendationReasons(entry, statusRegistry, now, completedContain
94
95
  reasons.push(`advances ${item.parent.trim()}`);
95
96
  }
96
97
  if (entry.unblocks.length > 0) {
97
- reasons.push(`unblocks ${entry.unblocks.length} item(s): ${entry.unblocks.join(", ")}`);
98
+ reasons.push(`unblocks ${entry.unblocks.length} item(s)`);
98
99
  }
99
100
  return reasons;
100
101
  }
@@ -118,12 +119,12 @@ export const _testOnlyNextCommand = {
118
119
  resolveNextCallerAuthor: (options, settingsAuthor) => resolveNextCallerAuthor(options, settingsAuthor),
119
120
  };
120
121
  /** Separates human-gated decisions from autonomous agent work unless the caller explicitly opts into decision claims. */
121
- export function partitionDecisionEntries(ready, includeDecisions) {
122
- const decisions = ready.filter((entry) => entry.item.type.trim().toLowerCase() === "decision");
122
+ export function partitionDecisionEntries(ready, includeDecisions, typeRegistry) {
123
+ const decisions = ready.filter((entry) => resolveItemExecutionRole(entry.item.type, typeRegistry) === "human");
123
124
  return {
124
125
  agent: includeDecisions
125
126
  ? ready
126
- : ready.filter((entry) => entry.item.type.trim().toLowerCase() !== "decision"),
127
+ : ready.filter((entry) => resolveItemExecutionRole(entry.item.type, typeRegistry) !== "human"),
127
128
  decisions: includeDecisions ? [] : decisions,
128
129
  };
129
130
  }
@@ -195,13 +196,17 @@ function buildNextRecommendation(params) {
195
196
  reasons: buildRecommendationReasons(params.projectedReady[0], params.statusRegistry, params.now, params.completedContainer),
196
197
  };
197
198
  }
198
- function buildNextSuggestions(recommended, blockedRows) {
199
+ function buildNextSuggestions(recommended, blockedRows, gateCount) {
199
200
  if (recommended !== null) {
200
201
  return undefined;
201
202
  }
203
+ const gateGuidance = gateCount > 0
204
+ ? ["No executable recommendation: outcome gates require verification. Inspect gate_needed, or use pm next --include-gates to opt in."]
205
+ : [];
202
206
  if (blockedRows.length > 0) {
203
207
  const blockedWithReferences = blockedRows.find((item) => item.blockers.length > 0);
204
208
  return [
209
+ ...gateGuidance,
205
210
  blockedWithReferences
206
211
  ? `${blockedRows.length} item(s) are blocked; unblock the top one by closing ${blockedWithReferences.blockers.map((blocker) => blocker.id).join(", ")}`
207
212
  : `${blockedRows.length} item(s) are blocked; add blocker context or move the top item back to an active status`,
@@ -210,6 +215,7 @@ function buildNextSuggestions(recommended, blockedRows) {
210
215
  ];
211
216
  }
212
217
  return [
218
+ ...gateGuidance,
213
219
  'pm create --type Task --title "..." to add a new work item',
214
220
  "pm list --status in_progress to review work already underway",
215
221
  "pm context --depth deep for the full project snapshot",
@@ -271,7 +277,7 @@ async function finalizeNextResult(params) {
271
277
  params.result.ranking = toContextRankingSummary(params.readyRanking, params.featureStore);
272
278
  params.result.packing = toContextPackingSummary(params.packing);
273
279
  }
274
- params.result.suggestions = buildNextSuggestions(params.recommended, params.blockedRows);
280
+ params.result.suggestions = buildNextSuggestions(params.recommended, params.blockedRows, params.gateCount);
275
281
  await attachNextUsageFeedback({
276
282
  result: params.result,
277
283
  pmRoot: params.pmRoot,
@@ -291,9 +297,9 @@ function filterCandidatesByParentScope(candidates, corpus, parentScope) {
291
297
  }
292
298
  return candidates.filter((item) => subtree.ids.has(item.id.trim().toLowerCase()));
293
299
  }
294
- async function rankReadyEntriesWithRelevance(rankedReady, childrenByParent, statusRegistry, now, callerAuthor, pmRoot, tokenBudget) {
300
+ async function rankReadyEntriesWithRelevance(rankedReady, childrenByParent, statusRegistry, now, callerAuthor, pmRoot, tokenBudget, includeContainers) {
295
301
  const concreteReady = rankedReady.filter((entry) => !hasCompletedDescendants(entry.item, childrenByParent));
296
- const structuralReady = concreteReady.length > 0 ? concreteReady : rankedReady;
302
+ const structuralReady = includeContainers || concreteReady.length === 0 ? rankedReady : concreteReady;
297
303
  const usage = await readContextUsageAffinity({
298
304
  pmRoot,
299
305
  author: callerAuthor,
@@ -316,7 +322,7 @@ async function rankReadyEntriesWithRelevance(rankedReady, childrenByParent, stat
316
322
  return {
317
323
  projectedReady,
318
324
  ranking,
319
- completedContainer: concreteReady.length === 0,
325
+ completedContainer: concreteReady.length === 0 && !includeContainers,
320
326
  packing,
321
327
  featureStore,
322
328
  };
@@ -335,6 +341,7 @@ export async function runNext(options, global) {
335
341
  const settings = await readSettings(pmRoot);
336
342
  const callerAuthor = resolveNextCallerAuthor(options, settings.author_default);
337
343
  const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);
344
+ const typeRegistry = resolveItemTypeRegistry(settings, getActiveExtensionRegistrations());
338
345
  const now = nowIso();
339
346
  const limit = parseNextLimit(options.limit, "--limit", DEFAULT_NEXT_LIMIT);
340
347
  const tokenBudget = resolveContextTokenBudget(options.tokenBudget, Math.max(192, limit * 128));
@@ -350,16 +357,18 @@ export async function runNext(options, global) {
350
357
  const corpusList = await runList(undefined, { excludeTerminal: false, noTruncate: true }, global);
351
358
  const candidates = filterCandidatesByParentScope(candidatesList.items, corpusList.items, parentScope);
352
359
  const corpus = corpusList.items;
353
- const report = computeActionabilityReport(candidates, corpus, statusRegistry);
360
+ const report = computeActionabilityReport(candidates, corpus, statusRegistry, typeRegistry);
354
361
  const childrenByParent = buildChildrenByParent(corpus);
355
- const partitionedByDecision = partitionDecisionEntries(report.ready, options.includeDecisions === true);
356
- const callerPartition = partitionCallerOwnedReady(partitionedByDecision.agent, settings.author_default, callerAuthor);
362
+ const selected = selectActionableEntries(report, statusRegistry, options, typeRegistry);
363
+ const callerPartition = partitionCallerOwnedReady(selected.ready, settings.author_default, callerAuthor);
357
364
  const rankedReady = rankNextReadyEntries(callerPartition.available, childrenByParent, statusRegistry);
358
365
  const rankedBlocked = [...report.blocked].sort((left, right) => compareCriticalItems(left.item, right.item, statusRegistry));
359
- const { projectedReady, ranking: readyRanking, completedContainer, packing: readyPacking, featureStore, } = await rankReadyEntriesWithRelevance(rankedReady, childrenByParent, statusRegistry, now, callerAuthor, pmRoot, tokenBudget);
366
+ const { projectedReady, ranking: readyRanking, completedContainer, packing: readyPacking, featureStore, } = await rankReadyEntriesWithRelevance(rankedReady, childrenByParent, statusRegistry, now, callerAuthor, pmRoot, tokenBudget, options.includeContainers === true);
360
367
  const readyRows = projectedReady.map((entry, index) => toNextActionableItem(entry, statusRegistry, childrenByParent, index + 1));
361
368
  const blockedRows = rankedBlocked.map((entry, index) => toNextActionableItem(entry, statusRegistry, childrenByParent, index + 1));
362
- const decisionRows = rankNextReadyEntries(partitionedByDecision.decisions, childrenByParent, statusRegistry).map((entry, index) => toNextActionableItem(entry, statusRegistry, childrenByParent, index + 1));
369
+ const decisionRows = rankNextReadyEntries(selected.decisions, childrenByParent, statusRegistry).map((entry, index) => toNextActionableItem(entry, statusRegistry, childrenByParent, index + 1));
370
+ const gateRows = rankNextReadyEntries(selected.gates, childrenByParent, statusRegistry).map((entry, index) => toNextActionableItem(entry, statusRegistry, childrenByParent, index + 1));
371
+ const containerRows = rankNextReadyEntries(report.containers, childrenByParent, statusRegistry).map((entry, index) => toNextActionableItem(entry, statusRegistry, childrenByParent, index + 1));
363
372
  const recommended = buildNextRecommendation({
364
373
  projectedReady,
365
374
  readyRows,
@@ -367,13 +376,19 @@ export async function runNext(options, global) {
367
376
  now,
368
377
  completedContainer,
369
378
  });
370
- const truncation = buildNextTruncation(decisionRows.length, callerPartition.held.length, limit);
379
+ const truncation = buildNextTruncation(decisionRows.length, callerPartition.held.length, limit) ?? {};
380
+ if (gateRows.length > limit)
381
+ truncation.gate_needed_total = gateRows.length;
382
+ if (containerRows.length > limit)
383
+ truncation.containers_total = containerRows.length;
371
384
  const result = {
372
385
  output_default: "toon",
373
386
  now,
374
387
  recommended,
375
388
  ready: readyRows.slice(1, limit + 1),
376
389
  decision_needed: decisionRows.slice(0, limit),
390
+ gate_needed: gateRows.slice(0, limit),
391
+ containers: containerRows.slice(0, limit),
377
392
  blocked: readyOnly ? [] : blockedRows.slice(0, blockedLimit),
378
393
  held_by_others: callerPartition.held.slice(0, limit),
379
394
  summary: {
@@ -384,6 +399,7 @@ export async function runNext(options, global) {
384
399
  candidates: report.active_count,
385
400
  containers: report.container_count,
386
401
  decision_needed: decisionRows.length,
402
+ gate_needed: gateRows.length,
387
403
  held_by_others: callerPartition.held.length,
388
404
  },
389
405
  filters: {
@@ -399,14 +415,17 @@ export async function runNext(options, global) {
399
415
  blocked_limit: blockedLimit,
400
416
  ready_only: readyOnly,
401
417
  include_decisions: options.includeDecisions === true,
418
+ include_gates: options.includeGates === true,
419
+ include_containers: options.includeContainers === true,
402
420
  token_budget: tokenBudget,
403
421
  },
404
- truncation,
422
+ truncation: Object.keys(truncation).length > 0 ? truncation : undefined,
405
423
  };
406
424
  return finalizeNextResult({
407
425
  result,
408
426
  recommended,
409
427
  blockedRows,
428
+ gateCount: gateRows.length,
410
429
  candidatesWarnings: candidatesList.warnings,
411
430
  corpusWarnings: corpusList.warnings,
412
431
  readyRanking,
@@ -424,6 +443,10 @@ export async function runNext(options, global) {
424
443
  */
425
444
  export function renderNextMarkdown(result) {
426
445
  const lines = ["# pm next", "", ...renderNextSummaryLines(result)];
446
+ for (const [title, rows] of [["Gate verification needed", result.gate_needed], ["Containers", result.containers]]) {
447
+ if (rows && rows.length > 0)
448
+ lines.push(...renderNextSection(title, rows.map((item) => `- ${formatNextLine(item)}`), ""));
449
+ }
427
450
  lines.push(...renderNextSection("Recommended", renderNextRecommendedRows(result.recommended), "No ready work."));
428
451
  lines.push(...renderNextSection("Decision needed",
429
452
  // `?? []` tolerates JSON payloads from pre-decision-queue builds (the
@@ -488,4 +511,4 @@ function formatNextLine(item) {
488
511
  return `${rankToken}${item.id} p${item.priority} ${item.status} ${item.type} deadline:${deadlineToken}${parentToken}${unblocksToken} ${item.title}`;
489
512
  }
490
513
  //# sourceMappingURL=next.js.map
491
- //# debugId=b0f3d72c-ff0b-59bb-a6bc-8476eb9fb6c1
514
+ //# debugId=6035e85b-81ed-512e-b291-b0d86af1848b
@@ -13,6 +13,7 @@ export { CONTEXT_OUTPUT_VALUES, applyContextTagProjection, buildChildrenByParent
13
13
  export * from "./query/get.js";
14
14
  export * from "./query/history.js";
15
15
  export * from "./query/next.js";
16
+ export * from "./actionability.js";
16
17
  export * from "./query/search-pagination.js";
17
18
  export * from "./query/search-rendering.js";
18
19
  export * from "./query/item-filter-options.js";
@@ -27,3 +28,5 @@ export * from "./context-signal-store.js";
27
28
  export * from "./context-usage.js";
28
29
  export * from "./workspace-memory.js";
29
30
  export * from "./duplicates.js";
31
+ export { analyzeDuplicateItems, type DuplicateItemInput, type DuplicateItemsAnalysis } from "./similarity.js";
32
+ export { measureItemMetadataReadWork, type ItemMetadataReadWork } from "../core/store/item-metadata-read-work.js";
package/dist/sdk/query.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Provides focused read, search, pagination, and context-ranking primitives.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="036baf5a-3ba9-55ea-a11b-d9e94a848b92")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="affe6060-5129-5914-9d4e-46c300d49080")}catch(e){}}();
8
8
  export * from "./query/list.js";
9
9
  export * from "./query/complete-list.js";
10
10
  export * from "./query/search.js";
@@ -15,6 +15,7 @@ export { CONTEXT_OUTPUT_VALUES, applyContextTagProjection, buildChildrenByParent
15
15
  export * from "./query/get.js";
16
16
  export * from "./query/history.js";
17
17
  export * from "./query/next.js";
18
+ export * from "./actionability.js";
18
19
  export * from "./query/search-pagination.js";
19
20
  export * from "./query/search-rendering.js";
20
21
  export * from "./query/item-filter-options.js";
@@ -29,5 +30,7 @@ export * from "./context-signal-store.js";
29
30
  export * from "./context-usage.js";
30
31
  export * from "./workspace-memory.js";
31
32
  export * from "./duplicates.js";
33
+ export { analyzeDuplicateItems } from "./similarity.js";
34
+ export { measureItemMetadataReadWork } from "../core/store/item-metadata-read-work.js";
32
35
  //# sourceMappingURL=query.js.map
33
- //# debugId=036baf5a-3ba9-55ea-a11b-d9e94a848b92
36
+ //# debugId=affe6060-5129-5914-9d4e-46c300d49080
@@ -5,7 +5,7 @@
5
5
  * budgets and resolvable references to item facts served by earlier reads.
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]="642f6dec-c0b2-5377-a401-dfa4f8ef6a2a")}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]="bb73f77d-d372-5d68-8ef2-90b208129f21")}catch(e){}}();
9
9
  import { EXIT_CODE } from "../core/shared/constants.js";
10
10
  import { PmCliError } from "../core/shared/errors.js";
11
11
  import { mapReadOutputRows, readOutputRowCollections, } from "./read-output-rows.js";
@@ -119,7 +119,9 @@ function summarizeReadOutputSessionRows(result) {
119
119
  continue;
120
120
  }
121
121
  const itemId = itemIdFromRow(row);
122
- if (itemId !== undefined)
122
+ // Activity can contain workspace pseudo-identities. Preserve those rows
123
+ // in the response, but only carry ids the next invocation can consume.
124
+ if (itemId !== undefined && ITEM_ID_PATTERN.test(itemId))
123
125
  deliveredIds.add(itemId);
124
126
  }
125
127
  }
@@ -204,4 +206,4 @@ export function readOutputSessionRemainingTokens(state) {
204
206
  return Math.max(0, state.token_budget - state.spent_tokens);
205
207
  }
206
208
  //# sourceMappingURL=read-output-session.js.map
207
- //# debugId=642f6dec-c0b2-5377-a401-dfa4f8ef6a2a
209
+ //# debugId=bb73f77d-d372-5d68-8ef2-90b208129f21
@@ -34,7 +34,7 @@ export { isTerminalStatus, normalizeStatusForRegistry, normalizeStatusInput, } f
34
34
  export { COMMON_MUTATION_COMMAND_OPTION_KEYS, type ItemTypeRegistry, type ResolvedItemTypeDefinition, canonicalizeCommandOptionKey, commandOptionFlagLabel, resolveCommandOptionPolicyState, resolveItemTypeRegistry, resolveTypeDefinition, resolveTypeName, validateTypeOptions, } from "../core/item/type-registry.js";
35
35
  export { resolveTypeSynonym } from "../core/item/type-synonyms.js";
36
36
  export { acquireLock } from "../core/lock/lock.js";
37
- export { printError, printResult, writeStdout } from "../core/output/output.js";
37
+ export { printError, printResult, writeStderr, writeStdout } from "../core/output/output.js";
38
38
  export { renderRowsAsCsv, renderRowsAsTable } from "../core/output/tabular.js";
39
39
  export { parseLimit } from "../core/shared/numeric-parsers.js";
40
40
  export { resolveConfiguredPmPackageRoot, resolvePmCliVersion, resolvePmPackageRootFromModule, } from "../core/packages/root.js";
@@ -7,7 +7,7 @@
7
7
  * prefer the typed operations exported by the main SDK barrel.
8
8
  */
9
9
 
10
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="aba5403c-0694-5c51-95a8-ae7e7575347b")}catch(e){}}();
10
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="619b20e3-78d9-55bd-9d2d-857a3bc1bee8")}catch(e){}}();
11
11
  export { createCheckpointId, loadMutationCheckpoint, restoreCheckpointItems, writeMutationCheckpoint, } from "../core/checkpoint/mutation-checkpoint.js";
12
12
  export { flattenFlagListValue, resolveFlagValueKind, } from "../core/extensions/flag-value-types.js";
13
13
  export { createUnknownSubcommandError, } from "./agent/subcommand-recovery.js";
@@ -36,7 +36,7 @@ export { isTerminalStatus, normalizeStatusForRegistry, normalizeStatusInput, } f
36
36
  export { COMMON_MUTATION_COMMAND_OPTION_KEYS, canonicalizeCommandOptionKey, commandOptionFlagLabel, resolveCommandOptionPolicyState, resolveItemTypeRegistry, resolveTypeDefinition, resolveTypeName, validateTypeOptions, } from "../core/item/type-registry.js";
37
37
  export { resolveTypeSynonym } from "../core/item/type-synonyms.js";
38
38
  export { acquireLock } from "../core/lock/lock.js";
39
- export { printError, printResult, writeStdout } from "../core/output/output.js";
39
+ export { printError, printResult, writeStderr, writeStdout } from "../core/output/output.js";
40
40
  export { renderRowsAsCsv, renderRowsAsTable } from "../core/output/tabular.js";
41
41
  export { parseLimit } from "../core/shared/numeric-parsers.js";
42
42
  export { resolveConfiguredPmPackageRoot, resolvePmCliVersion, resolvePmPackageRootFromModule, } from "../core/packages/root.js";
@@ -82,4 +82,4 @@ export { maybeRunFirstUseTelemetryPrompt } from "../core/telemetry/consent.js";
82
82
  export { deriveTelemetryCommandResolution, } from "../core/telemetry/observability.js";
83
83
  export { emitTelemetryErrorEvent, finishTelemetryCommand, startTelemetryCommand, } from "../core/telemetry/runtime.js";
84
84
  //# sourceMappingURL=runtime-primitives.js.map
85
- //# debugId=aba5403c-0694-5c51-95a8-ae7e7575347b
85
+ //# debugId=619b20e3-78d9-55bd-9d2d-857a3bc1bee8
@@ -4,7 +4,7 @@
4
4
  * Defines public SDK APIs and package-author helpers for Runtime.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="099c7cda-4a92-553c-ad60-00e185b5971b")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="422ff60b-97b1-5477-a7b8-ed4f983155e0")}catch(e){}}();
8
8
  export { PM_GITIGNORE_END, PM_GITIGNORE_START, ensurePmGitignore, getPmGitignoreBlock, } from "./workspace.js";
9
9
  export { SEARCH_EXTENSION_FLAG_DEFINITIONS } from "./extension-contracts.js";
10
10
  export * from "./cli-contracts/agent-output-contracts.js";
@@ -1770,11 +1770,13 @@ const SDK_ACTION_HANDLERS = {
1770
1770
  return runGet(id, ctx.global, applyContextIntentProjection("get", ctx.options, [id]));
1771
1771
  },
1772
1772
  search: runMcpSearchAction,
1773
+ /** Normalize MCP duplicate controls before delegating to the shared SDK action. */
1773
1774
  duplicates: (ctx) => {
1774
1775
  const status = typeof ctx.options.status === "string"
1775
1776
  ? [ctx.options.status]
1776
1777
  : readStringArray(ctx.options.status);
1777
1778
  return runDuplicates(ctx.global, {
1779
+ exhaustive: ctx.options.exhaustive === true,
1778
1780
  ...(status.length === 0 ? {} : { status }),
1779
1781
  since: readString(ctx.options, "since"),
1780
1782
  threshold: typeof ctx.options.threshold === "number"
@@ -1924,4 +1926,4 @@ async function loadWorkspaceExtensionRegistrations(pmRoot, settings, cwd) {
1924
1926
  }
1925
1927
  }
1926
1928
  //# sourceMappingURL=runtime.js.map
1927
- //# debugId=099c7cda-4a92-553c-ad60-00e185b5971b
1929
+ //# debugId=422ff60b-97b1-5477-a7b8-ed4f983155e0
@@ -1,3 +1,4 @@
1
+ import { collectDuplicateCandidatePairs } from "./query/duplicate-candidates.js";
1
2
  import type { ItemMetadata } from "../types/index.js";
2
3
  import { type PreparedSimilarityText } from "./similarity-scoring.js";
3
4
  export { jaccardSimilarity, normalizeSimilarityText, prepareSimilarityText, scoreItemSimilarity, scorePreparedItemSimilarity, tokenizeSimilarityText, type ItemSimilarityScore, type PreparedSimilarityText, } from "./similarity-scoring.js";
@@ -51,6 +52,8 @@ export interface SimilarItemsResult {
51
52
  }
52
53
  /** Query controls for one all-status duplicate-cluster sweep. */
53
54
  export interface FindDuplicateClustersOptions extends FindSimilarItemsOptions {
55
+ /** Compare every pair as an exact oracle; retains the declared safety bound. */
56
+ exhaustive?: boolean;
54
57
  /** Lifecycle statuses to include; omit to inspect every status. */
55
58
  statuses?: readonly string[];
56
59
  /** Inclusive ISO timestamp lower bound on item creation. */
@@ -97,6 +100,14 @@ export interface DuplicateClustersResult {
97
100
  cost: {
98
101
  /** Items retained after filters. */
99
102
  item_count: number;
103
+ /** Algorithm used to generate candidates before canonical scoring. */
104
+ algorithm: "prefix_exact" | "exhaustive";
105
+ /** Pairs in the exhaustive population, including pairs pruned without scoring. */
106
+ possible_pairs: number;
107
+ /** Safety ceiling, exceeded only by an explicit refusal. */
108
+ pair_limit: number;
109
+ /** Every qualifying pair is retained by candidate generation. */
110
+ recall_guarantee: "exact";
100
111
  /** Candidate pairs sharing at least one deterministic signal. */
101
112
  candidate_pairs: number;
102
113
  /** Candidate pairs scored. */
@@ -104,7 +115,7 @@ export interface DuplicateClustersResult {
104
115
  };
105
116
  }
106
117
  interface PreparedDuplicateItem {
107
- item: ItemMetadata;
118
+ item: Pick<ItemMetadata, "id" | "title" | "status" | "type">;
108
119
  prepared: PreparedSimilarityText;
109
120
  }
110
121
  /** Create/copy governance envelope attached when likely duplicates exist. */
@@ -131,7 +142,6 @@ export interface SimilarityRecovery {
131
142
  }
132
143
  /** Render compact warning tokens shared by create and copy results. */
133
144
  export declare function similarityAdvisoryWarnings(advisory: SimilarityAdvisory | undefined): string[];
134
- declare function collectDuplicateCandidatePairs(items: PreparedDuplicateItem[], maxPairEvaluations?: number): Set<string>;
135
145
  /** Internal deterministic seams used to prove bounded batch behavior without building a million-pair fixture. */
136
146
  export declare const _testOnlySimilarity: {
137
147
  buildDuplicateClusters: typeof buildDuplicateClusters;
@@ -142,10 +152,21 @@ declare function createDuplicateUnionFind(size: number): {
142
152
  parent: number[];
143
153
  findRoot: (index: number) => number;
144
154
  };
155
+ /** Group unioned endpoints and their scored evidence once, then deterministically rank and limit complete components. */
145
156
  declare function buildDuplicateClusters(items: PreparedDuplicateItem[], matches: DuplicateClusterMatch[], union: ReturnType<typeof createDuplicateUnionFind>, limit: number): DuplicateCluster[];
146
157
  /** Find likely existing duplicates through an index-first bounded read. */
147
158
  export declare function findSimilarItems(candidate: SimilarItemCandidate, options?: FindSimilarItemsOptions): Promise<SimilarItemsResult>;
148
- /** Find connected duplicate clusters with one metadata read and one prepared representation per item. */
159
+ /** Minimal metadata accepted from package-owned or remote project stores. */
160
+ export type DuplicateItemInput = Pick<ItemMetadata, "id" | "title" | "status" | "type">;
161
+ /** Exact in-memory duplicate analysis, independent of a filesystem workspace. */
162
+ export type DuplicateItemsAnalysis = Omit<DuplicateClustersResult, "source" | "filters">;
163
+ /**
164
+ * Cluster caller-owned metadata using the same scorer and safety bounds as the CLI.
165
+ * Candidate filtering is lossless; exhaustive mode provides an independent pair
166
+ * population for recall checks. Inputs are copied, sorted and never mutated.
167
+ */
168
+ export declare function analyzeDuplicateItems(input: readonly DuplicateItemInput[], options?: Pick<FindDuplicateClustersOptions, "threshold" | "limit" | "exhaustive">): DuplicateItemsAnalysis;
169
+ /** Find connected duplicate clusters with one metadata read and shared package analysis. */
149
170
  export declare function findDuplicateClusters(options?: FindDuplicateClustersOptions): Promise<DuplicateClustersResult>;
150
171
  /** Apply off/advisory/strict mutation governance through the shared query. */
151
172
  export declare function evaluateSimilarityGovernance(candidate: SimilarItemCandidate, options: FindSimilarItemsOptions & {
@@ -5,7 +5,7 @@
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]="33a43890-abcf-58fc-bf9d-bf2619d4cf35")}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]="9baf5711-9796-57e8-99e5-8990fd481ef8")}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";
@@ -16,6 +16,7 @@ import { resolvePmRoot } from "../core/store/paths.js";
16
16
  import { readSettings } from "../core/store/settings.js";
17
17
  import { readItemMetadataDerivedIndexState } from "../core/store/item-metadata-cache.js";
18
18
  import { querySimilarItemMetadataIndex } from "../core/store/item-metadata-query-index.js";
19
+ import { collectDuplicateCandidatePairs, MAX_DUPLICATE_CANDIDATE_PAIRS } from "./query/duplicate-candidates.js";
19
20
  import { prepareSimilarityText, scoreItemSimilarity, scorePreparedItemSimilarity, tokenizeSimilarityText, } from "./similarity-scoring.js";
20
21
  export { jaccardSimilarity, normalizeSimilarityText, prepareSimilarityText, scoreItemSimilarity, scorePreparedItemSimilarity, tokenizeSimilarityText, } from "./similarity-scoring.js";
21
22
  const DEFAULT_SIMILARITY_LIMIT = 3;
@@ -23,7 +24,6 @@ const MAX_SIMILARITY_LIMIT = 20;
23
24
  const DEFAULT_SIMILARITY_THRESHOLD = 0.8;
24
25
  const DEFAULT_DUPLICATE_CLUSTER_LIMIT = 100;
25
26
  const MAX_DUPLICATE_CLUSTER_LIMIT = 1_000;
26
- const MAX_BATCH_PAIR_EVALUATIONS = 1_000_000;
27
27
  /** Render compact warning tokens shared by create and copy results. */
28
28
  export function similarityAdvisoryWarnings(advisory) {
29
29
  return advisory === undefined
@@ -80,7 +80,8 @@ function validateDuplicateClusterOptions(options) {
80
80
  }
81
81
  return { limit, since };
82
82
  }
83
- async function loadPreparedDuplicateItems(options, since) {
83
+ /** Resolve registered statuses and load the metadata population before applying status and creation-time filters. */
84
+ async function loadDuplicateItems(options, since) {
84
85
  const pmRoot = resolvePmRoot(options.cwd ?? process.cwd(), options.pmRoot);
85
86
  const settings = await readSettings(pmRoot);
86
87
  const typeRegistry = resolveItemTypeRegistry(settings);
@@ -111,43 +112,10 @@ async function loadPreparedDuplicateItems(options, since) {
111
112
  return {
112
113
  items: (await listAllItemMetadataLight(pmRoot, settings.item_format, typeRegistry.type_to_folder, undefined, settings.schema))
113
114
  .filter((item) => (!allowedStatuses || allowedStatuses.has(item.status)) &&
114
- (!since || new Date(item.created_at).getTime() >= since.getTime()))
115
- .sort((left, right) => left.id.localeCompare(right.id))
116
- .map((item) => ({ item, prepared: prepareSimilarityText(item.title) })),
115
+ (!since || new Date(item.created_at).getTime() >= since.getTime())),
117
116
  statuses: effectiveStatuses,
118
117
  };
119
118
  }
120
- function collectDuplicateCandidatePairs(items, maxPairEvaluations = MAX_BATCH_PAIR_EVALUATIONS) {
121
- const candidates = new Set();
122
- const indexes = [
123
- new Map(),
124
- new Map(),
125
- new Map(),
126
- ];
127
- for (const [index, entry] of items.entries()) {
128
- const signals = [
129
- [entry.prepared.normalized],
130
- entry.prepared.issueCodes,
131
- entry.prepared.tokens,
132
- ];
133
- for (const [signalKind, values] of signals.entries()) {
134
- for (const value of values) {
135
- const matches = indexes[signalKind].get(value) ?? [];
136
- for (const other of matches)
137
- candidates.add(`${other}:${index}`);
138
- if (candidates.size > maxPairEvaluations) {
139
- throw new PmCliError(`Duplicate sweep exceeded ${MAX_BATCH_PAIR_EVALUATIONS} candidate pairs; narrow it with statuses or since.`, EXIT_CODE.CONFLICT, {
140
- code: "duplicate_sweep_cost_limit",
141
- required: "Narrow the batch duplicate query until its deterministic candidate set fits the disclosed safety bound.",
142
- });
143
- }
144
- matches.push(index);
145
- indexes[signalKind].set(value, matches);
146
- }
147
- }
148
- }
149
- return candidates;
150
- }
151
119
  /** Internal deterministic seams used to prove bounded batch behavior without building a million-pair fixture. */
152
120
  export const _testOnlySimilarity = {
153
121
  buildDuplicateClusters,
@@ -190,6 +158,7 @@ function scoreDuplicateCandidates(items, candidates, threshold, union) {
190
158
  }
191
159
  return matches;
192
160
  }
161
+ /** Group unioned endpoints and their scored evidence once, then deterministically rank and limit complete components. */
193
162
  function buildDuplicateClusters(items, matches, union, limit) {
194
163
  const components = new Map();
195
164
  for (const index of items.keys()) {
@@ -198,12 +167,19 @@ function buildDuplicateClusters(items, matches, union, limit) {
198
167
  component.push(index);
199
168
  components.set(root, component);
200
169
  }
170
+ const rootsById = new Map(items.map(({ item }, index) => [item.id, union.findRoot(index)]));
171
+ const matchesByRoot = new Map();
172
+ for (const match of matches) {
173
+ // Scored matches have already joined both endpoints in the union above.
174
+ const root = rootsById.get(match.left_id);
175
+ const rows = matchesByRoot.get(root) ?? [];
176
+ rows.push(match);
177
+ matchesByRoot.set(root, rows);
178
+ }
201
179
  return [...components.values()]
202
180
  .filter((component) => component.length > 1)
203
181
  .map((component) => {
204
- const ids = new Set(component.map((index) => items[index].item.id));
205
- const clusterMatches = matches
206
- .filter((match) => ids.has(match.left_id) && ids.has(match.right_id))
182
+ const clusterMatches = (matchesByRoot.get(union.findRoot(component[0])) ?? [])
207
183
  .sort((left, right) => right.score - left.score ||
208
184
  left.left_id.localeCompare(right.left_id) ||
209
185
  left.right_id.localeCompare(right.right_id));
@@ -272,15 +248,24 @@ export async function findSimilarItems(candidate, options = {}) {
272
248
  source: indexed ? "persistent_index" : "metadata_fallback",
273
249
  };
274
250
  }
275
- /** Find connected duplicate clusters with one metadata read and one prepared representation per item. */
276
- export async function findDuplicateClusters(options = {}) {
277
- const { threshold } = validateSimilarityOptions({
278
- ...options,
279
- limit: undefined,
280
- });
281
- const { limit, since } = validateDuplicateClusterOptions(options);
282
- const { items, statuses } = await loadPreparedDuplicateItems(options, since);
283
- const candidates = collectDuplicateCandidatePairs(items);
251
+ /**
252
+ * Cluster caller-owned metadata using the same scorer and safety bounds as the CLI.
253
+ * Candidate filtering is lossless; exhaustive mode provides an independent pair
254
+ * population for recall checks. Inputs are copied, sorted and never mutated.
255
+ */
256
+ export function analyzeDuplicateItems(input, options = {}) {
257
+ const { threshold } = validateSimilarityOptions({ ...options, limit: undefined });
258
+ const { limit } = validateDuplicateClusterOptions(options);
259
+ const ids = new Set();
260
+ for (const item of input) {
261
+ if (!item.id.trim() || ids.has(item.id)) {
262
+ throw new PmCliError("Duplicate analysis requires unique non-empty item ids.", EXIT_CODE.USAGE);
263
+ }
264
+ ids.add(item.id);
265
+ }
266
+ const items = [...input].sort((left, right) => left.id.localeCompare(right.id))
267
+ .map((item) => ({ item, prepared: prepareSimilarityText(item.title) }));
268
+ const candidates = collectDuplicateCandidatePairs(items, MAX_DUPLICATE_CANDIDATE_PAIRS, threshold, options.exhaustive === true);
284
269
  const union = createDuplicateUnionFind(items.length);
285
270
  const matches = scoreDuplicateCandidates(items, candidates, threshold, union);
286
271
  const clusters = buildDuplicateClusters(items, matches, union, limit);
@@ -288,18 +273,28 @@ export async function findDuplicateClusters(options = {}) {
288
273
  clusters,
289
274
  count: clusters.length,
290
275
  threshold,
291
- source: "metadata_scan",
292
- filters: {
293
- statuses: statuses ?? null,
294
- since: options.since ?? null,
295
- },
296
276
  cost: {
297
277
  item_count: items.length,
278
+ algorithm: options.exhaustive === true || threshold === 0 ? "exhaustive" : "prefix_exact",
279
+ possible_pairs: items.length * (items.length - 1) / 2,
280
+ pair_limit: MAX_DUPLICATE_CANDIDATE_PAIRS,
281
+ recall_guarantee: "exact",
298
282
  candidate_pairs: candidates.size,
299
283
  scored_pairs: candidates.size,
300
284
  },
301
285
  };
302
286
  }
287
+ /** Find connected duplicate clusters with one metadata read and shared package analysis. */
288
+ export async function findDuplicateClusters(options = {}) {
289
+ validateSimilarityOptions({ ...options, limit: undefined });
290
+ const { since } = validateDuplicateClusterOptions(options);
291
+ const { items, statuses } = await loadDuplicateItems(options, since);
292
+ return {
293
+ ...analyzeDuplicateItems(items, options),
294
+ source: "metadata_scan",
295
+ filters: { statuses: statuses ?? null, since: options.since ?? null },
296
+ };
297
+ }
303
298
  /** Apply off/advisory/strict mutation governance through the shared query. */
304
299
  export async function evaluateSimilarityGovernance(candidate, options) {
305
300
  if (options.mode === "off")
@@ -329,4 +324,4 @@ export async function evaluateSimilarityGovernance(candidate, options) {
329
324
  return { mode: options.mode, bypassed, result, recovery };
330
325
  }
331
326
  //# sourceMappingURL=similarity.js.map
332
- //# debugId=33a43890-abcf-58fc-bf9d-bf2619d4cf35
327
+ //# debugId=9baf5711-9796-57e8-99e5-8990fd481ef8
package/dist/types.d.ts CHANGED
@@ -428,6 +428,8 @@ export interface ItemTypeDefinition {
428
428
  description?: string;
429
429
  /** Status assigned to newly created items of this type when `--status` is not provided. Falls back to the workflow's open status when unset or invalid. */
430
430
  default_status?: string;
431
+ /** Agent work can be dispatched automatically; human work needs a decision, and gates track outcomes. Omission inherits the built-in role or defaults custom types to agent work. */
432
+ execution_role?: "agent" | "human" | "gate";
431
433
  /** Value that configures or reports folder for this contract. */
432
434
  folder?: string;
433
435
  /** Value that configures or reports aliases for this contract. */
package/dist/types.js CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
  /** Supported values accepted by the builtin item type contract. */
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]="4ee067ac-f28d-5411-9ac7-c92112384a64")}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]="8173bdb0-39c8-56c0-85c2-5c39f5c213cc")}catch(e){}}();
9
9
  export const BUILTIN_ITEM_TYPE_VALUES = [
10
10
  "Epic",
11
11
  "Feature",
@@ -243,4 +243,4 @@ export const CONTEXT_SECTION_VALUES = [
243
243
  "tests",
244
244
  ];
245
245
  //# sourceMappingURL=types.js.map
246
- //# debugId=4ee067ac-f28d-5411-9ac7-c92112384a64
246
+ //# debugId=8173bdb0-39c8-56c0-85c2-5c39f5c213cc