@unbrained/pm-cli 2026.8.20 → 2026.8.22

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/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +38 -0
  3. package/dist/cli/error-guidance.d.ts +29 -4
  4. package/dist/cli/error-guidance.js +43 -43
  5. package/dist/cli/main.d.ts +3 -0
  6. package/dist/cli/main.js +40 -7
  7. package/dist/cli-bundle/bundle-manifest.json +150 -150
  8. package/dist/cli-bundle/chunks/{chunk-L4LGPBJU.js → chunk-BWX4SK46.js} +58 -58
  9. package/dist/cli-bundle/chunks/chunk-EMWDM5FI.js +13 -0
  10. package/dist/cli-bundle/chunks/chunk-HYG6LRL4.js +200 -0
  11. package/dist/cli-bundle/chunks/{chunk-TSMWLCHS.js → chunk-MYYI3ZOA.js} +2 -2
  12. package/dist/cli-bundle/chunks/{chunk-4GB2LTOQ.js → chunk-MZTZQTMS.js} +2 -2
  13. package/dist/cli-bundle/chunks/{chunk-7BRF2BX2.js → chunk-QI2CT3IX.js} +2 -2
  14. package/dist/cli-bundle/chunks/chunk-SMWXHMVB.js +35 -0
  15. package/dist/cli-bundle/chunks/{chunk-US2ZQW2M.js → chunk-U6GQ4VMV.js} +47 -47
  16. package/dist/cli-bundle/chunks/{chunk-K3EN3LK2.js → chunk-VMMIL3SI.js} +2 -2
  17. package/dist/cli-bundle/chunks/chunk-WG4STC36.js +5 -0
  18. package/dist/cli-bundle/chunks/{chunk-XYDDWBZN.js → chunk-YBPJAOQQ.js} +3 -3
  19. package/dist/cli-bundle/chunks/{register-list-query-JSAQCVOC.js → register-list-query-HZKBQNWW.js} +2 -2
  20. package/dist/cli-bundle/chunks/{register-mutation-XJPAW63D.js → register-mutation-HTKEEGRD.js} +2 -2
  21. package/dist/cli-bundle/chunks/{register-operations-DSOSB2XQ.js → register-operations-6EMRWJJY.js} +2 -2
  22. package/dist/cli-bundle/chunks/{register-setup-QUR6QL2C.js → register-setup-QK4OTOXN.js} +2 -2
  23. package/dist/cli-bundle/focused-chunks/{chunk-TDR5OGG3.js → chunk-2ARQGDDW.js} +2 -2
  24. package/dist/cli-bundle/focused-chunks/{chunk-AZBACT5T.js → chunk-2AZT5WXZ.js} +2 -2
  25. package/dist/cli-bundle/focused-chunks/{chunk-M32HFIQB.js → chunk-2POVTNLY.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-3LKU6PTJ.js +4 -0
  27. package/dist/cli-bundle/focused-chunks/chunk-5SHSBMX2.js +2 -0
  28. package/dist/cli-bundle/focused-chunks/{chunk-UGSS4F5X.js → chunk-6S2MO3Q3.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/{chunk-5L7M4AI7.js → chunk-A2BC5PQL.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/chunk-C4S4A4JJ.js +5 -0
  31. package/dist/cli-bundle/focused-chunks/{chunk-OM65KHGF.js → chunk-D6RBZEJJ.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/chunk-E2EWYPY6.js +16 -0
  33. package/dist/cli-bundle/focused-chunks/{chunk-G3C5ERXG.js → chunk-GMYPD4FX.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/chunk-L5LDSRO2.js +155 -0
  35. package/dist/cli-bundle/focused-chunks/{chunk-BLJCGGID.js → chunk-MB37AYN6.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/chunk-MCOR37QK.js +2 -0
  37. package/dist/cli-bundle/focused-chunks/{chunk-FUREQLQN.js → chunk-PH2OXXVZ.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/{chunk-325UXIO7.js → chunk-PMGJUFNQ.js} +2 -2
  39. package/dist/cli-bundle/focused-chunks/chunk-R6SR45XU.js +50 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-WZE4LIPW.js +8 -0
  41. package/dist/cli-bundle/main.js +13 -13
  42. package/dist/cli-bundle/sdk-authoring.js +1 -1
  43. package/dist/cli-bundle/sdk-contracts.js +2 -2
  44. package/dist/cli-bundle/sdk-core.js +40 -40
  45. package/dist/cli-bundle/sdk-governance.js +1 -1
  46. package/dist/cli-bundle/sdk-graph.js +1 -1
  47. package/dist/cli-bundle/sdk-merge.js +32 -32
  48. package/dist/cli-bundle/sdk-query.js +1 -1
  49. package/dist/cli-bundle/sdk-runtime.js +1 -1
  50. package/dist/cli-bundle/sdk-testing.js +1 -1
  51. package/dist/cli-bundle/sdk.js +5 -5
  52. package/dist/core/extensions/extension-hook-runtime.js +22 -20
  53. package/dist/core/extensions/extension-types.d.ts +8 -1
  54. package/dist/core/extensions/extension-types.js +2 -2
  55. package/dist/core/extensions/loader.js +7 -3
  56. package/dist/core/extensions/service-ownership.d.ts +8 -0
  57. package/dist/core/extensions/service-ownership.js +20 -0
  58. package/dist/core/item/id.js +20 -5
  59. package/dist/core/item/item-format.js +27 -2
  60. package/dist/core/shared/errors.d.ts +20 -0
  61. package/dist/core/shared/errors.js +2 -2
  62. package/dist/core/store/item-store.js +6 -91
  63. package/dist/core/store/tracker-preflight.d.ts +7 -0
  64. package/dist/core/store/tracker-preflight.js +142 -0
  65. package/dist/sdk/agent/refusal-reachability.d.ts +1 -1
  66. package/dist/sdk/agent/tracker-preflight-contracts.d.ts +63 -0
  67. package/dist/sdk/agent/tracker-preflight-contracts.js +127 -0
  68. package/dist/sdk/annotations.js +6 -7
  69. package/dist/sdk/authoring.d.ts +1 -1
  70. package/dist/sdk/authoring.js +2 -2
  71. package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +77 -0
  72. package/dist/sdk/cli-contracts/agent-output-contracts.js +324 -2
  73. package/dist/sdk/cli-contracts/commander-mutation-options.js +3 -3
  74. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +3 -1
  75. package/dist/sdk/cli-contracts/runtime-contracts.js +4 -3
  76. package/dist/sdk/cli-contracts.d.ts +1 -1
  77. package/dist/sdk/cli-contracts.js +3 -3
  78. package/dist/sdk/config.d.ts +5 -0
  79. package/dist/sdk/config.js +5 -5
  80. package/dist/sdk/context-intent-runtime.js +7 -4
  81. package/dist/sdk/contracts.d.ts +1 -0
  82. package/dist/sdk/contracts.js +3 -2
  83. package/dist/sdk/dependencies.js +5 -7
  84. package/dist/sdk/dependency-flag-validation.d.ts +1 -1
  85. package/dist/sdk/dependency-flag-validation.js +10 -4
  86. package/dist/sdk/environment/tracker-preflight.d.ts +6 -0
  87. package/dist/sdk/environment/tracker-preflight.js +10 -0
  88. package/dist/sdk/eval.js +5 -7
  89. package/dist/sdk/extension/install-sources.d.ts +1 -1
  90. package/dist/sdk/extension/install-sources.js +6 -3
  91. package/dist/sdk/extension/output-ownership.d.ts +1 -0
  92. package/dist/sdk/extension/output-ownership.js +3 -2
  93. package/dist/sdk/files.js +6 -10
  94. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +75 -15
  95. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +81 -8
  96. package/dist/sdk/governance/assurance.d.ts +36 -2
  97. package/dist/sdk/governance/assurance.js +184 -10
  98. package/dist/sdk/governance/gc.js +5 -6
  99. package/dist/sdk/governance/health.d.ts +3 -0
  100. package/dist/sdk/governance/health.js +42 -11
  101. package/dist/sdk/governance/hierarchy-validation.d.ts +29 -0
  102. package/dist/sdk/governance/hierarchy-validation.js +109 -0
  103. package/dist/sdk/governance/reindex.js +5 -6
  104. package/dist/sdk/governance/validate.d.ts +2 -9
  105. package/dist/sdk/governance/validate.js +51 -109
  106. package/dist/sdk/graph/assembly.d.ts +6 -1
  107. package/dist/sdk/graph/assembly.js +16 -22
  108. package/dist/sdk/graph/governance-contracts.d.ts +1 -1
  109. package/dist/sdk/graph/governance-contracts.js +8 -2
  110. package/dist/sdk/graph/governance.d.ts +7 -5
  111. package/dist/sdk/graph/governance.js +76 -16
  112. package/dist/sdk/graph/hierarchy-integrity.d.ts +94 -0
  113. package/dist/sdk/graph/hierarchy-integrity.js +311 -0
  114. package/dist/sdk/graph/index.d.ts +2 -1
  115. package/dist/sdk/graph/index.js +3 -2
  116. package/dist/sdk/graph/remediation.js +32 -2
  117. package/dist/sdk/graph/run.d.ts +31 -2
  118. package/dist/sdk/graph/run.js +83 -36
  119. package/dist/sdk/graph/traversal.js +14 -5
  120. package/dist/sdk/graph/workspace-relationship-kind-registry.d.ts +8 -0
  121. package/dist/sdk/graph/workspace-relationship-kind-registry.js +28 -0
  122. package/dist/sdk/history-compact.js +6 -9
  123. package/dist/sdk/history-read.js +5 -7
  124. package/dist/sdk/history-redact.js +5 -6
  125. package/dist/sdk/history-repair.js +6 -9
  126. package/dist/sdk/improvement-ledger.js +7 -10
  127. package/dist/sdk/index.d.ts +4 -2
  128. package/dist/sdk/index.js +5 -3
  129. package/dist/sdk/item-children.js +21 -8
  130. package/dist/sdk/lifecycle/append.d.ts +0 -5
  131. package/dist/sdk/lifecycle/append.js +5 -6
  132. package/dist/sdk/lifecycle/claim.d.ts +0 -5
  133. package/dist/sdk/lifecycle/claim.js +6 -9
  134. package/dist/sdk/lifecycle/close-many.d.ts +0 -5
  135. package/dist/sdk/lifecycle/close-many.js +5 -6
  136. package/dist/sdk/lifecycle/close.d.ts +0 -5
  137. package/dist/sdk/lifecycle/close.js +5 -6
  138. package/dist/sdk/lifecycle/copy.d.ts +0 -5
  139. package/dist/sdk/lifecycle/copy.js +5 -6
  140. package/dist/sdk/lifecycle/create.d.ts +0 -5
  141. package/dist/sdk/lifecycle/create.js +127 -102
  142. package/dist/sdk/lifecycle/delete.js +5 -6
  143. package/dist/sdk/lifecycle/focus.d.ts +0 -5
  144. package/dist/sdk/lifecycle/focus.js +5 -6
  145. package/dist/sdk/lifecycle/hierarchy-mutation-lock.d.ts +15 -0
  146. package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +25 -0
  147. package/dist/sdk/lifecycle/plan.d.ts +0 -5
  148. package/dist/sdk/lifecycle/plan.js +5 -6
  149. package/dist/sdk/lifecycle/restore.js +5 -6
  150. package/dist/sdk/lifecycle/update-many.d.ts +0 -5
  151. package/dist/sdk/lifecycle/update-many.js +5 -6
  152. package/dist/sdk/lifecycle/update.d.ts +2 -5
  153. package/dist/sdk/lifecycle/update.js +211 -84
  154. package/dist/sdk/linked-artifacts.js +6 -7
  155. package/dist/sdk/merge/install.js +5 -6
  156. package/dist/sdk/mutation-events.js +5 -7
  157. package/dist/sdk/package-import-adapters.js +6 -8
  158. package/dist/sdk/profile.js +6 -7
  159. package/dist/sdk/query/activity.js +5 -6
  160. package/dist/sdk/query/calendar.js +5 -6
  161. package/dist/sdk/query/complete-list.d.ts +3 -3
  162. package/dist/sdk/query/complete-list.js +46 -3
  163. package/dist/sdk/query/get.d.ts +0 -5
  164. package/dist/sdk/query/get.js +5 -6
  165. package/dist/sdk/query/history.d.ts +0 -5
  166. package/dist/sdk/query/history.js +5 -6
  167. package/dist/sdk/query/list.d.ts +4 -3
  168. package/dist/sdk/query/list.js +92 -54
  169. package/dist/sdk/query/search.js +5 -7
  170. package/dist/sdk/read-output-contracts.d.ts +5 -1
  171. package/dist/sdk/read-output-contracts.js +87 -22
  172. package/dist/sdk/relationship-context.js +3 -4
  173. package/dist/sdk/relationship-kinds/contract.d.ts +8 -0
  174. package/dist/sdk/relationship-kinds/contract.js +13 -13
  175. package/dist/sdk/relationships.d.ts +1 -1
  176. package/dist/sdk/relationships.js +27 -2
  177. package/dist/sdk/runtime.d.ts +3 -2
  178. package/dist/sdk/runtime.js +3 -2
  179. package/dist/sdk/schema-migration.js +6 -7
  180. package/dist/sdk/schema.js +5 -6
  181. package/dist/sdk/stats.js +5 -8
  182. package/dist/sdk/templates.js +5 -6
  183. package/dist/sdk/test/batch.js +15 -9
  184. package/dist/sdk/test/execution.js +12 -5
  185. package/dist/sdk/test/runs.js +5 -7
  186. package/dist/types.d.ts +11 -0
  187. package/dist/types.js +2 -2
  188. package/docs/AGENT_UX_CONTRACTS.md +24 -1
  189. package/docs/COMMANDS.md +29 -14
  190. package/docs/DEPENDENCY_KIND_CONTRACT.md +40 -1
  191. package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +98 -0
  192. package/docs/EXTENSIONS.md +2 -1
  193. package/docs/README.md +1 -0
  194. package/docs/READ_OUTPUT_CONTRACTS.md +9 -2
  195. package/docs/RECURRENCE_RECOVERY_CONTRACTS.md +37 -0
  196. package/docs/RELATIONSHIP_GRAPH.md +97 -12
  197. package/docs/SDK_PRIMITIVE_INVENTORY.md +15 -1
  198. package/docs/TESTING.md +15 -0
  199. package/marketplace.json +2 -2
  200. package/package.json +1 -1
  201. package/packages/pm-beads/package.json +1 -1
  202. package/packages/pm-calendar/package.json +1 -1
  203. package/packages/pm-command-kit/package.json +1 -1
  204. package/packages/pm-digital-twin/package.json +1 -1
  205. package/packages/pm-governance-audit/package.json +1 -1
  206. package/packages/pm-guide-shell/package.json +1 -1
  207. package/packages/pm-kanban/package.json +1 -1
  208. package/packages/pm-lifecycle-hooks/package.json +1 -1
  209. package/packages/pm-linked-test-adapters/package.json +1 -1
  210. package/packages/pm-search-advanced/package.json +1 -1
  211. package/packages/pm-templates/package.json +1 -1
  212. package/packages/pm-todos/package.json +1 -1
  213. package/packages/pm-vcs/package.json +1 -1
  214. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  215. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  216. package/sdk/public-surface.json +848 -56
  217. package/dist/cli-bundle/chunks/chunk-2RDVGYWE.js +0 -13
  218. package/dist/cli-bundle/chunks/chunk-BLVQHFSY.js +0 -35
  219. package/dist/cli-bundle/chunks/chunk-CU25EQVV.js +0 -5
  220. package/dist/cli-bundle/chunks/chunk-RS3IV2R6.js +0 -197
  221. package/dist/cli-bundle/focused-chunks/chunk-6AHG2YW7.js +0 -50
  222. package/dist/cli-bundle/focused-chunks/chunk-GXWQBE3T.js +0 -8
  223. package/dist/cli-bundle/focused-chunks/chunk-JWKEDZJI.js +0 -4
  224. package/dist/cli-bundle/focused-chunks/chunk-KYZ6JGPL.js +0 -155
  225. package/dist/cli-bundle/focused-chunks/chunk-Q47EX4DI.js +0 -2
  226. package/dist/cli-bundle/focused-chunks/chunk-RIHDMCUM.js +0 -2
  227. package/dist/cli-bundle/focused-chunks/chunk-T4ZFY7ZJ.js +0 -16
  228. package/dist/cli-bundle/focused-chunks/chunk-Z3PLWSGM.js +0 -2
@@ -7,7 +7,7 @@
7
7
  * one deterministic SDK path.
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]="2bf52d6d-7e4f-5ec5-86bd-3e817ef521bb")}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]="d4c380ce-031e-5b2d-aaa8-658bb4aa3087")}catch(e){}}();
11
11
  import { createHash } from "node:crypto";
