@unbrained/pm-cli 2026.8.12 → 2026.8.14

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 (218) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +162 -111
  3. package/dist/cli/error-guidance.js +4 -8
  4. package/dist/cli/main.js +40 -3
  5. package/dist/cli/register-assurance.js +28 -17
  6. package/dist/cli-bundle/bundle-manifest.json +141 -173
  7. package/dist/cli-bundle/chunks/chunk-BBOFSVAK.js +197 -0
  8. package/dist/cli-bundle/chunks/chunk-BFALV3YP.js +5 -0
  9. package/dist/cli-bundle/chunks/{chunk-WLAHPSOX.js → chunk-FIW4P64F.js} +2 -2
  10. package/dist/cli-bundle/chunks/{chunk-BM5YJ2UW.js → chunk-G5JUB66K.js} +52 -52
  11. package/dist/cli-bundle/chunks/chunk-SBRKH6OR.js +2 -0
  12. package/dist/cli-bundle/chunks/chunk-TMJ7URWS.js +13 -0
  13. package/dist/cli-bundle/chunks/chunk-TXMEUUUY.js +8 -0
  14. package/dist/cli-bundle/chunks/{chunk-KOQMGCTA.js → chunk-XAVMOK6B.js} +50 -50
  15. package/dist/cli-bundle/chunks/chunk-YAMTJL6Y.js +34 -0
  16. package/dist/cli-bundle/chunks/chunk-YSVSMJ2B.js +2 -0
  17. package/dist/cli-bundle/chunks/{register-list-query-XPCJRUUD.js → register-list-query-T3EHHNNV.js} +2 -2
  18. package/dist/cli-bundle/chunks/{register-mutation-4RJCBKRS.js → register-mutation-E27WCLSZ.js} +2 -2
  19. package/dist/cli-bundle/chunks/register-operations-4R6I7CPY.js +2 -0
  20. package/dist/cli-bundle/chunks/{register-setup-LLHAKP2V.js → register-setup-3YXZ444E.js} +2 -2
  21. package/dist/cli-bundle/focused-chunks/{chunk-JAFRIWVF.js → chunk-4DQ2YERC.js} +4 -4
  22. package/dist/cli-bundle/focused-chunks/chunk-F43TT2QQ.js +8 -0
  23. package/dist/cli-bundle/focused-chunks/chunk-K7RWWOAG.js +4 -0
  24. package/dist/cli-bundle/focused-chunks/chunk-KFSZ3BP6.js +16 -0
  25. package/dist/cli-bundle/focused-chunks/{chunk-KHM2CS2C.js → chunk-KSEDUWVZ.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-KYUQGTSB.js +2 -0
  27. package/dist/cli-bundle/focused-chunks/{chunk-ZMGWAYH7.js → chunk-LZ4MV3RH.js} +2 -2
  28. package/dist/cli-bundle/focused-chunks/chunk-PGXJQLCT.js +155 -0
  29. package/dist/cli-bundle/focused-chunks/{chunk-RWPAWUOT.js → chunk-PPFGQDSI.js} +2 -2
  30. package/dist/cli-bundle/focused-chunks/chunk-Q4YY3XNF.js +2 -0
  31. package/dist/cli-bundle/focused-chunks/chunk-QK753U3U.js +46 -0
  32. package/dist/cli-bundle/focused-chunks/chunk-QUMB2J27.js +2 -0
  33. package/dist/cli-bundle/focused-chunks/chunk-SM7MTG27.js +2 -0
  34. package/dist/cli-bundle/focused-chunks/{chunk-GQW4BPUT.js → chunk-WCVE2NIG.js} +2 -2
  35. package/dist/cli-bundle/focused-chunks/chunk-XDNAW5OI.js +18 -0
  36. package/dist/cli-bundle/focused-chunks/{chunk-YIAXMC3T.js → chunk-YOOI3UZX.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/chunk-ZM6TZVHC.js +2 -0
  38. package/dist/cli-bundle/main.js +13 -13
  39. package/dist/cli-bundle/sdk-authoring.js +1 -1
  40. package/dist/cli-bundle/sdk-contracts.js +1 -1
  41. package/dist/cli-bundle/sdk-core.js +45 -41
  42. package/dist/cli-bundle/sdk-governance.js +1 -1
  43. package/dist/cli-bundle/sdk-graph.js +1 -1
  44. package/dist/cli-bundle/sdk-merge.js +109 -1
  45. package/dist/cli-bundle/sdk-query.js +1 -1
  46. package/dist/cli-bundle/sdk-runtime.js +1 -1
  47. package/dist/cli-bundle/sdk-testing.js +1 -1
  48. package/dist/cli-bundle/sdk.js +2 -2
  49. package/dist/core/checkpoint/checkpoint-gc.js +7 -12
  50. package/dist/core/extensions/activation-summary-markdown.js +5 -4
  51. package/dist/core/extensions/activation-summary.d.ts +2 -0
  52. package/dist/core/extensions/activation-summary.js +6 -2
  53. package/dist/core/extensions/capability-usage.d.ts +1 -1
  54. package/dist/core/extensions/capability-usage.js +4 -3
  55. package/dist/core/extensions/contribution-inventory.d.ts +1 -0
  56. package/dist/core/extensions/contribution-inventory.js +6 -2
  57. package/dist/core/extensions/extension-registries.js +3 -2
  58. package/dist/core/extensions/extension-types.d.ts +83 -1
  59. package/dist/core/extensions/extension-types.js +3 -2
  60. package/dist/core/extensions/loader.d.ts +2 -8
  61. package/dist/core/extensions/loader.js +53 -144
  62. package/dist/core/extensions/registration-values.d.ts +43 -0
  63. package/dist/core/extensions/registration-values.js +210 -0
  64. package/dist/core/extensions/runtime-registrations.d.ts +3 -1
  65. package/dist/core/extensions/runtime-registrations.js +12 -2
  66. package/dist/core/fs/fs-utils.d.ts +2 -0
  67. package/dist/core/fs/fs-utils.js +7 -3
  68. package/dist/core/history/drift-cache.js +4 -3
  69. package/dist/core/history/drift-scan.d.ts +6 -0
  70. package/dist/core/history/drift-scan.js +76 -58
  71. package/dist/core/history/workspace-history.d.ts +59 -0
  72. package/dist/core/history/workspace-history.js +197 -10
  73. package/dist/core/lock/lock-gc.js +8 -13
  74. package/dist/core/lock/lock.js +5 -4
  75. package/dist/core/output/output.js +12 -4
  76. package/dist/core/search/vector-stores.js +6 -11
  77. package/dist/core/store/item-metadata-cache.js +4 -4
  78. package/dist/core/telemetry/runtime.d.ts +6 -0
  79. package/dist/core/telemetry/runtime.js +101 -30
  80. package/dist/sdk/agent/command-recovery.d.ts +2 -0
  81. package/dist/sdk/agent/command-recovery.js +10 -2
  82. package/dist/sdk/annotations.js +4 -4
  83. package/dist/sdk/cli-contracts/completeness.js +9 -3
  84. package/dist/sdk/cli-contracts/enum-contracts.d.ts +1 -1
  85. package/dist/sdk/cli-contracts/flag-contracts.js +6 -3
  86. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +6 -1
  87. package/dist/sdk/cli-contracts/runtime-contracts.js +155 -60
  88. package/dist/sdk/cli-contracts/schemas/assurance-definition.d.ts +32 -0
  89. package/dist/sdk/cli-contracts/schemas/assurance-definition.js +25 -2
  90. package/dist/sdk/cli-contracts/tool-parameter-tables.js +35 -5
  91. package/dist/sdk/cli-contracts/tool-schema.js +17 -2
  92. package/dist/sdk/cli-program.js +3 -3
  93. package/dist/sdk/comments.js +4 -3
  94. package/dist/sdk/compile-cache.js +4 -3
  95. package/dist/sdk/compose.d.ts +3 -1
  96. package/dist/sdk/compose.js +16 -2
  97. package/dist/sdk/context-relevance.js +12 -4
  98. package/dist/sdk/context-signal-store.js +4 -3
  99. package/dist/sdk/extension/install-runtime.js +4 -4
  100. package/dist/sdk/extension/managed-state.js +4 -3
  101. package/dist/sdk/extension-contracts.d.ts +1 -1
  102. package/dist/sdk/flag-invocation-contracts.js +5 -6
  103. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +14 -2
  104. package/dist/sdk/governance/assurance-action.d.ts +27 -3
  105. package/dist/sdk/governance/assurance-action.js +112 -28
  106. package/dist/sdk/governance/assurance-mutation-error.d.ts +7 -0
  107. package/dist/sdk/governance/assurance-mutation-error.js +25 -2
  108. package/dist/sdk/governance/assurance-presets.d.ts +54 -0
  109. package/dist/sdk/governance/assurance-presets.js +136 -0
  110. package/dist/sdk/governance/assurance-runtime.d.ts +4 -0
  111. package/dist/sdk/governance/assurance-runtime.js +109 -8
  112. package/dist/sdk/governance/assurance.d.ts +39 -0
  113. package/dist/sdk/governance/assurance.js +145 -6
  114. package/dist/sdk/governance/gc.js +5 -11
  115. package/dist/sdk/governance/health.js +18 -6
  116. package/dist/sdk/governance/validate-history-drift.d.ts +7 -0
  117. package/dist/sdk/governance/validate-history-drift.js +68 -0
  118. package/dist/sdk/governance/validate.js +6 -48
  119. package/dist/sdk/graph/assembly.d.ts +23 -0
  120. package/dist/sdk/graph/assembly.js +54 -2
  121. package/dist/sdk/graph/durable-cache.js +91 -3
  122. package/dist/sdk/graph/governance-contracts.d.ts +12 -0
  123. package/dist/sdk/graph/governance-contracts.js +29 -0
  124. package/dist/sdk/graph/governance.d.ts +51 -3
  125. package/dist/sdk/graph/governance.js +216 -35
  126. package/dist/sdk/graph/index.d.ts +2 -2
  127. package/dist/sdk/graph/index.js +4 -4
  128. package/dist/sdk/graph/mutation-advisory.js +15 -7
  129. package/dist/sdk/graph/remediation.js +12 -2
  130. package/dist/sdk/guide.js +4 -4
  131. package/dist/sdk/history-read.d.ts +8 -0
  132. package/dist/sdk/history-read.js +2 -2
  133. package/dist/sdk/index.d.ts +3 -2
  134. package/dist/sdk/index.js +5 -4
  135. package/dist/sdk/linked-artifacts.d.ts +2 -0
  136. package/dist/sdk/linked-artifacts.js +29 -4
  137. package/dist/sdk/merge/install.js +5 -4
  138. package/dist/sdk/query/activity.js +4 -3
  139. package/dist/sdk/query/history.js +20 -3
  140. package/dist/sdk/read-output-budget.d.ts +2 -0
  141. package/dist/sdk/read-output-budget.js +12 -5
  142. package/dist/sdk/read-output-contracts.d.ts +61 -5
  143. package/dist/sdk/read-output-contracts.js +210 -28
  144. package/dist/sdk/read-output-rows.d.ts +11 -0
  145. package/dist/sdk/read-output-rows.js +47 -2
  146. package/dist/sdk/relationship-history.js +5 -5
  147. package/dist/sdk/runtime-primitives.d.ts +1 -1
  148. package/dist/sdk/runtime-primitives.js +3 -3
  149. package/dist/sdk/runtime.js +5 -3
  150. package/dist/sdk/schema-migration.js +4 -4
  151. package/dist/sdk/test/execution.js +4 -4
  152. package/dist/sdk/workspace-memory.js +4 -4
  153. package/dist/sdk/workspace-snapshot.js +8 -14
  154. package/dist/sdk/workspace-transaction-gc.js +4 -9
  155. package/dist/sdk/workspace-transaction.js +4 -4
  156. package/dist/sdk/workspace.js +4 -3
  157. package/docs/ASSURANCE.md +163 -3
  158. package/docs/COMMANDS.md +31 -2
  159. package/docs/CONFIGURATION.md +2 -0
  160. package/docs/EXTENSIONS.md +11 -12
  161. package/docs/READ_OUTPUT_CONTRACTS.md +44 -8
  162. package/docs/RELATIONSHIP_GRAPH.md +34 -4
  163. package/docs/SDK.md +29 -8
  164. package/docs/SDK_CONTEXT_INTEGRITY.md +32 -1
  165. package/docs/TESTING.md +47 -16
  166. package/docs/performance/sdk-entrypoint-import-costs.md +20 -13
  167. package/marketplace.json +2 -2
  168. package/package.json +6 -6
  169. package/packages/pm-beads/extensions/beads/index.ts +1 -0
  170. package/packages/pm-beads/package.json +1 -1
  171. package/packages/pm-calendar/extensions/calendar/index.ts +1 -0
  172. package/packages/pm-calendar/package.json +1 -1
  173. package/packages/pm-command-kit/package.json +1 -1
  174. package/packages/pm-digital-twin/extensions/digital-twin/index.ts +7 -0
  175. package/packages/pm-digital-twin/package.json +1 -1
  176. package/packages/pm-governance-audit/extensions/governance-audit/index.ts +4 -0
  177. package/packages/pm-governance-audit/package.json +1 -1
  178. package/packages/pm-guide-shell/extensions/guide-shell/index.ts +5 -0
  179. package/packages/pm-guide-shell/package.json +1 -1
  180. package/packages/pm-kanban/package.json +1 -1
  181. package/packages/pm-lifecycle-hooks/package.json +1 -1
  182. package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/index.ts +6 -0
  183. package/packages/pm-linked-test-adapters/package.json +1 -1
  184. package/packages/pm-search-advanced/extensions/search-advanced/index.ts +2 -0
  185. package/packages/pm-search-advanced/package.json +1 -1
  186. package/packages/pm-templates/extensions/templates/index.ts +4 -0
  187. package/packages/pm-templates/package.json +1 -1
  188. package/packages/pm-todos/extensions/todos/index.ts +2 -0
  189. package/packages/pm-todos/package.json +1 -1
  190. package/packages/pm-vcs/extensions/vcs/index.ts +7 -0
  191. package/packages/pm-vcs/package.json +1 -1
  192. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  193. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  194. package/scripts/bundle-cli.mjs +17 -4
  195. package/sdk/public-surface.json +390 -60
  196. package/dist/cli-bundle/chunks/chunk-3PS7X6GS.js +0 -8
  197. package/dist/cli-bundle/chunks/chunk-7KL6KMWF.js +0 -13
  198. package/dist/cli-bundle/chunks/chunk-D55A5AEN.js +0 -33
  199. package/dist/cli-bundle/chunks/chunk-KIKDWECH.js +0 -196
  200. package/dist/cli-bundle/chunks/chunk-R76342NV.js +0 -2
  201. package/dist/cli-bundle/chunks/chunk-RBTK5EZV.js +0 -5
  202. package/dist/cli-bundle/chunks/chunk-VLUQOIDT.js +0 -2
  203. package/dist/cli-bundle/chunks/register-operations-2OGISOVR.js +0 -2
  204. package/dist/cli-bundle/focused-chunks/chunk-2DALMVF7.js +0 -5
  205. package/dist/cli-bundle/focused-chunks/chunk-5OQIMELN.js +0 -155
  206. package/dist/cli-bundle/focused-chunks/chunk-5TAGTDRE.js +0 -2
  207. package/dist/cli-bundle/focused-chunks/chunk-ECADZMVC.js +0 -18
  208. package/dist/cli-bundle/focused-chunks/chunk-EJGXRA6B.js +0 -2
  209. package/dist/cli-bundle/focused-chunks/chunk-FFGSZCPK.js +0 -16
  210. package/dist/cli-bundle/focused-chunks/chunk-ISKOBJCM.js +0 -2
  211. package/dist/cli-bundle/focused-chunks/chunk-KPSXVBDV.js +0 -2
  212. package/dist/cli-bundle/focused-chunks/chunk-KTLOKKCQ.js +0 -2
  213. package/dist/cli-bundle/focused-chunks/chunk-LGYK3NUF.js +0 -8
  214. package/dist/cli-bundle/focused-chunks/chunk-NBLUKITQ.js +0 -4
  215. package/dist/cli-bundle/focused-chunks/chunk-NYLOUPOB.js +0 -2
  216. package/dist/cli-bundle/focused-chunks/chunk-Q4T463SD.js +0 -31
  217. package/dist/cli-bundle/focused-chunks/chunk-QVZVY53N.js +0 -2
  218. package/dist/cli-bundle/focused-chunks/chunk-UTEWXDEQ.js +0 -17
@@ -13,11 +13,11 @@
13
13
  * agents can plan remediation without re-deriving context.
14
14
  */
15
15
  import type { RelationshipEdge } from "../relationships.js";
16
- import type { WorkspaceRelationshipAssembly } from "./assembly.js";
16
+ import type { OrderingStorageContradiction, WorkspaceRelationshipAssembly } from "./assembly.js";
17
+ import { type RelationshipAuditFindingCode } from "./governance-contracts.js";
18
+ export type { RelationshipAuditFindingCode } from "./governance-contracts.js";
17
19
  /** Severity ladder shared by every relationship audit finding. */
18
20
  export type RelationshipAuditSeverity = "error" | "warning" | "info";
19
- /** Machine-readable identifier for one relationship audit finding family. */
20
- export type RelationshipAuditFindingCode = "missing_reference_active" | "missing_reference_terminal" | "legacy_no_blocker_sentinel" | "ordering_cycle" | "legacy_ordering_cycle" | "duplicate_edge" | "legacy_duplicate_edge" | "duplicate_dependency_row" | "legacy_duplicate_dependency_row" | "stale_lifecycle_block" | "isolated_active_node" | "sparse_active_node";
21
21
  /** One policy-aware relationship audit finding with bounded evidence. */
22
22
  export interface RelationshipAuditFinding {
23
23
  /** Machine-readable finding family identifier. */
@@ -32,6 +32,8 @@ export interface RelationshipAuditFinding {
32
32
  count: number;
33
33
  /** Deterministic bounded sample of affected subjects. */
34
34
  sample: string[];
35
+ /** Optional exact evidence that explains why the sampled subjects were selected. */
36
+ evidence?: string[];
35
37
  /** Whether {@link sample} was truncated by the configured sample bound. */
36
38
  sample_truncated: boolean;
37
39
  /** Safe machine-actionable next step; audits never invent or auto-apply edges. */
@@ -50,8 +52,16 @@ export interface RelationshipCoverageTypeProfile {
50
52
  export interface RelationshipCoverageProfile {
51
53
  /** Total indexed nodes including materialized missing placeholders. */
52
54
  nodes: number;
55
+ /** Tracker-recorded nodes, excluding synthesized missing and external placeholders. */
56
+ recorded_nodes?: number;
53
57
  /** Total deduplicated indexed edges. */
54
58
  edges: number;
59
+ /** Edges that remain after excluding redundant and contradictory storage rows. */
60
+ informative_edges?: number;
61
+ /** Stored edges implied by a bounded longer path in the same semantic family. */
62
+ redundant_edges?: number;
63
+ /** Structured ordering edges that contradict a same-target scalar blocker. */
64
+ ordering_contradiction_edges?: number;
55
65
  /** Counting convention used by edges and edges_by_kind. */
56
66
  edge_basis: "deduplicated_directed";
57
67
  /** Deterministic per-kind edge counts. */
@@ -72,6 +82,34 @@ export interface RelationshipCoverageProfile {
72
82
  degree_leq_one_active_nodes: number;
73
83
  /** Deterministic per-type connectivity coverage; untyped nodes bucket under `(untyped)`. */
74
84
  coverage_by_type: Record<string, RelationshipCoverageTypeProfile>;
85
+ /** Undirected cut vertices whose removal increases the component count. */
86
+ articulation_points: number;
87
+ /** Undirected cut edges whose removal disconnects their endpoints. */
88
+ bridge_edges: number;
89
+ /** Milestone nodes whose title explicitly declares an outcome. */
90
+ outcome_nodes: number;
91
+ /** Active non-outcome nodes with a typed hierarchy or implements path to an outcome. */
92
+ active_outcome_reachable_nodes: number;
93
+ /** Active non-outcome nodes without a typed hierarchy or implements path to an outcome. */
94
+ active_outcome_unreachable_nodes: number;
95
+ /** Active typed-outcome reachability expressed as integer basis points. */
96
+ active_outcome_reachability_basis_points: number;
97
+ /** Terminal non-placeholder, non-outcome nodes included in the all-status census. */
98
+ terminal_nodes: number;
99
+ /** Terminal non-outcome nodes with a typed hierarchy or implements path to an outcome. */
100
+ terminal_outcome_reachable_nodes: number;
101
+ /** Terminal non-outcome nodes without a typed hierarchy or implements path to an outcome. */
102
+ terminal_outcome_unreachable_nodes: number;
103
+ /** Terminal typed-outcome reachability expressed as integer basis points. */
104
+ terminal_outcome_reachability_basis_points: number;
105
+ /** All active and terminal non-outcome nodes with a typed path to an outcome. */
106
+ outcome_reachable_nodes: number;
107
+ /** All active and terminal non-outcome nodes without a typed path to an outcome. */
108
+ outcome_unreachable_nodes: number;
109
+ /** All-status typed-outcome reachability expressed as integer basis points. */
110
+ outcome_reachability_basis_points: number;
111
+ /** Affected-subject census containing every known finding code, including zero populations. */
112
+ finding_subjects_by_code: Record<RelationshipAuditFindingCode, number>;
75
113
  }
76
114
  /** Tuning and policy inputs accepted by the relationship audit. */
77
115
  export interface RelationshipAuditOptions {
@@ -106,6 +144,8 @@ export declare function isSemanticContextKind(kind: string): boolean;
106
144
  * advisories) share one cycle semantics with the workspace audit.
107
145
  */
108
146
  export declare function collectOrderingCycles(successors: Map<string, string[]>): string[][];
147
+ /** Format the exact contradictory storage pair that manufactures an ordering cycle. */
148
+ export declare function formatOrderingStorageContradiction(contradiction: Pick<OrderingStorageContradiction, "holder_id" | "target_id" | "dependency_kind">): string;
109
149
  /** One duplicate-edge group in semantic orientation with its stored spellings. */
110
150
  export interface DuplicateRelationshipEdgeGroup {
111
151
  /** Semantic tail node shared by every stored spelling. */
@@ -161,8 +201,16 @@ export interface RelationshipAuditDelta {
161
201
  profile: {
162
202
  /** Node-count change including missing placeholders. */
163
203
  nodes: number;
204
+ /** Tracker-recorded node-count change excluding synthesized placeholders. */
205
+ recorded_nodes?: number;
164
206
  /** Deduplicated edge-count change. */
165
207
  edges: number;
208
+ /** Informative edge-count change after non-information deductions. */
209
+ informative_edges?: number;
210
+ /** Redundant edge-count change. */
211
+ redundant_edges?: number;
212
+ /** Ordering contradiction edge-count change. */
213
+ ordering_contradiction_edges?: number;
166
214
  /** Active-node-count change. */
167
215
  active_nodes: number;
168
216
  /** Missing-placeholder-count change. */
@@ -1,8 +1,13 @@
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]="996cfe8f-1b9b-534e-9e22-ede6f76d7cac")}catch(e){}}();
3
- import { isTransitiveKind, orientTransitiveEdge, transitiveFamilyKey, } from "./analytics.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]="1955b7a4-385d-5a5d-8856-6db9f94814aa")}catch(e){}}();
3
+ import { findRedundantRelationshipEdges, isTransitiveKind, orientTransitiveEdge, transitiveFamilyKey, } from "./analytics.js";
4
+ import { findRelationshipCutStructure } from "./centrality.js";
5
+ import { RELATIONSHIP_AUDIT_FINDING_CODES, } from "./governance-contracts.js";
4
6
  /** Default bounded sample size applied to every finding family. */
5
7
  const DEFAULT_MAX_SAMPLE_SIZE = 25;
8
+ /** Canonical item type and title marker that identify explicit outcomes. */
9
+ const OUTCOME_MILESTONE_TYPE = "milestone";
10
+ const OUTCOME_MILESTONE_TITLE_PREFIX = "outcome milestone:";
6
11
  /** Relationship kinds that preserve why work exists or how evidence supports it. */
7
12
  const SEMANTIC_CONTEXT_KINDS = new Set([
8
13
  "discovered_from",
@@ -20,8 +25,14 @@ const SEVERITY_RANK = {
20
25
  warning: 1,
21
26
  info: 2,
22
27
  };
28
+ /** Normalize optional exemption identifiers into a case-insensitive lookup set. */
29
+ function normalizeExemptionSet(values) {
30
+ return new Set((values ?? [])
31
+ .filter((value) => typeof value === "string")
32
+ .map((value) => value.trim().toLowerCase()));
33
+ }
23
34
  /** Build one finding with deterministic bounded sampling over sorted subjects. */
24
- function buildFinding(code, severity, policy, describe, subjects, maxSampleSize, remediation) {
35
+ function buildFinding(code, severity, policy, describe, subjects, maxSampleSize, remediation, evidence) {
25
36
  const sorted = [...subjects].sort((left, right) => left.localeCompare(right));
26
37
  return {
27
38
  code,
@@ -32,6 +43,13 @@ function buildFinding(code, severity, policy, describe, subjects, maxSampleSize,
32
43
  sample: sorted.slice(0, maxSampleSize),
33
44
  sample_truncated: sorted.length > maxSampleSize,
34
45
  remediation,
46
+ ...(evidence === undefined
47
+ ? {}
48
+ : {
49
+ evidence: [...evidence]
50
+ .sort((left, right) => left.localeCompare(right))
51
+ .slice(0, maxSampleSize),
52
+ }),
35
53
  };
36
54
  }
37
55
  /** Resolve the ordering-only successor adjacency for cycle analysis. */
@@ -172,6 +190,21 @@ function isActiveAuditMember(member, nodeStates) {
172
190
  return true;
173
191
  return !state.terminal;
174
192
  }
193
+ /** Format the exact contradictory storage pair that manufactures an ordering cycle. */
194
+ export function formatOrderingStorageContradiction(contradiction) {
195
+ return `${contradiction.holder_id} -> ${contradiction.target_id} (blocked_by scalar + ${contradiction.dependency_kind} dependency)`;
196
+ }
197
+ /** Report active and historical scalar-versus-structured ordering contradictions. */
198
+ function collectOrderingStorageContradictionFindings(assembly, maxSampleSize) {
199
+ const active = [];
200
+ const legacy = [];
201
+ for (const contradiction of assembly.orderingContradictions ?? [])
202
+ (contradiction.legacy_terminal ? legacy : active).push(formatOrderingStorageContradiction(contradiction));
203
+ const findings = [];
204
+ appendFinding(findings, active, maxSampleSize, "ordering_storage_contradiction", "error", "A scalar blocker and same-target structured ordering row must not assert opposite precedence.", (count) => `${count} active scalar-versus-structured ordering contradiction(s)`, "Remove the structured dependency row that points the holder before its scalar blocker, or replace both representations with one unambiguous ordering edge.");
205
+ appendFinding(findings, legacy, maxSampleSize, "legacy_ordering_storage_contradiction", "info", "Terminal scalar-versus-structured ordering contradictions are exact historical storage defects with no live scheduling effect.", (count) => `${count} terminal scalar-versus-structured ordering contradiction(s)`, "Remove the contradictory blocks dependency row in a dedicated changelog-safe closed-item batch; preserve the scalar blocker as the historical precedence record.");
206
+ return findings;
207
+ }
175
208
  /** Collect ordering-cycle and lifecycle-block governance findings. */
176
209
  function collectOrderingFindings(assembly, nodeStates, isBlocked, maxSampleSize) {
177
210
  const findings = [];
@@ -181,9 +214,18 @@ function collectOrderingFindings(assembly, nodeStates, isBlocked, maxSampleSize)
181
214
  // it cannot make live work unschedulable, and "repairing" it would touch
182
215
  // closed items (changelog drag), so it downgrades to informational debt.
183
216
  const active = cycle.some((member) => isActiveAuditMember(member, nodeStates));
217
+ const cycleMembers = new Set(cycle.map((member) => member.toLowerCase()));
218
+ const contradictionEvidence = (assembly.orderingContradictions ?? [])
219
+ .filter((row) => cycleMembers.has(row.holder_id.toLowerCase()) &&
220
+ cycleMembers.has(row.target_id.toLowerCase()))
221
+ .map(formatOrderingStorageContradiction);
184
222
  findings.push(active
185
- ? buildFinding("ordering_cycle", "error", "Order-bearing relationship kinds must stay acyclic; a cycle makes every member unschedulable.", (count) => `execution-order cycle across ${count} item(s)`, cycle, maxSampleSize, "Remove or retype one ordering edge inside the cycle, or split the work so precedence is linear.")
186
- : buildFinding("legacy_ordering_cycle", "info", "Ordering cycles between exclusively terminal items are historical contradictions with no scheduling effect.", (count) => `terminal-only execution-order cycle across ${count} item(s)`, cycle, maxSampleSize, "Leave as history debt, or clean up in a dedicated changelog-safe closed-item batch; never repair closed items ad hoc."));
223
+ ? buildFinding("ordering_cycle", "error", "Order-bearing relationship kinds must stay acyclic; a cycle makes every member unschedulable.", (count) => `execution-order cycle across ${count} item(s)`, cycle, maxSampleSize, "Remove or retype one ordering edge inside the cycle, or split the work so precedence is linear.", contradictionEvidence.length === 0
224
+ ? undefined
225
+ : contradictionEvidence)
226
+ : buildFinding("legacy_ordering_cycle", "info", "Ordering cycles between exclusively terminal items are historical contradictions with no scheduling effect.", (count) => `terminal-only execution-order cycle across ${count} item(s)`, cycle, maxSampleSize, "Leave as history debt, or clean up in a dedicated changelog-safe closed-item batch; never repair closed items ad hoc.", contradictionEvidence.length === 0
227
+ ? undefined
228
+ : contradictionEvidence));
187
229
  }
188
230
  const predecessorsByNode = new Map();
189
231
  for (const [predecessor, adjacent] of orderingAdjacency)
@@ -290,6 +332,104 @@ function collectDuplicateRowFindings(assembly, maxSampleSize) {
290
332
  appendFinding(findings, legacy, maxSampleSize, "legacy_duplicate_dependency_row", "info", "Identical repeated dependency rows on terminal holders are historical storage noise with no scheduling effect.", (count) => `${count} exactly duplicated stored dependency row(s) on terminal holder(s)`, "Leave as history debt or clean up in a dedicated changelog-safe closed-item batch.");
291
333
  return findings;
292
334
  }
335
+ /** Build the reverse typed paths that can carry context toward an outcome. */
336
+ function buildOutcomeReverseIndex(assembly, signal) {
337
+ const reverse = new Map();
338
+ for (const edge of assembly.graph.edges()) {
339
+ signal?.throwIfAborted();
340
+ const definition = assembly.graph.registry().require(edge.kind);
341
+ let oriented;
342
+ if (definition.hierarchy) {
343
+ oriented = orientTransitiveEdge(edge, definition);
344
+ }
345
+ else if (definition.kind === "implements") {
346
+ oriented = { from: edge.source, to: edge.target };
347
+ }
348
+ if (oriented === undefined)
349
+ continue;
350
+ const sources = reverse.get(oriented.to) ?? new Set();
351
+ sources.add(oriented.from);
352
+ reverse.set(oriented.to, sources);
353
+ }
354
+ return reverse;
355
+ }
356
+ /** Expand outcome nodes through every deterministic reverse typed path. */
357
+ function expandOutcomeReachability(outcomes, reverse, signal) {
358
+ const reachable = new Set(outcomes);
359
+ const queue = [...outcomes].sort();
360
+ for (let index = 0; index < queue.length; index += 1) {
361
+ signal?.throwIfAborted();
362
+ for (const source of [...(reverse.get(queue[index]) ?? [])].sort()) {
363
+ if (reachable.has(source))
364
+ continue;
365
+ reachable.add(source);
366
+ queue.push(source);
367
+ }
368
+ }
369
+ return reachable;
370
+ }
371
+ /** Count active and terminal nodes against the resolved outcome-reachable set. */
372
+ function tallyOutcomeReachability(nodeStates, outcomes, reachable, signal) {
373
+ let activeReachable = 0;
374
+ let activeUnreachable = 0;
375
+ let terminal = 0;
376
+ let terminalReachable = 0;
377
+ for (const state of nodeStates.values()) {
378
+ signal?.throwIfAborted();
379
+ if (outcomes.has(state.id))
380
+ continue;
381
+ if (state.missing || state.status === "external")
382
+ continue;
383
+ if (state.terminal) {
384
+ terminal += 1;
385
+ if (reachable.has(state.id))
386
+ terminalReachable += 1;
387
+ continue;
388
+ }
389
+ if (reachable.has(state.id))
390
+ activeReachable += 1;
391
+ else
392
+ activeUnreachable += 1;
393
+ }
394
+ const active = activeReachable + activeUnreachable;
395
+ const terminalUnreachable = terminal - terminalReachable;
396
+ const outcomeReachable = activeReachable + terminalReachable;
397
+ const outcomeUnreachable = activeUnreachable + terminalUnreachable;
398
+ const outcomePopulation = outcomeReachable + outcomeUnreachable;
399
+ return {
400
+ active_outcome_reachable_nodes: activeReachable,
401
+ active_outcome_unreachable_nodes: activeUnreachable,
402
+ active_outcome_reachability_basis_points: active === 0 ? 0 : Math.floor((activeReachable * 10_000) / active),
403
+ terminal_nodes: terminal,
404
+ terminal_outcome_reachable_nodes: terminalReachable,
405
+ terminal_outcome_unreachable_nodes: terminalUnreachable,
406
+ terminal_outcome_reachability_basis_points: terminal === 0 ? 0 : Math.floor((terminalReachable * 10_000) / terminal),
407
+ outcome_reachable_nodes: outcomeReachable,
408
+ outcome_unreachable_nodes: outcomeUnreachable,
409
+ outcome_reachability_basis_points: outcomePopulation === 0
410
+ ? 0
411
+ : Math.floor((outcomeReachable * 10_000) / outcomePopulation),
412
+ };
413
+ }
414
+ /**
415
+ * Count nodes with an explicit typed path to an outcome milestone. Hierarchy
416
+ * edges flow child to parent and `implements` flows implementation to outcome;
417
+ * unrelated association edges deliberately contribute no reachability.
418
+ */
419
+ function profileOutcomeReachability(assembly, nodeStates, signal) {
420
+ const outcomes = new Set(assembly.details
421
+ .filter((detail) => detail.type?.toLowerCase() === OUTCOME_MILESTONE_TYPE &&
422
+ detail.title
423
+ .trim()
424
+ .toLowerCase()
425
+ .startsWith(OUTCOME_MILESTONE_TITLE_PREFIX))
426
+ .map((detail) => detail.id));
427
+ const reachable = expandOutcomeReachability(outcomes, buildOutcomeReverseIndex(assembly, signal), signal);
428
+ return {
429
+ outcome_nodes: outcomes.size,
430
+ ...tallyOutcomeReachability(nodeStates, outcomes, reachable, signal),
431
+ };
432
+ }
293
433
  /** Fold one active node's degree into the aggregate and per-type coverage tallies. */
294
434
  function tallyActiveCoverageNode(tallies, state, degree, exemptIsolates, isolateExemptTypes) {
295
435
  tallies.active += 1;
@@ -316,7 +456,7 @@ function tallyActiveCoverageNode(tallies, state, degree, exemptIsolates, isolate
316
456
  else
317
457
  tallies.sparse.push(state.id);
318
458
  }
319
- function collectCoverageReport(assembly, nodeStates, exemptIsolates, isolateExemptTypes, maxSampleSize, edgesByKind) {
459
+ function collectCoverageReport(assembly, nodeStates, exemptIsolates, isolateExemptTypes, maxSampleSize, edgesByKind, signal) {
320
460
  const tallies = {
321
461
  active: 0,
322
462
  missing: 0,
@@ -327,6 +467,7 @@ function collectCoverageReport(assembly, nodeStates, exemptIsolates, isolateExem
327
467
  byType: new Map(),
328
468
  };
329
469
  for (const id of assembly.graph.nodes()) {
470
+ signal?.throwIfAborted();
330
471
  const state = nodeStates.get(id);
331
472
  /* c8 ignore next -- every graph node originates from assembly details by construction */
332
473
  if (!state)
@@ -344,13 +485,27 @@ function collectCoverageReport(assembly, nodeStates, exemptIsolates, isolateExem
344
485
  appendFinding(findings, isolates, maxSampleSize, "isolated_active_node", "warning", "Active work should be reachable from the graph unless explicitly declared a valid isolate.", (count) => `${count} active item(s) with no relationships`, "Link the item to its parent, prerequisite, or origin — or register it as an exempt isolate.");
345
486
  appendFinding(findings, sparse, maxSampleSize, "sparse_active_node", "info", "Active items with a single edge often miss an honest semantic partner; sparse is not automatically defective.", (count) => `${count} active item(s) with exactly one relationship`, "Investigate for an honest second edge (story->task, defect family, cross-epic complement); do not fabricate one.");
346
487
  const edgeCount = assembly.graph.edges().length;
488
+ const redundantEdges = findRedundantRelationshipEdges(assembly.graph, {
489
+ signal,
490
+ }).value;
491
+ const nonInformativeEdgeKeys = new Set(redundantEdges.map(({ edge }) => `${edge.source.toLowerCase()}\u0000${edge.target.toLowerCase()}\u0000${edge.kind}`));
492
+ for (const contradiction of assembly.orderingContradictions ?? [])
493
+ nonInformativeEdgeKeys.add(`${contradiction.holder_id.toLowerCase()}\u0000${contradiction.target_id.toLowerCase()}\u0000${contradiction.dependency_kind}`);
347
494
  const sortedEdgesByKind = Object.fromEntries(Object.entries(edgesByKind).sort(([left], [right]) => left.localeCompare(right)));
348
495
  const semanticEdges = Object.entries(sortedEdgesByKind).reduce((count, [kind, value]) => count + (isSemanticContextKind(kind) ? value : 0), 0);
496
+ const cutStructure = findRelationshipCutStructure(assembly.graph, {
497
+ signal,
498
+ }).value;
499
+ const outcomeReachability = profileOutcomeReachability(assembly, nodeStates, signal);
349
500
  return {
350
501
  findings,
351
502
  profile: {
352
503
  nodes: assembly.graph.nodes().length,
504
+ recorded_nodes: assembly.details.filter((detail) => detail.status !== "missing" && detail.status !== "external").length,
353
505
  edges: edgeCount,
506
+ informative_edges: edgeCount - nonInformativeEdgeKeys.size,
507
+ redundant_edges: redundantEdges.length,
508
+ ordering_contradiction_edges: assembly.orderingContradictions?.length ?? 0,
354
509
  edge_basis: "deduplicated_directed",
355
510
  edges_by_kind: sortedEdgesByKind,
356
511
  edge_share_by_kind: Object.fromEntries(Object.entries(sortedEdgesByKind).map(([kind, count]) => [
@@ -364,6 +519,10 @@ function collectCoverageReport(assembly, nodeStates, exemptIsolates, isolateExem
364
519
  isolated_active_nodes: tallies.isolated,
365
520
  degree_leq_one_active_nodes: tallies.degreeLeqOne,
366
521
  coverage_by_type: Object.fromEntries([...tallies.byType.entries()].sort(([left], [right]) => left < right ? -1 : 1)),
522
+ articulation_points: cutStructure.articulationPoints.length,
523
+ bridge_edges: cutStructure.bridges.length,
524
+ ...outcomeReachability,
525
+ finding_subjects_by_code: Object.fromEntries(RELATIONSHIP_AUDIT_FINDING_CODES.map((code) => [code, 0])),
367
526
  },
368
527
  };
369
528
  }
@@ -384,12 +543,8 @@ export function auditWorkspaceRelationshipGraph(assembly, options = {}) {
384
543
  const maxSampleSize = options.maxSampleSize ?? DEFAULT_MAX_SAMPLE_SIZE;
385
544
  if (!Number.isInteger(maxSampleSize) || maxSampleSize < 1)
386
545
  throw new TypeError(`Invalid audit sample bound: ${String(options.maxSampleSize)}`);
387
- const exemptIsolates = new Set((options.exemptIsolates ?? [])
388
- .filter((id) => typeof id === "string")
389
- .map((id) => id.trim().toLowerCase()));
390
- const isolateExemptTypes = new Set((options.isolateExemptTypes ?? [])
391
- .filter((type) => typeof type === "string")
392
- .map((type) => type.trim().toLowerCase()));
546
+ const exemptIsolates = normalizeExemptionSet(options.exemptIsolates);
547
+ const isolateExemptTypes = normalizeExemptionSet(options.isolateExemptTypes);
393
548
  const nodeStates = new Map(assembly.details.map((detail) => [
394
549
  detail.id,
395
550
  {
@@ -406,18 +561,27 @@ export function auditWorkspaceRelationshipGraph(assembly, options = {}) {
406
561
  edgesByKind[edge.kind] = (edgesByKind[edge.kind] ?? 0) + 1;
407
562
  const findings = collectIntegrityFindings(assembly, isTerminal, maxSampleSize);
408
563
  options.signal?.throwIfAborted();
564
+ findings.push(...collectOrderingStorageContradictionFindings(assembly, maxSampleSize));
565
+ options.signal?.throwIfAborted();
409
566
  findings.push(...collectOrderingFindings(assembly, nodeStates, isBlocked, maxSampleSize));
410
567
  options.signal?.throwIfAborted();
411
568
  findings.push(...collectDuplicateEdgeFindings(assembly, nodeStates, maxSampleSize), ...collectDuplicateRowFindings(assembly, maxSampleSize));
412
569
  options.signal?.throwIfAborted();
413
- const coverage = collectCoverageReport(assembly, nodeStates, exemptIsolates, isolateExemptTypes, maxSampleSize, edgesByKind);
570
+ const coverage = collectCoverageReport(assembly, nodeStates, exemptIsolates, isolateExemptTypes, maxSampleSize, edgesByKind, options.signal);
414
571
  findings.push(...coverage.findings);
415
572
  findings.sort((left, right) => SEVERITY_RANK[left.severity] - SEVERITY_RANK[right.severity] ||
416
573
  left.code.localeCompare(right.code) ||
417
574
  left.sample[0].localeCompare(right.sample[0]));
575
+ const findingSubjectsByCode = coverage.profile.finding_subjects_by_code;
576
+ for (const finding of findings) {
577
+ findingSubjectsByCode[finding.code] += finding.count;
578
+ }
418
579
  return {
419
580
  findings,
420
- profile: coverage.profile,
581
+ profile: {
582
+ ...coverage.profile,
583
+ finding_subjects_by_code: findingSubjectsByCode,
584
+ },
421
585
  };
422
586
  }
423
587
  /** Subtract two count records, keeping only non-zero signed deltas in sorted key order. */
@@ -437,45 +601,62 @@ function diffCountRecords(baseline, current) {
437
601
  function readSnapshotCoverageByType(snapshot) {
438
602
  return snapshot.profile.coverage_by_type ?? {};
439
603
  }
440
- /**
441
- * Compare two point-in-time audit snapshots and return the signed change in
442
- * findings and structural coverage. This is the temporal primitive behind
443
- * change-since-baseline audit reporting: agents track census drift across
444
- * sessions without re-deriving or storing full finding collections.
445
- */
446
- export function diffRelationshipAuditSnapshots(baseline, current) {
447
- const coverageByType = {};
448
- // Public SDK callers may compare legacy or JSON-decoded snapshots created
449
- // before per-type coverage was added, even though current typed snapshots
450
- // always carry this field.
451
- const baselineCoverageByType = readSnapshotCoverageByType(baseline);
452
- const currentCoverageByType = readSnapshotCoverageByType(current);
453
- const typeKeys = [
604
+ /** Diff per-type coverage while omitting unchanged type rows. */
605
+ function diffCoverageByType(baseline, current) {
606
+ const deltas = {};
607
+ const baselineCoverage = readSnapshotCoverageByType(baseline);
608
+ const currentCoverage = readSnapshotCoverageByType(current);
609
+ const types = [
454
610
  ...new Set([
455
- ...Object.keys(baselineCoverageByType),
456
- ...Object.keys(currentCoverageByType),
611
+ ...Object.keys(baselineCoverage),
612
+ ...Object.keys(currentCoverage),
457
613
  ]),
458
614
  ].sort();
459
- for (const type of typeKeys) {
460
- const before = Object.assign({ active: 0, isolated: 0, degree_leq_one: 0 }, baselineCoverageByType[type]);
461
- const after = Object.assign({ active: 0, isolated: 0, degree_leq_one: 0 }, currentCoverageByType[type]);
615
+ for (const type of types) {
616
+ const before = Object.assign({ active: 0, isolated: 0, degree_leq_one: 0 }, baselineCoverage[type]);
617
+ const after = Object.assign({ active: 0, isolated: 0, degree_leq_one: 0 }, currentCoverage[type]);
462
618
  const delta = {
463
619
  active: after.active - before.active,
464
620
  isolated: after.isolated - before.isolated,
465
621
  degree_leq_one: after.degree_leq_one - before.degree_leq_one,
466
622
  };
467
623
  if (Object.values(delta).some((value) => value !== 0))
468
- coverageByType[type] = delta;
624
+ deltas[type] = delta;
469
625
  }
626
+ return deltas;
627
+ }
628
+ /**
629
+ * Compare two point-in-time audit snapshots and return the signed change in
630
+ * findings and structural coverage. This is the temporal primitive behind
631
+ * change-since-baseline audit reporting: agents track census drift across
632
+ * sessions without re-deriving or storing full finding collections.
633
+ */
634
+ export function diffRelationshipAuditSnapshots(baseline, current) {
635
+ // Public SDK callers may compare legacy or JSON-decoded snapshots created
636
+ // before per-type coverage was added, even though current typed snapshots
637
+ // always carry this field.
638
+ const coverageByType = diffCoverageByType(baseline, current);
470
639
  const { edge_share_by_kind: baselineEdgeShareByKind = {}, semantic_edges: baselineSemanticEdges = 0, semantic_edge_share: baselineSemanticEdgeShare = 0, } = baseline.profile;
471
640
  const { edge_share_by_kind: currentEdgeShareByKind = {}, semantic_edges: currentSemanticEdges = 0, semantic_edge_share: currentSemanticEdgeShare = 0, } = current.profile;
641
+ const baselineRecordedNodes = baseline.profile.recorded_nodes ?? baseline.profile.nodes;
642
+ const currentRecordedNodes = current.profile.recorded_nodes ?? current.profile.nodes;
643
+ const baselineInformativeEdges = baseline.profile.informative_edges ?? baseline.profile.edges;
644
+ const currentInformativeEdges = current.profile.informative_edges ?? current.profile.edges;
645
+ const baselineRedundantEdges = baseline.profile.redundant_edges ?? 0;
646
+ const currentRedundantEdges = current.profile.redundant_edges ?? 0;
647
+ const baselineOrderingContradictionEdges = baseline.profile.ordering_contradiction_edges ?? 0;
648
+ const currentOrderingContradictionEdges = current.profile.ordering_contradiction_edges ?? 0;
472
649
  return {
473
650
  baseline_saved_at: baseline.saved_at,
474
651
  same_snapshot: baseline.fingerprint === current.fingerprint,
475
652
  affected_subjects_by_code: diffCountRecords(baseline.affected_subjects_by_code, current.affected_subjects_by_code),
476
653
  profile: {
477
654
  nodes: current.profile.nodes - baseline.profile.nodes,
655
+ recorded_nodes: currentRecordedNodes - baselineRecordedNodes,
478
656
  edges: current.profile.edges - baseline.profile.edges,
657
+ informative_edges: currentInformativeEdges - baselineInformativeEdges,
658
+ redundant_edges: currentRedundantEdges - baselineRedundantEdges,
659
+ ordering_contradiction_edges: currentOrderingContradictionEdges - baselineOrderingContradictionEdges,
479
660
  active_nodes: current.profile.active_nodes - baseline.profile.active_nodes,
480
661
  missing_nodes: current.profile.missing_nodes - baseline.profile.missing_nodes,
481
662
  isolated_active_nodes: current.profile.isolated_active_nodes -
@@ -491,4 +672,4 @@ export function diffRelationshipAuditSnapshots(baseline, current) {
491
672
  };
492
673
  }
493
674
  //# sourceMappingURL=governance.js.map
494
- //# debugId=996cfe8f-1b9b-534e-9e22-ede6f76d7cac
675
+ //# debugId=1955b7a4-385d-5a5d-8856-6db9f94814aa
@@ -12,8 +12,8 @@ export { computeRelationshipCentrality, findRelationshipCutStructure, type Graph
12
12
  export { createRelationshipGraphSnapshot, federateRelationshipGraphSnapshots, loadRelationshipGraphAdapter, MemoryRelationshipGraphAdapter, parseRelationshipGraphSnapshot, RELATIONSHIP_GRAPH_SNAPSHOT_VERSION, syncRelationshipGraphAdapter, type LoadedRelationshipGraphSnapshot, type RelationshipGraphAdapter, type RelationshipGraphAdapterContext, type RelationshipGraphAdapterReplaceContext, type RelationshipGraphAdapterSyncResult, type RelationshipGraphSnapshot, } from "./adapter.js";
13
13
  export { assertMemoryRelationshipGraphAdapterConformance, assertRelationshipGraphAdapterConformance, createRelationshipGraphScaleFixture, type RelationshipGraphAdapterConformanceCase, type RelationshipGraphAdapterConformanceReport, type RelationshipGraphScaleFixture, type RelationshipGraphScaleFixtureOptions, type RelationshipGraphScaleTopology, } from "./conformance.js";
14
14
  export { analyzeRelationshipSchedule, type GraphScheduleOptions, type RelationshipScheduleAnalysis, type RelationshipScheduleRow, } from "./scheduling.js";
15
- export { assembleWorkspaceRelationshipGraph, collectDanglingDependencyReferences, collectDuplicateDependencyRows, collectMissingDependencyTargetIds, normalizeDependencyGraphTarget, resolveWorkspaceRelationshipKindRegistry, type DanglingDependencyReference, type DanglingDependencyReferenceSummary, type DependencyReferenceHolder, type DependencyReferenceSource, type DuplicateDependencyRow, type WorkspaceRelationshipAssembly, type WorkspaceRelationshipItem, type WorkspaceRelationshipNodeDetails, } from "./assembly.js";
16
- export { auditWorkspaceRelationshipGraph, collectDuplicateRelationshipEdgeGroups, collectOrderingCycles, diffRelationshipAuditSnapshots, formatDuplicateEdgeGroup, type DuplicateRelationshipEdgeGroup, type RelationshipAuditDelta, type RelationshipAuditFinding, type RelationshipAuditFindingCode, type RelationshipAuditOptions, type RelationshipAuditReport, type RelationshipAuditSeverity, type RelationshipAuditSnapshot, type RelationshipCoverageProfile, type RelationshipCoverageTypeProfile, } from "./governance.js";
15
+ export { assembleWorkspaceRelationshipGraph, collectDanglingDependencyReferences, collectDuplicateDependencyRows, collectMissingDependencyTargetIds, collectOrderingStorageContradictions, normalizeDependencyGraphTarget, resolveWorkspaceRelationshipKindRegistry, type DanglingDependencyReference, type DanglingDependencyReferenceSummary, type DependencyReferenceHolder, type DependencyReferenceSource, type DuplicateDependencyRow, type OrderingStorageContradiction, type WorkspaceRelationshipAssembly, type WorkspaceRelationshipItem, type WorkspaceRelationshipNodeDetails, } from "./assembly.js";
16
+ export { auditWorkspaceRelationshipGraph, collectDuplicateRelationshipEdgeGroups, collectOrderingCycles, diffRelationshipAuditSnapshots, formatDuplicateEdgeGroup, formatOrderingStorageContradiction, type DuplicateRelationshipEdgeGroup, type RelationshipAuditDelta, type RelationshipAuditFinding, type RelationshipAuditFindingCode, type RelationshipAuditOptions, type RelationshipAuditReport, type RelationshipAuditSeverity, type RelationshipAuditSnapshot, type RelationshipCoverageProfile, type RelationshipCoverageTypeProfile, } from "./governance.js";
17
17
  export { clearDurableGraphCache, durableGraphCachePath, durableGraphCacheStatus, GRAPH_DURABLE_CACHE_MIN_ITEMS, GRAPH_DURABLE_CACHE_VERSION, graphAuditBaselinePath, loadGraphAuditBaseline, openDurableGraphCache, persistDurableGraphResult, saveGraphAuditBaseline, shouldPersistDurableGraphCache, type DurableGraphCacheEnvelope, type DurableGraphCacheStatus, type DurableGraphCacheView, } from "./durable-cache.js";
18
18
  export { enumerateRelationshipPaths, hierarchyAncestors, hierarchyDescendants, orderingPredecessors, orderingSuccessors, type GraphPathOptions, type GraphTraversalOptions, type RelationshipPath, } from "./traversal.js";
19
19
  export { computeWorkspaceGraphFingerprint, resetWorkspaceGraphCache, workspaceGraphCache, WorkspaceGraphCache, type GraphCacheMetadata, type WorkspaceGraphCacheLookup, } from "./cache.js";
@@ -8,14 +8,14 @@
8
8
  * `sdk/relationships` and is safe for CLI, MCP, extension, and non-PM callers.
9
9
  */
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]="2c3df51e-6b10-5da6-8340-126465d9c434")}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]="b5509c6e-1431-5316-a563-16681ced4f33")}catch(e){}}();
12
12
  export { computeRelationshipDominators, detectRelationshipCommunities, findRedundantRelationshipEdges, forEachMatchedRelationshipEdge, } from "./analytics.js";
13
13
  export { computeRelationshipCentrality, findRelationshipCutStructure, } from "./centrality.js";
14
14
  export { createRelationshipGraphSnapshot, federateRelationshipGraphSnapshots, loadRelationshipGraphAdapter, MemoryRelationshipGraphAdapter, parseRelationshipGraphSnapshot, RELATIONSHIP_GRAPH_SNAPSHOT_VERSION, syncRelationshipGraphAdapter, } from "./adapter.js";
15
15
  export { assertMemoryRelationshipGraphAdapterConformance, assertRelationshipGraphAdapterConformance, createRelationshipGraphScaleFixture, } from "./conformance.js";
16
16
  export { analyzeRelationshipSchedule, } from "./scheduling.js";
17
- export { assembleWorkspaceRelationshipGraph, collectDanglingDependencyReferences, collectDuplicateDependencyRows, collectMissingDependencyTargetIds, normalizeDependencyGraphTarget, resolveWorkspaceRelationshipKindRegistry, } from "./assembly.js";
18
- export { auditWorkspaceRelationshipGraph, collectDuplicateRelationshipEdgeGroups, collectOrderingCycles, diffRelationshipAuditSnapshots, formatDuplicateEdgeGroup, } from "./governance.js";
17
+ export { assembleWorkspaceRelationshipGraph, collectDanglingDependencyReferences, collectDuplicateDependencyRows, collectMissingDependencyTargetIds, collectOrderingStorageContradictions, normalizeDependencyGraphTarget, resolveWorkspaceRelationshipKindRegistry, } from "./assembly.js";
18
+ export { auditWorkspaceRelationshipGraph, collectDuplicateRelationshipEdgeGroups, collectOrderingCycles, diffRelationshipAuditSnapshots, formatDuplicateEdgeGroup, formatOrderingStorageContradiction, } from "./governance.js";
19
19
  export { clearDurableGraphCache, durableGraphCachePath, durableGraphCacheStatus, GRAPH_DURABLE_CACHE_MIN_ITEMS, GRAPH_DURABLE_CACHE_VERSION, graphAuditBaselinePath, loadGraphAuditBaseline, openDurableGraphCache, persistDurableGraphResult, saveGraphAuditBaseline, shouldPersistDurableGraphCache, } from "./durable-cache.js";
20
20
  export { enumerateRelationshipPaths, hierarchyAncestors, hierarchyDescendants, orderingPredecessors, orderingSuccessors, } from "./traversal.js";
21
21
  export { computeWorkspaceGraphFingerprint, resetWorkspaceGraphCache, workspaceGraphCache, WorkspaceGraphCache, } from "./cache.js";
@@ -23,4 +23,4 @@ export { planRelationshipRemediation, } from "./remediation.js";
23
23
  export { collectNewOrderingCycleWarnings } from "./mutation-advisory.js";
24
24
  export { GRAPH_SUBCOMMAND_VALUES, parseGraphSubcommand, runGraph, } from "./run.js";
25
25
  //# sourceMappingURL=index.js.map
26
- //# debugId=2c3df51e-6b10-5da6-8340-126465d9c434
26
+ //# debugId=b5509c6e-1431-5316-a563-16681ced4f33
@@ -1,7 +1,7 @@
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]="4198a3b2-2541-50eb-bec5-93ac58601f40")}catch(e){}}();
3
- import { normalizeDependencyGraphTarget, resolveWorkspaceRelationshipKindRegistry, } from "./assembly.js";
4
- import { collectOrderingCycles } from "./governance.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]="94e8b95b-35fa-5fc7-843a-481497e09b54")}catch(e){}}();
3
+ import { collectOrderingStorageContradictions, normalizeDependencyGraphTarget, resolveWorkspaceRelationshipKindRegistry, } from "./assembly.js";
4
+ import { collectOrderingCycles, formatOrderingStorageContradiction, } from "./governance.js";
5
5
  function cycleKey(component) {
6
6
  return [...component].sort().join("\u0000");
7
7
  }
@@ -153,9 +153,17 @@ export function collectNewOrderingCycleWarnings(beforeItems, afterItems, changed
153
153
  const before = collectScopedCycles(beforeItems, registry, changedItemId);
154
154
  const after = collectScopedCycles(afterItems, registry, changedItemId);
155
155
  const beforeCycles = new Set(before.cycles.map(cycleKey));
156
- return after.cycles
157
- .filter((component) => !beforeCycles.has(cycleKey(component)))
158
- .map((component) => `ordering_cycle_created:${findCyclePath(after.successors, component, after.canonical).join(" -> ")}:items_will_not_be_ready:run_pm_graph_audit`);
156
+ const beforeContradictions = new Set(collectOrderingStorageContradictions(beforeItems, undefined, registry).map((row) => `${row.holder_id.toLowerCase()}\u0000${row.target_id.toLowerCase()}\u0000${row.dependency_kind}`));
157
+ const contradictionWarnings = collectOrderingStorageContradictions(afterItems, undefined, registry)
158
+ .filter((row) => row.holder_id.toLowerCase() === changedItemId.trim().toLowerCase() &&
159
+ !beforeContradictions.has(`${row.holder_id.toLowerCase()}\u0000${row.target_id.toLowerCase()}\u0000${row.dependency_kind}`))
160
+ .map((row) => `ordering_storage_contradiction_created:${formatOrderingStorageContradiction(row)}:remove_contradicting_dependency_row:run_pm_graph_audit`);
161
+ return [
162
+ ...contradictionWarnings,
163
+ ...after.cycles
164
+ .filter((component) => !beforeCycles.has(cycleKey(component)))
165
+ .map((component) => `ordering_cycle_created:${findCyclePath(after.successors, component, after.canonical).join(" -> ")}:items_will_not_be_ready:run_pm_graph_audit`),
166
+ ];
159
167
  }
160
168
  /** White-box graph-path helpers for exhaustive SDK primitive verification. */
161
169
  export const mutationAdvisoryTestOnly = {
@@ -166,4 +174,4 @@ export const mutationAdvisoryTestOnly = {
166
174
  findPathBackToStart,
167
175
  };
168
176
  //# sourceMappingURL=mutation-advisory.js.map
169
- //# debugId=4198a3b2-2541-50eb-bec5-93ac58601f40
177
+ //# debugId=94e8b95b-35fa-5fc7-843a-481497e09b54
@@ -1,5 +1,5 @@
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]="9b8e22ad-6268-515d-994e-6b98a062ba93")}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]="1eb0e2cb-084a-5a54-b41c-29dfdd6d2f34")}catch(e){}}();
3
3
  import { findRedundantRelationshipEdges } from "./analytics.js";
4
4
  import { auditWorkspaceRelationshipGraph, collectDuplicateRelationshipEdgeGroups, formatDuplicateEdgeGroup, } from "./governance.js";
5
5
  /** Per-finding-code operation template applied to every sampled subject. */
@@ -29,6 +29,16 @@ const FINDING_STEP_TEMPLATES = {
29
29
  confidence: "high",
30
30
  rationale: "Terminal-only ordering cycles have no scheduling effect; repairing them would mutate closed history (changelog drag).",
31
31
  },
32
+ ordering_storage_contradiction: {
33
+ op: "remove",
34
+ confidence: "high",
35
+ rationale: "The structured ordering row asserts the opposite precedence from the same-target scalar blocker; remove that exact dependency row or replace both representations with one unambiguous edge.",
36
+ },
37
+ legacy_ordering_storage_contradiction: {
38
+ op: "remove",
39
+ confidence: "high",
40
+ rationale: "The terminal holder contains a proven contradictory structured ordering row; remove it in a dedicated changelog-safe batch while preserving the scalar blocker as history.",
41
+ },
32
42
  // duplicate_edge findings are expanded into per-stored-edge remove steps by
33
43
  // the planner itself (see stepsFromDuplicateGroup); this template documents
34
44
  // the family for exhaustiveness and backs any future sample-only fallback.
@@ -155,4 +165,4 @@ export function planRelationshipRemediation(assembly, options = {}) {
155
165
  };
156
166
  }
157
167
  //# sourceMappingURL=remediation.js.map
158
- //# debugId=9b8e22ad-6268-515d-994e-6b98a062ba93
168
+ //# debugId=1eb0e2cb-084a-5a54-b41c-29dfdd6d2f34
package/dist/sdk/guide.js CHANGED
@@ -4,7 +4,8 @@
4
4
  * Implements the pm guide 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]="9376fbdf-c942-5fa6-9252-92125ae44e73")}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]="27be0fa5-1c6a-53de-a0e8-179aab7c795a")}catch(e){}}();
8
+ import { isFileAbsentError } from "../core/fs/fs-utils.js";
8
9
  import { readFile } from "node:fs/promises";
9
10
  import path from "node:path";
10
11
  import { resolveConfiguredPmPackageRoot, EXIT_CODE, PmCliError, } from "./runtime-primitives.js";
@@ -119,8 +120,7 @@ async function renderGuideDocs(topic, depth, packageRoot) {
119
120
  }
120
121
  }
121
122
  catch (error) {
122
- const code = error?.code;
123
- const missing = code === "ENOENT";
123
+ const missing = isFileAbsentError(error);
124
124
  docs.push({
125
125
  path: doc.path,
126
126
  purpose: doc.purpose,
@@ -277,4 +277,4 @@ export function renderGuideMarkdown(result) {
277
277
  return lines.join("\n");
278
278
  }
279
279
  //# sourceMappingURL=guide.js.map
280
- //# debugId=9376fbdf-c942-5fa6-9252-92125ae44e73
280
+ //# debugId=27be0fa5-1c6a-53de-a0e8-179aab7c795a