@duckcodeailabs/dql-core 1.6.21 → 1.6.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/contracts/index.d.ts +1 -1
  2. package/dist/contracts/index.d.ts.map +1 -1
  3. package/dist/contracts/index.js.map +1 -1
  4. package/dist/contracts/registry.d.ts +25 -1
  5. package/dist/contracts/registry.d.ts.map +1 -1
  6. package/dist/contracts/registry.js +104 -12
  7. package/dist/contracts/registry.js.map +1 -1
  8. package/dist/contracts/types.d.ts +55 -0
  9. package/dist/contracts/types.d.ts.map +1 -1
  10. package/dist/contracts/types.js.map +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/lineage/builder.d.ts.map +1 -1
  16. package/dist/lineage/builder.js +12 -0
  17. package/dist/lineage/builder.js.map +1 -1
  18. package/dist/lineage/column-lineage.d.ts +27 -0
  19. package/dist/lineage/column-lineage.d.ts.map +1 -1
  20. package/dist/lineage/column-lineage.js +147 -0
  21. package/dist/lineage/column-lineage.js.map +1 -1
  22. package/dist/lineage/derivation.d.ts +111 -0
  23. package/dist/lineage/derivation.d.ts.map +1 -0
  24. package/dist/lineage/derivation.js +177 -0
  25. package/dist/lineage/derivation.js.map +1 -0
  26. package/dist/lineage/impact.d.ts +127 -0
  27. package/dist/lineage/impact.d.ts.map +1 -0
  28. package/dist/lineage/impact.js +305 -0
  29. package/dist/lineage/impact.js.map +1 -0
  30. package/dist/lineage/index.d.ts +3 -1
  31. package/dist/lineage/index.d.ts.map +1 -1
  32. package/dist/lineage/index.js +3 -1
  33. package/dist/lineage/index.js.map +1 -1
  34. package/dist/manifest/builder.d.ts +16 -0
  35. package/dist/manifest/builder.d.ts.map +1 -1
  36. package/dist/manifest/builder.js +117 -10
  37. package/dist/manifest/builder.js.map +1 -1
  38. package/dist/manifest/dbt-freshness.d.ts +65 -0
  39. package/dist/manifest/dbt-freshness.d.ts.map +1 -0
  40. package/dist/manifest/dbt-freshness.js +264 -0
  41. package/dist/manifest/dbt-freshness.js.map +1 -0
  42. package/dist/manifest/domain-writer.d.ts +66 -0
  43. package/dist/manifest/domain-writer.d.ts.map +1 -0
  44. package/dist/manifest/domain-writer.js +114 -0
  45. package/dist/manifest/domain-writer.js.map +1 -0
  46. package/dist/manifest/index.d.ts +4 -1
  47. package/dist/manifest/index.d.ts.map +1 -1
  48. package/dist/manifest/index.js +3 -0
  49. package/dist/manifest/index.js.map +1 -1
  50. package/dist/manifest/output-drift.d.ts +31 -0
  51. package/dist/manifest/output-drift.d.ts.map +1 -0
  52. package/dist/manifest/output-drift.js +97 -0
  53. package/dist/manifest/output-drift.js.map +1 -0
  54. package/dist/manifest/types.d.ts +156 -1
  55. package/dist/manifest/types.d.ts.map +1 -1
  56. package/dist/parser/parser.d.ts.map +1 -1
  57. package/dist/parser/parser.js +8 -0
  58. package/dist/parser/parser.js.map +1 -1
  59. package/dist/semantic/analyzer.d.ts +8 -0
  60. package/dist/semantic/analyzer.d.ts.map +1 -1
  61. package/dist/semantic/analyzer.js +8 -0
  62. package/dist/semantic/analyzer.js.map +1 -1
  63. package/dist/semantic/conflicts.d.ts +33 -0
  64. package/dist/semantic/conflicts.d.ts.map +1 -0
  65. package/dist/semantic/conflicts.js +270 -0
  66. package/dist/semantic/conflicts.js.map +1 -0
  67. package/dist/semantic/index.d.ts +1 -1
  68. package/dist/semantic/index.d.ts.map +1 -1
  69. package/dist/semantic/index.js +1 -1
  70. package/dist/semantic/index.js.map +1 -1
  71. package/dist/trust/index.d.ts +3 -0
  72. package/dist/trust/index.d.ts.map +1 -0
  73. package/dist/trust/index.js +2 -0
  74. package/dist/trust/index.js.map +1 -0
  75. package/dist/trust/labels.d.ts +122 -0
  76. package/dist/trust/labels.d.ts.map +1 -0
  77. package/dist/trust/labels.js +185 -0
  78. package/dist/trust/labels.js.map +1 -0
  79. package/package.json +5 -1
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Impact analysis + re-certification gating for DQL's answer layer.
3
+ *
4
+ * DQL composition is unbounded freeform `ref()`. The safety net: when a block
5
+ * changes, you must know what downstream *certified* work it invalidates.
6
+ *
7
+ * Given changed block name(s) — from `dql diff` or explicit input — this module:
8
+ * 1. walks the dependency DAG **downstream** to the full transitive set,
9
+ * 2. flags the cross-domain edges that get invalidated,
10
+ * 3. computes the resulting `domainTrust` delta,
11
+ * 4. produces a **required re-cert** list (certified downstream depending on
12
+ * a changed block's semantics).
13
+ *
14
+ * It also classifies whether a given block change is *semantic* (affects what
15
+ * the block computes) or purely cosmetic (description/owner/tag/viz only).
16
+ * The classification is **conservative**: when unsure, treat the change as
17
+ * semantic so re-cert is required rather than silently skipped.
18
+ *
19
+ * This is the LOCAL engine. It does not manage org-wide workflow, approvals,
20
+ * audit, or auto-re-certification — that is the cloud product.
21
+ */
22
+ import { getDomainTrustOverview } from './domain-lineage.js';
23
+ // ---- Semantic-change classification ----
24
+ /**
25
+ * Block-diff field paths that are **non-semantic**: they describe or annotate
26
+ * the block but do not change what it computes. A change confined entirely to
27
+ * these fields does not invalidate certification.
28
+ *
29
+ * Everything else (query, params, metric refs, tests, domain, type, business
30
+ * rules, visualization, …) is treated as semantic. This is deliberately a
31
+ * deny-list of known-cosmetic fields rather than an allow-list of semantic
32
+ * ones, so an unrecognized/new field defaults to "semantic" → needs re-cert.
33
+ */
34
+ const NON_SEMANTIC_BLOCK_FIELDS = new Set([
35
+ 'description',
36
+ 'owner',
37
+ 'tags',
38
+ 'businessOutcome',
39
+ 'businessOwner',
40
+ 'decisionUse',
41
+ 'reviewCadence',
42
+ 'caveats',
43
+ ]);
44
+ /**
45
+ * Classify a single block-level `DiffChange` as semantic or non-semantic.
46
+ *
47
+ * - Added/removed blocks are always semantic (structural).
48
+ * - A changed block is non-semantic only when **every** changed field is in
49
+ * {@link NON_SEMANTIC_BLOCK_FIELDS}. A single unrecognized or semantic field
50
+ * makes the whole change semantic (conservative default).
51
+ */
52
+ export function classifyBlockChange(change) {
53
+ switch (change.kind) {
54
+ case 'block-added':
55
+ case 'block-removed':
56
+ return { name: change.name, verdict: 'semantic', changedFields: [], structural: true };
57
+ case 'block-changed': {
58
+ const fields = change.fields.map((f) => f.path);
59
+ const allCosmetic = fields.length > 0 && fields.every((p) => isNonSemanticField(p));
60
+ return {
61
+ name: change.name,
62
+ verdict: allCosmetic ? 'non-semantic' : 'semantic',
63
+ changedFields: fields,
64
+ structural: false,
65
+ };
66
+ }
67
+ default:
68
+ return null;
69
+ }
70
+ }
71
+ /** A field path is non-semantic when its leading segment is a known cosmetic field. */
72
+ function isNonSemanticField(path) {
73
+ // `tags`, `description`, … are top-level; `params.region`, `tests[..]`,
74
+ // `visualization.title`, `query` are semantic. Only the exact known cosmetic
75
+ // field names count as non-semantic.
76
+ return NON_SEMANTIC_BLOCK_FIELDS.has(path);
77
+ }
78
+ /**
79
+ * Extract the set of changed blocks from a diff report's changes, classifying
80
+ * each as semantic or non-semantic. Non-block changes (dashboards, workbooks,
81
+ * cells, notebooks) are ignored here — impact is keyed off block identity.
82
+ */
83
+ export function changedBlocksFromDiff(changes) {
84
+ const out = [];
85
+ for (const change of changes) {
86
+ const classified = classifyBlockChange(change);
87
+ if (classified)
88
+ out.push(classified);
89
+ }
90
+ return out;
91
+ }
92
+ /**
93
+ * Compute the impact of a set of changed blocks against a lineage graph.
94
+ *
95
+ * Only blocks with a **semantic** verdict propagate downstream impact; a purely
96
+ * cosmetic change (description/owner/tag only) produces an empty downstream set
97
+ * and never trips the re-cert gate.
98
+ */
99
+ export function computeImpact(graph, changedBlocks, options = {}) {
100
+ const ignoreAlreadyPending = options.ignoreAlreadyPending ?? true;
101
+ const semantic = changedBlocks.filter((b) => b.verdict === 'semantic');
102
+ const semanticNames = semantic.map((b) => b.name);
103
+ // Resolve each changed block to its graph node id. A changed block may not
104
+ // exist in the graph (e.g. removed, or never compiled) — skip those for the
105
+ // downstream walk but keep them in the report.
106
+ const sourceIds = [];
107
+ const sourceNameById = new Map();
108
+ for (const block of semantic) {
109
+ const id = `block:${block.name}`;
110
+ if (graph.getNode(id)) {
111
+ sourceIds.push(id);
112
+ sourceNameById.set(id, block.name);
113
+ }
114
+ }
115
+ // Full transitive downstream union. Track which changed block(s) reach each
116
+ // downstream node so the re-cert list can attribute invalidations precisely.
117
+ const downstreamById = new Map();
118
+ const reachedBy = new Map();
119
+ for (const id of sourceIds) {
120
+ const blockName = sourceNameById.get(id);
121
+ for (const node of graph.descendants(id)) {
122
+ if (node.type === 'domain')
123
+ continue;
124
+ downstreamById.set(node.id, node);
125
+ const set = reachedBy.get(node.id) ?? new Set();
126
+ set.add(blockName);
127
+ reachedBy.set(node.id, set);
128
+ }
129
+ }
130
+ const downstream = [...downstreamById.values()]
131
+ .map(toImpactedNode)
132
+ .sort((a, b) => a.id.localeCompare(b.id));
133
+ // Cross-domain edges fully contained in the invalidated zone (source = a
134
+ // changed block or a downstream node; target = a downstream node).
135
+ const zoneIds = new Set([...sourceIds, ...downstreamById.keys()]);
136
+ const crossDomainImpacts = collectCrossDomainImpacts(graph, zoneIds);
137
+ // Re-cert list: certified downstream nodes invalidated by a semantic change.
138
+ const requiresRecert = [];
139
+ for (const node of downstreamById.values()) {
140
+ if (node.status !== 'certified') {
141
+ if (!(node.status === 'pending_recertification' && !ignoreAlreadyPending))
142
+ continue;
143
+ }
144
+ requiresRecert.push({
145
+ id: node.id,
146
+ name: node.name,
147
+ domain: node.domain,
148
+ owner: node.owner,
149
+ invalidatedBy: [...(reachedBy.get(node.id) ?? new Set())].sort(),
150
+ });
151
+ }
152
+ requiresRecert.sort((a, b) => a.id.localeCompare(b.id));
153
+ const domainTrustDelta = computeDomainTrustDelta(graph, requiresRecert);
154
+ return {
155
+ changedBlocks,
156
+ semanticChanges: semanticNames,
157
+ downstream,
158
+ crossDomainImpacts,
159
+ requiresRecert,
160
+ domainTrustDelta,
161
+ hasCertifiedInvalidation: requiresRecert.length > 0,
162
+ };
163
+ }
164
+ function toImpactedNode(node) {
165
+ return {
166
+ id: node.id,
167
+ type: node.type,
168
+ name: node.name,
169
+ domain: node.domain,
170
+ status: node.status,
171
+ owner: node.owner,
172
+ };
173
+ }
174
+ /**
175
+ * Collect cross-domain edges whose source and target both lie inside the
176
+ * invalidated zone, grouped by `from → to` domain pair.
177
+ */
178
+ function collectCrossDomainImpacts(graph, zoneIds) {
179
+ const byPair = new Map();
180
+ for (const edge of graph.getCrossDomainEdges()) {
181
+ if (!zoneIds.has(edge.source) || !zoneIds.has(edge.target))
182
+ continue;
183
+ const from = edge.sourceDomain ?? graph.getNode(edge.source)?.domain ?? '(unknown)';
184
+ const to = edge.targetDomain ?? graph.getNode(edge.target)?.domain ?? '(unknown)';
185
+ const key = `${from}→${to}`;
186
+ let impact = byPair.get(key);
187
+ if (!impact) {
188
+ impact = { from, to, edges: [] };
189
+ byPair.set(key, impact);
190
+ }
191
+ impact.edges.push({ source: edge.source, target: edge.target });
192
+ }
193
+ return [...byPair.values()].sort((a, b) => `${a.from}→${a.to}`.localeCompare(`${b.from}→${b.to}`));
194
+ }
195
+ /**
196
+ * Compute the per-domain `domainTrust` delta that results from demoting every
197
+ * re-cert artifact out of `certified`. The "before" view uses the live graph;
198
+ * the "after" view recomputes trust as if each re-cert block were no longer
199
+ * certified (it would become `pending_recertification`).
200
+ *
201
+ * Only block-typed re-cert items affect trust scores, mirroring
202
+ * {@link getDomainTrustOverview} which counts blocks only.
203
+ */
204
+ function computeDomainTrustDelta(graph, requiresRecert) {
205
+ // Map domain → set of block names losing certified status.
206
+ const demotedByDomain = new Map();
207
+ const affectedDomains = new Set();
208
+ for (const item of requiresRecert) {
209
+ const node = graph.getNode(item.id);
210
+ if (!node || node.type !== 'block')
211
+ continue;
212
+ const domain = node.domain ?? '(unassigned)';
213
+ demotedByDomain.set(domain, (demotedByDomain.get(domain) ?? 0) + 1);
214
+ affectedDomains.add(domain);
215
+ }
216
+ const deltas = [];
217
+ for (const domain of affectedDomains) {
218
+ const overview = getDomainTrustOverview(graph, domain);
219
+ const total = overview.totalBlocks;
220
+ const certifiedBefore = overview.certified;
221
+ const demoted = demotedByDomain.get(domain) ?? 0;
222
+ const certifiedAfter = Math.max(0, certifiedBefore - demoted);
223
+ const trustBefore = total > 0 ? certifiedBefore / total : 0;
224
+ const trustAfter = total > 0 ? certifiedAfter / total : 0;
225
+ deltas.push({
226
+ domain,
227
+ certifiedBefore,
228
+ certifiedAfter,
229
+ total,
230
+ trustBefore,
231
+ trustAfter,
232
+ delta: trustAfter - trustBefore,
233
+ });
234
+ }
235
+ return deltas.sort((a, b) => a.domain.localeCompare(b.domain));
236
+ }
237
+ // ---- Convenience: full report from a diff ----
238
+ /**
239
+ * One-shot helper: classify a diff's block changes and compute the impact
240
+ * report in a single call.
241
+ */
242
+ export function computeImpactFromDiff(graph, changes, options) {
243
+ return computeImpact(graph, changedBlocksFromDiff(changes), options);
244
+ }
245
+ // ---- Text rendering ----
246
+ /** Render an impact report as human-readable text for the CLI. */
247
+ export function renderImpactText(report) {
248
+ const lines = [];
249
+ lines.push(' Impact Analysis');
250
+ lines.push(' ' + '='.repeat(50));
251
+ if (report.changedBlocks.length === 0) {
252
+ lines.push('');
253
+ lines.push(' No block changes detected.');
254
+ return lines.join('\n');
255
+ }
256
+ lines.push('');
257
+ lines.push(' Changed blocks:');
258
+ for (const block of report.changedBlocks) {
259
+ const tag = block.structural
260
+ ? '(structural)'
261
+ : block.verdict === 'non-semantic'
262
+ ? '(non-semantic — no re-cert)'
263
+ : '(semantic)';
264
+ lines.push(` ${block.name} ${tag}`);
265
+ }
266
+ lines.push('');
267
+ lines.push(` Transitive downstream affected: ${report.downstream.length}`);
268
+ for (const node of report.downstream) {
269
+ const badge = node.status === 'certified' ? ' [certified]' : node.status ? ` [${node.status}]` : '';
270
+ const domain = node.domain ? ` (${node.domain})` : '';
271
+ lines.push(` - ${node.type}:${node.name}${domain}${badge}`);
272
+ }
273
+ if (report.crossDomainImpacts.length > 0) {
274
+ lines.push('');
275
+ lines.push(' Cross-domain edges invalidated:');
276
+ for (const impact of report.crossDomainImpacts) {
277
+ lines.push(` ${impact.from} -> ${impact.to} (${impact.edges.length} edge(s))`);
278
+ }
279
+ }
280
+ if (report.domainTrustDelta.length > 0) {
281
+ lines.push('');
282
+ lines.push(' domainTrust delta:');
283
+ for (const d of report.domainTrustDelta) {
284
+ const before = (d.trustBefore * 100).toFixed(0);
285
+ const after = (d.trustAfter * 100).toFixed(0);
286
+ const pts = (d.delta * 100).toFixed(0);
287
+ lines.push(` ${d.domain}: ${d.certifiedBefore}/${d.total} -> ${d.certifiedAfter}/${d.total} certified ` +
288
+ `(${before}% -> ${after}%, ${pts} pts)`);
289
+ }
290
+ }
291
+ lines.push('');
292
+ if (report.requiresRecert.length > 0) {
293
+ lines.push(` Requires re-certification (${report.requiresRecert.length}):`);
294
+ for (const item of report.requiresRecert) {
295
+ const owner = item.owner ? ` — ${item.owner}` : '';
296
+ lines.push(` ! ${item.name}${item.domain ? ` (${item.domain})` : ''}${owner}`);
297
+ lines.push(` invalidated by: ${item.invalidatedBy.join(', ')}`);
298
+ }
299
+ }
300
+ else {
301
+ lines.push(' No certified downstream invalidated. Re-cert not required.');
302
+ }
303
+ return lines.join('\n');
304
+ }
305
+ //# sourceMappingURL=impact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impact.js","sourceRoot":"","sources":["../../src/lineage/impact.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,10 @@
1
1
  export { analyzeSqlReferences, extractTablesFromSql, type SqlColumnReference, type SqlParseResult, type SqlReferenceAnalysis, } from './sql-parser.js';
2
- export { extractColumnLineage, type ColumnLineageEntry, type ColumnLineageResult, type ColumnSource, } from './column-lineage.js';
2
+ export { extractColumnLineage, extractRefColumnUsage, type ColumnLineageEntry, type ColumnLineageResult, type ColumnSource, type RefColumnUsage, } from './column-lineage.js';
3
3
  export { resolveDependencies, getUpstream, getDownstream, type BlockDependencyInfo, type DependencyResolutionResult, } from './dependency-resolver.js';
4
4
  export { LineageGraph, getLayerForNodeType, type LineageNode, type LineageEdge, type LineageNodeType, type LineageEdgeType, type LineageLayer, type LineageGraphJSON, } from './lineage-graph.js';
5
5
  export { buildLineageGraph, type LineageBlockInput, type LineageMetricInput, type LineageDimensionInput, type LineageDbtModelInput, type LineageDashboardInput, type LineageBusinessViewInput, type LineageTermInput, type LineageBuilderOptions, } from './builder.js';
6
6
  export { queryLineage, queryBusiness360, queryCompleteLineagePaths, type LineageQuery, type LineageQueryResult, type Business360Asset, type Business360BlockContract, type Business360Edge, type Business360Gap, type Business360Options, type Business360Reusability, type Business360Result, type Business360ResultV2, type LineagePath, type CompletePathResult, type CompletePathOptions, } from './query.js';
7
7
  export { buildTrustChain, analyzeImpact, detectDomainFlows, getDomainTrustOverview, type TrustChain, type TrustChainNode, type ImpactAnalysis, type DomainImpact, type DomainFlow, } from './domain-lineage.js';
8
+ export { buildDerivationWalk, type DerivationWalk, type DerivationStep, type DerivationStepKind, type DerivationFocusBlock, type BuildDerivationWalkInput, } from './derivation.js';
9
+ export { classifyBlockChange, changedBlocksFromDiff, computeImpact, computeImpactFromDiff, renderImpactText, type ChangedBlock, type SemanticVerdict, type ImpactReport, type ImpactedNode, type CrossDomainImpact, type DomainTrustDelta, type RecertItem, type ComputeImpactOptions, } from './impact.js';
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lineage/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,YAAY,GAClB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,GAChC,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,yBAAyB,EACzB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lineage/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,GAChC,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,yBAAyB,EACzB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,UAAU,GAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,GAC9B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,oBAAoB,GAC1B,MAAM,aAAa,CAAC"}
@@ -1,9 +1,11 @@
1
1
  // Lineage analysis — SQL parsing, dependency resolution, graph building, and domain lineage
2
2
  export { analyzeSqlReferences, extractTablesFromSql, } from './sql-parser.js';
3
- export { extractColumnLineage, } from './column-lineage.js';
3
+ export { extractColumnLineage, extractRefColumnUsage, } from './column-lineage.js';
4
4
  export { resolveDependencies, getUpstream, getDownstream, } from './dependency-resolver.js';
5
5
  export { LineageGraph, getLayerForNodeType, } from './lineage-graph.js';
6
6
  export { buildLineageGraph, } from './builder.js';
7
7
  export { queryLineage, queryBusiness360, queryCompleteLineagePaths, } from './query.js';
8
8
  export { buildTrustChain, analyzeImpact, detectDomainFlows, getDomainTrustOverview, } from './domain-lineage.js';
9
+ export { buildDerivationWalk, } from './derivation.js';
10
+ export { classifyBlockChange, changedBlocksFromDiff, computeImpact, computeImpactFromDiff, renderImpactText, } from './impact.js';
9
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lineage/index.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAE5F,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GAIrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,GAIrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,aAAa,GAGd,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,YAAY,EACZ,mBAAmB,GAOpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,GASlB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,yBAAyB,GAc1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,sBAAsB,GAMvB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lineage/index.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAE5F,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GAIrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,GAKtB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,aAAa,GAGd,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,YAAY,EACZ,mBAAmB,GAOpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,GASlB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,yBAAyB,GAc1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,sBAAsB,GAMvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,GAMpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,gBAAgB,GASjB,MAAM,aAAa,CAAC"}
@@ -91,6 +91,22 @@ interface ProjectConfig {
91
91
  /** Path to datalex-manifest.json, absolute or relative to projectRoot */
92
92
  manifestPath?: string;
93
93
  };
94
+ /**
95
+ * Optional `dql propose` conventions. Refines the convention-agnostic
96
+ * classifier + bounded selection. All fields are optional; defaults apply.
97
+ */
98
+ propose?: {
99
+ /** Folders/tags that mean business (default ["marts","core","reporting"]). */
100
+ businessLayers?: string[];
101
+ /** Folders/tags that mean plumbing (default ["staging","intermediate","base"]). */
102
+ excludeLayers?: string[];
103
+ /** Max candidates generated per domain (default 8). */
104
+ maxPerDomain?: number;
105
+ /** Minimum demand score a candidate needs to be selected (default 0). */
106
+ minScore?: number;
107
+ /** Optional AI enrichment toggle (default "auto"). */
108
+ aiEnrichment?: 'auto' | 'on' | 'off';
109
+ };
94
110
  }
