@unbrained/pm-cli 2026.9.3 → 2026.9.5

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 (228) 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 +52 -0
  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 +15 -6
  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 +6 -2
  21. package/dist/cli-bundle/bundle-manifest.json +154 -154
  22. package/dist/cli-bundle/chunks/{chunk-EDPIS7EB.js → chunk-3HBDTJRK.js} +2 -2
  23. package/dist/cli-bundle/chunks/chunk-66VPKFP2.js +5 -0
  24. package/dist/cli-bundle/chunks/{chunk-Y4PJ6HPK.js → chunk-HUAQITVW.js} +2 -2
  25. package/dist/cli-bundle/chunks/{chunk-DKGP3QLE.js → chunk-IBYREGSP.js} +2 -2
  26. package/dist/cli-bundle/chunks/chunk-LUI4E32I.js +231 -0
  27. package/dist/cli-bundle/chunks/{chunk-UPPM2FOZ.js → chunk-QPZHQY2O.js} +2 -2
  28. package/dist/cli-bundle/chunks/{chunk-34HASX3S.js → chunk-SF6SM6U5.js} +2 -2
  29. package/dist/cli-bundle/chunks/chunk-SVK5Y64D.js +40 -0
  30. package/dist/cli-bundle/chunks/chunk-V5GBD5KV.js +9 -0
  31. package/dist/cli-bundle/chunks/{chunk-LENC656S.js → chunk-WWAEGW4V.js} +67 -70
  32. package/dist/cli-bundle/chunks/chunk-ZRAQS3UA.js +8 -0
  33. package/dist/cli-bundle/chunks/{register-list-query-W2ZTOMKP.js → register-list-query-AYKGODWT.js} +3 -3
  34. package/dist/cli-bundle/chunks/{register-mutation-RVND5NCG.js → register-mutation-YZYG53SQ.js} +4 -4
  35. package/dist/cli-bundle/chunks/register-operations-KFSCC7QV.js +2 -0
  36. package/dist/cli-bundle/chunks/{register-setup-D3ZFHY3L.js → register-setup-UJCRVGBG.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/chunk-24IG6ONG.js +2 -0
  38. package/dist/cli-bundle/focused-chunks/{chunk-M5M6SVHS.js → chunk-3ASU6WGI.js} +2 -2
  39. package/dist/cli-bundle/focused-chunks/chunk-3ITLIBKS.js +56 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-3NQD5YDG.js +2 -0
  41. package/dist/cli-bundle/focused-chunks/{chunk-S27XMBIN.js → chunk-4OSHLNUE.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/chunk-65N6JHAN.js +2 -0
  43. package/dist/cli-bundle/focused-chunks/{chunk-5RFXUPN5.js → chunk-B2TTQGFG.js} +2 -2
  44. package/dist/cli-bundle/focused-chunks/chunk-GUBM3RGY.js +20 -0
  45. package/dist/cli-bundle/focused-chunks/{chunk-Q45NHPF2.js → chunk-H5FOGAVD.js} +2 -2
  46. package/dist/cli-bundle/focused-chunks/chunk-JX3RCVCX.js +2 -0
  47. package/dist/cli-bundle/focused-chunks/{chunk-D7KXFBXV.js → chunk-KWWCBWFO.js} +2 -2
  48. package/dist/cli-bundle/focused-chunks/chunk-MV752SUC.js +12 -0
  49. package/dist/cli-bundle/focused-chunks/chunk-PKIPMCFS.js +6 -0
  50. package/dist/cli-bundle/focused-chunks/{chunk-4HJFY4MB.js → chunk-QL66QNWG.js} +2 -2
  51. package/dist/cli-bundle/focused-chunks/chunk-RJMR2JIF.js +9 -0
  52. package/dist/cli-bundle/focused-chunks/chunk-WIUPWIZR.js +177 -0
  53. package/dist/cli-bundle/focused-chunks/chunk-WOCIPMW7.js +2 -0
  54. package/dist/cli-bundle/focused-chunks/{chunk-GT7MXDLW.js → chunk-YEF5PQQ2.js} +2 -2
  55. package/dist/cli-bundle/main.js +22 -15
  56. package/dist/cli-bundle/sdk-authoring.js +1 -1
  57. package/dist/cli-bundle/sdk-contracts.js +1 -1
  58. package/dist/cli-bundle/sdk-core.js +29 -32
  59. package/dist/cli-bundle/sdk-governance.js +1 -1
  60. package/dist/cli-bundle/sdk-graph.js +1 -1
  61. package/dist/cli-bundle/sdk-merge.js +45 -37
  62. package/dist/cli-bundle/sdk-query.js +1 -1
  63. package/dist/cli-bundle/sdk-runtime.js +1 -1
  64. package/dist/cli-bundle/sdk-testing.js +1 -1
  65. package/dist/cli-bundle/sdk.js +10 -10
  66. package/dist/core/diagnostics/remediation.js +4 -4
  67. package/dist/core/extensions/extension-types.d.ts +2 -0
  68. package/dist/core/extensions/extension-types.js +2 -2
  69. package/dist/core/history/drift-scan.d.ts +5 -0
  70. package/dist/core/history/drift-scan.js +16 -6
  71. package/dist/core/history/event-classification.d.ts +1 -1
  72. package/dist/core/history/event-classification.js +3 -2
  73. package/dist/core/history/history.d.ts +37 -1
  74. package/dist/core/history/history.js +311 -59
  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 +70 -36
  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/lock/lock.d.ts +10 -0
  91. package/dist/core/lock/lock.js +42 -3
  92. package/dist/core/shared/author.d.ts +3 -1
  93. package/dist/core/shared/author.js +111 -23
  94. package/dist/core/shared/errors.d.ts +8 -0
  95. package/dist/core/shared/errors.js +2 -2
  96. package/dist/core/store/item-store.d.ts +14 -0
  97. package/dist/core/store/item-store.js +48 -10
  98. package/dist/core/store/settings-validator.js +3 -2
  99. package/dist/sdk/actionability.d.ts +1 -1
  100. package/dist/sdk/actionability.js +3 -3
  101. package/dist/sdk/cli-contracts/command-aliases.js +11 -2
  102. package/dist/sdk/cli-contracts/completeness.js +4 -3
  103. package/dist/sdk/cli-contracts/flag-contracts.js +29 -5
  104. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +19 -12
  105. package/dist/sdk/cli-contracts/tool-parameter-tables.js +14 -2
  106. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  107. package/dist/sdk/cli-contracts/tool-schema.js +19 -6
  108. package/dist/sdk/context-intent-contracts.js +3 -3
  109. package/dist/sdk/context-usage.js +9 -11
  110. package/dist/sdk/define.d.ts +1 -1
  111. package/dist/sdk/define.js +2 -2
  112. package/dist/sdk/environment/agent-environment.d.ts +296 -0
  113. package/dist/sdk/environment/agent-environment.js +467 -0
  114. package/dist/sdk/environment/project-runtime-compatibility.d.ts +19 -1
  115. package/dist/sdk/environment/project-runtime-compatibility.js +118 -21
  116. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +98 -38
  117. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +50 -2
  118. package/dist/sdk/governance/health.js +13 -44
  119. package/dist/sdk/governance/merge-receipt-history.d.ts +62 -0
  120. package/dist/sdk/governance/merge-receipt-history.js +207 -0
  121. package/dist/sdk/governance/provenance-health.js +9 -3
  122. package/dist/sdk/governance/validate-history-drift.js +16 -5
  123. package/dist/sdk/guide-topics.js +116 -2
  124. package/dist/sdk/history/salvage.d.ts +35 -0
  125. package/dist/sdk/history/salvage.js +248 -0
  126. package/dist/sdk/history-compact.js +41 -37
  127. package/dist/sdk/history-read.d.ts +3 -2
  128. package/dist/sdk/history-read.js +6 -3
  129. package/dist/sdk/history-redact.js +58 -23
  130. package/dist/sdk/history-repair.d.ts +5 -0
  131. package/dist/sdk/history-repair.js +28 -6
  132. package/dist/sdk/index.d.ts +2 -0
  133. package/dist/sdk/index.js +4 -2
  134. package/dist/sdk/lifecycle/claim.d.ts +4 -0
  135. package/dist/sdk/lifecycle/claim.js +9 -3
  136. package/dist/sdk/lifecycle/restore.d.ts +1 -0
  137. package/dist/sdk/lifecycle/restore.js +19 -7
  138. package/dist/sdk/merge/driver.d.ts +3 -1
  139. package/dist/sdk/merge/driver.js +9 -9
  140. package/dist/sdk/merge/index.d.ts +1 -1
  141. package/dist/sdk/merge/index.js +3 -3
  142. package/dist/sdk/merge/receipts.d.ts +17 -6
  143. package/dist/sdk/merge/receipts.js +246 -75
  144. package/dist/sdk/merge/reconcile.d.ts +9 -7
  145. package/dist/sdk/merge/reconcile.js +175 -12
  146. package/dist/sdk/merge/three-way.d.ts +22 -2
  147. package/dist/sdk/merge/three-way.js +92 -25
  148. package/dist/sdk/output-projection.d.ts +3 -1
  149. package/dist/sdk/output-projection.js +11 -3
  150. package/dist/sdk/public-surface.json +1 -0
  151. package/dist/sdk/query/context.d.ts +9 -0
  152. package/dist/sdk/query/context.js +33 -21
  153. package/dist/sdk/query/get.d.ts +2 -2
  154. package/dist/sdk/query/get.js +4 -3
  155. package/dist/sdk/query/history.d.ts +11 -0
  156. package/dist/sdk/query/history.js +34 -10
  157. package/dist/sdk/query/next.d.ts +15 -1
  158. package/dist/sdk/query/next.js +43 -20
  159. package/dist/sdk/query.d.ts +1 -0
  160. package/dist/sdk/query.js +3 -2
  161. package/dist/sdk/runtime-primitives.d.ts +3 -3
  162. package/dist/sdk/runtime-primitives.js +5 -5
  163. package/dist/types.d.ts +27 -0
  164. package/dist/types.js +2 -2
  165. package/docs/AGENT_ENVIRONMENTS.md +47 -0
  166. package/docs/AGENT_GUIDE.md +36 -0
  167. package/docs/AGENT_PROVENANCE_ADR.md +17 -2
  168. package/docs/CONTEXT_INTEGRITY_CONTRACTS.md +38 -1
  169. package/docs/HISTORY_RECOVERY.md +146 -0
  170. package/docs/MERGE_SAFETY.md +119 -23
  171. package/docs/README.md +2 -0
  172. package/docs/RELEASING.md +25 -11
  173. package/docs/SDK_CONTEXT_EVIDENCE_CONTRACTS.md +24 -2
  174. package/docs/SDK_CONTEXT_TRUTH_CONTRACTS.md +24 -1
  175. package/docs/SDK_EXECUTION_CONTRACTS.md +105 -0
  176. package/docs/SDK_RUNTIME_BOUNDARIES.md +17 -1
  177. package/docs/TESTING.md +5 -0
  178. package/docs/agent-task-token-baseline.json +17 -17
  179. package/docs/agent-task-transcripts.json +2 -1
  180. package/docs/examples/sdk-agent-environment/README.md +14 -0
  181. package/docs/examples/sdk-agent-environment/package.json +8 -0
  182. package/docs/examples/sdk-agent-environment/run-agent-environment.mjs +154 -0
  183. package/docs/generated/FLAG_LEXICON_BUDGETS.md +10 -10
  184. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +12 -3
  185. package/marketplace.json +2 -2
  186. package/package.json +5 -6
  187. package/packages/pm-beads/package.json +1 -1
  188. package/packages/pm-calendar/package.json +1 -1
  189. package/packages/pm-command-kit/package.json +1 -1
  190. package/packages/pm-digital-twin/package.json +1 -1
  191. package/packages/pm-governance-audit/package.json +1 -1
  192. package/packages/pm-guide-shell/package.json +1 -1
  193. package/packages/pm-kanban/package.json +1 -1
  194. package/packages/pm-lifecycle-hooks/package.json +1 -1
  195. package/packages/pm-linked-test-adapters/package.json +1 -1
  196. package/packages/pm-search-advanced/package.json +1 -1
  197. package/packages/pm-templates/package.json +1 -1
  198. package/packages/pm-todos/package.json +1 -1
  199. package/packages/pm-vcs/package.json +1 -1
  200. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  201. package/plugins/pm-claude/skills/pm-audit/SKILL.md +2 -2
  202. package/plugins/pm-claude/skills/pm-developer/SKILL.md +2 -2
  203. package/plugins/pm-claude/skills/pm-planner/SKILL.md +2 -2
  204. package/plugins/pm-claude/skills/pm-release/SKILL.md +2 -2
  205. package/plugins/pm-claude/skills/pm-workflow/SKILL.md +3 -3
  206. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  207. package/plugins/pm-codex/skills/pm-auditor/SKILL.md +2 -2
  208. package/plugins/pm-codex/skills/pm-native/SKILL.md +3 -4
  209. package/plugins/pm-codex/skills/pm-release/SKILL.md +2 -2
  210. package/scripts/finalize-build.mjs +13 -1
  211. package/dist/cli-bundle/chunks/chunk-AY324UJ5.js +0 -9
  212. package/dist/cli-bundle/chunks/chunk-E5RBBCXO.js +0 -230
  213. package/dist/cli-bundle/chunks/chunk-IEHHE6GA.js +0 -5
  214. package/dist/cli-bundle/chunks/chunk-L7B7343B.js +0 -8
  215. package/dist/cli-bundle/chunks/chunk-LISESA7W.js +0 -35
  216. package/dist/cli-bundle/chunks/register-operations-TTROIVC4.js +0 -2
  217. package/dist/cli-bundle/focused-chunks/chunk-3ZBI35HU.js +0 -176
  218. package/dist/cli-bundle/focused-chunks/chunk-4RRHC3S4.js +0 -6
  219. package/dist/cli-bundle/focused-chunks/chunk-4WR46RMG.js +0 -9
  220. package/dist/cli-bundle/focused-chunks/chunk-BWKHEIVF.js +0 -2
  221. package/dist/cli-bundle/focused-chunks/chunk-GHCZ3YFR.js +0 -20
  222. package/dist/cli-bundle/focused-chunks/chunk-KBLZRU62.js +0 -2
  223. package/dist/cli-bundle/focused-chunks/chunk-KZ2NB3ET.js +0 -2
  224. package/dist/cli-bundle/focused-chunks/chunk-OL5EH6I3.js +0 -2
  225. package/dist/cli-bundle/focused-chunks/chunk-QKHCSUGJ.js +0 -51
  226. package/dist/cli-bundle/focused-chunks/chunk-TXE4TSHM.js +0 -12
  227. package/dist/cli-bundle/focused-chunks/chunk-UCLO3XDU.js +0 -2
  228. package/sdk/public-surface.json +0 -32524
@@ -0,0 +1,15 @@
1
+ /** @module core/history/identity
2
+ * Identifies multiple genesis records without inferring an unrecorded writer.
3
+ */
4
+ import type { HistoryEntry } from "../../types/index.js";
5
+ /** Evidence of a second subject occupying one append-only identity stream. */
6
+ export interface HistoryIdentityDiscontinuity {
7
+ /** One-based physical ordinal of the original create or retained baseline. */
8
+ prior_genesis_index: number;
9
+ /** One-based physical ordinal of the later create record. */
10
+ repeated_create_index: number;
11
+ /** Observed record sequence, not an assertion about branch or writer origin. */
12
+ sequence: "delete_then_create" | "multiple_creates" | "checkpoint_then_create";
13
+ }
14
+ /** Find every repeated genesis, including histories that start at a checkpoint. */
15
+ export declare function findHistoryIdentityDiscontinuities(entries: readonly HistoryEntry[]): HistoryIdentityDiscontinuity[];
@@ -0,0 +1,31 @@
1
+ /** Find every repeated genesis, including histories that start at a checkpoint. */
2
+
3
+ !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]="ffaea7bd-daa4-5ff5-8cd5-82ad04e2256e")}catch(e){}}();
4
+ export function findHistoryIdentityDiscontinuities(entries) {
5
+ const findings = [];
6
+ const checkpoint = entries[0]?.op === "history_compact_baseline";
7
+ let genesis = checkpoint ? 1 : undefined;
8
+ let deleted = false;
9
+ for (const [index, entry] of entries.entries()) {
10
+ if (entry.op === "delete")
11
+ deleted = true;
12
+ if (entry.op !== "create")
13
+ continue;
14
+ if (genesis !== undefined) {
15
+ findings.push({
16
+ prior_genesis_index: genesis,
17
+ repeated_create_index: index + 1,
18
+ sequence: deleted
19
+ ? "delete_then_create"
20
+ : checkpoint
21
+ ? "checkpoint_then_create"
22
+ : "multiple_creates",
23
+ });
24
+ }
25
+ genesis ??= index + 1;
26
+ deleted = false;
27
+ }
28
+ return findings;
29
+ }
30
+ //# sourceMappingURL=identity.js.map
31
+ //# debugId=ffaea7bd-daa4-5ff5-8cd5-82ad04e2256e
@@ -6,10 +6,11 @@
6
6
  * replay kernel so read and write paths cannot disagree about a version.
