@unbrained/pm-cli 2026.8.21 → 2026.8.23

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 +44 -0
  3. package/dist/cli/error-guidance.d.ts +22 -4
  4. package/dist/cli/error-guidance.js +73 -16
  5. package/dist/cli/main.d.ts +3 -0
  6. package/dist/cli/main.js +51 -9
  7. package/dist/cli/register-mutation.js +6 -6
  8. package/dist/cli/register-operations.d.ts +73 -0
  9. package/dist/cli/register-operations.js +39 -14
  10. package/dist/cli/register-setup.js +3 -3
  11. package/dist/cli/schema-registration-helpers.d.ts +8 -1
  12. package/dist/cli/schema-registration-helpers.js +14 -5
  13. package/dist/cli-bundle/bundle-manifest.json +149 -149
  14. package/dist/cli-bundle/chunks/chunk-3ENVHX6C.js +36 -0
  15. package/dist/cli-bundle/chunks/{chunk-LOVTQ52Z.js → chunk-DW2CGEMU.js} +2 -2
  16. package/dist/cli-bundle/chunks/chunk-EG6CLDZT.js +5 -0
  17. package/dist/cli-bundle/chunks/{chunk-LARJJ3RK.js → chunk-F3AI2JFL.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-N3DSV4CH.js → chunk-HWZ6CELB.js} +8 -8
  19. package/dist/cli-bundle/chunks/chunk-HZBMLSZI.js +201 -0
  20. package/dist/cli-bundle/chunks/chunk-JB4KZWBF.js +3 -0
  21. package/dist/cli-bundle/chunks/chunk-KPH73XXJ.js +2 -0
  22. package/dist/cli-bundle/chunks/{chunk-BKYB45NQ.js → chunk-KXPMB2YL.js} +50 -50
  23. package/dist/cli-bundle/chunks/{chunk-BUNIEJNE.js → chunk-QDVHNHAQ.js} +2 -2
  24. package/dist/cli-bundle/chunks/chunk-TFI5V5N2.js +13 -0
  25. package/dist/cli-bundle/chunks/{register-list-query-F57EWDEF.js → register-list-query-UJB5FMV2.js} +2 -2
  26. package/dist/cli-bundle/chunks/register-mutation-NX5XE4VX.js +20 -0
  27. package/dist/cli-bundle/chunks/register-operations-RXIJIYXC.js +2 -0
  28. package/dist/cli-bundle/chunks/{register-setup-HXU7IZZD.js → register-setup-GFUZEKRE.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/{chunk-F4YEW5I2.js → chunk-37Z4OEAP.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/chunk-3UPTYG44.js +8 -0
  31. package/dist/cli-bundle/focused-chunks/chunk-5S7E72FY.js +16 -0
  32. package/dist/cli-bundle/focused-chunks/{chunk-4ERLO7QD.js → chunk-AUFH5H35.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/chunk-E7X25GC3.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/chunk-FQCSTBWE.js +4 -0
  35. package/dist/cli-bundle/focused-chunks/{chunk-FH6DYED3.js → chunk-FZS4H32O.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/chunk-IFLWKY2R.js +156 -0
  37. package/dist/cli-bundle/focused-chunks/{chunk-YKBKIEAJ.js → chunk-JOWDB2MH.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/chunk-LYVSW5VM.js +2 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-MRCNF3U6.js +2 -0
  40. package/dist/cli-bundle/focused-chunks/{chunk-UZE7XUZK.js → chunk-OFVUGTLJ.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/{chunk-FJ22HNRP.js → chunk-OGLQBL46.js} +2 -2
  42. package/dist/cli-bundle/focused-chunks/chunk-OY7QAV4Y.js +51 -0
  43. package/dist/cli-bundle/focused-chunks/chunk-QGFNI65N.js +26 -0
  44. package/dist/cli-bundle/focused-chunks/{chunk-KDNG7TKQ.js → chunk-R27IDGHD.js} +2 -2
  45. package/dist/cli-bundle/focused-chunks/chunk-UYRFYCDQ.js +5 -0
  46. package/dist/cli-bundle/focused-chunks/{chunk-2NIY5NKG.js → chunk-ZHVA42R5.js} +9 -9
  47. package/dist/cli-bundle/main.js +13 -13
  48. package/dist/cli-bundle/sdk-authoring.js +1 -1
  49. package/dist/cli-bundle/sdk-contracts.js +2 -2
  50. package/dist/cli-bundle/sdk-core.js +40 -40
  51. package/dist/cli-bundle/sdk-governance.js +1 -1
  52. package/dist/cli-bundle/sdk-graph.js +1 -1
  53. package/dist/cli-bundle/sdk-merge.js +33 -32
  54. package/dist/cli-bundle/sdk-query.js +1 -1
  55. package/dist/cli-bundle/sdk-runtime.js +1 -1
  56. package/dist/cli-bundle/sdk-testing.js +1 -1
  57. package/dist/cli-bundle/sdk.js +5 -5
  58. package/dist/core/config/nested-settings.js +8 -2
  59. package/dist/core/config/positional-value.d.ts +1 -1
  60. package/dist/core/config/positional-value.js +5 -2
  61. package/dist/core/extensions/extension-hook-runtime.js +22 -20
  62. package/dist/core/extensions/extension-types.d.ts +8 -1
  63. package/dist/core/extensions/extension-types.js +2 -2
  64. package/dist/core/extensions/loader.js +7 -3
  65. package/dist/core/extensions/service-ownership.d.ts +8 -0
  66. package/dist/core/extensions/service-ownership.js +20 -0
  67. package/dist/core/history/history.js +3 -3
  68. package/dist/core/item/id.js +20 -5
  69. package/dist/core/item/item-format.js +81 -2
  70. package/dist/core/reproducibility/context.d.ts +4 -0
  71. package/dist/core/reproducibility/context.js +14 -8
  72. package/dist/core/shared/author.d.ts +55 -1
  73. package/dist/core/shared/author.js +333 -16
  74. package/dist/core/shared/constants.js +3 -2
  75. package/dist/core/shared/errors.d.ts +4 -0
  76. package/dist/core/shared/errors.js +2 -2
  77. package/dist/core/store/settings-validator.d.ts +1 -0
  78. package/dist/core/store/settings-validator.js +6 -3
  79. package/dist/core/store/settings.js +5 -2
  80. package/dist/mcp/server.d.ts +1 -1
  81. package/dist/mcp/server.js +30 -9
  82. package/dist/sdk/agent/identity-config.d.ts +63 -0
  83. package/dist/sdk/agent/identity-config.js +107 -0
  84. package/dist/sdk/agent/refusal-corpus-contracts.d.ts +76 -0
  85. package/dist/sdk/agent/refusal-corpus-contracts.js +194 -0
  86. package/dist/sdk/agent/refusal-reachability.d.ts +1 -1
  87. package/dist/sdk/authoring.d.ts +1 -1
  88. package/dist/sdk/authoring.js +2 -2
  89. package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +77 -0
  90. package/dist/sdk/cli-contracts/agent-output-contracts.js +326 -2
  91. package/dist/sdk/cli-contracts/commander-mutation-options.js +3 -3
  92. package/dist/sdk/cli-contracts/enum-contracts.d.ts +4 -0
  93. package/dist/sdk/cli-contracts/enum-contracts.js +9 -2
  94. package/dist/sdk/cli-contracts/flag-contracts.js +9 -2
  95. package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +4 -4
  96. package/dist/sdk/cli-contracts/registration-helpers.js +5 -2
  97. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +3 -1
  98. package/dist/sdk/cli-contracts/runtime-contracts.js +4 -3
  99. package/dist/sdk/cli-contracts/tool-parameter-tables.js +22 -2
  100. package/dist/sdk/cli-contracts/tool-schema.d.ts +1 -1
  101. package/dist/sdk/cli-contracts/tool-schema.js +14 -3
  102. package/dist/sdk/cli-contracts.d.ts +4 -4
  103. package/dist/sdk/cli-contracts.js +5 -5
  104. package/dist/sdk/config.d.ts +6 -1
  105. package/dist/sdk/config.js +119 -14
  106. package/dist/sdk/context-intent-contracts.js +4 -2
  107. package/dist/sdk/core-governance.d.ts +1 -1
  108. package/dist/sdk/core-governance.js +3 -3
  109. package/dist/sdk/extension/install-sources.d.ts +1 -1
  110. package/dist/sdk/extension/install-sources.js +6 -3
  111. package/dist/sdk/extension/output-ownership.d.ts +1 -0
  112. package/dist/sdk/extension/output-ownership.js +3 -2
  113. package/dist/sdk/extension/scaffold.js +11 -5
  114. package/dist/sdk/flag-invocation-contracts.js +4 -2
  115. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +74 -2
  116. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +87 -26
  117. package/dist/sdk/governance/assurance-action.js +30 -10
  118. package/dist/sdk/governance/health.d.ts +3 -0
  119. package/dist/sdk/governance/health.js +39 -7
  120. package/dist/sdk/governance/hierarchy-validation.d.ts +29 -0
  121. package/dist/sdk/governance/hierarchy-validation.js +109 -0
  122. package/dist/sdk/governance/validate.d.ts +3 -9
  123. package/dist/sdk/governance/validate.js +84 -111
  124. package/dist/sdk/graph/assembly.d.ts +6 -1
  125. package/dist/sdk/graph/assembly.js +16 -22
  126. package/dist/sdk/graph/governance-contracts.d.ts +1 -1
  127. package/dist/sdk/graph/governance-contracts.js +8 -2
  128. package/dist/sdk/graph/governance.js +46 -2
  129. package/dist/sdk/graph/hierarchy-integrity.d.ts +94 -0
  130. package/dist/sdk/graph/hierarchy-integrity.js +311 -0
  131. package/dist/sdk/graph/index.d.ts +2 -1
  132. package/dist/sdk/graph/index.js +3 -2
  133. package/dist/sdk/graph/remediation.js +32 -2
  134. package/dist/sdk/graph/run.d.ts +31 -2
  135. package/dist/sdk/graph/run.js +80 -31
  136. package/dist/sdk/graph/workspace-relationship-kind-registry.d.ts +8 -0
  137. package/dist/sdk/graph/workspace-relationship-kind-registry.js +28 -0
  138. package/dist/sdk/index.d.ts +8 -3
  139. package/dist/sdk/index.js +9 -4
  140. package/dist/sdk/item-children.js +21 -8
  141. package/dist/sdk/lifecycle/create.js +129 -97
  142. package/dist/sdk/lifecycle/hierarchy-mutation-lock.d.ts +15 -0
  143. package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +25 -0
  144. package/dist/sdk/lifecycle/update.d.ts +2 -0
  145. package/dist/sdk/lifecycle/update.js +174 -84
  146. package/dist/sdk/merge/three-way.js +105 -26
  147. package/dist/sdk/query/complete-list.d.ts +3 -3
  148. package/dist/sdk/query/complete-list.js +46 -3
  149. package/dist/sdk/query/list.d.ts +4 -3
  150. package/dist/sdk/query/list.js +89 -49
  151. package/dist/sdk/read-output-budget.d.ts +21 -0
  152. package/dist/sdk/read-output-budget.js +39 -2
  153. package/dist/sdk/read-output-contracts.d.ts +10 -4
  154. package/dist/sdk/read-output-contracts.js +105 -32
  155. package/dist/sdk/relationship-context.js +3 -4
  156. package/dist/sdk/relationships.js +17 -10
  157. package/dist/sdk/reproducibility/process.d.ts +19 -0
  158. package/dist/sdk/reproducibility/process.js +88 -0
  159. package/dist/sdk/runtime-primitives.d.ts +1 -1
  160. package/dist/sdk/runtime-primitives.js +3 -3
  161. package/dist/sdk/runtime.d.ts +3 -2
  162. package/dist/sdk/runtime.js +7 -5
  163. package/dist/sdk/test/batch.d.ts +6 -0
  164. package/dist/sdk/test/batch.js +26 -6
  165. package/dist/sdk/test/entry.d.ts +1 -1
  166. package/dist/sdk/test/entry.js +3 -2
  167. package/dist/sdk/test/execution.d.ts +28 -3
  168. package/dist/sdk/test/execution.js +201 -34
  169. package/dist/sdk/test/parsers.d.ts +6 -0
  170. package/dist/sdk/test/parsers.js +21 -2
  171. package/dist/sdk/test/trust.d.ts +33 -0
  172. package/dist/sdk/test/trust.js +207 -0
  173. package/dist/types.d.ts +34 -0
  174. package/dist/types.js +2 -2
  175. package/docs/AGENT_PROVENANCE_ADR.md +41 -5
  176. package/docs/AGENT_UX_CONTRACTS.md +20 -4
  177. package/docs/COMMANDS.md +29 -14
  178. package/docs/CONFIGURATION.md +4 -0
  179. package/docs/DEPENDENCY_KIND_CONTRACT.md +31 -3
  180. package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +115 -0
  181. package/docs/EXTENSIONS.md +2 -1
  182. package/docs/MERGE_SAFETY.md +15 -1
  183. package/docs/README.md +1 -0
  184. package/docs/READ_OUTPUT_CONTRACTS.md +19 -2
  185. package/docs/RELATIONSHIP_GRAPH.md +6 -3
  186. package/docs/REPRODUCIBLE_WORKSPACES.md +42 -8
  187. package/docs/SDK.md +9 -0
  188. package/docs/SDK_AGENT_SESSION_CONTEXT.md +6 -4
  189. package/docs/SDK_EVIDENCE_TRACEABILITY.md +1 -1
  190. package/docs/TESTING.md +83 -1
  191. package/docs/agent-task-token-baseline.json +2 -2
  192. package/docs/generated/FLAG_LEXICON_BUDGETS.md +2 -2
  193. package/marketplace.json +2 -2
  194. package/package.json +2 -2
  195. package/packages/pm-beads/package.json +1 -1
  196. package/packages/pm-calendar/package.json +1 -1
  197. package/packages/pm-command-kit/package.json +1 -1
  198. package/packages/pm-digital-twin/package.json +1 -1
  199. package/packages/pm-governance-audit/package.json +1 -1
  200. package/packages/pm-guide-shell/package.json +1 -1
  201. package/packages/pm-kanban/package.json +1 -1
  202. package/packages/pm-lifecycle-hooks/package.json +1 -1
  203. package/packages/pm-linked-test-adapters/package.json +1 -1
  204. package/packages/pm-search-advanced/package.json +1 -1
  205. package/packages/pm-templates/package.json +1 -1
  206. package/packages/pm-todos/package.json +1 -1
  207. package/packages/pm-vcs/package.json +1 -1
  208. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  209. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  210. package/sdk/public-surface.json +1021 -53
  211. package/dist/cli-bundle/chunks/chunk-2MA6ZQVZ.js +0 -197
  212. package/dist/cli-bundle/chunks/chunk-3AFAUHUE.js +0 -2
  213. package/dist/cli-bundle/chunks/chunk-7VHZS727.js +0 -5
  214. package/dist/cli-bundle/chunks/chunk-JSLOFXGA.js +0 -3
  215. package/dist/cli-bundle/chunks/chunk-OCLPLFAY.js +0 -35
  216. package/dist/cli-bundle/chunks/chunk-S4IRYKBU.js +0 -13
  217. package/dist/cli-bundle/chunks/register-mutation-7IY2D5KO.js +0 -20
  218. package/dist/cli-bundle/chunks/register-operations-FALL3YTU.js +0 -2
  219. package/dist/cli-bundle/focused-chunks/chunk-7WZRLCYF.js +0 -50
  220. package/dist/cli-bundle/focused-chunks/chunk-CKKQSJNN.js +0 -2
  221. package/dist/cli-bundle/focused-chunks/chunk-EEFKYMDR.js +0 -16
  222. package/dist/cli-bundle/focused-chunks/chunk-F2A56DHV.js +0 -8
  223. package/dist/cli-bundle/focused-chunks/chunk-FV5ACOK7.js +0 -2
  224. package/dist/cli-bundle/focused-chunks/chunk-JEAVFOWM.js +0 -2
  225. package/dist/cli-bundle/focused-chunks/chunk-NJ63LOP6.js +0 -155
  226. package/dist/cli-bundle/focused-chunks/chunk-NXRTQ7HI.js +0 -2
  227. package/dist/cli-bundle/focused-chunks/chunk-TJ7EMJ46.js +0 -4
  228. package/dist/cli-bundle/focused-chunks/chunk-ZOSEELYJ.js +0 -26
@@ -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
@@ -7,6 +7,7 @@ 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";
@@ -198,18 +199,11 @@ declare function buildDependencyReferencesCheck(items: ItemWithBody[], verboseDi
198
199
  };
199
200
  declare function extractItemIds(value: string, idPrefix?: string): string[];
200
201
  declare function findLifecycleDependencyCycleComponents(graph: Map<string, string[]>): string[][];
201
- declare function resolveLifecycleDependencyCycleSamplePath(component: string[], graph: Map<string, string[]>): string[];
202
202
  declare function detectLifecycleDependencyCycles(activeItems: ItemWithBody[], idPrefix?: string): {
203
203
  cycle_count: number;
204
204
  cycle_item_ids: string[];
205
205
  cycle_sample_paths: string[];
206
206
  };
207
- declare function buildLifecycleParentGraph(items: ItemWithBody[], relationshipRegistry?: RelationshipKindRegistry): Map<string, string[]>;
208
- declare function detectLifecycleParentCycles(items: ItemWithBody[]): {
209
- cycle_count: number;
210
- cycle_item_ids: string[];
211
- cycle_sample_paths: string[];
212
- };
213
207
  interface OrphanedPathRow {
214
208
  path: string;
215
209
  classification: OrphanedPathClassification;
@@ -237,10 +231,10 @@ declare function buildFilesCheck(items: ItemWithBody[], workspaceRoot: string, p
237
231
  warnings: string[];
238
232
  staleLinkPruneRows: StaleLinkPruneRow[];
239
233
  }>;
240
- declare function buildCommandReferencesCheck(items: ItemWithBody[], idPrefix: string, verboseDiagnostics: boolean): {
234
+ declare function buildCommandReferencesCheck(items: ItemWithBody[], idPrefix: string, verboseDiagnostics: boolean, pmRoot: string): Promise<{
241
235
  check: ValidateCheck;
242
236
  warnings: string[];
243
- };
237
+ }>;
244
238
  /** Apply one planned fix through the SAME audited command paths an operator would use by hand (`pm update` / `pm files --remove` / `pm docs --remove`), so every applied fix carries normal history, locking, and hook behavior. Command modules are imported lazily: plain validate runs stay read-only and never pay the mutation-stack import cost. */
245
239
  declare function applyValidateFix(fix: ValidateFixRecord, global: GlobalOptions, services: ValidateMutationServices): Promise<void>;
246
240
  declare function applyValidateFixes(applicable: ValidateFixRecord[], global: GlobalOptions, services: ValidateMutationServices): Promise<{
@@ -4,7 +4,7 @@
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]="1ed6b663-882a-5936-bd89-46bbbb17d806")}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]="aca69fd7-c47a-5424-b58f-f305f690edd9")}catch(e){}}();
8
8
  import { assertInitializedTracker } from "../environment/tracker-preflight.js";
9
9
  import fs from "node:fs/promises";
10
10
  import { realpathSync } from "node:fs";
@@ -42,8 +42,10 @@ import { createRelationshipKindRegistry, } from "../relationships.js";
42
42
  import { runDocs } from "../docs.js";
43
43
  import { runFiles } from "../files.js";
44
44
  import { extractReferencedPmItemIdsFromCommand } from "../test/linked-command-detection.js";
45
+ import { resolveLinkedTestSourceRef, resolveLinkedTestSourceWorkspaceRoot, resolveLinkedTestTrustBatch, } from "../test/trust.js";
45
46
  import { readValidateItems, } from "./validate-item-reader.js";
46
47
  import { buildValidateHistoryDriftCheck } from "./validate-history-drift.js";
48
+ import { buildLifecycleParentGraph, detectLifecycleParentCycles, resolveLifecycleDependencyCycleSamplePath, } from "./hierarchy-validation.js";
47
49
  const FILE_SCAN_DIRECTORIES = ["src", "tests", "docs"];
48
50
  const FILE_SCAN_ROOT_FILES = [
49
51
  "README.md",
@@ -1107,41 +1109,6 @@ function findLifecycleDependencyCycleComponents(graph) {
1107
1109
  left.join(",").localeCompare(right.join(",")));
1108
1110
  }
1109
1111
  /* c8 ignore stop */
1110
- /* c8 ignore start -- cycle sample-path fallback branches are covered by lifecycle graph integration tests */
1111
- function resolveLifecycleDependencyCycleSamplePath(component, graph) {
1112
- const start = component[0];
1113
- if (component.length === 1) {
1114
- return [start, start];
1115
- }
1116
- const componentSet = new Set(component);
1117
- const path = [start];
1118
- const visited = new Set([start]);
1119
- const search = (current) => {
1120
- const neighbors = (graph.get(current) ?? []).filter((candidate) => componentSet.has(candidate));
1121
- for (const next of neighbors) {
1122
- if (next === start && path.length > 1) {
1123
- path.push(start);
1124
- return true;
1125
- }
1126
- if (visited.has(next)) {
1127
- continue;
1128
- }
1129
- visited.add(next);
1130
- path.push(next);
1131
- if (search(next)) {
1132
- return true;
1133
- }
1134
- path.pop();
1135
- visited.delete(next);
1136
- }
1137
- return false;
1138
- };
1139
- if (search(start)) {
1140
- return [...path];
1141
- }
1142
- return [...component, start];
1143
- }
1144
- /* c8 ignore stop */
1145
1112
  function detectLifecycleDependencyCycles(activeItems, idPrefix = "pm") {
1146
1113
  const graph = buildLifecycleDependencyGraph(activeItems, idPrefix);
1147
1114
  const cycleComponents = findLifecycleDependencyCycleComponents(graph);
@@ -1153,62 +1120,6 @@ function detectLifecycleDependencyCycles(activeItems, idPrefix = "pm") {
1153
1120
  cycle_sample_paths: cycleSamplePaths,
1154
1121
  };
1155
1122
  }
1156
- // Parent (composition) cycle detection (pm-8vul / GH-280). The dependency-cycle
1157
- // path above only walks blocked_by/definition_of_ready edges across ACTIVE items;
1158
- // it never traverses item.parent, so a parent cycle (A.parent=B, B.parent=A, or
1159
- // any longer ring) goes undetected while it silently breaks `pm list --tree`,
1160
- // orphan detection, and progress rollups. We reuse the generic Tarjan SCC helper
1161
- // on a child->[parent] adjacency map. Unlike dependency cycles we scan ALL items
1162
- // (not just active ones) because a parent cycle among closed items is still
1163
- // structural corruption of the hierarchy.
1164
- function buildLifecycleParentGraph(items, relationshipRegistry = createRelationshipKindRegistry()) {
1165
- // PR #279 made parent matching case-insensitive (e.g. `parent: PM-FK49`
1166
- // resolves to `id: pm-fk49`). Resolve parent references to their canonical
1167
- // item id the same way so a casing mismatch can never silently drop a cycle
1168
- // edge and hide a parent cycle (false negative).
1169
- const canonicalIdByLowercase = new Map(items.map((item) => [item.id.toLowerCase(), item.id]));
1170
- const graph = new Map(items.map((item) => [item.id, []]));
1171
- const sortedItems = [...items].sort((left, right) => left.id.localeCompare(right.id));
1172
- sortedItems.forEach((item) => {
1173
- const edges = graph.get(item.id);
1174
- const parentId = toMeaningfulString(item.parent);
1175
- const canonicalParentId = parentId
1176
- ? canonicalIdByLowercase.get(parentId.toLowerCase())
1177
- : undefined;
1178
- if (canonicalParentId) {
1179
- edges.push(canonicalParentId);
1180
- }
1181
- (item.dependencies ?? []).forEach((dependency) => {
1182
- const definition = relationshipRegistry.resolve(dependency.kind);
1183
- if (!definition?.hierarchy)
1184
- return;
1185
- const target = canonicalIdByLowercase.get(dependency.id.toLowerCase());
1186
- if (!target)
1187
- return;
1188
- if (definition.hierarchyDirection === "source_parent") {
1189
- graph.get(target).push(item.id);
1190
- }
1191
- else {
1192
- edges.push(target);
1193
- }
1194
- });
1195
- });
1196
- graph.forEach((edges, id) => {
1197
- graph.set(id, [...new Set(edges)].sort((left, right) => left.localeCompare(right)));
1198
- });
1199
- return graph;
1200
- }
1201
- function detectLifecycleParentCycles(items) {
1202
- const graph = buildLifecycleParentGraph(items);
1203
- const cycleComponents = findLifecycleDependencyCycleComponents(graph);
1204
- const cycleItemIds = [...new Set(cycleComponents.flat())].sort((left, right) => left.localeCompare(right));
1205
- const cycleSamplePaths = cycleComponents.map((component) => resolveLifecycleDependencyCycleSamplePath(component, graph).join("->"));
1206
- return {
1207
- cycle_count: cycleComponents.length,
1208
- cycle_item_ids: cycleItemIds,
1209
- cycle_sample_paths: cycleSamplePaths,
1210
- };
1211
- }
1212
1123
  function classifyOrphanedPath(pathValue) {
1213
1124
  if (pathValue.startsWith("docs/")) {
1214
1125
  return "docs_unowned";
@@ -1435,7 +1346,10 @@ function lifecycleCycleWarningToken(prefix, severity, count) {
1435
1346
  }
1436
1347
  return `${severity === "error" ? `${prefix}_error` : prefix}:${count}`;
1437
1348
  }
1438
- function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, dependencyCycleCount, parentCycleCount) {
1349
+ function lifecycleAdvisoryWarningToken(prefix, severity, count) {
1350
+ return count > 0 && severity !== "off" ? `${prefix}:${count}` : null;
1351
+ }
1352
+ function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, counts) {
1439
1353
  const warnings = [];
1440
1354
  if (rows.closureLikeRows.length > 0) {
1441
1355
  warnings.push(`validate_lifecycle_active_closure_like_metadata:${rows.closureLikeRows.length}`);
@@ -1446,12 +1360,22 @@ function buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSever
1446
1360
  if (includeStaleBlockers && rows.staleBlockerRows.length > 0) {
1447
1361
  warnings.push(`validate_lifecycle_stale_blockers:${rows.staleBlockerRows.length}`);
1448
1362
  }
1449
- const dependencyWarning = lifecycleCycleWarningToken("validate_lifecycle_dependency_cycles", dependencyCycleSeverity, dependencyCycleCount);
1450
- const parentWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_cycle", parentCycleSeverity, parentCycleCount);
1363
+ const dependencyWarning = lifecycleCycleWarningToken("validate_lifecycle_dependency_cycles", dependencyCycleSeverity, counts.dependencyCycles);
1364
+ const parentWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_cycle", parentCycleSeverity, counts.activeHierarchyCycles);
1365
+ const cardinalityWarning = lifecycleCycleWarningToken("validate_hierarchy_cardinality_violation", parentCycleSeverity, counts.activeHierarchyCardinality);
1366
+ const divergenceWarning = lifecycleCycleWarningToken("validate_hierarchy_parent_divergence", parentCycleSeverity, counts.activeHierarchyDivergence);
1367
+ const legacyCycleWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_parent_cycle", parentCycleSeverity, counts.legacyHierarchyCycles);
1368
+ const legacyCardinalityWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_cardinality_violation", parentCycleSeverity, counts.legacyHierarchyCardinality);
1369
+ const legacyDivergenceWarning = lifecycleAdvisoryWarningToken("validate_legacy_hierarchy_parent_divergence", parentCycleSeverity, counts.legacyHierarchyDivergence);
1451
1370
  return [
1452
1371
  ...warnings,
1453
1372
  ...(dependencyWarning ? [dependencyWarning] : []),
1454
1373
  ...(parentWarning ? [parentWarning] : []),
1374
+ ...(cardinalityWarning ? [cardinalityWarning] : []),
1375
+ ...(divergenceWarning ? [divergenceWarning] : []),
1376
+ ...(legacyCycleWarning ? [legacyCycleWarning] : []),
1377
+ ...(legacyCardinalityWarning ? [legacyCardinalityWarning] : []),
1378
+ ...(legacyDivergenceWarning ? [legacyDivergenceWarning] : []),
1455
1379
  ];
1456
1380
  }
1457
1381
  function linkedArtifactPathExceedsFilesystemLimits(artifactPath) {
@@ -1557,8 +1481,16 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
1557
1481
  const rows = collectLifecycleScanRows(items, includeStaleBlockers, statusRegistry, lifecyclePatternPolicy);
1558
1482
  sortLifecycleScanRows(rows);
1559
1483
  const dependencyCycleDiagnostics = detectLifecycleDependencyCycles(rows.activeItems, idPrefix);
1560
- const parentCycleDiagnostics = detectLifecycleParentCycles(items);
1561
- const warnings = buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, dependencyCycleDiagnostics.cycle_count, parentCycleDiagnostics.cycle_count);
1484
+ const parentCycleDiagnostics = detectLifecycleParentCycles(items, statusRegistry);
1485
+ const warnings = buildLifecycleWarnings(rows, includeStaleBlockers, dependencyCycleSeverity, parentCycleSeverity, {
1486
+ dependencyCycles: dependencyCycleDiagnostics.cycle_count,
1487
+ activeHierarchyCycles: parentCycleDiagnostics.active_cycle_count,
1488
+ activeHierarchyCardinality: parentCycleDiagnostics.active_cardinality_violation_count,
1489
+ activeHierarchyDivergence: parentCycleDiagnostics.active_parent_divergence_count,
1490
+ legacyHierarchyCycles: parentCycleDiagnostics.legacy_cycle_count,
1491
+ legacyHierarchyCardinality: parentCycleDiagnostics.legacy_cardinality_violation_count,
1492
+ legacyHierarchyDivergence: parentCycleDiagnostics.legacy_parent_divergence_count,
1493
+ });
1562
1494
  const diagnosticLimit = verboseDiagnostics
1563
1495
  ? Number.POSITIVE_INFINITY
1564
1496
  : DIAGNOSTIC_LIST_SUMMARY_LIMIT;
@@ -1569,9 +1501,14 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
1569
1501
  const summarizedDependencyCycleSamplePaths = summarizeList(dependencyCycleDiagnostics.cycle_sample_paths, diagnosticLimit);
1570
1502
  const summarizedParentCycleItemIds = summarizeList(parentCycleDiagnostics.cycle_item_ids, diagnosticLimit);
1571
1503
  const summarizedParentCycleSamplePaths = summarizeList(parentCycleDiagnostics.cycle_sample_paths, diagnosticLimit);
1504
+ const summarizedHierarchyCardinalityRows = summarizeList(parentCycleDiagnostics.cardinality_violation_rows, diagnosticLimit);
1505
+ const summarizedHierarchyDivergenceRows = summarizeList(parentCycleDiagnostics.parent_divergence_rows, diagnosticLimit);
1572
1506
  const hasErrorSeverityCycle = (dependencyCycleDiagnostics.cycle_count > 0 &&
1573
1507
  dependencyCycleSeverity === "error") ||
1574
- (parentCycleDiagnostics.cycle_count > 0 && parentCycleSeverity === "error");
1508
+ ((parentCycleDiagnostics.active_cycle_count > 0 ||
1509
+ parentCycleDiagnostics.active_cardinality_violation_count > 0 ||
1510
+ parentCycleDiagnostics.active_parent_divergence_count > 0) &&
1511
+ parentCycleSeverity === "error");
1575
1512
  return {
1576
1513
  check: {
1577
1514
  name: "lifecycle",
@@ -1602,11 +1539,23 @@ function buildLifecycleCheck(items, includeStaleBlockers, dependencyCycleSeverit
1602
1539
  dependency_cycle_sample_paths_truncated: summarizedDependencyCycleSamplePaths.truncated,
1603
1540
  parent_cycle_severity_policy: parentCycleSeverity,
1604
1541
  parent_cycle_count: parentCycleDiagnostics.cycle_count,
1542
+ active_parent_cycle_count: parentCycleDiagnostics.active_cycle_count,
1543
+ legacy_parent_cycle_count: parentCycleDiagnostics.legacy_cycle_count,
1605
1544
  parent_cycle_item_count: parentCycleDiagnostics.cycle_item_ids.length,
1606
1545
  parent_cycle_item_ids: summarizedParentCycleItemIds.values,
1607
1546
  parent_cycle_item_ids_truncated: summarizedParentCycleItemIds.truncated,
1608
1547
  parent_cycle_sample_paths: summarizedParentCycleSamplePaths.values,
1609
1548
  parent_cycle_sample_paths_truncated: summarizedParentCycleSamplePaths.truncated,
1549
+ hierarchy_cardinality_violation_count: parentCycleDiagnostics.cardinality_violation_count,
1550
+ active_hierarchy_cardinality_violation_count: parentCycleDiagnostics.active_cardinality_violation_count,
1551
+ legacy_hierarchy_cardinality_violation_count: parentCycleDiagnostics.legacy_cardinality_violation_count,
1552
+ hierarchy_cardinality_violation_rows: summarizedHierarchyCardinalityRows.values,
1553
+ hierarchy_cardinality_violation_rows_truncated: summarizedHierarchyCardinalityRows.truncated,
1554
+ hierarchy_parent_divergence_count: parentCycleDiagnostics.parent_divergence_count,
1555
+ active_hierarchy_parent_divergence_count: parentCycleDiagnostics.active_parent_divergence_count,
1556
+ legacy_hierarchy_parent_divergence_count: parentCycleDiagnostics.legacy_parent_divergence_count,
1557
+ hierarchy_parent_divergence_rows: summarizedHierarchyDivergenceRows.values,
1558
+ hierarchy_parent_divergence_rows_truncated: summarizedHierarchyDivergenceRows.truncated,
1610
1559
  stale_blocker_reason_patterns: [
1611
1560
  ...lifecyclePatternPolicy.stale_blocker_reason_patterns,
1612
1561
  ],
@@ -1762,23 +1711,40 @@ async function buildFilesCheck(items, workspaceRoot, pmRoot, fileScanMode, inclu
1762
1711
  };
1763
1712
  }
1764
1713
  /* c8 ignore stop */
1765
- /* c8 ignore start -- command preview truncation formatting is covered by command-reference integration fixtures */
1766
- function summarizeCommandReferenceRow(ownerId, referencedId, command) {
1714
+ /** Collapse a linked command to the bounded preview used in validation rows. */
1715
+ function summarizeCommandPreview(command) {
1767
1716
  const normalizedCommand = command.trim().replaceAll(/\s+/g, " ");
1768
- const commandPreview = normalizedCommand.length > 120
1717
+ return normalizedCommand.length > 120
1769
1718
  ? `${normalizedCommand.slice(0, 117)}...`
1770
1719
  : normalizedCommand;
1771
- return `${ownerId}:${referencedId}:${commandPreview}`;
1772
1720
  }
1773
- /* c8 ignore stop */
1721
+ /** Join command-reference identity and its bounded preview for diagnostics. */
1722
+ function summarizeCommandReferenceRow(ownerId, referencedId, command) {
1723
+ return `${ownerId}:${referencedId}:${summarizeCommandPreview(command)}`;
1724
+ }
1725
+ /** Render portable diagnostics for linked tests that this clone has not trusted. */
1726
+ function buildUntrustedLinkedTestRows(linkedTests, trustDecisions) {
1727
+ const rows = [];
1728
+ linkedTests.forEach(({ item, linkedTest }, index) => {
1729
+ const trust = trustDecisions[index];
1730
+ if (trust.trusted)
1731
+ return;
1732
+ const command = summarizeCommandPreview(linkedTest.command ?? "");
1733
+ rows.push(`${item.id}:${trust.source_ref ?? "unknown"}->${trust.current_source_ref ?? "unknown"}:${command}`);
1734
+ });
1735
+ return rows;
1736
+ }
1774
1737
  /* c8 ignore start -- command-reference discovery/stale-id permutations are covered by linked-test integration suites */
1775
- function buildCommandReferencesCheck(items, idPrefix, verboseDiagnostics) {
1738
+ async function buildCommandReferencesCheck(items, idPrefix, verboseDiagnostics, pmRoot) {
1776
1739
  const knownIds = new Set(items.map((item) => item.id.toLowerCase()));
1777
1740
  let linkedCommandsScanned = 0;
1778
1741
  let referencedPmIdCount = 0;
1779
1742
  const referencedPmIds = new Set();
1780
1743
  const staleReferenceRows = [];
1781
1744
  const linkedTests = items.flatMap((item) => (item.tests ?? []).map((linkedTest) => ({ item, linkedTest })));
1745
+ const currentSourceRef = await resolveLinkedTestSourceRef(resolveLinkedTestSourceWorkspaceRoot());
1746
+ const trustDecisions = await resolveLinkedTestTrustBatch(pmRoot, linkedTests.map(({ linkedTest }) => linkedTest), currentSourceRef);
1747
+ const untrustedRows = buildUntrustedLinkedTestRows(linkedTests, trustDecisions);
1782
1748
  for (const { item, linkedTest } of linkedTests) {
1783
1749
  if (typeof linkedTest.command !== "string" ||
1784
1750
  linkedTest.command.trim().length === 0) {
@@ -1803,17 +1769,20 @@ function buildCommandReferencesCheck(items, idPrefix, verboseDiagnostics) {
1803
1769
  ]
1804
1770
  .filter((value) => value.length > 0)
1805
1771
  .sort((left, right) => left.localeCompare(right));
1806
- const warnings = uniqueStaleReferenceRows.length > 0
1807
- ? [
1808
- `validate_command_references_stale_pm_ids:${uniqueStaleReferenceRows.length}`,
1809
- ]
1810
- : [];
1772
+ const warnings = [];
1773
+ if (uniqueStaleReferenceRows.length > 0) {
1774
+ warnings.push(`validate_command_references_stale_pm_ids:${uniqueStaleReferenceRows.length}`);
1775
+ }
1776
+ if (untrustedRows.length > 0) {
1777
+ warnings.push(`validate_linked_test_trust_unacknowledged:${untrustedRows.length}`);
1778
+ }
1811
1779
  const diagnosticLimit = verboseDiagnostics
1812
1780
  ? Number.POSITIVE_INFINITY
1813
1781
  : DIAGNOSTIC_LIST_SUMMARY_LIMIT;
1814
1782
  const summarizedRows = summarizeList(uniqueStaleReferenceRows, diagnosticLimit);
1815
1783
  const summarizedStalePmIds = summarizeList(stalePmIds, diagnosticLimit);
1816
1784
  const summarizedReferencedPmIds = summarizeList([...referencedPmIds].sort((left, right) => left.localeCompare(right)), diagnosticLimit);
1785
+ const summarizedUntrustedRows = summarizeList(untrustedRows, diagnosticLimit);
1817
1786
  return {
1818
1787
  check: {
1819
1788
  name: "command_references",
@@ -1826,6 +1795,10 @@ function buildCommandReferencesCheck(items, idPrefix, verboseDiagnostics) {
1826
1795
  unique_referenced_pm_ids_count: referencedPmIds.size,
1827
1796
  unique_referenced_pm_ids: summarizedReferencedPmIds.values,
1828
1797
  unique_referenced_pm_ids_truncated: summarizedReferencedPmIds.truncated,
1798
+ linked_test_trust_current_source_ref: currentSourceRef ?? null,
1799
+ untrusted_linked_tests_count: untrustedRows.length,
1800
+ untrusted_linked_tests: summarizedUntrustedRows.values,
1801
+ untrusted_linked_tests_truncated: summarizedUntrustedRows.truncated,
1829
1802
  stale_pm_id_references_count: uniqueStaleReferenceRows.length,
1830
1803
  stale_pm_ids_count: stalePmIds.length,
1831
1804
  stale_pm_ids: summarizedStalePmIds.values,
@@ -2145,7 +2118,7 @@ async function executeRequestedValidateChecks(params) {
2145
2118
  recordValidateCheck(state, built, fixHintsEnabled);
2146
2119
  }
2147
2120
  if (params.requestedChecks.has("command_references")) {
2148
- recordValidateCheck(state, buildCommandReferencesCheck(params.items, params.settings.id_prefix, fullDiagnostics), fixHintsEnabled);
2121
+ recordValidateCheck(state, await buildCommandReferencesCheck(params.items, params.settings.id_prefix, fullDiagnostics, params.pmRoot), fixHintsEnabled);
2149
2122
  }
2150
2123
  if (params.requestedChecks.has("history_drift")) {
2151
2124
  recordValidateCheck(state, await buildValidateHistoryDriftCheck(params.pmRoot, params.items, fullDiagnostics), fixHintsEnabled);
@@ -2307,4 +2280,4 @@ export async function runValidate(options, global, services = {}) {
2307
2280
  }
2308
2281
  /* c8 ignore stop */
2309
2282
  //# sourceMappingURL=validate.js.map
2310
- //# debugId=1ed6b663-882a-5936-bd89-46bbbb17d806
2283
+ //# debugId=aca69fd7-c47a-5424-b58f-f305f690edd9
@@ -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