@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,14 +7,14 @@ import type { ValidateMetadataProfile, ValidateMetadataRequiredField } from "../
7
7
  import { type OutputProjectionDeclaration } from "../output-projection.js";
8
8
  import { type RelationshipKindRegistry } from "../relationships.js";
9
9
  import { type ValidateItem } from "./validate-item-reader.js";
10
+ import { buildLifecycleParentGraph, detectLifecycleParentCycles, resolveLifecycleDependencyCycleSamplePath } from "./hierarchy-validation.js";
10
11
  type ValidateCheckName = "metadata" | "resolution" | "lifecycle" | "dependency_references" | "files" | "command_references" | "history_drift" | "format_version" | "storage_integrity";
11
12
  type ValidateStatus = "ok" | "warn" | "error";
12
13
  type ValidateDependencyCycleSeverity = "off" | "warn" | "error";
13
14
  type ValidateFileScanMode = "default" | "tracked-all" | "tracked-all-strict";
14
15
  type ItemWithBody = ValidateItem;
15
16
  type OrphanedPathClassification = "docs_unowned" | "tests_unowned" | "source_unowned" | "unlinked_existing";
16
- declare const LIFECYCLE_PATTERN_FIELD_KEYS: readonly ["blocked_reason", "resolution", "actual_result"];
17
- type LifecyclePatternFieldKey = (typeof LIFECYCLE_PATTERN_FIELD_KEYS)[number];
17
+ type LifecyclePatternFieldKey = "blocked_reason" | "resolution" | "actual_result";
18
18
  /** Documents the validate command options payload exchanged by command, SDK, and package integrations. */
19
19
  export interface ValidateCommandOptions {
20
20
  /** Value that configures or reports check metadata for this contract. */
@@ -199,18 +199,11 @@ declare function buildDependencyReferencesCheck(items: ItemWithBody[], verboseDi
199
199
  };
200
200
  declare function extractItemIds(value: string, idPrefix?: string): string[];
201
201
  declare function findLifecycleDependencyCycleComponents(graph: Map<string, string[]>): string[][];
202
- declare function resolveLifecycleDependencyCycleSamplePath(component: string[], graph: Map<string, string[]>): string[];
203
202
  declare function detectLifecycleDependencyCycles(activeItems: ItemWithBody[], idPrefix?: string): {
204
203
  cycle_count: number;
205
204
  cycle_item_ids: string[];
206
205
  cycle_sample_paths: string[];
207
206
  };
208
- declare function buildLifecycleParentGraph(items: ItemWithBody[], relationshipRegistry?: RelationshipKindRegistry): Map<string, string[]>;
209
- declare function detectLifecycleParentCycles(items: ItemWithBody[]): {
210
- cycle_count: number;
211
- cycle_item_ids: string[];
212
- cycle_sample_paths: string[];
213
- };
214
207
  interface OrphanedPathRow {
215
208
  path: string;
216
209
  classification: OrphanedPathClassification;
@@ -4,7 +4,8 @@
4
4
  * Implements the pm validate 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]="b8c4707f-5445-5079-96e5-dc0dbd649b34")}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]="87e3932d-26f0-5d44-ac35-5d0d7d1f4467")}catch(e){}}();
8
+ import { assertInitializedTracker } from "../environment/tracker-preflight.js";
8
9
  import fs from "node:fs/promises";
9
10
  import { realpathSync } from "node:fs";
10
11
  import { execFile } from "node:child_process";
@@ -13,7 +14,7 @@ import { promisify } from "node:util";
13
14
  import { buildRemediationCommands } from "../../core/diagnostics/remediation.js";
14
15
  import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
15
16
  import { collectRegisteredItemFieldNames } from "../../core/extensions/item-fields.js";
16
- import { isFileMissingError, pathExists } from "../../core/fs/fs-utils.js";
17
+ import { isFileMissingError } from "../../core/fs/fs-utils.js";
17
18
  import { normalizeStatusInput } from "../../core/item/status.js";
18
19
  import { resolveItemTypeRegistry } from "../../core/item/type-registry.js";
19
20
  import { resolveRuntimeStatusRegistry, } from "../../core/schema/runtime-schema.js";
@@ -22,7 +23,7 @@ import { PmCliError } from "../../core/shared/errors.js";
22
23
  import { toNonEmptyStringOrUndefined } from "../../core/shared/primitives.js";
23
24
  import { nowIso } from "../../core/shared/time.js";
24
25
  import { CURRENT_ITEM_FORMAT_VERSION, effectiveItemFormatVersion, scanItemFormatVersions, } from "../../core/item/item-format-version.js";
25
- import { getSettingsPath, resolvePmRoot } from "../../core/store/paths.js";
26
+ import { resolvePmRoot } from "../../core/store/paths.js";
26
27
  import { readSettings } from "../../core/store/settings.js";
27
28
  import { partitionFixesByGrant, planCloseReasonBackfillFixes, planEstimateBackfillFixes, planResolutionBackfillFixes, planStaleLinkPruneFixes, planTerminalParentFixes, resolveGrantedFixScopes, toFixOutputRow, } from "../../core/validate/fix-planning.js";
28
29
  import { findDuplicateIssueCodes, } from "../../core/governance/issue-codes.js";
@@ -43,6 +44,7 @@ import { runFiles } from "../files.js";
43
44
  import { extractReferencedPmItemIdsFromCommand } from "../test/linked-command-detection.js";