95
111
  /**
96
112
  * Load `dql.config.json`. Exposed so CLI commands can read the same config
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/manifest/builder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuBH,OAAO,KAAK,EACV,WAAW,EAeZ,MAAM,YAAY,CAAC;AAcpB;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAmDzE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,WAAW,CA8HxE;AAID,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;OAGG;IACH,GAAG,CAAC,EAAE;QACJ,0EAA0E;QAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,6FAA6F;QAC7F,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,iGAAiG;IACjG,OAAO,CAAC,EAAE;QACR,yEAAyE;QACzE,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAQpE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAgBf;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,aAAa,GAC3B,MAAM,GAAG,IAAI,CAef"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/manifest/builder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwBH,OAAO,KAAK,EACV,WAAW,EAeZ,MAAM,YAAY,CAAC;AAepB;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAmDzE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,WAAW,CAgJxE;AAID,UAAU,aAAa;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;OAGG;IACH,GAAG,CAAC,EAAE;QACJ,0EAA0E;QAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,6FAA6F;QAC7F,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,iGAAiG;IACjG,OAAO,CAAC,EAAE;QACR,yEAAyE;QACzE,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACF;;;OAGG;IACH,OAAO,CAAC,EAAE;QACR,8EAA8E;QAC9E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,mFAAmF;QACnF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;QACzB,uDAAuD;QACvD,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,yEAAyE;QACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,sDAAsD;QACtD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;KACtC,CAAC;CACH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAQpE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAgBf;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,aAAa,GAC3B,MAAM,GAAG,IAAI,CAef"}
@@ -8,14 +8,16 @@
8
8
  import { readdirSync, readFileSync, existsSync } from 'node:fs';
9
9
  import { join, extname, relative } from 'node:path';
10
10
  import { Parser } from '../parser/index.js';
11
- import { analyze } from '../semantic/index.js';
11
+ import { analyze, detectTrustConflicts } from '../semantic/index.js';
12
12
  import { extractTablesFromSql } from '../lineage/sql-parser.js';
13
13
  import { extractColumnLineage } from '../lineage/column-lineage.js';
14
+ import { detectOutputDrift } from './output-drift.js';
14
15
  import { buildLineageGraph } from '../lineage/builder.js';
15
16
  import { detectDomainFlows, getDomainTrustOverview } from '../lineage/domain-lineage.js';
16
17
  import { loadSemanticLayerFromDir } from '../semantic/index.js';
17
18
  import { DataLexContractRegistry } from '../contracts/index.js';
18
19
  import { loadAppDocument, findAppDocuments, findDashboardsForApp, loadDashboardDocument, extractDashboardBlockRefs, appFolderRelPath, } from '../apps/index.js';
20
+ import { loadDbtRunState, applyBlockDataState } from './dbt-freshness.js';
19
21
  /**
20
22
  * Enumerate every file whose contents could change the manifest output.
21
23
  *
@@ -121,6 +123,14 @@ export function buildManifest(options) {
121
123
  validateDomains(domains, blocks, businessViews, terms, diagnostics);
122
124
  validateBusinessViews(businessViews, blocks, diagnostics);
123
125
  validateTermRefs(terms, blocks, businessViews, diagnostics);
126
+ // Cross-artifact trust-conflict detection: two certified terms / blocks that
127
+ // claim the same concept/grain but disagree. Additive `kind: 'conflict'`
128
+ // diagnostics; conservative heuristics avoid false positives.
129
+ diagnostics.push(...detectTrustConflicts(terms, blocks));
130
+ // Output-contract drift detection: a parent block `ref()`s a child and
131
+ // references a column the child no longer outputs. Additive `kind: 'drift'`
132
+ // warnings — never fails the build; freeform composition stays unrestricted.
133
+ diagnostics.push(...detectOutputDrift(blocks));
124
134
  // Load semantic layer
125
135
  const semanticDir = resolveSemanticPath(projectRoot, config);
126
136
  const { metrics, dimensions } = loadSemanticDefinitions(projectRoot, semanticDir);
@@ -148,11 +158,19 @@ export function buildManifest(options) {
148
158
  let dbtImport;
149
159
  if (options.dbtManifestPath) {
150
160
  const filters = options.dbtImportFilters ?? config.dbtImport;
161
+ // Freshness-aware trust — read dbt's run artifacts (run_results.json + source
162
+ // freshness) alongside the manifest, READ-ONLY. Empty/absent → every node is
163
+ // `unknown` and blocks are left untouched (additive, backward compatible).
164
+ const runState = loadDbtRunState(options.dbtManifestPath);
151
165
  dbtImport = importDbtManifest(options.dbtManifestPath, sources, referencedTables, {
152
166
  maxHops: options.maxDbtHops,
153
167
  selectiveThreshold: options.selectiveDbtThreshold ?? 200,
154
168
  filters,
169
+ runState,
155
170
  });
171
+ // Roll up per-model run state into each certified block's effective
172
+ // `dataState` via its transitive dbt upstreams.
173
+ applyBlockDataState(blocks, sources, dbtImport.dbtDag, runState);
156
174
  }
157
175
  // Apps & dashboards (consumption layer). Scanned after blocks/notebooks
158
176
  // because dashboard refs are resolved against the block path → name map.
@@ -1039,9 +1057,10 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
1039
1057
  const database = node.database;
1040
1058
  const dependsOn = Array.isArray(node.depends_on?.nodes) ? node.depends_on.nodes : [];
1041
1059
  const lookupKeys = buildLookupKeys(name, schema, database);
1060
+ const primaryLookupKeys = buildPrimaryLookupKeys(name, schema, database);
1042
1061
  const tags = Array.isArray(node.tags) ? node.tags : [];
1043
1062
  const path = (node.original_file_path ?? node.path);
1044
- index.set(uniqueId, { uniqueId, name, type: 'model', schema, database, dependsOn, lookupKeys, tags, path, raw: node });
1063
+ index.set(uniqueId, { uniqueId, name, type: 'model', schema, database, dependsOn, lookupKeys, primaryLookupKeys, tags, path, raw: node });
1045
1064
  }
1046
1065
  const rawSources = manifest.sources ?? {};
1047
1066
  for (const [uniqueId, src] of Object.entries(rawSources)) {
@@ -1051,9 +1070,10 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
1051
1070
  const schema = src.schema;
1052
1071
  const database = src.database;
1053
1072
  const lookupKeys = buildLookupKeys(name, schema, database);
1073
+ const primaryLookupKeys = buildPrimaryLookupKeys(name, schema, database);
1054
1074
  const tags = Array.isArray(src.tags) ? src.tags : [];
1055
1075
  const path = (src.original_file_path ?? src.path);
1056
- index.set(uniqueId, { uniqueId, name, type: 'source', schema, database, dependsOn: [], lookupKeys, tags, path, raw: src });
1076
+ index.set(uniqueId, { uniqueId, name, type: 'source', schema, database, dependsOn: [], lookupKeys, primaryLookupKeys, tags, path, raw: src });
1057
1077
  }
1058
1078
  const totalDbtModels = [...index.values()].filter((e) => e.type === 'model').length;
1059
1079
  const filters = opts.filters;
@@ -1073,21 +1093,33 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
1073
1093
  // Multiple dbt nodes may share a short alias, so short-name matches are
1074
1094
  // used only when they resolve unambiguously.
1075
1095
  const nameToIds = new Map();
1076
- const addNameLookup = (key, uniqueId) => {
1096
+ const primaryNameToIds = new Map();
1097
+ const addTo = (map, key, uniqueId) => {
1077
1098
  const normalized = key.toLowerCase();
1078
- const existing = nameToIds.get(normalized);
1099
+ const existing = map.get(normalized);
1079
1100
  if (existing)
1080
1101
  existing.add(uniqueId);
1081
1102
  else
1082
- nameToIds.set(normalized, new Set([uniqueId]));
1103
+ map.set(normalized, new Set([uniqueId]));
1083
1104
  };
1084
1105
  for (const entry of index.values()) {
1085
- for (const key of entry.lookupKeys) {
1086
- addNameLookup(key, entry.uniqueId);
1087
- }
1106
+ for (const key of entry.lookupKeys)
1107
+ addTo(nameToIds, key, entry.uniqueId);
1108
+ for (const key of entry.primaryLookupKeys)
1109
+ addTo(primaryNameToIds, key, entry.uniqueId);
1088
1110
  }
1089
1111
  const resolveDbtAnchor = (ref) => {
1090
- for (const key of buildReferencedTableLookupKeys(ref)) {
1112
+ const refKeys = buildReferencedTableLookupKeys(ref);
1113
+ // Prefer an unambiguous match on EXACT model names before falling back to
1114
+ // dbt role-prefix-stripped aliases — otherwise `dev.customers` is ambiguous
1115
+ // between the `customers` mart and the stripped alias of `stg_customers`,
1116
+ // and the anchor (and its whole upstream subgraph) is silently skipped.
1117
+ for (const key of refKeys) {
1118
+ const ids = primaryNameToIds.get(key);
1119
+ if (ids?.size === 1)
1120
+ return [...ids][0];
1121
+ }
1122
+ for (const key of refKeys) {
1091
1123
  const ids = nameToIds.get(key);
1092
1124
  if (ids?.size === 1)
1093
1125
  return [...ids][0];
@@ -1172,6 +1204,7 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
1172
1204
  type: v.data_type,
1173
1205
  }))
1174
1206
  : undefined;
1207
+ const modelRunState = opts.runState?.byUniqueId.get(uid);
1175
1208
  if (!sources[tableName]) {
1176
1209
  sources[tableName] = { name: tableName, origin: 'dbt', referencedBy: [] };
1177
1210
  }
@@ -1187,6 +1220,7 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
1187
1220
  { name: v.name, description: v.description, type: v.data_type },
1188
1221
  ]))
1189
1222
  : undefined,
1223
+ runState: modelRunState,
1190
1224
  };
1191
1225
  if (sources[tableName].origin === 'sql')
1192
1226
  sources[tableName].origin = 'dbt';
@@ -1202,6 +1236,7 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
1202
1236
  database: entry.database,
1203
1237
  materialized: node.config?.materialized,
1204
1238
  description: node.description,
1239
+ runState: modelRunState,
1205
1240
  });
1206
1241
  for (const dep of selectedDeps) {
1207
1242
  dbtDagEdges.push({ source: dep, target: uid });
@@ -1215,11 +1250,13 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
1215
1250
  if (!sources[tableName]) {
1216
1251
  sources[tableName] = { name: tableName, origin: 'dbt', referencedBy: [] };
1217
1252
  }
1253
+ const sourceRunState = opts.runState?.byUniqueId.get(uid);
1218
1254
  sources[tableName].dbtModel = {
1219
1255
  uniqueId: uid,
1220
1256
  database: entry.database,
1221
1257
  schema: entry.schema,
1222
1258
  description: src.description,
1259
+ runState: sourceRunState,
1223
1260
  };
1224
1261
  dbtDagModels.push({
1225
1262
  uniqueId: uid,
@@ -1229,6 +1266,7 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
1229
1266
  schema: entry.schema,
1230
1267
  database: entry.database,
1231
1268
  description: src.description,
1269
+ runState: sourceRunState,
1232
1270
  });
1233
1271
  sourcesImported++;
1234
1272
  }
@@ -1243,9 +1281,23 @@ function importDbtManifest(manifestPath, sources, referencedTables, opts = {}) {
1243
1281
  maxHops: opts.maxHops,
1244
1282
  importedAt: new Date().toISOString(),
1245
1283
  dbtDag: { models: dbtDagModels, edges: dbtDagEdges },
1284
+ runResultsPath: opts.runState?.runResultsPath,
1246
1285
  };
1247
1286
  }
1248
1287
  /** Build all normalised lookup keys for a dbt node name. */