7
7
  */
8
8
 
9
- !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]="47c087c2-124a-58b7-a5c9-09403235f4a2")}catch(e){}}();
9
+ !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]="2efbffc1-722c-5f3a-9ca5-e4e7a4f97729")}catch(e){}}();
10
10
  import { EXIT_CODE } from "../shared/constants.js";
11
11
  import { PmCliError } from "../shared/errors.js";
12
- import { EMPTY_REPLAY_DOCUMENT, replayHash, tryApplyReplayPatch, } from "./replay.js";
12
+ import { historyVersionOffset } from "./version-address.js";
13
+ import { EMPTY_REPLAY_DOCUMENT, verifyHistoryChain, tryApplyReplayPatch, } from "./replay.js";
13
14
  /** Find the final history entry recorded at or before a parsed timestamp. */
14
15
  function findHistoryIndexAtTimestamp(history, parsedTarget) {
15
16
  let historyIndex = -1;
@@ -25,7 +26,7 @@ function findHistoryIndexAtTimestamp(history, parsedTarget) {
25
26
  return historyIndex;
26
27
  }
27
28
  /** Build structured target-range guidance once for every resolver error path. */
28
- function buildHistoryTargetGuidance(history) {
29
+ function buildHistoryTargetGuidance(history, offset) {
29
30
  const firstTimestamp = history[0]?.ts ?? null;
30
31
  const lastTimestamp = history.at(-1)?.ts ?? null;
31
32
  const guidance = {
@@ -44,9 +45,12 @@ function buildHistoryTargetGuidance(history) {
44
45
  },
45
46
  };
46
47
  if (lastTimestamp !== null) {
47
- guidance.examples = ["1", String(history.length), lastTimestamp];
48
- guidance.valid_range.first_version = 1;
49
- guidance.valid_range.last_version = history.length;
48
+ guidance.examples = [lastTimestamp];
49
+ if (offset !== null) {
50
+ guidance.examples.unshift(String(offset + 1), String(offset + history.length));
51
+ guidance.valid_range.first_version = offset + 1;
52
+ guidance.valid_range.last_version = offset + history.length;
53
+ }
50
54
  }
51
55
  return { guidance, lastTimestamp };
52
56
  }
@@ -90,18 +94,25 @@ export function resolveHistoryTarget(target, history, options = {}) {
90
94
  const trimmed = target.trim();
91
95
  const errorSubject = options.errorSubject ?? "history";
92
96
  const errorSubjectTitle = errorSubject === "restore" ? "Restore" : "History";
93
- const { guidance, lastTimestamp } = buildHistoryTargetGuidance(history);
97
+ const { guidance, lastTimestamp } = buildHistoryTargetGuidance(history, historyVersionOffset(history, true));
94
98
  if (!trimmed) {
95
99
  throw new PmCliError(`Missing ${errorSubject} target. Use a timestamp or version number.`, EXIT_CODE.USAGE, guidance);
96
100
  }
97
101
  if (/^\d+$/.test(trimmed)) {
102
+ const offset = historyVersionOffset(history);
98
103
  const version = Number(trimmed);
104
+ if (version >= 1 && version <= offset) {
105
+ throw new PmCliError(`History version ${version} was pruned by compaction.`, EXIT_CODE.USAGE, {
106
+ ...guidance,
107
+ code: "history_version_pruned",
108
+ });
109
+ }
99
110
  if (!Number.isSafeInteger(version) ||
100
111
  version < 1 ||
101
- version > history.length) {
102
- throw new PmCliError(`${errorSubjectTitle} version must be between 1 and ${history.length} for this item.`, EXIT_CODE.USAGE, guidance);
112
+ version > offset + history.length) {
113
+ throw new PmCliError(`${errorSubjectTitle} version must be between ${offset + 1} and ${offset + history.length} for this item.`, EXIT_CODE.USAGE, guidance);
103
114
  }
104
- return { kind: "version", raw: trimmed, historyIndex: version - 1 };
115
+ return { kind: "version", raw: trimmed, historyIndex: version - offset - 1 };
105
116
  }
106
117
  const parsedTarget = Date.parse(trimmed);
107
118
  if (!Number.isFinite(parsedTarget)) {
@@ -137,28 +148,50 @@ export function applyHistoryPatch(current, patch, entryNumber, entryOp) {
137
148
  }
138
149
  /** Replay and hash-verify a history stream through a zero-based target entry. */
139
150
  export function replayHistoryToTarget(history, targetIndex) {
151
+ const first = history[0];
152
+ if (first && first.op !== "create" && first.op !== "history_compact_baseline") {
153
+ const baseline = tryApplyReplayPatch(EMPTY_REPLAY_DOCUMENT, first.patch);
154
+ if (!baseline.ok || typeof baseline.document.metadata.id !== "string") {
155
+ throw new PmCliError("History starts without a reconstructable item baseline.", EXIT_CODE.GENERIC_FAILURE, {
156
+ code: "history_baseline_unavailable",
157
+ required: "Recover the original create entry or a complete checkpoint from version control.",
158
+ nextSteps: ["Run pm history <id> --full to inspect the missing baseline."],
159
+ });
160
+ }
161
+ }
162
+ const selected = history.slice(0, targetIndex + 1);
163
+ const verification = verifyHistoryChain([...selected]);
140
164
  let document = structuredClone(EMPTY_REPLAY_DOCUMENT);
141
165
  for (let index = 0; index <= targetIndex; index += 1) {
142
166
  const entry = history[index];
143
167
  if (!entry) {
144
168
  throw new PmCliError(`History target entry ${index + 1} does not exist.`, EXIT_CODE.USAGE);
145
169
  }
146
- if (replayHash(document) !== entry.before_hash) {
147
- throw new PmCliError(`History hash mismatch before replay at entry ${index + 1}.`, EXIT_CODE.GENERIC_FAILURE);
148
- }
149
170
  document = applyHistoryPatch(document, entry.patch, index + 1, entry.op);
150
- if (replayHash(document) !== entry.after_hash) {
151
- throw new PmCliError(`History hash mismatch after replay at entry ${index + 1}.`, EXIT_CODE.GENERIC_FAILURE);
152
- }
171
+ }
172
+ if (!verification.ok) {
173
+ throw new PmCliError(`History cannot be replayed: ${verification.errors.join(", ")}.`, EXIT_CODE.GENERIC_FAILURE, {
174
+ code: "history_replay_invalid",
175
+ verification_errors: verification.errors,
176
+ required: "Inspect the history integrity findings before attempting recovery.",
177
+ nextSteps: ["Run pm history <id> --verify --full."],
178
+ });
153
179
  }
154
180
  return document;
155
181
  }
156
182
  /** Reject a version whose reconstructed state represents a deleted item. */
157
183
  export function ensureMaterializedHistoryTarget(replayDocument, target) {
158
184
  if (Object.keys(replayDocument.metadata).length > 0) {
185
+ if (typeof replayDocument.metadata.id !== "string" || !Array.isArray(replayDocument.metadata.tags)) {
186
+ throw new PmCliError("History has no complete item baseline for this target.", EXIT_CODE.GENERIC_FAILURE, {
187
+ code: "history_baseline_unavailable",
188
+ required: "Recover the original create entry or a complete checkpoint from version control.",
189
+ nextSteps: ["Run pm history <id> --full to inspect the first recorded state."],
190
+ });
191
+ }
159
192
  return replayDocument;
160
193
  }
161
194
  throw new PmCliError(`History target ${target.raw} resolves to a deleted state; choose a version or timestamp where the item exists.`, EXIT_CODE.USAGE);
162
195
  }
163
196
  //# sourceMappingURL=projection.js.map
164
- //# debugId=47c087c2-124a-58b7-a5c9-09403235f4a2
197
+ //# debugId=2efbffc1-722c-5f3a-9ca5-e4e7a4f97729
@@ -7,7 +7,7 @@
7
7
  * maintenance APIs from depending on presentation-layer modules.
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]="aa65b74d-ef5d-57ed-b613-3034da52a8ba")}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]="999c126a-112a-50f6-b53e-bd36dbc5b885")}catch(e){}}();
11
11
  import { readFileIfExists } from "../fs/fs-utils.js";
