@memberjunction/db-auto-doc 6.2.0-edge.4 → 6.2.0-edge.6

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 (183) hide show
  1. package/README.md +105 -0
  2. package/dist/api/DBAutoDocAPI.d.ts +2 -0
  3. package/dist/api/DBAutoDocAPI.d.ts.map +1 -1
  4. package/dist/api/DBAutoDocAPI.js +2 -1
  5. package/dist/api/DBAutoDocAPI.js.map +1 -1
  6. package/dist/commands/analyze.d.ts +1 -0
  7. package/dist/commands/analyze.d.ts.map +1 -1
  8. package/dist/commands/analyze.js +6 -0
  9. package/dist/commands/analyze.js.map +1 -1
  10. package/dist/commands/export.d.ts +28 -19
  11. package/dist/commands/export.d.ts.map +1 -1
  12. package/dist/commands/export.js +91 -12
  13. package/dist/commands/export.js.map +1 -1
  14. package/dist/core/AnalysisEngine.d.ts +39 -6
  15. package/dist/core/AnalysisEngine.d.ts.map +1 -1
  16. package/dist/core/AnalysisEngine.js +144 -50
  17. package/dist/core/AnalysisEngine.js.map +1 -1
  18. package/dist/core/AnalysisOrchestrator.d.ts +20 -1
  19. package/dist/core/AnalysisOrchestrator.d.ts.map +1 -1
  20. package/dist/core/AnalysisOrchestrator.js +144 -28
  21. package/dist/core/AnalysisOrchestrator.js.map +1 -1
  22. package/dist/core/PKProposalGate.d.ts +22 -0
  23. package/dist/core/PKProposalGate.d.ts.map +1 -0
  24. package/dist/core/PKProposalGate.js +52 -0
  25. package/dist/core/PKProposalGate.js.map +1 -0
  26. package/dist/database/Database.d.ts +8 -0
  27. package/dist/database/Database.d.ts.map +1 -1
  28. package/dist/database/Database.js +23 -1
  29. package/dist/database/Database.js.map +1 -1
  30. package/dist/discovery/ColumnNormalizer.d.ts +31 -0
  31. package/dist/discovery/ColumnNormalizer.d.ts.map +1 -1
  32. package/dist/discovery/ColumnNormalizer.js +68 -2
  33. package/dist/discovery/ColumnNormalizer.js.map +1 -1
  34. package/dist/discovery/Composer.d.ts +33 -2
  35. package/dist/discovery/Composer.d.ts.map +1 -1
  36. package/dist/discovery/Composer.js +126 -5
  37. package/dist/discovery/Composer.js.map +1 -1
  38. package/dist/discovery/DiscoveryEngine.d.ts +3 -0
  39. package/dist/discovery/DiscoveryEngine.d.ts.map +1 -1
  40. package/dist/discovery/DiscoveryEngine.js +1 -1
  41. package/dist/discovery/DiscoveryEngine.js.map +1 -1
  42. package/dist/discovery/EmbeddingProvider.d.ts +6 -1
  43. package/dist/discovery/EmbeddingProvider.d.ts.map +1 -1
  44. package/dist/discovery/EmbeddingProvider.js +11 -1
  45. package/dist/discovery/EmbeddingProvider.js.map +1 -1
  46. package/dist/discovery/EnumCandidateGate.d.ts +19 -0
  47. package/dist/discovery/EnumCandidateGate.d.ts.map +1 -1
  48. package/dist/discovery/EnumCandidateGate.js +47 -5
  49. package/dist/discovery/EnumCandidateGate.js.map +1 -1
  50. package/dist/discovery/FKGraphWalker.d.ts +35 -0
  51. package/dist/discovery/FKGraphWalker.d.ts.map +1 -1
  52. package/dist/discovery/FKGraphWalker.js +113 -12
  53. package/dist/discovery/FKGraphWalker.js.map +1 -1
  54. package/dist/discovery/IndexKeyEvidence.d.ts +36 -0
  55. package/dist/discovery/IndexKeyEvidence.d.ts.map +1 -0
  56. package/dist/discovery/IndexKeyEvidence.js +42 -0
  57. package/dist/discovery/IndexKeyEvidence.js.map +1 -0
  58. package/dist/discovery/JoinProbe.d.ts +148 -0
  59. package/dist/discovery/JoinProbe.d.ts.map +1 -0
  60. package/dist/discovery/JoinProbe.js +233 -0
  61. package/dist/discovery/JoinProbe.js.map +1 -0
  62. package/dist/discovery/LLMDiscoveryValidator.d.ts +10 -1
  63. package/dist/discovery/LLMDiscoveryValidator.d.ts.map +1 -1
  64. package/dist/discovery/LLMDiscoveryValidator.js +44 -12
  65. package/dist/discovery/LLMDiscoveryValidator.js.map +1 -1
  66. package/dist/discovery/LLMSanityChecker.d.ts.map +1 -1
  67. package/dist/discovery/LLMSanityChecker.js +10 -1
  68. package/dist/discovery/LLMSanityChecker.js.map +1 -1
  69. package/dist/discovery/OrganicKeyDetector.d.ts +29 -1
  70. package/dist/discovery/OrganicKeyDetector.d.ts.map +1 -1
  71. package/dist/discovery/OrganicKeyDetector.js +34 -5
  72. package/dist/discovery/OrganicKeyDetector.js.map +1 -1
  73. package/dist/discovery/OrganicKeyTranslator.d.ts +24 -5
  74. package/dist/discovery/OrganicKeyTranslator.d.ts.map +1 -1
  75. package/dist/discovery/OrganicKeyTranslator.js +32 -14
  76. package/dist/discovery/OrganicKeyTranslator.js.map +1 -1
  77. package/dist/discovery/PKDetector.d.ts +9 -0
  78. package/dist/discovery/PKDetector.d.ts.map +1 -1
  79. package/dist/discovery/PKDetector.js +86 -8
  80. package/dist/discovery/PKDetector.js.map +1 -1
  81. package/dist/discovery/SemanticPhase.d.ts +25 -0
  82. package/dist/discovery/SemanticPhase.d.ts.map +1 -1
  83. package/dist/discovery/SemanticPhase.js +55 -11
  84. package/dist/discovery/SemanticPhase.js.map +1 -1
  85. package/dist/discovery/StructuralPhase.d.ts +27 -5
  86. package/dist/discovery/StructuralPhase.d.ts.map +1 -1
  87. package/dist/discovery/StructuralPhase.js +42 -9
  88. package/dist/discovery/StructuralPhase.js.map +1 -1
  89. package/dist/discovery/TransitiveBridgeDetector.d.ts +5 -1
  90. package/dist/discovery/TransitiveBridgeDetector.d.ts.map +1 -1
  91. package/dist/discovery/TransitiveBridgeDetector.js +25 -3
  92. package/dist/discovery/TransitiveBridgeDetector.js.map +1 -1
  93. package/dist/discovery/embedding-provider-default.d.ts +25 -0
  94. package/dist/discovery/embedding-provider-default.d.ts.map +1 -0
  95. package/dist/discovery/embedding-provider-default.js +47 -0
  96. package/dist/discovery/embedding-provider-default.js.map +1 -0
  97. package/dist/drivers/BaseAutoDocDriver.d.ts +63 -0
  98. package/dist/drivers/BaseAutoDocDriver.d.ts.map +1 -1
  99. package/dist/drivers/BaseAutoDocDriver.js +3 -5
  100. package/dist/drivers/BaseAutoDocDriver.js.map +1 -1
  101. package/dist/drivers/MySQLDriver.d.ts +21 -1
  102. package/dist/drivers/MySQLDriver.d.ts.map +1 -1
  103. package/dist/drivers/MySQLDriver.js +64 -0
  104. package/dist/drivers/MySQLDriver.js.map +1 -1
  105. package/dist/drivers/PostgreSQLDriver.d.ts +43 -2
  106. package/dist/drivers/PostgreSQLDriver.d.ts.map +1 -1
  107. package/dist/drivers/PostgreSQLDriver.js +132 -19
  108. package/dist/drivers/PostgreSQLDriver.js.map +1 -1
  109. package/dist/drivers/SQLServerDriver.d.ts +28 -1
  110. package/dist/drivers/SQLServerDriver.d.ts.map +1 -1
  111. package/dist/drivers/SQLServerDriver.js +108 -1
  112. package/dist/drivers/SQLServerDriver.js.map +1 -1
  113. package/dist/drivers/probeErrors.d.ts +30 -0
  114. package/dist/drivers/probeErrors.d.ts.map +1 -0
  115. package/dist/drivers/probeErrors.js +94 -0
  116. package/dist/drivers/probeErrors.js.map +1 -0
  117. package/dist/generators/AdditionalSchemaInfoGenerator.d.ts +34 -18
  118. package/dist/generators/AdditionalSchemaInfoGenerator.d.ts.map +1 -1
  119. package/dist/generators/AdditionalSchemaInfoGenerator.js +130 -8
  120. package/dist/generators/AdditionalSchemaInfoGenerator.js.map +1 -1
  121. package/dist/generators/ErdClusterGenerator.d.ts +44 -0
  122. package/dist/generators/ErdClusterGenerator.d.ts.map +1 -0
  123. package/dist/generators/ErdClusterGenerator.js +216 -0
  124. package/dist/generators/ErdClusterGenerator.js.map +1 -0
  125. package/dist/generators/ErdClusterer.d.ts +130 -0
  126. package/dist/generators/ErdClusterer.d.ts.map +1 -0
  127. package/dist/generators/ErdClusterer.js +308 -0
  128. package/dist/generators/ErdClusterer.js.map +1 -0
  129. package/dist/generators/HTMLGenerator.d.ts +6 -0
  130. package/dist/generators/HTMLGenerator.d.ts.map +1 -1
  131. package/dist/generators/HTMLGenerator.js +41 -6
  132. package/dist/generators/HTMLGenerator.js.map +1 -1
  133. package/dist/generators/MermaidGenerator.d.ts +3 -0
  134. package/dist/generators/MermaidGenerator.d.ts.map +1 -1
  135. package/dist/generators/MermaidGenerator.js +4 -0
  136. package/dist/generators/MermaidGenerator.js.map +1 -1
  137. package/dist/generators/index.d.ts +2 -0
  138. package/dist/generators/index.d.ts.map +1 -1
  139. package/dist/generators/index.js +2 -0
  140. package/dist/generators/index.js.map +1 -1
  141. package/dist/prompts/PromptEngine.d.ts.map +1 -1
  142. package/dist/prompts/PromptEngine.js +10 -1
  143. package/dist/prompts/PromptEngine.js.map +1 -1
  144. package/dist/types/analysis.d.ts +2 -0
  145. package/dist/types/analysis.d.ts.map +1 -1
  146. package/dist/types/config.d.ts +128 -4
  147. package/dist/types/config.d.ts.map +1 -1
  148. package/dist/types/config.js +7 -1
  149. package/dist/types/config.js.map +1 -1
  150. package/dist/types/discovery.d.ts +41 -0
  151. package/dist/types/discovery.d.ts.map +1 -1
  152. package/dist/types/state.d.ts +29 -0
  153. package/dist/types/state.d.ts.map +1 -1
  154. package/dist/utils/call-deadline.d.ts +43 -0
  155. package/dist/utils/call-deadline.d.ts.map +1 -0
  156. package/dist/utils/call-deadline.js +81 -0
  157. package/dist/utils/call-deadline.js.map +1 -0
  158. package/dist/utils/config-loader.d.ts +5 -0
  159. package/dist/utils/config-loader.d.ts.map +1 -1
  160. package/dist/utils/config-loader.js +37 -0
  161. package/dist/utils/config-loader.js.map +1 -1
  162. package/dist/utils/index-hints.d.ts +43 -0
  163. package/dist/utils/index-hints.d.ts.map +1 -0
  164. package/dist/utils/index-hints.js +80 -0
  165. package/dist/utils/index-hints.js.map +1 -0
  166. package/dist/utils/index.d.ts +2 -0
  167. package/dist/utils/index.d.ts.map +1 -1
  168. package/dist/utils/index.js +2 -0
  169. package/dist/utils/index.js.map +1 -1
  170. package/dist/utils/llm-factory.d.ts +1 -0
  171. package/dist/utils/llm-factory.d.ts.map +1 -1
  172. package/dist/utils/llm-factory.js +6 -1
  173. package/dist/utils/llm-factory.js.map +1 -1
  174. package/dist/utils/seed-context.d.ts +23 -0
  175. package/dist/utils/seed-context.d.ts.map +1 -0
  176. package/dist/utils/seed-context.js +38 -0
  177. package/dist/utils/seed-context.js.map +1 -0
  178. package/package.json +5 -5
  179. package/prompts/fk-pruning-holistic.md +7 -0
  180. package/prompts/fk-pruning-table.md +20 -1
  181. package/prompts/pk-pruning-holistic.md +7 -0
  182. package/prompts/pk-pruning-table.md +17 -0
  183. package/prompts/table-analysis.md +11 -0