12
12
  import path from "node:path";
13
13
  import { EXIT_CODE } from "../../core/shared/constants.js";
@@ -34,6 +34,7 @@ export const ASSURANCE_GATE_TRIGGERS = [
34
34
  export const ASSURANCE_MEASUREMENT_SOURCE_KINDS = [
35
35
  "items",
36
36
  "dependency_kind",
37
+ "prose_edge_gap",
37
38
  "graph",
38
39
  "validate",
39
40
  "health",
@@ -167,6 +168,53 @@ function validateItemsMeasurementSource(source) {
167
168
  throw new AssuranceMutationRefusalError("items source equals/state requires field");
168
169
  }
169
170
  }
171
+ /** Validate relationship-provenance predicates on a dependency-kind measurement. */
172
+ function validateDependencyKindMeasurementSource(source) {
173
+ if (typeof source.dependency_kind !== "string" ||
174
+ source.dependency_kind.trim().length === 0) {
175
+ throw new AssuranceMutationRefusalError("dependency_kind source requires dependency_kind");
176
+ }
177
+ const provenancePredicateCount = [
178
+ source.source_kind,
179
+ source.source_kind_prefix,
180
+ source.source_kind_state,
181
+ ].filter((value) => value !== undefined).length;
182
+ if (provenancePredicateCount > 1) {
183
+ throw new AssuranceMutationRefusalError("dependency_kind source accepts at most one source_kind predicate");
184
+ }
185
+ if ((source.source_kind !== undefined &&
186
+ source.source_kind.trim().length === 0) ||
187
+ (source.source_kind_prefix !== undefined &&
188
+ source.source_kind_prefix.trim().length === 0)) {
189
+ throw new AssuranceMutationRefusalError("dependency_kind source provenance values must not be empty");
190
+ }
191
+ if (source.source_kind_state !== undefined &&
192
+ source.source_kind_state !== "present" &&
193
+ source.source_kind_state !== "missing") {
194
+ throw new AssuranceMutationRefusalError("dependency_kind source source_kind_state must be present or missing");
195
+ }
196
+ }
197
+ /** Validate bounded, reasoned prose-parity exemptions. */
198
+ function validateProseEdgeGapSource(source) {
199
+ if (source.sample_limit !== undefined &&
200
+ (!Number.isInteger(source.sample_limit) || source.sample_limit < 1)) {
201
+ throw new AssuranceMutationRefusalError("prose_edge_gap sample_limit must be a positive integer");
202
+ }
203
+ for (const exemption of source.exemptions ?? []) {
204
+ if (typeof exemption.holder_id !== "string" ||
205
+ exemption.holder_id.trim().length === 0 ||
206
+ typeof exemption.reason !== "string" ||
207
+ exemption.reason.trim().length === 0) {
208
+ throw new AssuranceMutationRefusalError("prose_edge_gap exemptions require holder_id and reason");
209
+ }
210
+ if ((exemption.target_id !== undefined &&
211
+ exemption.target_id.trim().length === 0) ||
212
+ (exemption.text_contains !== undefined &&
213
+ exemption.text_contains.trim().length === 0)) {
214
+ throw new AssuranceMutationRefusalError("prose_edge_gap exemption selectors must not be empty");
215
+ }
216
+ }
217
+ }
170
218
  /** Validate and return one measurement declaration. */
171
219
  export function validateMeasurementDefinition(definition) {
172
220
  requireStableId(definition.id, "measurement.id");
@@ -185,11 +233,10 @@ export function validateMeasurementDefinition(definition) {
185
233
  if (source.kind === "items") {
186
234
  validateItemsMeasurementSource(source);
187
235
  }
188
- if (source.kind === "dependency_kind" &&
189
- (typeof source.dependency_kind !== "string" ||
190
- source.dependency_kind.trim().length === 0)) {
191
- throw new AssuranceMutationRefusalError("dependency_kind source requires dependency_kind");
192
- }
236
+ if (source.kind === "dependency_kind")
237
+ validateDependencyKindMeasurementSource(source);
238
+ if (source.kind === "prose_edge_gap")
239
+ validateProseEdgeGapSource(source);
193
240
  if ((source.kind === "graph" ||
194
241
  source.kind === "validate" ||
195
242
  source.kind === "health") &&
@@ -419,6 +466,24 @@ function itemsSourceResult(source, context) {
419
466
  contributors: matches.map((item) => item.id),
420
467
  };
421
468
  }
469
+ /** Match one normalized dependency provenance against an optional partition. */
470
+ function dependencyProvenanceMatches(source, sourceKind) {
471
+ if (source.source_kind !== undefined) {
472
+ return (sourceKind?.toLowerCase() === source.source_kind.trim().toLowerCase());
473
+ }
474
+ if (source.source_kind_prefix !== undefined) {
475
+ return (sourceKind
476
+ ?.toLowerCase()
477
+ .startsWith(source.source_kind_prefix.trim().toLowerCase()) === true);
478
+ }
479
+ if (source.source_kind_state === "present") {
480
+ return sourceKind !== undefined && sourceKind.length > 0;
481
+ }
482
+ if (source.source_kind_state === "missing") {
483
+ return sourceKind === undefined || sourceKind.length === 0;
484
+ }
485
+ return true;
486
+ }
422
487
  function dependencyKindSourceResult(source, context) {
423
488
  const contributors = [];
424
489
  const requestedKind = resolveCanonicalRelationshipKind(source.dependency_kind) ??
@@ -427,9 +492,10 @@ function dependencyKindSourceResult(source, context) {
427
492
  for (const dependency of item.dependencies ?? []) {
428
493
  const dependencyKind = resolveCanonicalRelationshipKind(dependency.kind) ??
429
494
  dependency.kind.trim().toLowerCase();
430
- if (dependencyKind === requestedKind) {
431
- contributors.push(`${item.id}->${dependency.id}`);
432
- }
495
+ const sourceKind = dependency.source_kind?.trim();
496
+ if (dependencyKind === requestedKind &&
497
+ dependencyProvenanceMatches(source, sourceKind))
498
+ contributors.push(`${item.id}->${dependency.id}${sourceKind ? `@${sourceKind}` : ""}`);
433
499
  }
434
500
  }
435
501
  return {
@@ -439,6 +505,109 @@ function dependencyKindSourceResult(source, context) {
439
505
  contributors,
440
506
  };
441
507
  }
508
+ /** Return every text segment whose item identifiers form the prose graph. */
509
+ function assuranceItemProseSegments(item) {
510
+ const segments = [item.description, item.body]
511
+ .filter((value) => typeof value === "string")
512
+ .map((value) => value.trim())
513
+ .filter(Boolean);
514
+ for (const key of ["comments", "notes", "learnings"]) {
515
+ const entries = item[key];
516
+ if (!Array.isArray(entries))
517
+ continue;
518
+ for (const entry of entries) {
519
+ if (typeof entry === "string")
520
+ segments.push(entry);
521
+ else if (typeof entry === "object" && entry !== null) {
522
+ const text = Reflect.get(entry, "text");
523
+ if (typeof text === "string")
524
+ segments.push(text);
525
+ }
526
+ }
527
+ }
528
+ return segments;
529
+ }
530
+ /** Return whether a reviewed exemption applies to one prose reference. */
531
+ function proseReferenceIsExempt(exemptions, holderId, targetId, segment) {
532
+ return exemptions.some((exemption) => exemption.holder_id.trim().toLowerCase() === holderId.toLowerCase() &&
533
+ (exemption.target_id === undefined ||
534
+ exemption.target_id.trim().toLowerCase() === targetId.toLowerCase()) &&
535
+ (exemption.text_contains === undefined ||
536
+ segment
537
+ .toLowerCase()
538
+ .includes(exemption.text_contains.trim().toLowerCase())));
539
+ }
540
+ /** Return the direction-independent identity of a prose or structured item pair. */
541
+ function prosePairKey(left, right) {
542
+ return [left.toLowerCase(), right.toLowerCase()].sort().join("\u0000");
543
+ }
544
+ /** Collect canonical ids, typed links, and raw prose mentions in one item pass. */
545
+ function collectProseEdgeCensusInputs(items) {
546
+ const canonicalIds = new Map();
547
+ const linkedPairs = new Set();
548
+ const proseMentions = [];
549
+ const mentionPattern = /\b[a-z][a-z0-9]*-[a-z0-9]+\b/giu;
550
+ let mentionCount = 0;
551
+ for (const item of items) {
552
+ canonicalIds.set(item.id.toLowerCase(), item.id);
553
+ for (const dependency of item.dependencies ?? [])
554
+ linkedPairs.add(prosePairKey(item.id, dependency.id));
555
+ for (const scalar of [item.parent, item.blocked_by])
556
+ if (typeof scalar === "string")
557
+ linkedPairs.add(prosePairKey(item.id, scalar));
558
+ for (const segment of assuranceItemProseSegments(item)) {
559
+ const mentionedIds = [...segment.matchAll(mentionPattern)].map((match) => match[0]);
560
+ mentionCount += mentionedIds.length;
561
+ proseMentions.push({
562
+ holder_id: item.id,
563
+ segment,
564
+ mentioned_ids: mentionedIds,
565
+ });
566
+ }
567
+ }
568
+ return {
569
+ canonical_ids: canonicalIds,
570
+ linked_pairs: linkedPairs,
571
+ prose_mentions: proseMentions,
572
+ mention_count: mentionCount,
573
+ };
574
+ }
575
+ /** Count distinct prose-named item pairs that have no typed edge in either direction. */
576
+ function proseEdgeGapSourceResult(source, context) {
577
+ const census = collectProseEdgeCensusInputs(context.items);
578
+ const gaps = new Map();
579
+ for (const proseMention of census.prose_mentions) {
580
+ const mentions = proseMention.mentioned_ids
581
+ .map((id) => census.canonical_ids.get(id.toLowerCase()))
582
+ .filter((id) => id !== undefined && id !== proseMention.holder_id);
583
+ const distinctMentions = [...new Set(mentions)];
584
+ for (const targetId of distinctMentions) {
585
+ const key = prosePairKey(proseMention.holder_id, targetId);
586
+ if (census.linked_pairs.has(key) ||
587
+ proseReferenceIsExempt(source.exemptions ?? [], proseMention.holder_id, targetId, proseMention.segment))
588
+ continue;
589
+ const explicit = distinctMentions.some((candidate) => candidate !== targetId);
590
+ if (gaps.get(key)?.explicit === true && !explicit)
591
+ continue;
592
+ gaps.set(key, {
593
+ contributor: `${proseMention.holder_id}->${targetId}|subject=${explicit ? "explicit" : "implicit"}`,
594
+ explicit,
595
+ });
596
+ }
597
+ }
598
+ const gapRows = [...gaps.values()];
599
+ const contributors = gapRows.map((entry) => entry.contributor).sort();
600
+ return {
601
+ value: contributors.length,
602
+ population_size: census.canonical_ids.size,
603
+ cost: context.items.length + census.linked_pairs.size + census.mention_count,
604
+ contributors: contributors.slice(0, source.sample_limit ?? 25),
605
+ partitions: {
606
+ explicit_subject: gapRows.filter((entry) => entry.explicit).length,
607
+ implicit_subject: gapRows.filter((entry) => !entry.explicit).length,
608
+ },
609
+ };
610
+ }
442
611
  function historySourceResult(source, context) {
443
612
  const matches = context.history.filter((entry) => (source.op === undefined || entry.op === source.op) &&
444
613
  (source.author === undefined || entry.author === source.author) &&
@@ -471,6 +640,8 @@ function sourceResult(definition, context) {
471
640
  if (source.kind === "dependency_kind") {
472
641
  return dependencyKindSourceResult(source, context);
473
642
  }
643
+ if (source.kind === "prose_edge_gap")
644
+ return proseEdgeGapSourceResult(source, context);
474
645
  if (source.kind === "history")
475
646
  return historySourceResult(source, context);
476
647
  if (source.kind === "links")
@@ -583,6 +754,7 @@ async function evaluateMeasurementInternal(definition, context, definitions, sta
583
754
  units: result.cost,
584
755
  items_scanned: definition.source.kind === "items" ||
585
756
  definition.source.kind === "dependency_kind" ||
757
+ definition.source.kind === "prose_edge_gap" ||
586
758
  definition.source.kind === "links"
587
759
  ? context.items.length
588
760
  : 0,
@@ -603,6 +775,7 @@ async function evaluateMeasurementInternal(definition, context, definitions, sta
603
775
  population_size: result.population_size,
604
776
  cost,
605
777
  contributors: result.contributors ?? [],
778
+ ...(result.partitions ? { partitions: result.partitions } : {}),
606
779
  };
607
780
  }
608
781
  function evaluateMeasurementCached(definition, context, definitions, stack, state) {
@@ -670,6 +843,7 @@ export function evaluateAssuranceAssertion(definition, measurement, options = {}
670
843
  negative_control_proven: proveNegativeControl(definition),
671
844
  cost: measurement.cost,
672
845
  contributors: measurement.contributors,
846
+ ...(measurement.partitions ? { partitions: measurement.partitions } : {}),
673
847
  };
674
848
  }
675
849
  function enforceGateProviderPolicy(gate, document, context, trigger) {
@@ -1006,4 +1180,4 @@ export async function listAssuranceVerdicts(pmRoot, options = {}) {
1006
1180
  .slice(0, limit);
1007
1181
  }
1008
1182
  //# sourceMappingURL=assurance.js.map
1009
- //# debugId=2bf52d6d-7e4f-5ec5-86bd-3e817ef521bb
1183
+ //# debugId=d4c380ce-031e-5b2d-aaa8-658bb4aa3087
@@ -4,7 +4,8 @@
4
4
  * Implements the pm gc command surface and its agent-facing runtime behavior.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e982158e-4a8d-57a9-bd0b-24bd73aef342")}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]="76f8405f-138d-5119-a300-f068354394a5")}catch(e){}}();
8
+ import { assertInitializedTracker } from "../environment/tracker-preflight.js";
8
9
  import fs from "node:fs/promises";
9
10
  import path from "node:path";
10
11
  import { runCheckpointGc } from "../../core/checkpoint/checkpoint-gc.js";
@@ -15,7 +16,7 @@ import { runLockGc } from "../../core/lock/lock-gc.js";
15
16
  import { EXIT_CODE } from "../../core/shared/constants.js";
16
17
  import { PmCliError } from "../../core/shared/errors.js";
17
18
  import { nowIso } from "../../core/shared/time.js";
18
- import { getSettingsPath, resolvePmRoot } from "../../core/store/paths.js";
19
+ import { resolvePmRoot } from "../../core/store/paths.js";
19
20
  import { readSettings } from "../../core/store/settings.js";
20
21
  const GC_SCOPE_VALUES = [
21
22
  "index",
@@ -234,9 +235,7 @@ function buildGcGuidance(params) {
234
235
  /** Implements run gc for the public runtime surface of this module. */
235
236
  export async function runGc(global, options = {}) {
236
237
  const pmRoot = resolvePmRoot(process.cwd(), global.path);
237
- if (!(await pathExists(getSettingsPath(pmRoot)))) {
238
- throw new PmCliError(`Tracker is not initialized at ${pmRoot}. Run pm init first.`, EXIT_CODE.NOT_FOUND);
239
- }
238
+ await assertInitializedTracker(pmRoot);
240
239
  const settings = await readSettings(pmRoot);
241
240
  const dryRun = options.dryRun === true;
242
241
  const scopes = parseScopes(options.scope);
@@ -367,4 +366,4 @@ export async function runGc(global, options = {}) {
367
366
  };
368
367
  }
369
368
  //# sourceMappingURL=gc.js.map
370
- //# debugId=e982158e-4a8d-57a9-bd0b-24bd73aef342
369
+ //# debugId=76f8405f-138d-5119-a300-f068354394a5
@@ -7,6 +7,7 @@ import { type HistoryAuthorAttributionScan } from "../author-attribution.js";
7
7
  import type { PmSettings } from "../../types/index.js";
8
8
  import { buildCapabilityContractMetadata, collectUnknownCapabilityGuidance } from "../extension/doctor.js";
9
9
  import { type StaleInProgressScan } from "./stale-work.js";
10
+ import { type HierarchyIntegrityAnalysis } from "../graph/hierarchy-integrity.js";
10
11
  type HealthStatus = "ok" | "warn";
11
12
  type MigrationRuntimeStatus = "pending" | "failed" | "applied";
12
13
  /** Documents the health check payload exchanged by command, SDK, and package integrations. */
@@ -145,6 +146,7 @@ declare function normalizeExtensionNameForMatch(value: string): string;
145
146
  declare function isExpectedUnmanagedExtension(name: string, directory: string): boolean;
146
147
  declare function isDirectory(targetPath: string): Promise<boolean>;
147
148
  declare function listItemDocumentPaths(pmRoot: string, typeToFolder: Record<string, string>): Promise<string[]>;
149
+ declare function buildHierarchyIntegrityWarnings(analysis: HierarchyIntegrityAnalysis): string[];
148
150
  declare function buildExtensionHealthTriageSummary(warnings: string[], loadFailureCount: number, activationFailureCount: number, migrationStatus: MigrationStatusSummary, managedStateWarningCount: number, managedExtensionEntriesCount: number, unmanagedLoadedExtensions: string[], unmanagedExpectedExtensions: string[], unmanagedActionRequiredExtensions: string[]): ExtensionHealthTriageSummary;
149
151
  declare function summarizeRecordList(value: unknown, limit: number): {
150
152
  count: number;
@@ -218,6 +220,7 @@ declare function projectHealthResult(result: HealthResult, options: RunHealthOpt
218
220
  export declare function runHealth(global: GlobalOptions, options?: RunHealthOptions): Promise<HealthResult>;
219
221
  /** Public contract for test only health command, shared by SDK and presentation-layer consumers. */
220
222
  export declare const _testOnlyHealthCommand: {
223
+ buildHierarchyIntegrityWarnings: typeof buildHierarchyIntegrityWarnings;
221
224
  buildExtensionHealthTriageSummary: typeof buildExtensionHealthTriageSummary;
222
225
  buildHealthFindings: typeof buildHealthFindings;
223
226
  buildCapabilityContractMetadata: typeof buildCapabilityContractMetadata;
@@ -4,11 +4,14 @@
4
4
  * Implements the pm health command surface and its agent-facing runtime behavior.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e7be701e-085c-5903-96da-61149452c673")}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]="f7b3ff92-1fc7-55e9-aa76-af956714998f")}catch(e){}}();
8
8
  import fs from "node:fs/promises";
9
9
  import path from "node:path";
10
+ import { assertInitializedTracker } from "../environment/tracker-preflight.js";
11
+ import { isTerminalStatus } from "../../core/item/status.js";
10
12
  import { resolveItemTypeRegistry } from "../../core/item/type-registry.js";
11
- import { isFileMissingError, pathExists, readFileIfExists, } from "../../core/fs/fs-utils.js";
13
+ import { resolveRuntimeStatusRegistry } from "../../core/schema/runtime-schema.js";
14
+ import { isFileMissingError, readFileIfExists, } from "../../core/fs/fs-utils.js";
12
15
  import { activateExtensions, getActiveExtensionRegistrations, loadExtensions, runActiveOnReadHooks, } from "../../core/extensions/index.js";
13
16
  import { collectRegisteredItemFieldNames } from "../../core/extensions/item-fields.js";
14
17
  import { KNOWN_EXTENSION_CAPABILITIES, } from "../../core/extensions/loader.js";
@@ -43,6 +46,8 @@ import { scanStaleInProgressItems, } from "./stale-work.js";
43
46
  import { scanTrackedRuntimeCache } from "./tracked-runtime-cache.js";
44
47
  import { auditMergeDriverConfiguration, findGitWorkspaceRoot, } from "../merge/install.js";
45
48
  import { listMergeReceipts, partitionMergeReceipts, } from "../merge/receipts.js";
49
+ import { resolveWorkspaceRelationshipKindRegistry } from "../graph/assembly.js";
50
+ import { analyzeHierarchyIntegrity, } from "../graph/hierarchy-integrity.js";
46
51
  const PM_TELEMETRY_SOURCE_CONTEXT_SET = new Set(PM_TELEMETRY_SOURCE_CONTEXT_VALUES);
47
52
  const STALE_VECTORIZATION_SUMMARY_LIMIT = 25;
48
53
  const BRIEF_HEALTH_DETAIL_LIMIT = 8;
@@ -60,6 +65,7 @@ const TELEMETRY_SERVER_MAX_SCHEMA_VERSION_HEADERS = [
60
65
  function isAdvisoryHealthWarning(warning, requireMergeDrivers = false) {
61
66
  return (warning.startsWith("telemetry_") ||
62
67
  warning.startsWith("history_stream_over_compact_threshold:") ||
68
+ warning.startsWith("integrity_legacy_hierarchy_") ||
63
69
  warning.startsWith("stale_in_progress_items:") ||
64
70
  warning.startsWith("provenance_resolver_zero_success:") ||
65
71
  warning.startsWith("provenance_value_domain_invalid:") ||
@@ -314,10 +320,25 @@ async function scanHistoryIntegrity(pmRoot) {
314
320
  }
315
321
  return { files, unreadable, conflictMarkers, invalidJson };
316
322
  }
317
- async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDrivers) {
323
+ function buildHierarchyIntegrityWarnings(analysis) {
324
+ return [
325
+ ...analysis.cycles.map((cycle) => cycle.legacy_terminal
326
+ ? `integrity_legacy_hierarchy_cycle:${cycle.item_ids.join(",")}`
327
+ : `integrity_hierarchy_cycle:${cycle.item_ids.join(",")}`),
328
+ ...analysis.cardinality_violations.map((finding) => finding.legacy_terminal
329
+ ? `integrity_legacy_hierarchy_cardinality:${finding.child_id}`
330
+ : `integrity_hierarchy_cardinality:${finding.child_id}`),
331
+ ...analysis.divergences.map((finding) => finding.legacy_terminal
332
+ ? `integrity_legacy_hierarchy_divergence:${finding.child_id}`
333
+ : `integrity_hierarchy_divergence:${finding.child_id}`),
334
+ ];
335
+ }
336
+ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDrivers, items) {
318
337
  const itemScan = await scanItemIntegrity(pmRoot, typeToFolder, schema);
319
338
  const historyScan = await scanHistoryIntegrity(pmRoot);
320
339
  const formatVersionScan = scanItemFormatVersions(itemScan.formatVersions);
340
+ const statusRegistry = resolveRuntimeStatusRegistry(schema);
341
+ const hierarchyIntegrity = analyzeHierarchyIntegrity(items, (status) => isTerminalStatus(status, statusRegistry), resolveWorkspaceRelationshipKindRegistry());
321
342
  const trackedRuntimeCache = await scanTrackedRuntimeCache(pmRoot);
322
343
  const gitWorkspaceRoot = await findGitWorkspaceRoot(pmRoot);
323
344
  const mergeDriverAudit = gitWorkspaceRoot === null
@@ -344,6 +365,7 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
344
365
  ...formatVersionScan.outdated.map((entry) => `integrity_item_outdated_format_version:${entry}`),
345
366
  /* c8 ignore stop */
346
367
  ...formatVersionScan.ahead.map((entry) => `integrity_item_ahead_format_version:${entry}`),
368
+ ...buildHierarchyIntegrityWarnings(hierarchyIntegrity),
347
369
  ...(trackedRuntimeCache.tracked_path_count > 0
348
370
  ? [
349
371
  `tracked_runtime_cache_files:${trackedRuntimeCache.tracked_path_count}`,
@@ -359,11 +381,12 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
359
381
  : []),
360
382
  ];
361
383
  const normalizedWarnings = [...new Set(warnings)].sort((left, right) => left.localeCompare(right));
384
+ const actionableWarnings = normalizedWarnings.filter((warning) => !warning.startsWith("integrity_legacy_hierarchy_"));
362
385
  return {
363
386
  check: {
364
387
  name: "integrity",
365
- status: normalizedWarnings.length === 0 ? "ok" : "warn",
366
- ok: normalizedWarnings.length === 0,
388
+ status: actionableWarnings.length === 0 ? "ok" : "warn",
389
+ ok: actionableWarnings.length === 0,
367
390
  details: {
368
391
  checked_item_files: itemScan.paths.length,
369
392
  checked_history_streams: historyScan.files.length,
@@ -376,6 +399,9 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
376
399
  history_invalid_json: historyScan.invalidJson.length,
377
400
  item_outdated_format_version: formatVersionScan.outdated.length,
378
401
  item_ahead_format_version: formatVersionScan.ahead.length,
402
+ hierarchy_cycles: hierarchyIntegrity.cycles.length,
403
+ hierarchy_cardinality_violations: hierarchyIntegrity.cardinality_violations.length,
404
+ hierarchy_parent_divergences: hierarchyIntegrity.divergences.length,
379
405
  tracked_runtime_cache_files: trackedRuntimeCache.tracked_path_count,
380
406
  merge_driver_configuration: mergeDriverAudit === null || mergeDriverAudit.status === "ok"
381
407
  ? 0
@@ -389,6 +415,10 @@ async function buildIntegrityCheck(pmRoot, typeToFolder, schema, requireMergeDri
389
415
  item_parse_failures: itemScan.parseFailures,
390
416
  item_outdated_format_version: formatVersionScan.outdated,
391
417
  item_ahead_format_version: formatVersionScan.ahead,
418
+ hierarchy_integrity: {
419
+ ...hierarchyIntegrity,
420
+ relations: summarizeRecordList(hierarchyIntegrity.relations, BRIEF_HEALTH_DETAIL_LIMIT),
421
+ },
392
422
  history_unreadable: historyScan.unreadable,
393
423
  history_conflict_markers: historyScan.conflictMarkers,
394
424
  history_invalid_json: historyScan.invalidJson,
@@ -1831,7 +1861,9 @@ function extractHistoryDriftedCount(historyDriftCheck) {
1831
1861
  function buildHealthRemediationSources(params) {
1832
1862
  return {
1833
1863
  settings: params.normalizedSettingsReadWarnings,
1834
- directories: params.directoryState.missingDirs.map((dir) => `missing_directory:${dir}`).concat(params.directoryState.hookWarnings),
1864
+ directories: params.directoryState.missingDirs
1865
+ .map((dir) => `missing_directory:${dir}`)
1866
+ .concat(params.directoryState.hookWarnings),
1835
1867
  settings_values: params.settingWarnings,
1836
1868
  telemetry: params.telemetryCheck.warnings,
1837
1869
  extensions: params.extensionCheck.warnings,
@@ -1950,10 +1982,8 @@ function projectHealthResult(result, options, summaryMode) {
1950
1982
  /** Implements run health for the public runtime surface of this module. */
1951
1983
  export async function runHealth(global, options = {}) {
1952
1984
  const pmRoot = resolvePmRoot(process.cwd(), global.path);
1985
+ await assertInitializedTracker(pmRoot);
1953
1986
  const settingsPath = getSettingsPath(pmRoot);
1954
- if (!(await pathExists(settingsPath))) {
1955
- throw new PmCliError(`Tracker is not initialized at ${pmRoot}. Run pm init first.`, EXIT_CODE.NOT_FOUND);
1956
- }
1957
1987
  const { settings, warnings: settingsReadWarnings } = await readSettingsWithMetadata(pmRoot);
1958
1988
  const normalizedSettingsReadWarnings = [...new Set(settingsReadWarnings)];
1959
1989
  const typeRegistry = resolveItemTypeRegistry(settings, getActiveExtensionRegistrations());
@@ -2001,7 +2031,7 @@ export async function runHealth(global, options = {}) {
2001
2031
  const locksCheck = await buildLocksCheck(pmRoot);
2002
2032
  const integrityCheck = skipPolicy.skipIntegrity
2003
2033
  ? buildSkippedHealthCheck("integrity")
2004
- : await buildIntegrityCheck(pmRoot, typeRegistry.type_to_folder, settings.schema, options.requireMergeDrivers === true);
2034
+ : await buildIntegrityCheck(pmRoot, typeRegistry.type_to_folder, settings.schema, options.requireMergeDrivers === true, items);
2005
2035
  const historyDriftCheck = skipPolicy.skipDrift
2006
2036
  ? buildSkippedHealthCheck("history_drift")
2007
2037
  : await buildHistoryDriftCheck(pmRoot, itemsWithBody);
@@ -2083,6 +2113,7 @@ export async function runHealth(global, options = {}) {
2083
2113
  }
2084
2114
  /** Public contract for test only health command, shared by SDK and presentation-layer consumers. */
2085
2115
  export const _testOnlyHealthCommand = {
2116
+ buildHierarchyIntegrityWarnings,
2086
2117
  buildExtensionHealthTriageSummary,
2087
2118
  buildHealthFindings,
2088
2119
  buildCapabilityContractMetadata,
@@ -2111,4 +2142,4 @@ export const _testOnlyHealthCommand = {
2111
2142
  warningCode,
2112
2143
  };
2113
2144
  //# sourceMappingURL=health.js.map
2114
- //# debugId=e7be701e-085c-5903-96da-61149452c673
2145
+ //# debugId=f7b3ff92-1fc7-55e9-aa76-af956714998f
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @module sdk/governance/hierarchy-validation
3
+ *
4
+ * Adapts the shared hierarchy analyzer to validation-specific diagnostics and
5
+ * deterministic cycle paths without coupling the analyzer to CLI policy.
6
+ */
7
+ import { type RuntimeStatusRegistry } from "../../core/schema/runtime-schema.js";
8
+ import type { RelationshipKindRegistry } from "../relationships.js";
9
+ import type { ValidateItem } from "./validate-item-reader.js";
10
+ /** Resolve one deterministic closed walk through a known cycle component. */
11
+ export declare function resolveLifecycleDependencyCycleSamplePath(component: string[], graph: Map<string, string[]>): string[];
12
+ /** Build canonical child-to-parent adjacency across every hierarchy spelling. */
13
+ export declare function buildLifecycleParentGraph(items: ValidateItem[], relationshipRegistry?: RelationshipKindRegistry): Map<string, string[]>;
14
+ /** Project shared hierarchy analysis into the validation result contract. */
15
+ export declare function detectLifecycleParentCycles(items: ValidateItem[], statusRegistry?: RuntimeStatusRegistry, relationshipRegistry?: RelationshipKindRegistry): {
16
+ cycle_count: number;
17
+ active_cycle_count: number;
18
+ legacy_cycle_count: number;
19
+ cycle_item_ids: string[];
20
+ cycle_sample_paths: string[];
21
+ cardinality_violation_count: number;
22
+ active_cardinality_violation_count: number;
23
+ legacy_cardinality_violation_count: number;
24
+ cardinality_violation_rows: string[];
25
+ parent_divergence_count: number;
26
+ active_parent_divergence_count: number;
27
+ legacy_parent_divergence_count: number;
28
+ parent_divergence_rows: string[];
29
+ };
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @module sdk/governance/hierarchy-validation
3
+ *
4
+ * Adapts the shared hierarchy analyzer to validation-specific diagnostics and
5
+ * deterministic cycle paths without coupling the analyzer to CLI policy.
6
+ */
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]="77a3c404-bbcd-5a5e-80bf-cb61c45af55d")}catch(e){}}();
9
+ import { statusIsTerminal, } from "../../core/schema/runtime-schema.js";
10
+ import { resolveWorkspaceRelationshipKindRegistry } from "../graph/assembly.js";
11
+ import { analyzeHierarchyIntegrity } from "../graph/hierarchy-integrity.js";
12
+ /** Resolve one deterministic closed walk through a known cycle component. */
13
+ /* c8 ignore start -- fallback branches are covered by lifecycle graph integration tests */
14
+ export function resolveLifecycleDependencyCycleSamplePath(component, graph) {
15
+ const start = component[0];
16
+ if (component.length === 1)
17
+ return [start, start];
18
+ const componentSet = new Set(component);
19
+ const path = [start];
20
+ const visited = new Set([start]);
21
+ const search = (current) => {
22
+ for (const next of (graph.get(current) ?? []).filter((candidate) => componentSet.has(candidate))) {
23
+ if (next === start && path.length > 1) {
24
+ path.push(start);
25
+ return true;
26
+ }
27
+ if (visited.has(next))
28
+ continue;
29
+ visited.add(next);
30
+ path.push(next);
31
+ if (search(next))
32
+ return true;
33
+ path.pop();
34
+ visited.delete(next);
35
+ }
36
+ return false;
37
+ };
38
+ return search(start) ? [...path] : [...component, start];
39
+ }
40
+ /* c8 ignore stop */
41
+ /** Resolve a scalar parent spelling to the workspace's canonical identifier. */
42
+ function resolveCanonicalScalarParent(parent, canonicalIdByLowercase) {
43
+ if (typeof parent !== "string")
44
+ return undefined;
45
+ const normalized = parent.trim().toLowerCase();
46
+ return normalized.length > 0
47
+ ? canonicalIdByLowercase.get(normalized)
48
+ : undefined;
49
+ }
50
+ /** Append dependency-backed child-to-parent adjacency for one holder. */
51
+ function appendDependencyHierarchyEdges(item, graph, canonicalIdByLowercase, relationshipRegistry) {
52
+ for (const dependency of item.dependencies ?? []) {
53
+ if (typeof dependency !== "object" ||
54
+ dependency === null ||
55
+ typeof dependency.id !== "string")
56
+ continue;
57
+ const definition = relationshipRegistry.resolve(dependency.kind);
58
+ if (!definition?.hierarchy)
59
+ continue;
60
+ const target = canonicalIdByLowercase.get(dependency.id.toLowerCase());
61
+ if (!target)
62
+ continue;
63
+ if (definition.hierarchyDirection === "source_parent")
64
+ graph.get(target).push(item.id);
65
+ else
66
+ graph.get(item.id).push(target);
67
+ }
68
+ }
69
+ /** Build canonical child-to-parent adjacency across every hierarchy spelling. */
70
+ export function buildLifecycleParentGraph(items, relationshipRegistry = resolveWorkspaceRelationshipKindRegistry()) {
71
+ const canonicalIdByLowercase = new Map(items.map((item) => [item.id.toLowerCase(), item.id]));
72
+ const graph = new Map(items.map((item) => [item.id, []]));
73
+ for (const item of [...items].sort((left, right) => left.id.localeCompare(right.id))) {
74
+ const edges = graph.get(item.id);
75
+ const canonicalParentId = resolveCanonicalScalarParent(item.parent, canonicalIdByLowercase);
76
+ if (canonicalParentId)
77
+ edges.push(canonicalParentId);
78
+ appendDependencyHierarchyEdges(item, graph, canonicalIdByLowercase, relationshipRegistry);
79
+ }
80
+ for (const [id, edges] of graph) {
81
+ graph.set(id, [...new Set(edges)].sort());
82
+ }
83
+ return graph;
84
+ }
85
+ /** Project shared hierarchy analysis into the validation result contract. */
86
+ export function detectLifecycleParentCycles(items, statusRegistry, relationshipRegistry = resolveWorkspaceRelationshipKindRegistry()) {
87
+ const hierarchy = analyzeHierarchyIntegrity(items, (status) => statusRegistry
88
+ ? statusIsTerminal(status, statusRegistry)
89
+ : status === "closed" || status === "canceled", relationshipRegistry);
90
+ const graph = buildLifecycleParentGraph(items, relationshipRegistry);
91
+ const cycleComponents = hierarchy.cycles.map((cycle) => cycle.item_ids);
92
+ return {
93
+ cycle_count: cycleComponents.length,
94
+ active_cycle_count: hierarchy.cycles.filter((cycle) => !cycle.legacy_terminal).length,
95
+ legacy_cycle_count: hierarchy.cycles.filter((cycle) => cycle.legacy_terminal).length,
96
+ cycle_item_ids: [...new Set(cycleComponents.flat())].sort(),
97
+ cycle_sample_paths: cycleComponents.map((component) => resolveLifecycleDependencyCycleSamplePath(component, graph).join("->")),
98
+ cardinality_violation_count: hierarchy.cardinality_violations.length,
99
+ active_cardinality_violation_count: hierarchy.cardinality_violations.filter((finding) => !finding.legacy_terminal).length,
100
+ legacy_cardinality_violation_count: hierarchy.cardinality_violations.filter((finding) => finding.legacy_terminal).length,
101
+ cardinality_violation_rows: hierarchy.cardinality_violations.map((finding) => `${finding.child_id}:${finding.parent_ids.join(",")}:${finding.legacy_terminal ? "legacy" : "active"}`),
102
+ parent_divergence_count: hierarchy.divergences.length,
103
+ active_parent_divergence_count: hierarchy.divergences.filter((finding) => !finding.legacy_terminal).length,
104
+ legacy_parent_divergence_count: hierarchy.divergences.filter((finding) => finding.legacy_terminal).length,
105
+ parent_divergence_rows: hierarchy.divergences.map((finding) => `${finding.child_id}:scalar=${finding.scalar_parent_id}:dependencies=${finding.dependency_parent_ids.join(",")}:${finding.legacy_terminal ? "legacy" : "active"}`),
106
+ };
107
+ }
108
+ //# sourceMappingURL=hierarchy-validation.js.map
109
+ //# debugId=77a3c404-bbcd-5a5e-80bf-cb61c45af55d
@@ -4,10 +4,11 @@
4
4
  * Implements the pm reindex command surface and its agent-facing runtime behavior.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fc124f85-8d28-508b-a3f7-2fe767382197")}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]="1bcd20e1-2995-56ef-8d04-f265b8282c19")}catch(e){}}();
8
+ import { assertInitializedTracker } from "../environment/tracker-preflight.js";
8
9
  import fs from "node:fs/promises";
9
10
  import path from "node:path";
10
- import { toNonEmptyStringOrUndefined, getActiveExtensionRegistrations, runActiveOnIndexHooks, runActiveOnWriteHooks, collectRegisteredItemFieldNames, resolveRegisteredSearchProvider, resolveRegisteredVectorStoreAdapter, pathExists, writeFileAtomic, resolveItemTypeRegistry, parseItemDocument, acquireLock, buildSearchCorpus, buildSemanticCorpusInput, resolveSearchCorpusFields, resolveSemanticCorpusCharacterLimit, executeEmbeddingBatchesWithRetry, readVectorizationStatusLedger, writeVectorizationStatusLedger, REINDEX_LOCK_ID, resolveEmbeddingProviders, resolveSettingsWithSemanticRuntimeDefaults, executeVectorDelete, executeVectorReset, executeVectorUpsert, resolveVectorStores, buildVectorizationEmbeddingIdentity, buildVectorizationEmbeddingMetadata, hasVectorizationEmbeddingIdentityChanged, hasVectorizationVectorDimensionChanged, inferConsistentVectorDimension, EXIT_CODE, PmCliError, nowIso, listAllDocumentCandidatesCached, getSettingsPath, resolvePmRoot, readSettings, resolveAuthor, } from "../runtime-primitives.js";
11
+ import { toNonEmptyStringOrUndefined, getActiveExtensionRegistrations, runActiveOnIndexHooks, runActiveOnWriteHooks, collectRegisteredItemFieldNames, resolveRegisteredSearchProvider, resolveRegisteredVectorStoreAdapter, writeFileAtomic, resolveItemTypeRegistry, parseItemDocument, acquireLock, buildSearchCorpus, buildSemanticCorpusInput, resolveSearchCorpusFields, resolveSemanticCorpusCharacterLimit, executeEmbeddingBatchesWithRetry, readVectorizationStatusLedger, writeVectorizationStatusLedger, REINDEX_LOCK_ID, resolveEmbeddingProviders, resolveSettingsWithSemanticRuntimeDefaults, executeVectorDelete, executeVectorReset, executeVectorUpsert, resolveVectorStores, buildVectorizationEmbeddingIdentity, buildVectorizationEmbeddingMetadata, hasVectorizationEmbeddingIdentityChanged, hasVectorizationVectorDimensionChanged, inferConsistentVectorDimension, EXIT_CODE, PmCliError, nowIso, listAllDocumentCandidatesCached, resolvePmRoot, readSettings, resolveAuthor, } from "../runtime-primitives.js";
11
12
  const MANIFEST_PATH = "index/manifest.json";
12
13
  const EMBEDDINGS_PATH = "search/embeddings.jsonl";
13
14
  const EMBEDDING_MIGRATION_REINDEX_HINT = "Provider or model has changed since last index. Run pm reindex --mode semantic to rebuild.";
@@ -630,9 +631,7 @@ export async function runReindex(options, global) {
630
631
  const forceFullSemantic = options.full === true;
631
632
  emitReindexProgress(progressEnabled, `start mode=${requestedMode}`);
632
633
  const pmRoot = resolvePmRoot(process.cwd(), global.path);
633
- if (!(await pathExists(getSettingsPath(pmRoot)))) {
634
- throw new PmCliError(`Tracker is not initialized at ${pmRoot}. Run pm init first.`, EXIT_CODE.NOT_FOUND);
635
- }
634
+ await assertInitializedTracker(pmRoot);
636
635
  const settings = resolveSettingsWithSemanticRuntimeDefaults(await readSettings(pmRoot)).settings;
637
636
  let releaseReindexLock = null;
638
637
  try {
@@ -760,4 +759,4 @@ export async function runReindex(options, global) {
760
759
  }
761
760
  }
762
761
  //# sourceMappingURL=reindex.js.map
763
- //# debugId=fc124f85-8d28-508b-a3f7-2fe767382197
762
+ //# debugId=1bcd20e1-2995-56ef-8d04-f265b8282c19