12
12
  import { runActiveOnReadHooks } from "../extensions/index.js";
13
13
  import { EXIT_CODE } from "../shared/constants.js";
@@ -46,10 +46,13 @@ export async function readHistoryEntries(historyPath, itemId) {
46
46
  entries.push(JSON.parse(line));
47
47
  }
48
48
  catch {
49
- throw new PmCliError(`History for ${itemId} contains invalid JSON at line ${index + 1}. Repair or restore the history stream and retry.`, EXIT_CODE.GENERIC_FAILURE);
49
+ throw new PmCliError(`History for ${itemId} contains invalid JSON at line ${index + 1}. Repair or restore the history stream and retry.`, EXIT_CODE.GENERIC_FAILURE, {
50
+ code: "history_json_invalid",
51
+ nextSteps: [`Run pm history-repair ${itemId} --salvage-tail --dry-run to check whether only an invalid suffix is damaged.`],
52
+ });
50
53
  }
51
54
  }
52
55
  return entries;
53
56
  }
54
57
  //# sourceMappingURL=read.js.map
55
- //# debugId=aa65b74d-ef5d-57ed-b613-3034da52a8ba
58
+ //# debugId=999c126a-112a-50f6-b53e-bd36dbc5b885
@@ -47,7 +47,6 @@ export type ReplayApplyResult = {
47
47
  };