44
45
  import { readValidateItems, } from "./validate-item-reader.js";
45
46
  import { buildValidateHistoryDriftCheck } from "./validate-history-drift.js";
47
+ import { buildLifecycleParentGraph, detectLifecycleParentCycles, resolveLifecycleDependencyCycleSamplePath, } from "./hierarchy-validation.js";
46
48
  const FILE_SCAN_DIRECTORIES = ["src", "tests", "docs"];
47
49
  const FILE_SCAN_ROOT_FILES = [
48
50
  "README.md",
@@ -78,11 +80,6 @@ const VALIDATE_DEPENDENCY_CYCLE_SEVERITY_VALUES = [
78
80
  "warn",
79
81
  "error",
80
82
  ];
81
- const LIFECYCLE_PATTERN_FIELD_KEYS = [
82
- "blocked_reason",
83
- "resolution",
84
- "actual_result",
85
- ];
86
83
  const CORE_METADATA_REQUIRED_FIELDS = [
87
84
  "author",
88
85
  "acceptance_criteria",
@@ -1111,41 +1108,6 @@ function findLifecycleDependencyCycleComponents(graph) {
1111
1108
  left.join(",").localeCompare(right.join(",")));
1112
1109
  }
1113
1110
  /* c8 ignore stop */
1114
- /* c8 ignore start -- cycle sample-path fallback branches are covered by lifecycle graph integration tests */
1115
- function resolveLifecycleDependencyCycleSamplePath(component, graph) {
1116
- const start = component[0];
1117
- if (component.length === 1) {
1118
- return [start, start];
1119
- }
1120
- const componentSet = new Set(component);
1121
- const path = [start];
1122
- const visited = new Set([start]);
1123
- const search = (current) => {
1124
- const neighbors = (graph.get(current) ?? []).filter((candidate) => componentSet.has(candidate));
1125
- for (const next of neighbors) {
1126
- if (next === start && path.length > 1) {
1127
- path.push(start);
1128
- return true;
1129
- }
1130
- if (visited.has(next)) {
1131
- continue;
1132
- }
1133
- visited.add(next);
1134
- path.push(next);
1135
- if (search(next)) {
1136
- return true;
1137
- }
1138
- path.pop();
1139
- visited.delete(next);
1140
- }
1141
- return false;
1142
- };
1143
- if (search(start)) {
1144
- return [...path];
1145
- }
1146
- return [...component, start];
1147
- }
1148
- /* c8 ignore stop */
1149
1111
  function detectLifecycleDependencyCycles(activeItems, idPrefix = "pm") {
1150
1112
  const graph = buildLifecycleDependencyGraph(activeItems, idPrefix);
1151
1113
  const cycleComponents = findLifecycleDependencyCycleComponents(graph);
@@ -1157,62 +1119,6 @@ function detectLifecycleDependencyCycles(activeItems, idPrefix = "pm") {
1157
1119
  cycle_sample_paths: cycleSamplePaths,
1158
1120
  };
1159
1121
  }
1160
- // Parent (composition) cycle detection (pm-8vul / GH-280). The dependency-cycle
1161
- // path above only walks blocked_by/definition_of_ready edges across ACTIVE items;
1162
- // it never traverses item.parent, so a parent cycle (A.parent=B, B.parent=A, or
1163
- // any longer ring) goes undetected while it silently breaks `pm list --tree`,
1164
- // orphan detection, and progress rollups. We reuse the generic Tarjan SCC helper
1165
- // on a child->[parent] adjacency map. Unlike dependency cycles we scan ALL items
1166
- // (not just active ones) because a parent cycle among closed items is still
1167
- // structural corruption of the hierarchy.
1168
- function buildLifecycleParentGraph(items, relationshipRegistry = createRelationshipKindRegistry()) {
1169
- // PR #279 made parent matching case-insensitive (e.g. `parent: PM-FK49`
1170
- // resolves to `id: pm-fk49`). Resolve parent references to their canonical
1171
- // item id the same way so a casing mismatch can never silently drop a cycle
1172
- // edge and hide a parent cycle (false negative).
1173
- const canonicalIdByLowercase = new Map(items.map((item) => [item.id.toLowerCase(), item.id]));
1174
- const graph = new Map(items.map((item) => [item.id, []]));
1175
- const sortedItems = [...items].sort((left, right) => left.id.localeCompare(right.id));
1176
- sortedItems.forEach((item) => {
1177
- const edges = graph.get(item.id);
1178
- const parentId = toMeaningfulString(item.parent);
1179
- const canonicalParentId = parentId
1180
- ? canonicalIdByLowercase.get(parentId.toLowerCase())
1181
- : undefined;
1182
- if (canonicalParentId) {
1183
- edges.push(canonicalParentId);
1184
- }
1185
- (item.dependencies ?? []).forEach((dependency) => {
1186
- const definition = relationshipRegistry.resolve(dependency.kind);
1187
- if (!definition?.hierarchy)
1188
- return;
1189
- const target = canonicalIdByLowercase.get(dependency.id.toLowerCase());
1190
- if (!target)
1191
- return;
1192
- if (definition.hierarchyDirection === "source_parent") {
1193
- graph.get(target).push(item.id);
1194
- }
1195
- else {
1196
- edges.push(target);
1197
- }
1198
- });
1199
- });
1200
- graph.forEach((edges, id) => {
1201
- graph.set(id, [...new Set(edges)].sort((left, right) => left.localeCompare(right)));
1202
- });
1203
- return graph;
1204
- }
1205
- function detectLifecycleParentCycles(items) {
1206
- const graph = buildLifecycleParentGraph(items);
1207
- const cycleComponents = findLifecycleDependencyCycleComponents(graph);
1208
- const cycleItemIds = [...new Set(cycleComponents.flat())].sort((left, right) => left.localeCompare(right));
1209
- const cycleSamplePaths = cycleComponents.map((component) => resolveLifecycleDependencyCycleSamplePath(component, graph).join("->"));
1210
- return {
1211
- cycle_count: cycleComponents.length,
1212
- cycle_item_ids: cycleItemIds,
1213
- cycle_sample_paths: cycleSamplePaths,
1214
- };
1215
- }
1216
1122
  function classifyOrphanedPath(pathValue) {
1217
1123
  if (pathValue.startsWith("docs/")) {
1218
1124
  return "docs_unowned";
@@ -1439,7 +1345,10 @@ function lifecycleCycleWarningToken(prefix, severity, count) {
1439
1345
  }
1440
1346
  return `${severity === "error" ? `${prefix}_error` : prefix}:${count}`;
1441
1347
  }
1442
- function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, dependencyCycleCount, parentCycleCount) {
1348
+ function lifecycleAdvisoryWarningToken(prefix, severity, count) {
1349
+ return count > 0 && severity !== "off" ? `${prefix}:${count}` : null;
1350
+ }
1351
+ function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, counts) {
1443
1352
  const warnings = [];
1444
1353
  if (rows.closureLikeRows.length > 0) {
1445
1354
  warnings.push(`validate_lifecycle_active_closure_like_metadata:${rows.closureLikeRows.length}`);
@@ -1450,12 +1359,22 @@ function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSever
1450
1359
  if (includeStaleBlockers && rows.staleBlockerRows.length > 0) {
1451
1360
  warnings.push(`validate_lifecycle_stale_blockers:${rows.staleBlockerRows.length}`);
1452
1361
  }
1453
- const dependencyWarning = lifecycleCycleWarningToken("validate_lifecycle_dependency_cycles", dependencyCycleSeverity, dependencyCycleCount);
1454
- const parentWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_cycle", parentCycleSeverity, parentCycleCount);
1362
+ const dependencyWarning = lifecycleCycleWarningToken("validate_lifecycle_dependency_cycles", dependencyCycleSeverity, counts.dependencyCycles);
1363
+ const parentWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_cycle", parentCycleSeverity, counts.activeHierarchyCycles);
1364
+ const cardinalityWarning = lifecycleCycleWarningToken("validate_hierarchy_cardinality_violation", parentCycleSeverity, counts.activeHierarchyCardinality);
1365
+ const divergenceWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_divergence", parentCycleSeverity, counts.activeHierarchyDivergence);
1366
+ const legacyCycleWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_parent_cycle", parentCycleSeverity, counts.legacyHierarchyCycles);
1367
+ const legacyCardinalityWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_cardinality_violation", parentCycleSeverity, counts.legacyHierarchyCardinality);
1368
+ const legacyDivergenceWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_parent_divergence", parentCycleSeverity, counts.legacyHierarchyDivergence);
1455
1369
  return [
1456
1370
  ...warnings,
1457
1371
  ...(dependencyWarning ? [dependencyWarning] : []),
1458
1372
  ...(parentWarning ? [parentWarning] : []),
1373
+ ...(cardinalityWarning ? [cardinalityWarning] : []),
1374
+ ...(divergenceWarning ? [divergenceWarning] : []),
1375
+ ...(legacyCycleWarning ? [legacyCycleWarning] : []),
1376
+ ...(legacyCardinalityWarning ? [legacyCardinalityWarning] : []),
1377
+ ...(legacyDivergenceWarning ? [legacyDivergenceWarning] : []),
1459
1378
  ];
1460
1379
  }
1461
1380
  function linkedArtifactPathExceedsFilesystemLimits(artifactPath) {
@@ -1561,8 +1480,16 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
1561
1480
  const rows = collectLifecycleScanRows(items, includeStaleBlockers, statusRegistry, lifecyclePatternPolicy);
1562
1481
  sortLifecycleScanRows(rows);
1563
1482
  const dependencyCycleDiagnostics = detectLifecycleDependencyCycles(rows.activeItems, idPrefix);
1564
- const parentCycleDiagnostics = detectLifecycleParentCycles(items);
1565
- const warnings = buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, dependencyCycleDiagnostics.cycle_count, parentCycleDiagnostics.cycle_count);
1483
+ const parentCycleDiagnostics = detectLifecycleParentCycles(items, statusRegistry);
1484
+ const warnings = buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, {
1485
+ dependencyCycles: dependencyCycleDiagnostics.cycle_count,
1486
+ activeHierarchyCycles: parentCycleDiagnostics.active_cycle_count,
1487
+ activeHierarchyCardinality: parentCycleDiagnostics.active_cardinality_violation_count,
1488
+ activeHierarchyDivergence: parentCycleDiagnostics.active_parent_divergence_count,
1489
+ legacyHierarchyCycles: parentCycleDiagnostics.legacy_cycle_count,
1490
+ legacyHierarchyCardinality: parentCycleDiagnostics.legacy_cardinality_violation_count,
1491
+ legacyHierarchyDivergence: parentCycleDiagnostics.legacy_parent_divergence_count,
1492
+ });
1566
1493
  const diagnosticLimit = verboseDiagnostics
1567
1494
  ? Number.POSITIVE_INFINITY
1568
1495
  : DIAGNOSTIC_LIST_SUMMARY_LIMIT;
@@ -1573,9 +1500,14 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
1573
1500
  const summarizedDependencyCycleSamplePaths = summarizeList(dependencyCycleDiagnostics.cycle_sample_paths, diagnosticLimit);
1574
1501
  const summarizedParentCycleItemIds = summarizeList(parentCycleDiagnostics.cycle_item_ids, diagnosticLimit);
1575
1502
  const summarizedParentCycleSamplePaths = summarizeList(parentCycleDiagnostics.cycle_sample_paths, diagnosticLimit);
1503
+ const summarizedHierarchyCardinalityRows = summarizeList(parentCycleDiagnostics.cardinality_violation_rows, diagnosticLimit);
1504
+ const summarizedHierarchyDivergenceRows = summarizeList(parentCycleDiagnostics.parent_divergence_rows, diagnosticLimit);
1576
1505
  const hasErrorSeverityCycle = (dependencyCycleDiagnostics.cycle_count > 0 &&
1577
1506
  dependencyCycleSeverity === "error") ||
1578
- (parentCycleDiagnostics.cycle_count > 0 && parentCycleSeverity === "error");
1507
+ ((parentCycleDiagnostics.active_cycle_count > 0 ||
1508
+ parentCycleDiagnostics.active_cardinality_violation_count > 0 ||
1509
+ parentCycleDiagnostics.active_parent_divergence_count > 0) &&
1510
+ parentCycleSeverity === "error");
1579
1511
  return {
1580
1512
  check: {
1581
1513
  name: "lifecycle",
@@ -1606,11 +1538,23 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
1606
1538
  dependency_cycle_sample_paths_truncated: summarizedDependencyCycleSamplePaths.truncated,
1607
1539
  parent_cycle_severity_policy: parentCycleSeverity,
1608
1540
  parent_cycle_count: parentCycleDiagnostics.cycle_count,
1541
+ active_parent_cycle_count: parentCycleDiagnostics.active_cycle_count,
1542
+ legacy_parent_cycle_count: parentCycleDiagnostics.legacy_cycle_count,
1609
1543
  parent_cycle_item_count: parentCycleDiagnostics.cycle_item_ids.length,
1610
1544
  parent_cycle_item_ids: summarizedParentCycleItemIds.values,
1611
1545
  parent_cycle_item_ids_truncated: summarizedParentCycleItemIds.truncated,
1612
1546
  parent_cycle_sample_paths: summarizedParentCycleSamplePaths.values,
1613
1547
  parent_cycle_sample_paths_truncated: summarizedParentCycleSamplePaths.truncated,
1548
+ hierarchy_cardinality_violation_count: parentCycleDiagnostics.cardinality_violation_count,
1549
+ active_hierarchy_cardinality_violation_count: parentCycleDiagnostics.active_cardinality_violation_count,
1550
+ legacy_hierarchy_cardinality_violation_count: parentCycleDiagnostics.legacy_cardinality_violation_count,
1551
+ hierarchy_cardinality_violation_rows: summarizedHierarchyCardinalityRows.values,
1552
+ hierarchy_cardinality_violation_rows_truncated: summarizedHierarchyCardinalityRows.truncated,
1553
+ hierarchy_parent_divergence_count: parentCycleDiagnostics.parent_divergence_count,
1554
+ active_hierarchy_parent_divergence_count: parentCycleDiagnostics.active_parent_divergence_count,
1555
+ legacy_hierarchy_parent_divergence_count: parentCycleDiagnostics.legacy_parent_divergence_count,
1556
+ hierarchy_parent_divergence_rows: summarizedHierarchyDivergenceRows.values,
1557
+ hierarchy_parent_divergence_rows_truncated: summarizedHierarchyDivergenceRows.truncated,
1614
1558
  stale_blocker_reason_patterns: [
1615
1559
  ...lifecyclePatternPolicy.stale_blocker_reason_patterns,
1616
1560
  ],
@@ -2226,9 +2170,7 @@ export async function runValidate(options, global, services = {}) {
2226
2170
  // Resolved up-front so unknown --fix-scope values fail fast before any scan.
2227
2171
  const grantedFixScopes = resolveGrantedFixScopes(options.fixScope);
2228
2172
  const pmRoot = resolvePmRoot(process.cwd(), global.path);
2229
- if (!(await pathExists(getSettingsPath(pmRoot)))) {
2230
- throw new PmCliError(`Tracker is not initialized at ${pmRoot}. Run pm init first.`, EXIT_CODE.NOT_FOUND);
2231
- }
2173
+ await assertInitializedTracker(pmRoot);
2232
2174
  const settings = await readSettings(pmRoot);
2233
2175
  const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);
2234
2176
  const typeRegistry = resolveItemTypeRegistry(settings, getActiveExtensionRegistrations());
@@ -2313,4 +2255,4 @@ export async function runValidate(options, global, services = {}) {
2313
2255
  }
2314
2256
  /* c8 ignore stop */
2315
2257
  //# sourceMappingURL=validate.js.map
2316
- //# debugId=b8c4707f-5445-5079-96e5-dc0dbd649b34
2258
+ //# debugId=87e3932d-26f0-5d44-ac35-5d0d7d1f4467
@@ -11,6 +11,8 @@
11
11
  */
12
12
  import type { Dependency, ItemStatus } from "../../types/index.js";
13
13
  import { RelationshipGraph, type RelationshipKindRegistry } from "../relationships.js";
14
+ import { type HierarchyIntegrityAnalysis } from "./hierarchy-integrity.js";
15
+ export { resolveWorkspaceRelationshipKindRegistry } from "./workspace-relationship-kind-registry.js";
14
16
  /** Minimal item shape inspected by dependency-reference governance. */
15
17
  export interface DependencyReferenceHolder {
16
18
  /** Stable item identifier. */
@@ -61,7 +63,6 @@ export declare function normalizeDependencyGraphTarget(target: unknown): string
61
63
  * share this resolution so custom ordering semantics apply identically on
62
64
  * every surface.
63
65
  */
64
- export declare function resolveWorkspaceRelationshipKindRegistry(): RelationshipKindRegistry;
65
66
  /** One raw stored dependency row duplicated verbatim on a single holder. */
66
67
  export interface DuplicateDependencyRow {
67
68
  /** Item that stores the duplicated row. */
@@ -152,7 +153,11 @@ export interface WorkspaceRelationshipAssembly {
152
153
  orderingContradictions?: OrderingStorageContradiction[];
153
154
  /** Raw legacy alias rows grouped by accepted spelling before graph canonicalization. */
154
155
  legacyAliasCounts: Record<string, number>;
156
+ /** Shared hierarchy analysis retained across ordinary object spread copies. */
157
+ hierarchyIntegrity?: HierarchyIntegrityAnalysis;
155
158
  }
159
+ /** Read the hierarchy analysis paired with an assembly created by this module. */
160
+ export declare function getWorkspaceHierarchyIntegrity(assembly: WorkspaceRelationshipAssembly): HierarchyIntegrityAnalysis | undefined;
156
161
  /** Count raw dependency rows that use a registered compatibility alias instead of its canonical kind. */
157
162
  export declare function collectLegacyDependencyAliasCounts(items: readonly DependencyReferenceHolder[], registry?: RelationshipKindRegistry): Record<string, number>;
158
163
  /**
@@ -1,8 +1,10 @@
1
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]="a6fc5da1-b7d1-5b3c-8033-cecccc7861c3")}catch(e){}}();
3
- import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
4
- import { RelationshipGraph, createRelationshipKindRegistry, } from "../relationships.js";
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]="9a6974b3-63d7-5dc7-8329-0f15f0dc83ec")}catch(e){}}();
3
+ import { RelationshipGraph, } from "../relationships.js";
5
4
  import { isExternalDependencySourceKind } from "../dependency-provenance.js";
5
+ import { analyzeHierarchyIntegrity, } from "./hierarchy-integrity.js";
6
+ import { resolveWorkspaceRelationshipKindRegistry } from "./workspace-relationship-kind-registry.js";
7
+ export { resolveWorkspaceRelationshipKindRegistry } from "./workspace-relationship-kind-registry.js";
6
8
  /** Normalize a decoded reference target and reject empty legacy placeholders. */
7
9
  function normalizeDependencyReferenceTarget(target) {
8
10
  if (typeof target !== "string")
@@ -20,23 +22,6 @@ export function normalizeDependencyGraphTarget(target) {
20
22
  ? undefined
21
23
  : normalized;
22
24
  }
23
- /**
24
- * Resolve the default workspace relationship-kind registry: the built-in kinds
25
- * merged with every active extension-contributed relationship-kind
26
- * registration. Assembly, mutation advisories, and standalone graph consumers
27
- * share this resolution so custom ordering semantics apply identically on
28
- * every surface.
29
- */
30
- export function resolveWorkspaceRelationshipKindRegistry() {
31
- const registry = createRelationshipKindRegistry();
32
- for (const registration of getActiveExtensionRegistrations()
33
- ?.relationship_kinds ?? []) {
34
- for (const definition of registration.definitions) {
35
- registry.register(definition);
36
- }
37
- }
38
- return registry;
39
- }
40
25
  /** Compare text case-insensitively with an exact-spelling deterministic tie-breaker. */
41
26
  function compareCaseFoldedText(left, right) {
42
27
  const normalizedLeft = left.toLowerCase();
@@ -242,6 +227,11 @@ export function collectExternalDependencyTargetIds(items) {
242
227
  }
243
228
  return [...targets.values()].sort((left, right) => left.localeCompare(right));
244
229
  }
230
+ const hierarchyIntegrityByAssembly = new WeakMap();
231
+ /** Read the hierarchy analysis paired with an assembly created by this module. */
232
+ export function getWorkspaceHierarchyIntegrity(assembly) {
233
+ return assembly.hierarchyIntegrity ?? hierarchyIntegrityByAssembly.get(assembly);
234
+ }
245
235
  /** Count raw dependency rows that use a registered compatibility alias instead of its canonical kind. */
246
236
  export function collectLegacyDependencyAliasCounts(items, registry = resolveWorkspaceRelationshipKindRegistry()) {
247
237
  const counts = {};
@@ -316,7 +306,8 @@ export function assembleWorkspaceRelationshipGraph(items, isTerminal, registry)
316
306
  dependencies,
317
307
  };
318
308
  });
319
- return {
309
+ const hierarchyIntegrity = analyzeHierarchyIntegrity(safeItems, isTerminal, relationshipRegistry);
310
+ const assembly = {
320
311
  graph: RelationshipGraph.fromItems([
321
312
  ...graphItems,
322
313
  ...missingIds.map((id) => ({ id })),
@@ -349,7 +340,10 @@ export function assembleWorkspaceRelationshipGraph(items, isTerminal, registry)
349
340
  duplicateRows: collectDuplicateDependencyRows(safeItems, isTerminal),
350
341
  orderingContradictions: collectOrderingStorageContradictions(safeItems, isTerminal, relationshipRegistry),
351
342
  legacyAliasCounts: collectLegacyDependencyAliasCounts(safeItems, relationshipRegistry),
343
+ hierarchyIntegrity,
352
344
  };
345
+ hierarchyIntegrityByAssembly.set(assembly, hierarchyIntegrity);
346
+ return assembly;
353
347
  }
354
348
  //# sourceMappingURL=assembly.js.map
355
- //# debugId=a6fc5da1-b7d1-5b3c-8033-cecccc7861c3
349
+ //# debugId=9a6974b3-63d7-5dc7-8329-0f15f0dc83ec
@@ -7,6 +7,6 @@
7
7
  * drifting when a finding family is added.
8
8
  */
9
9
  /** Stable machine-readable relationship audit finding families. */
10
- export declare const RELATIONSHIP_AUDIT_FINDING_CODES: readonly ["missing_reference_active", "missing_reference_terminal", "legacy_no_blocker_sentinel", "ordering_cycle", "legacy_ordering_cycle", "ordering_storage_contradiction", "legacy_ordering_storage_contradiction", "duplicate_edge", "legacy_duplicate_edge", "duplicate_dependency_row", "legacy_duplicate_dependency_row", "stale_lifecycle_block", "isolated_active_node", "sparse_active_node"];
10
+ export declare const RELATIONSHIP_AUDIT_FINDING_CODES: readonly ["missing_reference_active", "missing_reference_terminal", "legacy_no_blocker_sentinel", "ordering_cycle", "legacy_ordering_cycle", "ordering_storage_contradiction", "legacy_ordering_storage_contradiction", "hierarchy_cycle", "legacy_hierarchy_cycle", "hierarchy_cardinality_violation", "legacy_hierarchy_cardinality_violation", "hierarchy_direction_violation", "legacy_hierarchy_direction_violation", "duplicate_edge", "legacy_duplicate_edge", "duplicate_dependency_row", "legacy_duplicate_dependency_row", "stale_lifecycle_block", "isolated_active_node", "sparse_active_node"];
11
11
  /** Machine-readable identifier for one relationship audit finding family. */
12
12
  export type RelationshipAuditFindingCode = (typeof RELATIONSHIP_AUDIT_FINDING_CODES)[number];
@@ -8,7 +8,7 @@
8
8
  */
9
9
  /** Stable machine-readable relationship audit finding families. */
10
10
 
11
- !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]="bc561e73-75b8-5ea7-a18c-da70fb991b69")}catch(e){}}();
11
+ !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]="a8bc0ebd-e5e4-57f3-8e6c-417f9809eb72")}catch(e){}}();
12
12
  export const RELATIONSHIP_AUDIT_FINDING_CODES = [
13
13
  "missing_reference_active",
14
14
  "missing_reference_terminal",
@@ -17,6 +17,12 @@ export const RELATIONSHIP_AUDIT_FINDING_CODES = [
17
17
  "legacy_ordering_cycle",
18
18
  "ordering_storage_contradiction",
19
19
  "legacy_ordering_storage_contradiction",
20
+ "hierarchy_cycle",
21
+ "legacy_hierarchy_cycle",
22
+ "hierarchy_cardinality_violation",
23
+ "legacy_hierarchy_cardinality_violation",
24
+ "hierarchy_direction_violation",
25
+ "legacy_hierarchy_direction_violation",
20
26
  "duplicate_edge",
21
27
  "legacy_duplicate_edge",
22
28
  "duplicate_dependency_row",
@@ -26,4 +32,4 @@ export const RELATIONSHIP_AUDIT_FINDING_CODES = [
26
32
  "sparse_active_node",
27
33
  ];
28
34
  //# sourceMappingURL=governance-contracts.js.map
29
- //# debugId=bc561e73-75b8-5ea7-a18c-da70fb991b69
35
+ //# debugId=a8bc0ebd-e5e4-57f3-8e6c-417f9809eb72
@@ -12,7 +12,7 @@
12
12
  * carries a deterministic bounded sample, and proposes a safe next action so
13
13
  * agents can plan remediation without re-deriving context.
14
14
  */
15
- import type { RelationshipEdge } from "../relationships.js";
15
+ import type { RelationshipEdge, RelationshipOutcomeTraversal } from "../relationships.js";
16
16
  import type { OrderingStorageContradiction, WorkspaceRelationshipAssembly } from "./assembly.js";
17
17
  import { type RelationshipAuditFindingCode } from "./governance-contracts.js";
18
18
  export type { RelationshipAuditFindingCode } from "./governance-contracts.js";
@@ -88,17 +88,19 @@ export interface RelationshipCoverageProfile {
88
88
  bridge_edges: number;
89
89
  /** Milestone nodes whose title explicitly declares an outcome. */
90
90
  outcome_nodes: number;
91
- /** Active non-outcome nodes with a typed hierarchy or implements path to an outcome. */
91
+ /** Declared edge kinds and directions admitted by typed outcome reachability. */
92
+ outcome_reachability_basis: Record<RelationshipOutcomeTraversal, string>;
93
+ /** Active non-outcome nodes with a path admitted by outcome_reachability_basis. */
92
94
  active_outcome_reachable_nodes: number;
93
- /** Active non-outcome nodes without a typed hierarchy or implements path to an outcome. */
95
+ /** Active non-outcome nodes without a path admitted by outcome_reachability_basis. */
94
96
  active_outcome_unreachable_nodes: number;
95
97
  /** Active typed-outcome reachability expressed as integer basis points. */
96
98
  active_outcome_reachability_basis_points: number;
97
99
  /** Terminal non-placeholder, non-outcome nodes included in the all-status census. */
98
100
  terminal_nodes: number;
99
- /** Terminal non-outcome nodes with a typed hierarchy or implements path to an outcome. */
101
+ /** Terminal non-outcome nodes with a path admitted by outcome_reachability_basis. */
100
102
  terminal_outcome_reachable_nodes: number;
101
- /** Terminal non-outcome nodes without a typed hierarchy or implements path to an outcome. */
103
+ /** Terminal non-outcome nodes without a path admitted by outcome_reachability_basis. */
102
104
  terminal_outcome_unreachable_nodes: number;
103
105
  /** Terminal typed-outcome reachability expressed as integer basis points. */
104
106
  terminal_outcome_reachability_basis_points: number;
@@ -1,7 +1,8 @@
1
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]="b63fba34-8ca1-5e3a-905d-772ea8ce0f11")}catch(e){}}();
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]="27de9c62-434c-566d-a000-54eddb9a16a9")}catch(e){}}();
3
3
  import { findRedundantRelationshipEdges, isTransitiveKind, orientTransitiveEdge, transitiveFamilyKey, } from "./analytics.js";