1288
+ /**
1289
+ * Lookup keys from a dbt node's EXACT name (no role-prefix stripping), so a
1290
+ * mart `customers` and a staging `stg_customers` never collide on the same key.
1291
+ */
1292
+ function buildPrimaryLookupKeys(name, schema, database) {
1293
+ const alias = name.toLowerCase();
1294
+ const keys = new Set([alias]);
1295
+ if (schema)
1296
+ keys.add(`${schema}.${alias}`.toLowerCase());
1297
+ if (schema && database)
1298
+ keys.add(`${database}.${schema}.${alias}`.toLowerCase());
1299
+ return [...keys];
1300
+ }
1249
1301
  function buildLookupKeys(name, schema, database) {
1250
1302
  const aliases = new Set([name.toLowerCase()]);
1251
1303
  const stripped = stripDbtRolePrefix(name);
@@ -1816,8 +1868,63 @@ function blockDeclToManifestBlock(block, filePath) {
1816
1868
  unresolved: c.unresolved,
1817
1869
  }))
1818
1870
  : undefined,
1871
+ outputContract: buildOutputContract(Array.isArray(block.outputs) ? block.outputs : undefined, columnLineage),
1819
1872
  };
1820
1873
  }
1874
+ /**
1875
+ * Build a block's typed `outputContract` — the columns a parent that `ref()`s
1876
+ * this block can rely on. Additive and distinct from `outputs` (column-lineage
1877
+ * shaped). Source of truth, in priority order:
1878
+ *
1879
+ * 1. `declaredOutputs` (reviewer-declared field names) when present. These
1880
+ * are the contract the block author committed to, so they win. Column
1881
+ * lineage is used only to enrich `role`/`type` for matching names.
1882
+ * 2. Otherwise, the resolved column-lineage output columns — but only when
1883
+ * the projection is fully resolved (no `*`/unresolved entries). A star or
1884
+ * unparsed projection means the schema is open, so we emit no contract and
1885
+ * drift detection stays silent for this block (conservative).
1886
+ *
1887
+ * Returns undefined when neither source yields a usable schema, keeping the
1888
+ * field optional and backward-compatible.
1889
+ */
1890
+ function buildOutputContract(declaredOutputs, columnLineage) {
1891
+ const roleByName = new Map();
1892
+ if (columnLineage?.parsed) {
1893
+ for (const col of columnLineage.columns) {
1894
+ if (col.isAggregate)
1895
+ roleByName.set(col.name.toLowerCase(), 'metric');
1896
+ }
1897
+ }
1898
+ if (Array.isArray(declaredOutputs) && declaredOutputs.length > 0) {
1899
+ const seen = new Set();
1900
+ const contract = [];
1901
+ for (const raw of declaredOutputs) {
1902
+ const name = typeof raw === 'string' ? raw.trim() : '';
1903
+ if (!name || seen.has(name.toLowerCase()))
1904
+ continue;
1905
+ seen.add(name.toLowerCase());
1906
+ contract.push({ name, role: roleByName.get(name.toLowerCase()) });
1907
+ }
1908
+ return contract.length > 0 ? contract : undefined;
1909
+ }
1910
+ if (!columnLineage?.parsed || columnLineage.columns.length === 0)
1911
+ return undefined;
1912
+ // Only emit a derived contract when every output column is resolved — a star
1913
+ // or unresolved entry means we cannot enumerate the real output schema.
1914
+ if (columnLineage.columns.some((c) => c.unresolved || c.name === '*' || c.name.endsWith('.*'))) {
1915
+ return undefined;
1916
+ }
1917
+ const seen = new Set();
1918
+ const contract = [];
1919
+ for (const col of columnLineage.columns) {
1920
+ const name = col.name?.trim();
1921
+ if (!name || seen.has(name.toLowerCase()))
1922
+ continue;
1923
+ seen.add(name.toLowerCase());
1924
+ contract.push({ name, role: col.isAggregate ? 'metric' : undefined });
1925
+ }
1926
+ return contract.length > 0 ? contract : undefined;
1927
+ }
1821
1928
  function domainDeclToManifestDomain(domain, filePath) {
1822
1929
  return {
1823
1930
  name: domain.name,