48
48
  /** Strictly apply a history patch (front_matter->metadata normalized) to a replay document. Returns a result envelope rather than throwing so each caller can format its own error contract. */
49
49
  export declare function tryApplyReplayPatch(current: ReplayDocument, patch: HistoryPatchOp[]): ReplayApplyResult;
50
- /** Deterministically verify a history chain: each entry's before_hash must equal the prior replayed after_hash, the patch must strictly apply, and the recorded after_hash must equal the replayed result. */
51
50
  /** Verify a chain and report the explicit or auto-detected item hash epoch. */
52
51
  export declare function verifyHistoryChainWithVersion(entries: HistoryEntry[]): {
53
52
  ok: boolean;
@@ -119,6 +118,6 @@ export interface ReanchorHistoryOptions {
119
118
  */
120
119
  export declare function resolveHistoryRepairItemHashVersion(entries: HistoryEntry[]): HistoryItemHashVersion;
121
120
  /** Re-anchor a drifted history chain: replay every entry from empty, recompute the before/after hashes, and only rewrite a patch when the original op set no longer strictly applies (legacy drift). Clean entries keep their patch verbatim so the on-disk diff stays minimal. The returned chain verifies via verifyHistoryChain. */
122
- export declare function reanchorHistoryEntries(entries: HistoryEntry[], itemHashVersion?: 1 | 2 | 3, options?: ReanchorHistoryOptions): ReanchorResult;
121
+ export declare function reanchorHistoryEntries(entries: HistoryEntry[], itemHashVersion?: HistoryItemHashVersion, options?: ReanchorHistoryOptions): ReanchorResult;
123
122
  /** Implements history entries to raw for the public runtime surface of this module. */
124
123
  export declare function historyEntriesToRaw(entries: HistoryEntry[]): string;
@@ -4,13 +4,14 @@
4
4
  * Implements append-only history and replay behavior for Replay.
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]="c9488ed4-8228-5e7c-9717-69dd7a3275c4")}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]="b0230e20-93c4-531c-9cc1-85f85c1def7a")}catch(e){}}();
8
8
  import jsonPatch from "fast-json-patch";