4
4
  import { findRelationshipCutStructure } from "./centrality.js";
5
+ import { getWorkspaceHierarchyIntegrity } from "./assembly.js";
5
6
  import { RELATIONSHIP_AUDIT_FINDING_CODES, } from "./governance-contracts.js";
6
7
  /** Default bounded sample size applied to every finding family. */
7
8
  const DEFAULT_MAX_SAMPLE_SIZE = 25;
@@ -53,6 +54,48 @@ function buildFinding(code, severity, policy, describe, subjects, maxSampleSize,
53
54
  }),
54
55
  };
55
56
  }
57
+ function collectHierarchyCycleFindings(analysis, maxSampleSize) {
58
+ return analysis.cycles.map((cycle) => buildFinding(cycle.legacy_terminal ? "legacy_hierarchy_cycle" : "hierarchy_cycle", cycle.legacy_terminal ? "info" : "error", "Structural hierarchy must be acyclic across scalar and registered dependency relationships.", (count) => `${count} items participate in one hierarchy cycle.`, cycle.item_ids, maxSampleSize, cycle.legacy_terminal
59
+ ? "Preserve historical rows unless an explicit legacy repair is approved."
60
+ : "Remove or retype one exact hierarchy relation before continuing mutation."));
61
+ }
62
+ function collectHierarchyCardinalityFindings(analysis, maxSampleSize, legacyTerminal) {
63
+ const rows = analysis.cardinality_violations.filter((finding) => finding.legacy_terminal === legacyTerminal);
64
+ if (rows.length === 0)
65
+ return [];
66
+ return [
67
+ buildFinding(legacyTerminal
68
+ ? "legacy_hierarchy_cardinality_violation"
69
+ : "hierarchy_cardinality_violation", legacyTerminal ? "info" : "error", "Every structural child must resolve to at most one logical parent across all hierarchy spellings.", (count) => `${count} child item${count === 1 ? "" : "s"} resolve to multiple hierarchy parents.`, rows.map((finding) => finding.child_id), maxSampleSize, legacyTerminal
70
+ ? "Preserve historical rows unless an explicit legacy repair is approved."
71
+ : "Remove the exact surplus hierarchy rows so each child has one parent.", rows.map((finding) => `${finding.child_id} <- ${finding.parent_ids.join(", ")}`)),
72
+ ];
73
+ }
74
+ function collectHierarchyDirectionFindings(analysis, maxSampleSize, legacyTerminal) {
75
+ const rows = analysis.divergences.filter((finding) => finding.legacy_terminal === legacyTerminal);
76
+ if (rows.length === 0)
77
+ return [];
78
+ return [
79
+ buildFinding(legacyTerminal
80
+ ? "legacy_hierarchy_direction_violation"
81
+ : "hierarchy_direction_violation", legacyTerminal ? "info" : "error", "Scalar parent metadata and dependency-backed hierarchy rows must resolve to the same logical parent.", (count) => `${count} child item${count === 1 ? "" : "s"} have divergent hierarchy storage surfaces.`, rows.map((finding) => finding.child_id), maxSampleSize, legacyTerminal
82
+ ? "Preserve historical rows unless an explicit legacy repair is approved."
83
+ : "Align the scalar parent and exact dependency rows to one canonical parent.", rows.map((finding) => `${finding.child_id}: scalar=${finding.scalar_parent_id} dependencies=${finding.dependency_parent_ids.join(",")}`)),
84
+ ];
85
+ }
86
+ /** Convert unified hierarchy analysis into lifecycle-aware governance findings. */
87
+ function collectHierarchyIntegrityFindings(assembly, maxSampleSize) {
88
+ const analysis = getWorkspaceHierarchyIntegrity(assembly);
89
+ if (!analysis)
90
+ return [];
91
+ return [
92
+ ...collectHierarchyCycleFindings(analysis, maxSampleSize),
93
+ ...collectHierarchyCardinalityFindings(analysis, maxSampleSize, false),
94
+ ...collectHierarchyCardinalityFindings(analysis, maxSampleSize, true),
95
+ ...collectHierarchyDirectionFindings(analysis, maxSampleSize, false),
96
+ ...collectHierarchyDirectionFindings(analysis, maxSampleSize, true),
97
+ ];
98
+ }
56
99
  /** Resolve the ordering-only successor adjacency for cycle analysis. */