@@ -3,6 +3,7 @@
3
3
  * Uses LLM intelligence to review and reject obviously wrong PK/FK candidates
4
4
  * This is a MACRO-LEVEL review that happens once after statistical detection
5
5
  */
6
+ import { ResolveCallTimeoutMs, WithCallDeadline } from '../utils/call-deadline.js';
6
7
  import { CreateLLMInstance } from '../utils/llm-factory.js';
7
8
  export class LLMSanityChecker {
8
9
  constructor(aiConfig, statsCache) {
@@ -37,7 +38,15 @@ export class LLMSanityChecker {
37
38
  responseFormat: 'JSON'
38
39
  };
39
40
  console.log(`[LLMSanityChecker] Calling LLM for sanity check...`);
40
- const chatResult = await this.llm.ChatCompletion(params);
41
+ // Bounded. This call reviews the ENTIRE candidate set in one request, so its prompt grows with
42
+ // the schema and it is the single largest call the discovery phase makes — and it had no
43
+ // deadline, no retry and no failover, so a stall here parked the whole run.
44
+ //
45
+ // The throw is deliberately NOT caught here: the caller in DiscoveryEngine already treats an
46
+ // exception from this checker as fatal to the iteration, whereas a returned failure is
47
+ // swallowed into "no candidates were invalid" — which would silently pass every statistically
48
+ // derived key through unreviewed. A timeout must not look like an all-clear.
49
+ const chatResult = await WithCallDeadline(ResolveCallTimeoutMs(this.aiConfig.callTimeoutMs), 'PK/FK sanity check', signal => this.llm.ChatCompletion({ ...params, cancellationToken: signal }));
41
50
  if (!chatResult.success) {
42
51
  console.warn(`[LLMSanityChecker] LLM call failed: ${chatResult.errorMessage}`);
43
52
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"LLMSanityChecker.js","sourceRoot":"","sources":["../../src/discovery/LLMSanityChecker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LLMSanityChecker.js","sourceRoot":"","sources":["../../src/discovery/LLMSanityChecker.ts"],"names":[],"mappings":"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"}
@@ -20,7 +20,9 @@ import { DatabaseDocumentation } from '../types/state.js';
20
20
  import { OrganicKeyCluster, OrganicKeyDetectionPhase } from '../types/organic-keys.js';
21
21
  import { ProgressCallback } from './SemanticPhase.js';
22
22
  import { BridgeViewProvider } from './BridgeViewSQLGenerator.js';
23
+ import { ClusterVerification } from './Composer.js';
23
24
  import { DetectedOrganicKeysOutput } from './OrganicKeyTranslator.js';
25
+ import { KeyVerifier } from './JoinProbe.js';
24
26
  export interface OrganicKeyDetectionResult {
25
27
  clusters: OrganicKeyCluster[];
26
28
  output: DetectedOrganicKeysOutput;
@@ -37,20 +39,46 @@ export interface OrganicKeyDetectionResult {
37
39
  outputKeys: number;
38
40
  outputSpokes: number;
39
41
  transitiveBridges: number;
42
+ /** Clusters the value-overlap probe refuted outright. */
43
+ clustersDroppedUnverified: number;
44
+ /** Members dropped for not sharing the anchor's value space. */
45
+ membersDroppedUnverified: number;
46
+ /** Concepts not emitted because no member owns the value as its table's primary key. */
47
+ conceptsSkippedUnanchored: number;
48
+ /** Probes spent, and the cap they ran under. */
49
+ probesUsed: number;
50
+ probesAllowed: number;
40
51
  };
52
+ /** Per-cluster probe record, so a dropped key can be explained rather than just missing. */
53
+ Verification: ClusterVerification[];
41
54
  }
42
55
  export interface DetectorRunOptions {
43
56
  OnProgress?: ProgressCallback;
44
57
  }
58
+ /** Emit-time behaviour for the detector. */
59
+ export interface OrganicKeyEmitOptions {
60
+ /**
61
+ * Set `AutoCreateRelatedViewOnForm` on emitted keys. Default false — a
62
+ * machine-proposed key should not silently create a grid per spoke on every form.
63
+ */
64
+ AutoCreateRelatedViewOnForm?: boolean;
65
+ }
45
66
  export declare class OrganicKeyDetector {
46
67
  private readonly config;
47
68
  private readonly aiConfig;
48
69
  private readonly databaseProvider?;
70
+ private readonly keyVerifier;
71
+ private readonly emitOptions;
49
72
  /**
50
73
  * @param databaseProvider - Platform of the analyzed database. Bridge-view SQL is emitted in
51
74
  * its dialect because CodeGen executes it verbatim. Default SQL Server.
75
+ * @param keyVerifier - Probes each cluster member against the cluster anchor before
76
+ * the key is emitted. Pass the SAME instance the analysis engine
77
+ * got, so one probe budget covers the whole run. When null, keys
78
+ * are emitted unverified — the previous behaviour — and the
79
+ * per-cluster record says they were never checked.
52
80
  */
53
- constructor(config: OrganicKeyDetectionConfig, aiConfig: AIConfig, databaseProvider?: BridgeViewProvider);
81
+ constructor(config: OrganicKeyDetectionConfig, aiConfig: AIConfig, databaseProvider?: BridgeViewProvider, keyVerifier?: KeyVerifier | null, emitOptions?: OrganicKeyEmitOptions);
54
82
  Detect(state: DatabaseDocumentation, opts?: DetectorRunOptions): Promise<OrganicKeyDetectionResult>;
55
83
  /** @deprecated Use {@link Detect}. */
56
84
  detect(state: DatabaseDocumentation, opts?: DetectorRunOptions): Promise<OrganicKeyDetectionResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"OrganicKeyDetector.d.ts","sourceRoot":"","sources":["../../src/discovery/OrganicKeyDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,QAAQ,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,EAAoB,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,MAAM,WAAW,yBAAyB;IACtC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,MAAM,EAAE,yBAAyB,CAAC;IAClC,KAAK,EAAE,wBAAwB,CAAC;IAChC,OAAO,EAAE;QACL,cAAc,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,2BAA2B,EAAE,MAAM,CAAC;QACpC,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,iBAAiB,EAAE,MAAM,CAAC;KAC7B,CAAC;CACL;AAED,MAAM,WAAW,kBAAkB;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAED,qBAAa,kBAAkB;IAMvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAPtC;;;OAGG;gBAEkB,MAAM,EAAE,yBAAyB,EACjC,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,CAAC,EAAE,kBAAkB;IAG7C,MAAM,CACf,KAAK,EAAE,qBAAqB,EAC5B,IAAI,GAAE,kBAAuB,GAC9B,OAAO,CAAC,yBAAyB,CAAC;IAmDrC,sCAAsC;IACzB,MAAM,CACf,KAAK,EAAE,qBAAqB,EAC5B,IAAI,GAAE,kBAAuB,GAC9B,OAAO,CAAC,yBAAyB,CAAC;IAIrC,OAAO,CAAC,YAAY;CAMvB"}
1
+ {"version":3,"file":"OrganicKeyDetector.d.ts","sourceRoot":"","sources":["../../src/discovery/OrganicKeyDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,QAAQ,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,EAAoB,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAW,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,MAAM,WAAW,yBAAyB;IACtC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,MAAM,EAAE,yBAAyB,CAAC;IAClC,KAAK,EAAE,wBAAwB,CAAC;IAChC,OAAO,EAAE;QACL,cAAc,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,2BAA2B,EAAE,MAAM,CAAC;QACpC,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,yDAAyD;QACzD,yBAAyB,EAAE,MAAM,CAAC;QAClC,gEAAgE;QAChE,wBAAwB,EAAE,MAAM,CAAC;QACjC,wFAAwF;QACxF,yBAAyB,EAAE,MAAM,CAAC;QAClC,gDAAgD;QAChD,UAAU,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,4FAA4F;IAC5F,YAAY,EAAE,mBAAmB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAED,4CAA4C;AAC5C,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACzC;AAED,qBAAa,kBAAkB;IAWvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAdhC;;;;;;;;OAQG;gBAEkB,MAAM,EAAE,yBAAyB,EACjC,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,CAAC,EAAE,kBAAkB,EACrC,WAAW,GAAE,WAAW,GAAG,IAAW,EACtC,WAAW,GAAE,qBAA0B;IAG/C,MAAM,CACf,KAAK,EAAE,qBAAqB,EAC5B,IAAI,GAAE,kBAAuB,GAC9B,OAAO,CAAC,yBAAyB,CAAC;IA6ErC,sCAAsC;IACzB,MAAM,CACf,KAAK,EAAE,qBAAqB,EAC5B,IAAI,GAAE,kBAAuB,GAC9B,OAAO,CAAC,yBAAyB,CAAC;IAIrC,OAAO,CAAC,YAAY;CAMvB"}
@@ -22,20 +22,43 @@ export class OrganicKeyDetector {
22
22
  /**
23
23
  * @param databaseProvider - Platform of the analyzed database. Bridge-view SQL is emitted in
24
24
  * its dialect because CodeGen executes it verbatim. Default SQL Server.
25
+ * @param keyVerifier - Probes each cluster member against the cluster anchor before
26
+ * the key is emitted. Pass the SAME instance the analysis engine
27
+ * got, so one probe budget covers the whole run. When null, keys
28
+ * are emitted unverified — the previous behaviour — and the
29
+ * per-cluster record says they were never checked.
25
30
  */
26
- constructor(config, aiConfig, databaseProvider) {
31
+ constructor(config, aiConfig, databaseProvider, keyVerifier = null, emitOptions = {}) {
27
32
  this.config = config;
28
33
  this.aiConfig = aiConfig;
29
34
  this.databaseProvider = databaseProvider;
35
+ this.keyVerifier = keyVerifier;
36
+ this.emitOptions = emitOptions;
30
37
  }
31
38
  async Detect(state, opts = {}) {
32
39
  const progress = opts.OnProgress ?? (() => { });
33
40
  const startedAt = new Date().toISOString();
34
41
  const a = await RunSemanticPhase(state, this.config, this.aiConfig, progress);
35
- const b = RunStructuralPhase(state, a.clusters, this.databaseProvider);
36
- progress(`structural: ${b.Summary.transitiveBridgesFound} bridges`);
37
- const c = Compose(a.clusters, b.Bridges);
38
- progress(`compose: emitted ${c.Emitted}/${a.clusters.length} clusters (${c.Summary.outputKeys} keys, ${c.Summary.outputSpokes} spokes)`);
42
+ const b = RunStructuralPhase(state, a.clusters, { Provider: this.databaseProvider });
43
+ // Say WHY when the phase produced nothing: "0 bridges" from a completed walk and
44
+ // "0 bridges" from a walk that never ran are different facts, and a truncated walk
45
+ // is a third. Reporting them separately is the difference between a schema with no
46
+ // transitive bridges and a schema whose walk we bounded.
47
+ progress(b.Summary.walked
48
+ ? `structural: ${b.Summary.transitiveBridgesFound} bridges${b.Summary.truncationReasons ? ` (walk truncated: ${b.Summary.truncationReasons.join(', ')})` : ''}`
49
+ : `structural: skipped (${b.Summary.skipReason})`);
50
+ const c = await Compose(a.clusters, b.Bridges, this.keyVerifier, {
51
+ AutoCreateRelatedViewOnForm: this.emitOptions.AutoCreateRelatedViewOnForm,
52
+ });
53
+ // Report what the probe removed, not just what survived: "emitted 5 clusters" and
54
+ // "emitted 5 of 161, 156 refuted" are the same output and completely different
55
+ // facts about the schema.
56
+ const budget = this.keyVerifier ? this.keyVerifier.Budget : null;
57
+ progress(`compose: emitted ${c.Emitted}/${a.clusters.length} clusters (${c.Summary.outputKeys} keys, ${c.Summary.outputSpokes} spokes)`
58
+ + (c.SkippedUnanchored > 0 ? `; skipped ${c.SkippedUnanchored} concepts with no primary-key owner` : '')
59
+ + (budget
60
+ ? `; probe refuted ${c.DroppedUnverified} clusters and ${c.DroppedMembers} members using ${budget.ProbesUsed}/${budget.ProbesAllowed} probes`
61
+ : '; keys NOT verified (no probe configured)'));
39
62
  // Net additional clusters produced by the concept-name split (sub-clusters created
40
63
  // beyond the raw clusterer output, counting both kept and dropped sub-clusters).
41
64
  const splitClusterCount = Math.max(0, a.Summary.clustersFound + a.Summary.clustersDropped - a.Summary.clustersBeforeSplit);
@@ -69,7 +92,13 @@ export class OrganicKeyDetector {
69
92
  outputKeys: c.Summary.outputKeys,
70
93
  outputSpokes: c.Summary.outputSpokes,
71
94
  transitiveBridges: b.Summary.transitiveBridgesFound,
95
+ clustersDroppedUnverified: c.DroppedUnverified,
96
+ membersDroppedUnverified: c.DroppedMembers,
97
+ conceptsSkippedUnanchored: c.SkippedUnanchored,
98
+ probesUsed: budget ? budget.ProbesUsed : 0,
99
+ probesAllowed: budget ? budget.ProbesAllowed : 0,
72
100
  },
101
+ Verification: c.Verification,
73
102
  };
74
103
  }
75
104
  /** @deprecated Use {@link Detect}. */
@@ -1 +1 @@
1
- {"version":3,"file":"OrganicKeyDetector.js","sourceRoot":"","sources":["../../src/discovery/OrganicKeyDetector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OrganicKeyDetector.js","sourceRoot":"","sources":["../../src/discovery/OrganicKeyDetector.ts"],"names":[],"mappings":"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"}
@@ -60,16 +60,35 @@ export interface TransitiveSpokeInput {
60
60
  relatedEntityJoinFieldName: string;
61
61
  hubConcept?: string;
62
62
  }
63
+ /** Emit-time options for {@link TranslateClusters}. */
64
+ export interface TranslateOptions {
65
+ /**
66
+ * Value for `AutoCreateRelatedViewOnForm` on every emitted key. Default FALSE.
67
+ *
68
+ * This was a hardcoded `true`, so every emitted key auto-created a related-record
69
+ * grid per spoke — around 22 grids per key on a real run, none of them asked for,
70
+ * each one a value-join on a column the detector had never verified. Opting in is
71
+ * the right default for a machine-proposed key.
72
+ */
73
+ AutoCreateRelatedViewOnForm?: boolean;
74
+ /**
75
+ * Called for each concept skipped because no member is its table's primary key, with the
76
+ * concept name and member count, so the caller can report what was not emitted.
77
+ */
78
+ OnUnanchored?: (concept: string, members: number) => void;
79
+ }
63
80
  /**
64
81
  * Render gated clusters into PR #2193 JSON. Same-concept consolidation +
65
- * per-table fan-out + transitive spoke attachment. No filters, no thresholds.
82
+ * hub-and-spoke fan-out + transitive spoke attachment. No thresholds.
66
83
  *
67
- * The caller (Composer) is responsible for filtering. Anything passed in
68
- * gets emitted.
84
+ * The caller (Composer) is responsible for filtering by evidence. The one
85
+ * structural rule applied here: a concept is emitted only around the members
86
+ * that own it as a primary key (the hubs), and non-hub members are never
87
+ * linked to each other.
69
88
  */
70
- export declare function TranslateClusters(clusters: OrganicKeyCluster[], transitiveSpokes?: TransitiveSpokeInput[]): DetectedOrganicKeysOutput;
89
+ export declare function TranslateClusters(clusters: OrganicKeyCluster[], transitiveSpokes?: TransitiveSpokeInput[], options?: TranslateOptions): DetectedOrganicKeysOutput;
71
90
  /** @deprecated Use {@link TranslateClusters}. */
72
- export declare function translateClusters(clusters: OrganicKeyCluster[], transitiveSpokes?: TransitiveSpokeInput[]): DetectedOrganicKeysOutput;
91
+ export declare function translateClusters(clusters: OrganicKeyCluster[], transitiveSpokes?: TransitiveSpokeInput[], options?: TranslateOptions): DetectedOrganicKeysOutput;
73
92
  /** Tally the emit payload: number of schemas, tables, organic keys, and spokes it contains. */
74
93
  export declare function CountOutputEntries(out: DetectedOrganicKeysOutput): {
75
94
  schemas: number;
@@ -1 +1 @@
1
- {"version":3,"file":"OrganicKeyTranslator.d.ts","sourceRoot":"","sources":["../../src/discovery/OrganicKeyTranslator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACH,iBAAiB,EAEjB,+BAA+B,EAGlC,MAAM,0BAA0B,CAAC;AAOlC,0EAA0E;AAC1E,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACf;AAED,kIAAkI;AAClI,MAAM,WAAW,6BAA6B;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,yBAAyB,CAAC,EAAE,MAAM,EAAE,CAAC;IACrC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,yGAAyG;AACzG,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,qBAAqB,EAAE,+BAA+B,CAAC;IACvD,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,eAAe,EAAE,6BAA6B,EAAE,CAAC;CACpD;AAED,kDAAkD;AAClD,MAAM,WAAW,qBAAqB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAED,4EAA4E;AAC5E,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAC;AAIhF,MAAM,WAAW,oBAAoB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,oBAAoB,CAAC;IACrC,yBAAyB,EAAE,MAAM,EAAE,CAAC;IACpC,yBAAyB,EAAE,MAAM,CAAC;IAClC,0BAA0B,EAAE,MAAM,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,gBAAgB,GAAE,oBAAoB,EAAO,GAC9C,yBAAyB,CAmG3B;AAED,iDAAiD;AACjD,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,gBAAgB,GAAE,oBAAoB,EAAO,GAC9C,yBAAyB,CAE3B;AAwCD,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,yBAAyB,GAAG;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAClB,CAaA;AAED,kDAAkD;AAClD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,yBAAyB,GAAG;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAClB,CAEA"}
1
+ {"version":3,"file":"OrganicKeyTranslator.d.ts","sourceRoot":"","sources":["../../src/discovery/OrganicKeyTranslator.ts"],"names":[],"mappings":"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"}
@@ -14,15 +14,17 @@
14
14
  * fan-out — deterministic equivalent of an LLM concept-merge pass.
15
15
  */
16
16
  import { MemberColumns, IsCompoundMember, } from '../types/organic-keys.js';
17
- // ─── Entry point — pure fan-out ─────────────────────────────────────────────
18
17
  /**
19
18
  * Render gated clusters into PR #2193 JSON. Same-concept consolidation +
20
- * per-table fan-out + transitive spoke attachment. No filters, no thresholds.
19
+ * hub-and-spoke fan-out + transitive spoke attachment. No thresholds.
21
20
  *
22
- * The caller (Composer) is responsible for filtering. Anything passed in
23
- * gets emitted.
21
+ * The caller (Composer) is responsible for filtering by evidence. The one
22
+ * structural rule applied here: a concept is emitted only around the members
23
+ * that own it as a primary key (the hubs), and non-hub members are never
24
+ * linked to each other.
24
25
  */
25
- export function TranslateClusters(clusters, transitiveSpokes = []) {
26
+ export function TranslateClusters(clusters, transitiveSpokes = [], options = {}) {
27
+ const autoCreateRelatedView = options.AutoCreateRelatedViewOnForm ?? false;
26
28
  // Step 1 — group by normalized canonical concept name.
27
29
  const byConcept = new Map();
28
30
  for (const cluster of clusters) {
@@ -58,13 +60,24 @@ export function TranslateClusters(clusters, transitiveSpokes = []) {
58
60
  .slice()
59
61
  .sort((a, b) => (b.confidence - a.confidence) || (b.members.length - a.members.length))[0];
60
62
  const name = prettyConceptName(anchor.concept);
61
- // Step 3 — fan out: one entry per unique owner, spokes = all OTHER unique members.
63
+ // Hubs are the members whose column is their table's primary key: the table that owns
64
+ // the value. Without one, every pair is a co-reference (two tables that both hold a
65
+ // customer key), which is many-to-many and multiplies rows when joined, so the concept
66
+ // is skipped rather than emitted as a join path.
67
+ const hubs = allMembers.filter((m) => m.isPrimaryKey);
68
+ if (hubs.length === 0) {
69
+ options.OnUnanchored?.(name, allMembers.length);
70
+ continue;
71
+ }
72
+ // Step 3 — fan out, hub and spoke: a hub links to every other member (one-to-many);
73
+ // any other member links only to the hubs (many-to-one), never to another non-hub.
62
74
  for (const owner of allMembers) {
63
75
  const tableEntry = upsertTable(out, owner.schema, owner.table);
76
+ const targets = owner.isPrimaryKey ? allMembers : hubs;
64
77
  const seenSpokes = new Set();
65
78
  const spokes = [];
66
- // Direct spokes — every other cluster member.
67
- for (const target of allMembers) {
79
+ // Direct spokes.
80
+ for (const target of targets) {
68
81
  if (target === owner)
69
82
  continue;
70
83
  const targetColumns = MemberColumns(target);
@@ -79,12 +92,17 @@ export function TranslateClusters(clusters, transitiveSpokes = []) {
79
92
  DisplayName: `${target.schema}.${target.table}`,
80
93
  });
81
94
  }
82
- // Transitive spokes — bridge views attached to matching hubs.
95
+ // Transitive spokes — bridge views attached to matching hubs. Only a hub owner gets
96
+ // them: a non-hub reaching another table through a bridge (orders -> registration via
97
+ // the customer key both hold) is the same co-reference the direct fan-out leaves out.
98
+ // A table the owner already links to directly is not added again through a bridge.
83
99
  const ownerColumns = MemberColumns(owner);
84
- const matchingTransitive = transitiveSpokes.filter((t) => t.hubSchema === owner.schema &&
100
+ const directTables = new Set(spokes.map((s) => `${s.SchemaName}.${s.TableName}`));
101
+ const matchingTransitive = !owner.isPrimaryKey ? [] : transitiveSpokes.filter((t) => t.hubSchema === owner.schema &&
85
102
  t.hubTable === owner.table &&
86
103
  t.hubKeyFields.length === ownerColumns.length &&
87
- t.hubKeyFields.every((f, idx) => f === ownerColumns[idx]));
104
+ t.hubKeyFields.every((f, idx) => f === ownerColumns[idx]) &&
105
+ !directTables.has(`${t.spokeSchema}.${t.spokeTable}`));
88
106
  for (const t of matchingTransitive) {
89
107
  spokes.push({
90
108
  SchemaName: t.spokeSchema,
@@ -110,7 +128,7 @@ export function TranslateClusters(clusters, transitiveSpokes = []) {
110
128
  MatchFieldNames: ownerColumns,
111
129
  NormalizationStrategy: ownerNormalization,
112
130
  CustomNormalizationExpression: ownerCustomExpression,
113
- AutoCreateRelatedViewOnForm: true,
131
+ AutoCreateRelatedViewOnForm: autoCreateRelatedView,
114
132
  RelatedEntities: spokes,
115
133
  });
116
134
  }
@@ -118,8 +136,8 @@ export function TranslateClusters(clusters, transitiveSpokes = []) {
118
136
  return out;
119
137
  }
120
138
  /** @deprecated Use {@link TranslateClusters}. */
121
- export function translateClusters(clusters, transitiveSpokes = []) {
122
- return TranslateClusters(clusters, transitiveSpokes);
139
+ export function translateClusters(clusters, transitiveSpokes = [], options = {}) {
140
+ return TranslateClusters(clusters, transitiveSpokes, options);
123
141
  }
124
142
  // ─── Helpers ────────────────────────────────────────────────────────────────
125
143
  function normalizeConceptName(name) {
@@ -1 +1 @@
1
- {"version":3,"file":"OrganicKeyTranslator.js","sourceRoot":"","sources":["../../src/discovery/OrganicKeyTranslator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"OrganicKeyTranslator.js","sourceRoot":"","sources":["../../src/discovery/OrganicKeyTranslator.ts"],"names":[],"mappings":"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"}
@@ -77,5 +77,14 @@ export declare class PKDetector {
77
77
  */
78
78
  private findConsecutivePKEligibleColumns;
79
79
  private detectCompositeKeys;
80
+ /** Whether index-backed candidates are switched on (default true). */
81
+ private useIndexes;
82
+ /**
83
+ * Composite candidates from multi-column unique (and primary-key) indexes. A unique index allows repeated NULLs,
84
+ * so every column must be NOT NULL, or nullable with no nulls in a non-empty table.
85
+ */
86
+ private indexBackedCompositeCandidates;
87
+ /** Adds candidates, keeping the higher-confidence one when the same column set is already present. */
88
+ private mergeCandidates;
80
89
  }
81
90
  //# sourceMappingURL=PKDetector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PKDetector.d.ts","sourceRoot":"","sources":["../../src/discovery/PKDetector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAoB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAmD,MAAM,uBAAuB,CAAC;AACrG,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,qBAAa,UAAU;IAEnB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,UAAU;gBAFV,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,2BAA2B,EACnC,UAAU,EAAE,gBAAgB;IAGtC;;;;OAIG;IACI,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAQ5E,oDAAoD;IAC7C,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAI5E;;OAEG;IACU,kBAAkB,CAC7B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,EAAE,CAAC;IA+EzB,kDAAkD;IACrC,kBAAkB,CAC7B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,EAAE,CAAC;IAIzB;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAsC/B;;OAEG;YACW,qBAAqB;IAoInC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAsC5B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA+B9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA+CzB;;;;;;;;OAQG;IACH,OAAO,CAAC,qBAAqB;IA4H7B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAoCvB;;;;OAIG;IAEH;;;;;OAKG;IACH,OAAO,CAAC,gCAAgC;YAuC1B,mBAAmB;CA4GlC"}
1
+ {"version":3,"file":"PKDetector.d.ts","sourceRoot":"","sources":["../../src/discovery/PKDetector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAoB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAmD,MAAM,uBAAuB,CAAC;AACrG,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAMzD,qBAAa,UAAU;IAEnB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,UAAU;gBAFV,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,2BAA2B,EACnC,UAAU,EAAE,gBAAgB;IAGtC;;;;OAIG;IACI,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAQ5E,oDAAoD;IAC7C,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAI5E;;OAEG;IACU,kBAAkB,CAC7B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,EAAE,CAAC;IAwFzB,kDAAkD;IACrC,kBAAkB,CAC7B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,EAAE,CAAC;IAIzB;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAsC/B;;OAEG;YACW,qBAAqB;IA6InC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAsC5B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA+B9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA+CzB;;;;;;;;OAQG;IACH,OAAO,CAAC,qBAAqB;IAkI7B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAoCvB;;;;OAIG;IAEH;;;;;OAKG;IACH,OAAO,CAAC,gCAAgC;YAuC1B,mBAAmB;IA6GjC,sEAAsE;IACtE,OAAO,CAAC,UAAU;IAIlB;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IAkCtC,sGAAsG;IACtG,OAAO,CAAC,eAAe;CAWxB"}
@@ -3,6 +3,7 @@
3
3
  * Analyzes columns to find potential primary keys based on uniqueness,
4
4
  * naming patterns, and data characteristics
5
5
  */
6
+ import { ClassifySingleColumnKey, CompositeKeyIndexes, INDEX_KEY_BOOST, UNIQUE_COMPOSITE_CONFIDENCE } from './IndexKeyEvidence.js';
6
7
  export class PKDetector {
7
8
  constructor(driver, config, statsCache) {
8
9
  this.driver = driver;
@@ -34,7 +35,7 @@ export class PKDetector {
34
35
  // Single-column PK detection
35
36
  for (let colIdx = 0; colIdx < table.columns.length; colIdx++) {
36
37
  const column = table.columns[colIdx];
37
- const candidate = await this.analyzeSingleColumnPK(schemaName, table.name, column, iteration, colIdx);
38
+ const candidate = await this.analyzeSingleColumnPK(schemaName, table, column, iteration, colIdx);
38
39
  if (candidate && candidate.confidence >= this.config.confidence.primaryKeyMinimum * 100) {
39
40
  candidates.push(candidate);
40
41
  }
@@ -50,6 +51,14 @@ export class PKDetector {
50
51
  const compositeCandidates = await this.detectCompositeKeys(schemaName, table, iteration);
51
52
  candidates.push(...compositeCandidates);
52
53
  }
54
+ // Composite keys backed by a unique index, for tables where no single column qualifies (junction
55
+ // tables, for example). When a single-column candidate already clears primaryKeyMinimum, a composite
56
+ // unique index is an alternate key: adding it as a candidate would get its columns flagged as part of
57
+ // the primary key too, because discovery applies every candidate above the minimum.
58
+ const hasSingleColumnKey = candidates.some(c => c.columnNames.length === 1);
59
+ if (this.useIndexes() && !hasSingleColumnKey) {
60
+ this.mergeCandidates(candidates, this.indexBackedCompositeCandidates(schemaName, table, iteration));
61
+ }
53
62
  // H11: Progressive discount for later PK-eligible columns
54
63
  // When multiple PK-eligible columns exist, later ones get discounted
55
64
  // to reduce false positives from FK columns with high uniqueness
@@ -130,7 +139,9 @@ export class PKDetector {
130
139
  /**
131
140
  * Analyze a single column as potential PK
132
141
  */
133
- async analyzeSingleColumnPK(schemaName, tableName, column, iteration, colOrdinal = 0) {
142
+ async analyzeSingleColumnPK(schemaName, table, column, iteration, colOrdinal = 0) {
143
+ const tableName = table.name;
144
+ const indexKey = this.useIndexes() ? ClassifySingleColumnKey(table, column) : undefined;
134
145
  // Get or compute column statistics
135
146
  let cachedStats = this.statsCache.getColumnStats(schemaName, tableName, column.name);
136
147
  if (!cachedStats) {
@@ -166,12 +177,14 @@ export class PKDetector {
166
177
  };
167
178
  this.statsCache.setColumnStats(cachedStats);
168
179
  }
169
- // Use cached stats for PK analysis
170
- const uniqueness = cachedStats.uniqueness;
180
+ // A unique index on a NOT NULL column guarantees uniqueness whatever the sample shows (even on an empty table).
181
+ const indexGuaranteesKey = indexKey?.Kind === 'UniqueNotNull';
182
+ const uniqueness = indexGuaranteesKey ? 1.0 : cachedStats.uniqueness;
171
183
  // Deterministic eligibility gate — a column is only PK-eligible if it has
172
184
  // zero nulls, zero blanks, and 100% unique values. This is the mathematical
173
185
  // definition of a primary key and cannot be overridden by the LLM.
174
- if (!cachedStats.pkEligible) {
186
+ // A unique index on a NOT NULL column is the same guarantee from the database itself.
187
+ if (!cachedStats.pkEligible && !indexGuaranteesKey) {
175
188
  const reasons = [];
176
189
  if (cachedStats.nullCount > 0)
177
190
  reasons.push(`${cachedStats.nullCount} nulls`);
@@ -193,6 +206,7 @@ export class PKDetector {
193
206
  // Use cached data pattern
194
207
  const dataPattern = cachedStats.dataPattern;
195
208
  // Calculate overall confidence
209
+ const indexBoost = indexKey ? INDEX_KEY_BOOST[indexKey.Kind] : 0;
196
210
  const confidence = this.calculatePKConfidence({
197
211
  uniqueness,
198
212
  nullCount: cachedStats.nullCount,
@@ -201,7 +215,7 @@ export class PKDetector {
201
215
  namingScore,
202
216
  dataTypeScore,
203
217
  warnings: []
204
- }, tableName, column.name, colOrdinal);
218
+ }, tableName, column.name, colOrdinal, indexBoost);
205
219
  // Build evidence
206
220
  const evidence = {
207
221
  uniqueness,
@@ -210,8 +224,12 @@ export class PKDetector {
210
224
  dataPattern,
211
225
  namingScore,
212
226
  dataTypeScore,
213
- warnings: []
227
+ warnings: [],
228
+ backingIndex: indexKey?.Index.IndexName
214
229
  };
230
+ if (indexKey) {
231
+ evidence.warnings.push(`Backed by ${indexKey.Kind === 'UniqueNotNull' ? 'a unique index on a NOT NULL column' : 'a unique index on a nullable column'}: ${indexKey.Index.IndexName}`);
232
+ }
215
233
  // Add warnings (nulls/blanks are already hard-rejected by eligibility gate)
216
234
  if (uniqueness < 1.0) {
217
235
  evidence.warnings.push(`Only ${(uniqueness * 100).toFixed(1)}% unique values`);
@@ -347,7 +365,7 @@ export class PKDetector {
347
365
  * - Composite keys: 50-75
348
366
  * - Everything else: <50
349
367
  */
350
- calculatePKConfidence(evidence, tableName, columnName, colOrdinal = 0) {
368
+ calculatePKConfidence(evidence, tableName, columnName, colOrdinal = 0, indexBoost = 0) {
351
369
  let score = 0;
352
370
  // **CRITICAL FIX**: Reject obvious non-PK columns immediately
353
371
  const blacklistPatterns = [
@@ -436,6 +454,11 @@ export class PKDetector {
436
454
  score += boost;
437
455
  console.log(`[PKDetector] ${columnName}: surrogate key boost +${boost} (final: ${Math.round(Math.min(score, 100))})`);
438
456
  }
457
+ // Existing unique index on the column: strong evidence the database itself enforces (see IndexKeyEvidence)
458
+ if (indexBoost > 0) {
459
+ score += indexBoost;
460
+ console.log(`[PKDetector] ${columnName}: unique index boost +${indexBoost}`);
461
+ }
439
462
  // H9: Position-based multiplier — PKs are almost always first columns
440
463
  // 100% of correct PKs in AW benchmark start at position 0
441
464
  const positionMultipliers = [1.0, 0.85, 0.70, 0.55, 0.40];
@@ -604,5 +627,60 @@ export class PKDetector {
604
627
  }
605
628
  return candidates;
606
629
  }
630
+ /** Whether index-backed candidates are switched on (default true). */
631
+ useIndexes() {
632
+ return this.config.indexBackedCandidates !== false;
633
+ }
634
+ /**
635
+ * Composite candidates from multi-column unique (and primary-key) indexes. A unique index allows repeated NULLs,
636
+ * so every column must be NOT NULL, or nullable with no nulls in a non-empty table.
637
+ */
638
+ indexBackedCompositeCandidates(schemaName, table, iteration) {
639
+ const candidates = [];
640
+ for (const ix of CompositeKeyIndexes(table)) {
641
+ const columns = ix.Columns.map((name) => table.columns.find((c) => c.name.toLowerCase() === name.toLowerCase()));
642
+ if (columns.some((c) => !c))
643
+ continue;
644
+ const cols = columns;
645
+ const names = cols.map((c) => c.name);
646
+ const stats = cols.map((c) => this.statsCache.getColumnStats(schemaName, table.name, c.name));
647
+ const totalRows = stats.find((s) => s)?.totalRows ?? table.rowCount ?? 0;
648
+ const allNotNull = cols.every((c) => !c.isNullable);
649
+ const noNullsObserved = totalRows > 0 && cols.every((c, i) => !c.isNullable || (stats[i] !== undefined && stats[i].nullCount === 0));
650
+ if (!allNotNull && !noNullsObserved) {
651
+ console.log(`[PKDetector] ${table.name}: unique index ${ix.IndexName} skipped - nullable columns without evidence of no nulls`);
652
+ continue;
653
+ }
654
+ const confidence = allNotNull ? UNIQUE_COMPOSITE_CONFIDENCE.NotNull : UNIQUE_COMPOSITE_CONFIDENCE.NoNullsObserved;
655
+ console.log(`[PKDetector] ${table.name} [${names.join(', ')}]: composite key backed by unique index ${ix.IndexName} (confidence ${confidence})`);
656
+ candidates.push({
657
+ schemaName,
658
+ tableName: table.name,
659
+ columnNames: names,
660
+ confidence,
661
+ evidence: {
662
+ uniqueness: 1.0, nullCount: 0, totalRows, dataPattern: 'composite', namingScore: 0.8, dataTypeScore: 0.9,
663
+ warnings: [`Backed by unique index ${ix.IndexName}`], backingIndex: ix.IndexName
664
+ },
665
+ discoveredInIteration: iteration,
666
+ validatedByLLM: false,
667
+ status: 'candidate'
668
+ });
669
+ }
670
+ return candidates;
671
+ }
672
+ /** Adds candidates, keeping the higher-confidence one when the same column set is already present. */
673
+ mergeCandidates(into, extra) {
674
+ const key = (c) => c.columnNames.map((n) => n.toLowerCase()).sort().join('\u0000');
675
+ for (const candidate of extra) {
676
+ const existing = into.findIndex((c) => key(c) === key(candidate));
677
+ if (existing < 0) {
678
+ into.push(candidate);
679
+ }
680
+ else if (candidate.confidence > into[existing].confidence) {
681
+ into[existing] = candidate;
682
+ }
683
+ }
684
+ }
607
685
  }
608
686
  //# sourceMappingURL=PKDetector.js.map