9
+ import { findHistoryIdentityDiscontinuities } from "./identity.js";
9
10
  import { ITEM_METADATA_KEY_ORDER } from "../shared/constants.js";
10
11
  import { canonicalDocument } from "../item/item-format.js";
11
12
  import { toItemRecord } from "../item/item-record.js";
12
13
  import { orderObject, sha256Hex, stableStringify, } from "../shared/serialization.js";
13
- import { CURRENT_HISTORY_ITEM_HASH_VERSION, SUPPORTED_HISTORY_ITEM_HASH_VERSIONS, hashDocumentVerificationCandidates, } from "./history.js";
14
+ import { CURRENT_HISTORY_ITEM_HASH_VERSION, SUPPORTED_HISTORY_ITEM_HASH_VERSIONS, hashDocumentVerificationCandidates, resealHistoryRewrite, verifyHistoryRecordHash, verifyHistoryRewriteEvidence, } from "./history.js";
14
15
  /** Public contract for empty replay document, shared by SDK and presentation-layer consumers. */
15
16
  export const EMPTY_REPLAY_DOCUMENT = {
16
17
  metadata: {},
@@ -165,13 +166,37 @@ function historyEntryHashCandidates(explicitVersion, authoritativeVersion) {
165
166
  ? [...SUPPORTED_HISTORY_ITEM_HASH_VERSIONS]
166
167
  : [authoritativeVersion];
167
168
  }
168
- /** Deterministically verify a history chain: each entry's before_hash must equal the prior replayed after_hash, the patch must strictly apply, and the recorded after_hash must equal the replayed result. */
169
+ /** Return the first record or retained-rewrite integrity failure for one entry. */
170
+ function historyEntryIntegrityError(entry) {
171
+ const recordVerification = verifyHistoryRecordHash(entry);
172
+ if (!recordVerification.ok)
173
+ return recordVerification.error;
174
+ const rewriteVerification = verifyHistoryRewriteEvidence(entry);
175
+ return rewriteVerification.ok ? undefined : rewriteVerification.error;
176
+ }
177
+ /** Validate immutable metadata and apply one patch for chain verification. */
178
+ function verifyHistoryEntryInput(entry, replay) {
179
+ const integrityError = historyEntryIntegrityError(entry);
180
+ if (integrityError)
181
+ return { ok: false, error: integrityError };
182
+ const applied = tryApplyReplayPatch(replay, entry.patch);
183
+ return applied.ok
184
+ ? { ok: true, document: applied.document }
185
+ : { ok: false, error: "patch_apply_failed" };
186
+ }
187
+ /** Refuse any entry whose current or retained immutable envelope is invalid. */
188
+ function assertHistoryEntryIntegrity(entry, index) {
189
+ const integrityError = historyEntryIntegrityError(entry);
190
+ if (integrityError) {
191
+ throw new TypeError(`${integrityError}:entry_${index + 1}`);
192
+ }
193
+ }
169
194
  /** Verify a chain and report the explicit or auto-detected item hash epoch. */
170
195
  export function verifyHistoryChainWithVersion(entries) {
171
196
  let replay = cloneEmptyReplayDocument();
172
197
  let detectedVersion;
173
198
  let authoritativeExplicitVersion;
174
- const errors = [];
199
+ const errors = findHistoryIdentityDiscontinuities(entries).map((finding) => `verify_failed:duplicate_create:entry_${finding.repeated_create_index}:prior_${finding.prior_genesis_index}`);
175
200
  for (let index = 0; index < entries.length; index += 1) {
176
201
  const entry = entries[index];
177
202
  const explicitVersion = entry.item_hash_version;
@@ -179,18 +204,15 @@ export function verifyHistoryChainWithVersion(entries) {
179
204
  if (unsupportedVersion) {
180
205
  errors.push(`verify_failed:unsupported_item_hash_version:${String(explicitVersion)}:entry_${index + 1}`);
181
206
  }
182
- const applied = tryApplyReplayPatch(replay, entry.patch);
183
- if (!applied.ok) {
207
+ const input = verifyHistoryEntryInput(entry, replay);
208
+ if (!input.ok) {
184
209
  return {
185
210
  ok: false,
186
- errors: [
187
- ...errors,
188
- `verify_failed:patch_apply_failed:entry_${index + 1}`,
189
- ],
211
+ errors: [...errors, `verify_failed:${input.error}:entry_${index + 1}`],
190
212
  };
191
213
  }
192
214
  if (unsupportedVersion) {
193
- replay = applied.document;
215
+ replay = input.document;
194
216
  detectedVersion = undefined;
195
217
  authoritativeExplicitVersion = undefined;
196
218
  continue;
@@ -201,7 +223,7 @@ export function verifyHistoryChainWithVersion(entries) {
201
223
  const candidates = historyEntryHashCandidates(explicitVersion, authoritativeExplicitVersion);
202
224
  const candidateMatches = candidates.map((version) => ({
203
225
  version,
204
- match: matchReplayHashCandidates(replay, applied.document, version, entry),
226
+ match: matchReplayHashCandidates(replay, input.document, version, entry),
205
227
  }));
206
228
  const matchingVersion = candidateMatches.find(({ match }) => match.pairIndex >= 0);
207
229
  if (matchingVersion === undefined) {
@@ -215,7 +237,7 @@ export function verifyHistoryChainWithVersion(entries) {
215
237
  };
216
238
  }
217
239
  const version = matchingVersion.version;
218
- replay = applied.document;
240
+ replay = input.document;
219
241
  detectedVersion = version;
220
242
  authoritativeExplicitVersion =
221
243
  explicitVersion ??
@@ -304,6 +326,27 @@ export function resolveHistoryRepairItemHashVersion(entries) {
304
326
  }
305
327
  return CURRENT_HISTORY_ITEM_HASH_VERSION;
306
328
  }
329
+ /** Apply one patch strictly or return its deterministic lenient repair projection. */
330
+ function applyReanchorPatch(replay, entry) {
331
+ const strict = tryApplyReplayPatch(replay, entry.patch);
332
+ if (strict.ok) {
333
+ return {
334
+ next: strict.document,
335
+ patch: entry.patch,
336
+ repaired: false,
337
+ converted: 0,
338
+ skipped: 0,
339
+ };
340
+ }
341
+ const lenient = lenientApplyReplayPatch(replay, entry.patch);
342
+ return {
343
+ next: lenient.document,
344
+ patch: jsonPatch.compare(replay, lenient.document),
345
+ repaired: true,
346
+ converted: lenient.convertedReplaceToAdd,
347
+ skipped: lenient.skippedOps,
348
+ };
349
+ }
307
350
  /** Re-anchor a drifted history chain: replay every entry from empty, recompute the before/after hashes, and only rewrite a patch when the original op set no longer strictly applies (legacy drift). Clean entries keep their patch verbatim so the on-disk diff stays minimal. The returned chain verifies via verifyHistoryChain. */
308
351
  export function reanchorHistoryEntries(entries, itemHashVersion = resolveHistoryRepairItemHashVersion(entries), options = {}) {
309
352
  const unsupportedIndex = entries.findIndex((entry) => isUnsupportedExplicitHistoryItemHashVersion(entry.item_hash_version));
@@ -322,27 +365,12 @@ export function reanchorHistoryEntries(entries, itemHashVersion = resolveHistory
322
365
  entries.some((entry) => entry.item_hash_version !== undefined);
323
366
  for (let index = 0; index < entries.length; index += 1) {
324
367
  const entry = entries[index];
325
- const strict = tryApplyReplayPatch(replay, entry.patch);
326
- let next;
327
- let outPatch;
328
- let patchRepaired = false;
329
- let entryConverted = 0;
330
- let entrySkipped = 0;
331
- if (strict.ok) {
332
- next = strict.document;
333
- outPatch = entry.patch;
334
- }
335
- else {
336
- const lenient = lenientApplyReplayPatch(replay, entry.patch);
337
- next = lenient.document;
338
- outPatch = jsonPatch.compare(replay, next);
339
- patchRepaired = true;
340
- entryConverted = lenient.convertedReplaceToAdd;
341
- entrySkipped = lenient.skippedOps;
342
- convertedReplaceToAdd += entryConverted;
343
- skippedOps += entrySkipped;
344
- entriesPatchRepaired += 1;
345
- }
368
+ assertHistoryEntryIntegrity(entry, index);
369
+ const patchApplication = applyReanchorPatch(replay, entry);
370
+ const { next, patch: outPatch, repaired: patchRepaired, converted: entryConverted, skipped: entrySkipped, } = patchApplication;
371
+ convertedReplaceToAdd += entryConverted;
372
+ skippedOps += entrySkipped;
373
+ entriesPatchRepaired += Number(patchRepaired);
346
374
  const hashMatch = matchReplayHashCandidates(replay, next, itemHashVersion, entry);
347
375
  if (semanticIndex === undefined || options.continuousHashSurface !== true) {
348
376
  semanticIndex = [
@@ -361,7 +389,7 @@ export function reanchorHistoryEntries(entries, itemHashVersion = resolveHistory
361
389
  if (rehashed) {
362
390
  entriesRehashed += 1;
363
391
  }
364
- const rewrittenEntry = {
392
+ let rewrittenEntry = {
365
393
  ...entry,
366
394
  patch: outPatch,
367
395
  before_hash: beforeHash,
@@ -373,6 +401,12 @@ export function reanchorHistoryEntries(entries, itemHashVersion = resolveHistory
373
401
  else {
374
402
  delete rewrittenEntry.item_hash_version;
375
403
  }
404
+ const versionChanged = rewrittenEntry.item_hash_version !== entry.item_hash_version;
405
+ if (rehashed || patchRepaired || versionChanged) {
406
+ rewrittenEntry = resealHistoryRewrite(entry, rewrittenEntry, {
407
+ retainOriginalPatch: patchRepaired,
408
+ });
409
+ }
376
410
  rewritten.push(rewrittenEntry);
377
411
  details.push({
378
412
  index: index + 1,
@@ -403,4 +437,4 @@ export function historyEntriesToRaw(entries) {
403
437
  return `${entries.map((entry) => JSON.stringify(entry)).join("\n")}\n`;
404
438
  }
405
439
  //# sourceMappingURL=replay.js.map
406
- //# debugId=c9488ed4-8228-5e7c-9717-69dd7a3275c4
440
+ //# debugId=b0230e20-93c4-531c-9cc1-85f85c1def7a
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @module core/history/version-address
3
+ * Stable ordinal addressing for streams whose retained prefix is a checkpoint.
4
+ */
5
+ import type { HistoryEntry } from "../../types/index.js";
6
+ /**
7
+ * Return the number added to physical one-based positions to obtain durable
8
+ * version addresses. Legacy checkpoints cannot prove their original ordinal;
9
+ * refusing them prevents silently selecting a different historical state.
10
+ */
11
+ export declare function historyVersionOffset(history: readonly HistoryEntry[], allowUnknown: true): number | null;
12
+ /** Resolve a required durable offset, refusing ambiguous legacy checkpoints. */
13
+ export declare function historyVersionOffset(history: readonly HistoryEntry[], allowUnknown?: false): number;
@@ -0,0 +1,33 @@
1
+
2
+ !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]="7ad4e6c0-7545-51cb-9af7-4ba1ad4c4758")}catch(e){}}();
3
+ import { EXIT_CODE } from "../shared/constants.js";
4
+ import { PmCliError } from "../shared/errors.js";
5
+ /** Resolve the checkpoint offset with an explicit opt-in for unknown legacy addresses. */
6
+ export function historyVersionOffset(history, allowUnknown = false) {
7
+ const first = history[0];
8
+ if (first?.op !== "history_compact_baseline")
9
+ return 0;
10
+ const receipt = first.context?.history_compaction;
11
+ const offset = typeof receipt === "object" &&
12
+ receipt !== null &&
13
+ "version_offset" in receipt
14
+ ? receipt.version_offset
15
+ : undefined;
16
+ if (typeof offset === "number" &&
17
+ Number.isSafeInteger(offset) &&
18
+ offset >= 0 &&
19
+ Number.isSafeInteger(offset + history.length)) {
20
+ return offset;
21
+ }
22
+ if (allowUnknown)
23
+ return null;
24
+ throw new PmCliError("This compacted stream has no trustworthy version offset; numeric history addresses are unavailable.", EXIT_CODE.USAGE, {
25
+ code: "history_version_mapping_unavailable",
26
+ required: "Use a retained timestamp or recover the pre-compaction stream from version control.",
27
+ nextSteps: [
28
+ "Run pm history <id> --full to inspect the retained timestamps and checkpoint evidence.",
29
+ ],
30
+ });
31
+ }
32
+ //# sourceMappingURL=version-address.js.map
33
+ //# debugId=7ad4e6c0-7545-51cb-9af7-4ba1ad4c4758
@@ -1,5 +1,8 @@
1
1
  import type { RuntimeStatusRegistry } from "../schema/runtime-schema.js";
2
2
  import type { ItemMetadata, ItemStatus } from "../../types/index.js";
3
+ import { type ItemTypeRegistry } from "./type-registry.js";
4
+ /** Resolve dispatch semantics once for SDK callers, honoring custom and extension type aliases before built-in defaults. */
5
+ export declare function resolveItemExecutionRole(type: string, registry?: ItemTypeRegistry): "agent" | "human" | "gate";
3
6
  /** Built-in ordering kinds that must remain reachable by blocker resolution. */
4
7
  export declare const ACTIONABILITY_ORDERING_KINDS: readonly string[];
5
8
  /** Collects the blocker item ids declared by an item: the legacy scalar `blocked_by` field plus every `blocked_by` dependency edge. Ids are trimmed, de-duplicated, and returned in stable lexicographic order. This is the single source of truth for "what must close before this item can proceed", shared by `pm next` readiness classification and the close-time auto-unblock sweep. */
@@ -54,11 +57,28 @@ export interface ActionabilityReport {
54
57
  ready: ActionableEntry[];
55
58
  /** Active leaves with at least one open blocker — waiting to become ready. */
56
59
  blocked: ActionableEntry[];
60
+ /** Active containers with unresolved descendants, including their blocker evidence. */
61
+ containers: ActionableEntry[];
62
+ /** Unblocked leaf work that requires a human decision. */
63
+ decisions: ActionableEntry[];
64
+ /** Unblocked leaf outcome gates held out of automatic dispatch. */
65
+ gates: ActionableEntry[];
57
66
  /** Active candidates considered before the leaf/blocker split. */
58
67
  active_count: number;
59
68
  /** Active candidates skipped because they still have open descendants. */
60
69
  container_count: number;
61
70
  }
71
+ /** Explicit dispatch opt-ins; dependency and lifecycle blocking always remain enforced. */
72
+ export interface ActionabilitySelectionOptions {
73
+ /** Include human decision work in the executable selection. */
74
+ includeDecisions?: boolean;
75
+ /** Include outcome gates in the executable selection. */
76
+ includeGates?: boolean;
77
+ /** Include containers whose descendants are still unfinished. */
78
+ includeContainers?: boolean;
79
+ }
80
+ /** Select dispatch candidates from a classified report while retaining held-out worklists. Ownership and ranking remain caller responsibilities. */
81
+ export declare function selectActionableEntries(report: ActionabilityReport, statusRegistry: RuntimeStatusRegistry, options?: ActionabilitySelectionOptions, typeRegistry?: ItemTypeRegistry): Pick<ActionabilityReport, "ready" | "decisions" | "gates">;
62
82
  /**
63
83
  * Partitions a candidate set into ready vs blocked leaf work using the corpus to
64
84
  * resolve blockers, descendants, and downstream dependents.
@@ -70,4 +90,4 @@ export interface ActionabilityReport {
70
90
  * blockers and the non-terminal items it would unblock. Returned lists preserve
71
91
  * the candidate order so the caller can apply its own ranking.
72
92
  */
73
- export declare function computeActionabilityReport(candidates: ItemMetadata[], corpus: ItemMetadata[], statusRegistry: RuntimeStatusRegistry): ActionabilityReport;
93
+ export declare function computeActionabilityReport(candidates: ItemMetadata[], corpus: ItemMetadata[], statusRegistry: RuntimeStatusRegistry, typeRegistry?: ItemTypeRegistry): ActionabilityReport;
@@ -10,9 +10,15 @@
10
10
  * `blocked_by` resolution a single source of truth.
11
11
  */
12
12
 
13
- !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]="0605f770-d868-5fe9-a186-45f00d2e93b4")}catch(e){}}();
13
+ !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]="7d98ca40-57e8-5afe-8e9d-b694da0130d1")}catch(e){}}();
14
14
  import { isTerminalStatus, normalizeStatusForRegistry } from "./status.js";
15
+ import { BUILTIN_ITEM_EXECUTION_ROLES, resolveTypeDefinition } from "./type-registry.js";
15
16
  import { isExternalDependencyReference, isExternalDependencySourceKind, } from "./dependency-reference.js";
17
+ /** Resolve dispatch semantics once for SDK callers, honoring custom and extension type aliases before built-in defaults. */
18
+ export function resolveItemExecutionRole(type, registry) {
19
+ const definition = registry === undefined ? undefined : resolveTypeDefinition(type, registry);
20
+ return definition?.execution_role ?? BUILTIN_ITEM_EXECUTION_ROLES[type.trim().toLowerCase()] ?? "agent";
21
+ }
16
22
  /** Dependency kind that marks "this item is blocked by the referenced item". */
17
23
  const BLOCKED_BY_DEPENDENCY_KIND = "blocked_by";
18
24
  /** Reverse dependency kind that marks "this item blocks the referenced item". */
@@ -177,6 +183,24 @@ export function collectDependencyBlockedIds(corpus, statusRegistry) {
177
183
  }
178
184
  return blocked;
179
185
  }
186
+ /** Select dispatch candidates from a classified report while retaining held-out worklists. Ownership and ranking remain caller responsibilities. */
187
+ export function selectActionableEntries(report, statusRegistry, options = {}, typeRegistry) {
188
+ const candidates = [...report.ready, ...report.decisions, ...report.gates];
189
+ if (options.includeContainers === true) {
190
+ candidates.push(...report.containers.filter((entry) => entry.open_blockers.length === 0 && !statusRegistry.blocked_statuses.has(normalizeStatusForRegistry(entry.item.status, statusRegistry))));
191
+ }
192
+ const selected = { ready: [], decisions: [], gates: [] };
193
+ for (const entry of candidates) {
194
+ const role = resolveItemExecutionRole(entry.item.type, typeRegistry);
195
+ if (role === "human" && options.includeDecisions !== true)
196
+ selected.decisions.push(entry);
197
+ else if (role === "gate" && options.includeGates !== true)
198
+ selected.gates.push(entry);
199
+ else
200
+ selected.ready.push(entry);
201
+ }
202
+ return selected;
203
+ }
180
204
  /** Resolves the registry's active status set, falling back to just the canonical open status when a custom schema declares none — the same safety net `pm context` applies so the report never misclassifies every item as inactive. */
181
205
  function resolveActiveStatusSet(statusRegistry) {
182
206
  /* c8 ignore start -- fallback applies only to custom schemas that intentionally define zero active statuses */
@@ -242,7 +266,7 @@ function indexCorpus(corpus) {
242
266
  * blockers and the non-terminal items it would unblock. Returned lists preserve
243
267
  * the candidate order so the caller can apply its own ranking.
244
268
  */
245
- export function computeActionabilityReport(candidates, corpus, statusRegistry) {
269
+ export function computeActionabilityReport(candidates, corpus, statusRegistry, typeRegistry) {
246
270
  const { itemsById, childrenByParent, blockedByReverse, blockerIdsByItem } = indexCorpus(corpus);
247
271
  const activeStatuses = new Set([
248
272
  ...resolveActiveStatusSet(statusRegistry),
@@ -255,16 +279,14 @@ export function computeActionabilityReport(candidates, corpus, statusRegistry) {
255
279
  .map((entry) => normalizeItemId(entry.id)));
256
280
  const ready = [];
257
281
  const blocked = [];
282
+ const containers = [];
283
+ const decisions = [];
284
+ const gates = [];
258
285
  let activeCount = 0;
259
- let containerCount = 0;
260
286
  for (const item of candidates) {
261
287
  if (!activeStatuses.has(normalizeStatusForRegistry(item.status, statusRegistry)))
262
288
  continue;
263
289
  activeCount += 1;
264
- if (hasOpenDescendant(item.id, childrenByParent, statusRegistry)) {
265
- containerCount += 1;
266
- continue;
267
- }
268
290
  const openBlockers = resolveItemBlockersWithIndex(item, itemsById, statusRegistry, blockerIdsByItem).filter((blocker) => !blocker.resolved);
269
291
  const unblocks = (blockedByReverse.get(normalizeItemId(item.id)) ?? [])
270
292
  .filter((dependentId) => nonTerminalIds.has(normalizeItemId(dependentId)))
@@ -274,9 +296,19 @@ export function computeActionabilityReport(candidates, corpus, statusRegistry) {
274
296
  open_blockers: openBlockers,
275
297
  unblocks,
276
298
  };
299
+ if (hasOpenDescendant(item.id, childrenByParent, statusRegistry)) {
300
+ containers.push(entry);
301
+ continue;
302
+ }
277
303
  const lifecycleBlocked = statusRegistry.blocked_statuses.has(normalizeStatusForRegistry(item.status, statusRegistry));
278
304
  if (openBlockers.length === 0 && !lifecycleBlocked) {
279
- ready.push(entry);
305
+ const role = resolveItemExecutionRole(item.type, typeRegistry);
306
+ if (role === "human")
307
+ decisions.push(entry);
308
+ else if (role === "gate")
309
+ gates.push(entry);
310
+ else
311
+ ready.push(entry);
280
312
  }
281
313
  else {
282
314
  blocked.push(entry);
@@ -285,9 +317,12 @@ export function computeActionabilityReport(candidates, corpus, statusRegistry) {
285
317
  return {
286
318
  ready,
287
319
  blocked,
320
+ containers,
321
+ decisions,
322
+ gates,
288
323
  active_count: activeCount,
289
- container_count: containerCount,
324
+ container_count: containers.length,
290
325
  };
291
326
  }
292
327
  //# sourceMappingURL=actionability.js.map
293
- //# debugId=0605f770-d868-5fe9-a186-45f00d2e93b4
328
+ //# debugId=7d98ca40-57e8-5afe-8e9d-b694da0130d1
@@ -4,7 +4,7 @@
4
4
  * Defines item parsing, formatting, and lifecycle helpers for Id.
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]="6b3f1585-ab12-57ca-80eb-d3cebc384c52")}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]="04f02467-a4ee-5616-9fbf-3df1141efccf")}catch(e){}}();
8
8
  import crypto from "node:crypto";
9
9
  import path from "node:path";
10
10
  import { TYPE_TO_FOLDER } from "../shared/constants.js";
@@ -60,11 +60,13 @@ function randomToken(length) {
60
60
  }
61
61
  return token;
62
62
  }
63
+ /** Reserve an identity when either a live item path or its retained history exists. */
63
64
  async function idExists(pmRoot, id, typeToFolder) {
64
65
  const checks = [...new Set(Object.values(typeToFolder))].flatMap((folder) => [
65
66
  path.join(pmRoot, folder, `${id}.md`),
66
67
  path.join(pmRoot, folder, `${id}.toon`),
67
68
  ]);
69
+ checks.push(path.join(pmRoot, "history", `${id}.jsonl`));
68
70
  return (await Promise.all(checks.map((target) => pathExists(target)))).some(Boolean);
69
71
  }
70
72
  /** Bounds accepted for the configurable random id token length (`ids.token_length`). */
@@ -95,8 +97,10 @@ export async function generateItemId(pmRoot, prefix, options = {}) {
95
97
  while (tokenLength <= maxTokenLength) {
96
98
  for (let i = 0; i < 32; i += 1) {
97
99
  const id = `${normalizePrefix(prefix)}${randomToken(tokenLength)}`;
98
- if (options.probeExisting === false ||
99
- !(await idExists(pmRoot, id, options.typeToFolder ?? TYPE_TO_FOLDER))) {
100
+ const occupied = await (options.probeExisting === false
101
+ ? pathExists(path.join(pmRoot, "history", `${id}.jsonl`))
102
+ : idExists(pmRoot, id, options.typeToFolder ?? TYPE_TO_FOLDER));
103
+ if (!occupied) {
100
104
  return id;
101
105
  }
102
106
  attempts += 1;
@@ -106,4 +110,4 @@ export async function generateItemId(pmRoot, prefix, options = {}) {
106
110
  throw new Error(`Unable to generate unique id after ${attempts} attempts`);
107
111
  }
108
112
  //# sourceMappingURL=id.js.map
109
- //# debugId=6b3f1585-ab12-57ca-80eb-d3cebc384c52
113
+ //# debugId=04f02467-a4ee-5616-9fbf-3df1141efccf