57
100
  function buildOrderingAdjacency(assembly) {
58
101
  const registry = assembly.graph.registry();
@@ -336,23 +379,37 @@ function collectDuplicateRowFindings(assembly, maxSampleSize) {
336
379
  /** Build the reverse typed paths that can carry context toward an outcome. */
337
380
  function buildOutcomeReverseIndex(assembly, signal) {
338
381
  const reverse = new Map();
382
+ const basisKinds = {
383
+ source_to_target: [],
384
+ target_to_source: [],
385
+ both: [],
386
+ };
387
+ for (const definition of assembly.graph.registry().list()) {
388
+ if (definition.outcomeTraversal !== undefined) {
389
+ basisKinds[definition.outcomeTraversal].push(definition.kind);
390
+ }
391
+ }
392
+ const basis = {
393
+ source_to_target: basisKinds.source_to_target.join(","),
394
+ target_to_source: basisKinds.target_to_source.join(","),
395
+ both: basisKinds.both.join(","),
396
+ };
397
+ const append = (from, to) => {
398
+ const sources = reverse.get(to) ?? new Set();
399
+ sources.add(from);
400
+ reverse.set(to, sources);
401
+ };
339
402
  for (const edge of assembly.graph.edges()) {
340
403
  signal?.throwIfAborted();
341
404
  const definition = assembly.graph.registry().require(edge.kind);
342
- let oriented;
343
- if (definition.hierarchy) {
344
- oriented = orientTransitiveEdge(edge, definition);
345
- }
346
- else if (definition.kind === "implements") {
347
- oriented = { from: edge.source, to: edge.target };
348
- }
349
- if (oriented === undefined)
350
- continue;
351
- const sources = reverse.get(oriented.to) ?? new Set();
352
- sources.add(oriented.from);
353
- reverse.set(oriented.to, sources);
405
+ if (definition.outcomeTraversal === "source_to_target" ||
406
+ definition.outcomeTraversal === "both")
407
+ append(edge.source, edge.target);
408
+ if (definition.outcomeTraversal === "target_to_source" ||
409
+ definition.outcomeTraversal === "both")
410
+ append(edge.target, edge.source);
354
411
  }
355
- return reverse;
412
+ return { basis, reverse };
356
413
  }
357
414
  /** Expand outcome nodes through every deterministic reverse typed path. */
358
415
  function expandOutcomeReachability(outcomes, reverse, signal) {
@@ -425,9 +482,11 @@ function profileOutcomeReachability(assembly, nodeStates, signal) {
425
482
  .toLowerCase()
426
483
  .startsWith(OUTCOME_MILESTONE_TITLE_PREFIX))
427
484
  .map((detail) => detail.id));
428
- const reachable = expandOutcomeReachability(outcomes, buildOutcomeReverseIndex(assembly, signal), signal);
485
+ const { basis, reverse } = buildOutcomeReverseIndex(assembly, signal);
486
+ const reachable = expandOutcomeReachability(outcomes, reverse, signal);
429
487
  return {
430
488
  outcome_nodes: outcomes.size,
489
+ outcome_reachability_basis: basis,
431
490
  ...tallyOutcomeReachability(nodeStates, outcomes, reachable, signal),
432
491
  };
433
492
  }
@@ -561,6 +620,7 @@ export function auditWorkspaceRelationshipGraph(assembly, options = {}) {
561
620
  for (const edge of assembly.graph.edges())
562
621
  edgesByKind[edge.kind] = (edgesByKind[edge.kind] ?? 0) + 1;
563
622
  const findings = collectIntegrityFindings(assembly, isTerminal, maxSampleSize);
623
+ findings.push(...collectHierarchyIntegrityFindings(assembly, maxSampleSize));
564
624
  options.signal?.throwIfAborted();
565
625
  findings.push(...collectOrderingStorageContradictionFindings(assembly, maxSampleSize));
566
626
  options.signal?.throwIfAborted();
@@ -673,4 +733,4 @@ export function diffRelationshipAuditSnapshots(baseline, current) {
673
733
  };
674
734
  }
675
735
  //# sourceMappingURL=governance.js.map
676
- //# debugId=b63fba34-8ca1-5e3a-905d-772ea8ce0f11
736
+ //# debugId=27de9c62-434c-566d-a000-54eddb9a16a9