@duckcodeailabs/dql-agent 1.6.34 → 1.7.1

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 (95) hide show
  1. package/dist/agent-run-engine.d.ts +14 -1
  2. package/dist/agent-run-engine.d.ts.map +1 -1
  3. package/dist/agent-run-engine.js +123 -3
  4. package/dist/agent-run-engine.js.map +1 -1
  5. package/dist/agent-run-gates.d.ts.map +1 -1
  6. package/dist/agent-run-gates.js +6 -0
  7. package/dist/agent-run-gates.js.map +1 -1
  8. package/dist/answer-loop.d.ts +57 -4
  9. package/dist/answer-loop.d.ts.map +1 -1
  10. package/dist/answer-loop.js +435 -44
  11. package/dist/answer-loop.js.map +1 -1
  12. package/dist/app-builder.d.ts +36 -3
  13. package/dist/app-builder.d.ts.map +1 -1
  14. package/dist/app-builder.js +285 -22
  15. package/dist/app-builder.js.map +1 -1
  16. package/dist/cascade/budgets.d.ts.map +1 -1
  17. package/dist/cascade/budgets.js +8 -3
  18. package/dist/cascade/budgets.js.map +1 -1
  19. package/dist/cascade/route-policy.d.ts.map +1 -1
  20. package/dist/cascade/route-policy.js +5 -0
  21. package/dist/cascade/route-policy.js.map +1 -1
  22. package/dist/dashboard-story.d.ts +36 -0
  23. package/dist/dashboard-story.d.ts.map +1 -0
  24. package/dist/dashboard-story.js +140 -0
  25. package/dist/dashboard-story.js.map +1 -0
  26. package/dist/domain-context.d.ts +30 -0
  27. package/dist/domain-context.d.ts.map +1 -0
  28. package/dist/domain-context.js +76 -0
  29. package/dist/domain-context.js.map +1 -0
  30. package/dist/hints/git-store.d.ts +39 -3
  31. package/dist/hints/git-store.d.ts.map +1 -1
  32. package/dist/hints/git-store.js +172 -2
  33. package/dist/hints/git-store.js.map +1 -1
  34. package/dist/hints/types.d.ts +41 -0
  35. package/dist/hints/types.d.ts.map +1 -1
  36. package/dist/hints/types.js +1 -0
  37. package/dist/hints/types.js.map +1 -1
  38. package/dist/index.d.ts +10 -6
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +39 -3
  41. package/dist/index.js.map +1 -1
  42. package/dist/kg/build.d.ts.map +1 -1
  43. package/dist/kg/build.js +220 -4
  44. package/dist/kg/build.js.map +1 -1
  45. package/dist/kg/sqlite-fts.d.ts.map +1 -1
  46. package/dist/kg/sqlite-fts.js +8 -3
  47. package/dist/kg/sqlite-fts.js.map +1 -1
  48. package/dist/kg/types.d.ts +9 -2
  49. package/dist/kg/types.d.ts.map +1 -1
  50. package/dist/metadata/analysis-planner.d.ts.map +1 -1
  51. package/dist/metadata/analysis-planner.js +134 -18
  52. package/dist/metadata/analysis-planner.js.map +1 -1
  53. package/dist/metadata/analytical-policy.d.ts +51 -0
  54. package/dist/metadata/analytical-policy.d.ts.map +1 -0
  55. package/dist/metadata/analytical-policy.js +309 -0
  56. package/dist/metadata/analytical-policy.js.map +1 -0
  57. package/dist/metadata/block-fit.js +37 -4
  58. package/dist/metadata/block-fit.js.map +1 -1
  59. package/dist/metadata/catalog.d.ts +49 -1
  60. package/dist/metadata/catalog.d.ts.map +1 -1
  61. package/dist/metadata/catalog.js +365 -54
  62. package/dist/metadata/catalog.js.map +1 -1
  63. package/dist/metadata/dbt-first-safety.d.ts +19 -0
  64. package/dist/metadata/dbt-first-safety.d.ts.map +1 -0
  65. package/dist/metadata/dbt-first-safety.js +17 -0
  66. package/dist/metadata/dbt-first-safety.js.map +1 -0
  67. package/dist/metadata/sql-context-validation.d.ts.map +1 -1
  68. package/dist/metadata/sql-context-validation.js +42 -2
  69. package/dist/metadata/sql-context-validation.js.map +1 -1
  70. package/dist/metadata/sql-grounding.d.ts.map +1 -1
  71. package/dist/metadata/sql-grounding.js +25 -2
  72. package/dist/metadata/sql-grounding.js.map +1 -1
  73. package/dist/metadata/sql-retrieval.d.ts +2 -0
  74. package/dist/metadata/sql-retrieval.d.ts.map +1 -1
  75. package/dist/metadata/sql-retrieval.js +5 -1
  76. package/dist/metadata/sql-retrieval.js.map +1 -1
  77. package/dist/propose/build-from-prompt.d.ts +34 -0
  78. package/dist/propose/build-from-prompt.d.ts.map +1 -1
  79. package/dist/propose/build-from-prompt.js +199 -19
  80. package/dist/propose/build-from-prompt.js.map +1 -1
  81. package/dist/semantic-bridge/compose.d.ts.map +1 -1
  82. package/dist/semantic-bridge/compose.js +169 -5
  83. package/dist/semantic-bridge/compose.js.map +1 -1
  84. package/dist/skills/loader.d.ts +19 -0
  85. package/dist/skills/loader.d.ts.map +1 -1
  86. package/dist/skills/loader.js +107 -12
  87. package/dist/skills/loader.js.map +1 -1
  88. package/dist/synthesize.d.ts.map +1 -1
  89. package/dist/synthesize.js +152 -16
  90. package/dist/synthesize.js.map +1 -1
  91. package/dist/tools/registry.d.ts +1 -1
  92. package/dist/tools/registry.d.ts.map +1 -1
  93. package/dist/tools/registry.js +42 -0
  94. package/dist/tools/registry.js.map +1 -1
  95. package/package.json +4 -4
@@ -1,8 +1,31 @@
1
- import { type AppDocument, type DashboardDocument, type DashboardTileFilterBinding, type DashboardTileParameterBinding, type DashboardTileSourceEvidence, type DashboardVizConfig } from "@duckcodeailabs/dql-core";
1
+ import { type AppDocument, type DashboardDocument, type DashboardSemanticQueryRef, type DashboardStoryEvidencePlan, type DashboardTileFilterBinding, type DashboardTileParameterBinding, type DashboardTileSourceEvidence, type DashboardVizConfig } from "@duckcodeailabs/dql-core";
2
2
  import type { KGStore } from "./kg/sqlite-fts.js";
3
3
  import type { NarrateResult } from "./narrate.js";
4
+ export type AppMode = "personal" | "stakeholder";
5
+ export interface AppRequirement {
6
+ id: string;
7
+ question: string;
8
+ role: "kpi" | "trend" | "breakdown" | "detail";
9
+ measures: string[];
10
+ dimensions: string[];
11
+ filters: string[];
12
+ grain?: string;
13
+ ranking?: {
14
+ direction: "top" | "bottom";
15
+ limit: number;
16
+ };
17
+ }
18
+ export interface RequirementCoverage {
19
+ requirementId: string;
20
+ status: "covered" | "gap";
21
+ source: "certified_block" | "semantic_query" | "gap";
22
+ tileId?: string;
23
+ sourceId?: string;
24
+ trustState: AppPlanTrustState;
25
+ reasons: string[];
26
+ }
4
27
  export type AppBuilderSkillId = "interpret_business_intent" | "match_certified_context" | "shape_business_story" | "design_dashboard_layout" | "draft_missing_sections" | "route_review";
5
- export type AppPlanTileKind = "certified_block" | "draft_placeholder" | "narrative";
28
+ export type AppPlanTileKind = "certified_block" | "semantic_query" | "draft_placeholder" | "narrative";
6
29
  export type AppPlanAnalysisIntent = "executive_summary" | "metric_monitoring" | "driver_analysis" | "entity_drilldown" | "trust_review" | "data_quality" | "experiment_readout";
7
30
  export type AppPlanTileRole = "business_summary" | "kpi" | "trend" | "breakdown" | "evidence" | "trust" | "research" | "narrative";
8
31
  export type AppPlanTrustState = "certified" | "review_required" | "draft_ready";
@@ -46,6 +69,8 @@ export interface AppPlanTile {
46
69
  kind: AppPlanTileKind;
47
70
  description?: string;
48
71
  blockId?: string;
72
+ semantic?: DashboardSemanticQueryRef;
73
+ requirementIds?: string[];
49
74
  sourceNodeId?: string;
50
75
  viz: DashboardVizConfig["type"];
51
76
  certification: "certified" | "uncertified";
@@ -98,7 +123,12 @@ export interface AppBuilderSkill {
98
123
  description: string;
99
124
  }
100
125
  export interface AppPlan {
101
- version: 1;
126
+ version: 2;
127
+ mode: AppMode;
128
+ snapshotId?: string;
129
+ requirements: AppRequirement[];
130
+ requirementCoverage: RequirementCoverage[];
131
+ storyEvidencePlan: DashboardStoryEvidencePlan;
102
132
  appId: string;
103
133
  name: string;
104
134
  prompt: string;
@@ -158,6 +188,9 @@ export interface PlanAppFromPromptInput {
158
188
  preferredBlockIds?: string[];
159
189
  maxCertifiedTiles?: number;
160
190
  plannerMode?: "deterministic" | "ai_assisted";
191
+ mode?: AppMode;
192
+ snapshotId?: string;
193
+ allowSemanticQueries?: boolean;
161
194
  }
162
195
  export interface AppPlanValidationIssue {
163
196
  level: "error" | "warning";
@@ -1 +1 @@
1
- {"version":3,"file":"app-builder.d.ts","sourceRoot":"","sources":["../src/app-builder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app-builder.d.ts","sourceRoot":"","sources":["../src/app-builder.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,9 @@
1
1
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { createHash } from "node:crypto";
2
3
  import { join, relative } from "node:path";
3
4
  import { parseAppDocument, parseDashboardDocument, suggestAppId, } from "@duckcodeailabs/dql-core";
5
+ import { buildAnalysisQuestionPlan } from "./metadata/analysis-planner.js";
6
+ import { certifiedFitAllowsTier1, evaluateCertifiedBlockFit } from "./metadata/block-fit.js";
4
7
  export const APP_BUILDER_SKILLS = [
5
8
  {
6
9
  id: "interpret_business_intent",
@@ -33,6 +36,163 @@ export const APP_BUILDER_SKILLS = [
33
36
  description: "Keep generated analysis review-required and attach concrete next steps before stakeholder use.",
34
37
  },
35
38
  ];
39
+ function appRequirementsForPrompt(prompt) {
40
+ const analysis = buildAnalysisQuestionPlan(prompt);
41
+ const measures = analysis.requestedShape.measures.length
42
+ ? analysis.requestedShape.measures
43
+ : analysis.metricTerms;
44
+ const dimensions = analysis.requestedShape.dimensions;
45
+ const filters = analysis.requestedShape.filters;
46
+ const ranking = analysis.requestedShape.topN
47
+ ? { direction: analysis.requestedShape.rankingDirection ?? "top", limit: analysis.requestedShape.topN.n }
48
+ : undefined;
49
+ const requirements = [{
50
+ id: "primary",
51
+ question: prompt,
52
+ role: ranking ? "detail" : dimensions.length ? "breakdown" : "kpi",
53
+ measures,
54
+ dimensions,
55
+ filters,
56
+ grain: analysis.requestedShape.grain,
57
+ ...(ranking ? { ranking } : {}),
58
+ }];
59
+ const wantsTrend = analysis.mode === "trend" || /\btrend|over time|weekly|monthly|quarterly\b/i.test(prompt);
60
+ if (wantsTrend && !requirements.some((requirement) => requirement.role === "trend")) {
61
+ requirements.push({
62
+ id: "trend",
63
+ question: `${measures.join(" and ") || "primary metric"} over time`,
64
+ role: "trend",
65
+ measures,
66
+ dimensions: analysis.timeTerms.length ? analysis.timeTerms : ["time"],
67
+ filters,
68
+ });
69
+ }
70
+ for (const dimension of dimensions.slice(0, 2)) {
71
+ if (requirements[0]?.dimensions.includes(dimension) && dimensions.length === 1)
72
+ continue;
73
+ requirements.push({
74
+ id: `by-${slugify(dimension)}`,
75
+ question: `${measures.join(" and ") || "primary metric"} by ${dimension}`,
76
+ role: "breakdown",
77
+ measures,
78
+ dimensions: [dimension],
79
+ filters,
80
+ });
81
+ }
82
+ return requirements;
83
+ }
84
+ function certifiedCoverageForRequirement(requirement, nodes) {
85
+ const plan = buildAnalysisQuestionPlan(requirement.question);
86
+ const ranked = nodes.map((node) => ({
87
+ node,
88
+ fit: evaluateCertifiedBlockFit({ question: requirement.question, plan, block: node }),
89
+ requirementFit: certifiedNodeCoversRequirement(node, requirement),
90
+ }));
91
+ const exact = ranked.find((candidate) => certifiedFitAllowsTier1(candidate.fit) && candidate.requirementFit.ok);
92
+ if (exact)
93
+ return { node: exact.node, reasons: exact.fit.reasons };
94
+ return {
95
+ reasons: Array.from(new Set(ranked.flatMap((candidate) => [
96
+ ...(certifiedFitAllowsTier1(candidate.fit) && !candidate.requirementFit.ok ? [] : candidate.fit.reasons),
97
+ ...candidate.requirementFit.reasons,
98
+ ]))).slice(0, 5),
99
+ };
100
+ }
101
+ /**
102
+ * App requirements are already structured, so they are a stronger contract than
103
+ * reparsing the short requirement question. This guard prevents descriptive
104
+ * metadata from making (for example) a product ranking look like a time trend,
105
+ * or an all-customer lifetime ranking look like a beverage-filtered ranking.
106
+ */
107
+ function certifiedNodeCoversRequirement(node, requirement) {
108
+ const haystack = canonicalRequirementText(JSON.stringify(node));
109
+ const missingDimensions = requirement.dimensions.filter((dimension) => !requirementTermAliases(dimension).some((alias) => haystack.includes(` ${alias} `)));
110
+ const filterTerms = requirement.filters.flatMap((filter) => canonicalRequirementText(filter).trim().split(/\s+/).filter((term) => term.length > 2 && !REQUIREMENT_FILTER_STOP_WORDS.has(term)));
111
+ const missingFilters = filterTerms.filter((term) => !requirementTermAliases(term).some((alias) => haystack.includes(` ${alias} `)));
112
+ const reasons = [
113
+ missingDimensions.length ? `certified source does not prove dimensions: ${missingDimensions.join(", ")}` : "",
114
+ missingFilters.length ? `certified source does not prove filters: ${missingFilters.join(", ")}` : "",
115
+ ].filter(Boolean);
116
+ return { ok: reasons.length === 0, reasons };
117
+ }
118
+ const REQUIREMENT_FILTER_STOP_WORDS = new Set([
119
+ "and", "for", "from", "with", "different", "type", "types", "specific", "only", "all",
120
+ ]);
121
+ function canonicalRequirementText(value) {
122
+ return ` ${value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim()} `;
123
+ }
124
+ function requirementTermAliases(value) {
125
+ const term = canonicalRequirementText(value).trim();
126
+ if (["time", "date"].includes(term))
127
+ return ["time", "date", "day", "week", "month", "quarter", "year", "ordered at"];
128
+ if (["beverage", "beverages"].includes(term))
129
+ return ["beverage", "beverages", "drink", "drinks"];
130
+ if (["customer", "customers"].includes(term))
131
+ return ["customer", "customers", "customer name", "customer id"];
132
+ if (["product", "products"].includes(term))
133
+ return ["product", "products", "product name", "product id", "item", "sku"];
134
+ return [term];
135
+ }
136
+ function semanticTileForRequirement(kg, requirement, domain, snapshotId) {
137
+ if (requirement.measures.length === 0)
138
+ return null;
139
+ const hits = kg.search({
140
+ query: requirement.question,
141
+ domain: domain === "general" ? undefined : domain,
142
+ kinds: ["metric", "measure", "semantic_model", "dimension"],
143
+ limit: 24,
144
+ }).map((hit) => hit.node);
145
+ const metricNodes = hits.filter((node) => node.kind === "metric" || node.kind === "measure");
146
+ const modelNodes = hits.filter((node) => node.kind === "semantic_model");
147
+ if (metricNodes.length === 0 || modelNodes.length === 0)
148
+ return null;
149
+ const requested = new Set(requirement.measures.map((measure) => slugify(measure).replace(/-/g, "_")));
150
+ const metric = metricNodes.find((node) => requested.has(slugify(node.name).replace(/-/g, "_"))) ?? metricNodes[0];
151
+ if (!metric)
152
+ return null;
153
+ const semanticModelRefs = Array.from(new Set(modelNodes.map((node) => node.name))).slice(0, 4);
154
+ const id = `semantic-${requirement.id}`;
155
+ const fingerprintPayload = JSON.stringify({
156
+ metric: metric.nodeId,
157
+ dimensions: requirement.dimensions,
158
+ filters: requirement.filters,
159
+ models: semanticModelRefs,
160
+ snapshotId,
161
+ });
162
+ const semantic = {
163
+ id,
164
+ provider: metric.provenance?.toLowerCase().includes("metricflow") ? "metricflow" : "native",
165
+ metrics: [metric.name],
166
+ ...(requirement.dimensions.length ? { dimensions: requirement.dimensions } : {}),
167
+ ...(requirement.ranking ? {
168
+ orderBy: [{ field: metric.name, direction: requirement.ranking.direction === "bottom" ? "asc" : "desc" }],
169
+ limit: requirement.ranking.limit,
170
+ } : {}),
171
+ semanticModelRefs,
172
+ definitionFingerprint: `sha256:${createHash("sha256").update(fingerprintPayload).digest("hex")}`,
173
+ ...(snapshotId ? { snapshotId } : {}),
174
+ };
175
+ const viz = requirement.role === "kpi"
176
+ ? "single_value"
177
+ : requirement.role === "trend" ? "line" : requirement.ranking ? "bar" : "table";
178
+ return {
179
+ id,
180
+ title: requirement.question,
181
+ kind: "semantic_query",
182
+ semantic,
183
+ requirementIds: [requirement.id],
184
+ viz,
185
+ certification: "uncertified",
186
+ reviewStatus: "review_required",
187
+ trustState: "review_required",
188
+ rationale: "Governed semantic coverage for a requirement not fully covered by a compatible certified block.",
189
+ sourceEvidence: [
190
+ { source: metric.nodeId, nodeId: metric.nodeId, kind: metric.kind, reason: "Reviewed semantic metric", trustState: "review_required" },
191
+ ...modelNodes.slice(0, 4).map((node) => ({ source: node.nodeId, nodeId: node.nodeId, kind: node.kind, reason: "Semantic model used by the canonical query", trustState: "review_required" })),
192
+ ],
193
+ reviewTasks: ["Review semantic definition freshness and result shape before stakeholder publication."],
194
+ };
195
+ }
36
196
  export function planAppFromPrompt(input) {
37
197
  const prompt = input.prompt.trim();
38
198
  if (!prompt)
@@ -54,7 +214,61 @@ export function planAppFromPrompt(input) {
54
214
  const promptFilters = inferFilters(prompt);
55
215
  const blockFilters = filtersFromCertifiedNodes(certifiedNodes);
56
216
  const filters = blockFilters.length > 0 ? mergeAppFilters(blockFilters, promptFilters) : promptFilters;
57
- const certifiedTiles = certifiedNodes.map((node, index) => tileFromCertifiedNode(node, index, analysisIntent, filters));
217
+ const candidateCertifiedTiles = certifiedNodes.map((node, index) => tileFromCertifiedNode(node, index, analysisIntent, filters));
218
+ const requirements = appRequirementsForPrompt(prompt);
219
+ const coverage = [];
220
+ const selectedCertifiedIds = new Set();
221
+ const semanticTiles = [];
222
+ for (const requirement of requirements) {
223
+ const match = certifiedCoverageForRequirement(requirement, certifiedNodes);
224
+ if (match.node) {
225
+ const tileId = slugify(match.node.name) || match.node.nodeId;
226
+ selectedCertifiedIds.add(match.node.nodeId);
227
+ coverage.push({
228
+ requirementId: requirement.id,
229
+ status: "covered",
230
+ source: "certified_block",
231
+ tileId,
232
+ sourceId: match.node.nodeId,
233
+ trustState: "certified",
234
+ reasons: match.reasons,
235
+ });
236
+ continue;
237
+ }
238
+ const semanticTile = input.allowSemanticQueries === false
239
+ ? null
240
+ : semanticTileForRequirement(input.kg, requirement, domain, input.snapshotId);
241
+ if (semanticTile) {
242
+ semanticTiles.push(semanticTile);
243
+ coverage.push({
244
+ requirementId: requirement.id,
245
+ status: "covered",
246
+ source: "semantic_query",
247
+ tileId: semanticTile.id,
248
+ sourceId: semanticTile.semantic?.id,
249
+ trustState: "review_required",
250
+ reasons: ["No fully compatible certified block; covered by a governed semantic query."],
251
+ });
252
+ }
253
+ else {
254
+ coverage.push({
255
+ requirementId: requirement.id,
256
+ status: "gap",
257
+ source: "gap",
258
+ trustState: "draft_ready",
259
+ reasons: match.reasons.length ? match.reasons : ["No compatible certified block or reviewed semantic definition was found."],
260
+ });
261
+ }
262
+ }
263
+ // Keep compatible certified context only. For legacy unshaped prompts, retain
264
+ // the best certified candidates so existing simple App generation stays useful.
265
+ // Requirement coverage is strict, while explicitly discovered certified
266
+ // context may still appear as supporting evidence tiles. It never closes a
267
+ // requirement unless the compatibility check above succeeds.
268
+ // Compatible blocks close requirements; other strongly related certified
269
+ // blocks may still appear as supporting context, but never change coverage.
270
+ const certifiedTiles = candidateCertifiedTiles;
271
+ const dashboardTiles = [...certifiedTiles, ...semanticTiles];
58
272
  const scopedReports = inferScopedReports(prompt, domain, analysisIntent, certifiedNodes);
59
273
  const certifiedContextSummary = summarizeCertifiedContext([
60
274
  ...certifiedNodes,
@@ -76,7 +290,18 @@ export function planAppFromPrompt(input) {
76
290
  filterLabels: filters.map((filter) => filter.label),
77
291
  });
78
292
  return {
79
- version: 1,
293
+ version: 2,
294
+ mode: input.mode ?? "stakeholder",
295
+ ...(input.snapshotId ? { snapshotId: input.snapshotId } : {}),
296
+ requirements,
297
+ requirementCoverage: coverage,
298
+ storyEvidencePlan: {
299
+ version: 1,
300
+ goal: prompt,
301
+ audience,
302
+ eligibleTileIds: dashboardTiles.map((tile) => tile.id),
303
+ driverTileIds: dashboardTiles.filter((tile) => tile.display?.role === "breakdown" || tile.requirementIds?.some((id) => id.startsWith("by-"))).map((tile) => tile.id),
304
+ },
80
305
  appId,
81
306
  name: appName,
82
307
  prompt,
@@ -139,7 +364,7 @@ export function planAppFromPrompt(input) {
139
364
  title: inferDashboardTitle(prompt, domain, appName),
140
365
  description: `Certified app surface for ${audience}. Draft gaps stay in reports until reviewed.`,
141
366
  filters,
142
- tiles: certifiedTiles,
367
+ tiles: dashboardTiles,
143
368
  },
144
369
  ],
145
370
  caveats: [
@@ -154,9 +379,9 @@ export function planAppFromPrompt(input) {
154
379
  ],
155
380
  coverage: {
156
381
  certifiedTiles: certifiedTiles.length,
157
- gaps: scopedReports.length,
158
- ratio: certifiedTiles.length + scopedReports.length > 0
159
- ? certifiedTiles.length / (certifiedTiles.length + scopedReports.length)
382
+ gaps: coverage.filter((item) => item.status === "gap").length,
383
+ ratio: coverage.length > 0
384
+ ? coverage.filter((item) => item.status === "covered").length / coverage.length
160
385
  : 0,
161
386
  },
162
387
  };
@@ -165,7 +390,7 @@ export function validateAppPlan(plan, kg) {
165
390
  const issues = [];
166
391
  let certifiedTiles = 0;
167
392
  let draftTiles = 0;
168
- if (plan.version !== 1)
393
+ if (plan.version !== 2)
169
394
  issues.push(error("version", "unsupported app plan version"));
170
395
  if (!plan.appId || !/^[a-z0-9][a-z0-9_-]*$/i.test(plan.appId)) {
171
396
  issues.push(error("appId", "appId must be folder-safe"));
@@ -207,6 +432,23 @@ export function validateAppPlan(plan, kg) {
207
432
  issues.push(error(path, "certified block tiles must be visibly certified"));
208
433
  }
209
434
  }
435
+ else if (tile.kind === "semantic_query") {
436
+ draftTiles += 1;
437
+ if (!tile.semantic) {
438
+ issues.push(error(`${path}.semantic`, "semantic query tile requires a canonical semantic reference"));
439
+ }
440
+ else {
441
+ if (tile.semantic.metrics.length === 0)
442
+ issues.push(error(`${path}.semantic.metrics`, "semantic query requires a metric"));
443
+ if (tile.semantic.semanticModelRefs.length === 0)
444
+ issues.push(error(`${path}.semantic.semanticModelRefs`, "semantic query requires reviewed model references"));
445
+ if (!tile.semantic.definitionFingerprint)
446
+ issues.push(error(`${path}.semantic.definitionFingerprint`, "semantic query requires a definition fingerprint"));
447
+ }
448
+ if (tile.certification === "certified" || tile.reviewStatus === "certified") {
449
+ issues.push(error(path, "semantic query tiles cannot be presented as certified blocks"));
450
+ }
451
+ }
210
452
  else {
211
453
  draftTiles += 1;
212
454
  issues.push(error(path, "stakeholder dashboard tiles must be certified blocks; route generated story, trust, and analysis work through scoped reports or Copilot investigations"));
@@ -235,11 +477,20 @@ export function generateAppFromPlan(projectRoot, plan, kg, options = {}) {
235
477
  if (errors.length > 0) {
236
478
  throw new Error(`AppPlan is invalid: ${errors.map((issue) => `${issue.path}: ${issue.message}`).join("; ")}`);
237
479
  }
238
- const appDir = resolveAppPackageDir(projectRoot, plan.domain, plan.appId);
480
+ const appDir = resolveAppPackageDir(projectRoot, plan.appId);
239
481
  if (existsSync(appDir) && !options.overwrite) {
240
482
  throw new Error(`App already exists: ${relative(projectRoot, appDir)}`);
241
483
  }
242
484
  const dashboardId = plan.pages[0]?.id || "overview";
485
+ const sourceNodes = plan.pages.flatMap((page) => page.tiles)
486
+ .flatMap((tile) => tile.sourceEvidence ?? [])
487
+ .map((evidence) => evidence.nodeId ? kg.getNode(evidence.nodeId) : undefined)
488
+ .filter((node) => Boolean(node));
489
+ const usedDomains = Array.from(new Set([plan.domain, ...sourceNodes.map((node) => node.domain).filter((domain) => Boolean(domain))]));
490
+ const requiredExports = Array.from(new Set(sourceNodes.flatMap((node) => {
491
+ const value = node.payload?.requiredExports;
492
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
493
+ })));
243
494
  const app = {
244
495
  version: 1,
245
496
  id: plan.appId,
@@ -253,7 +504,10 @@ export function generateAppFromPlan(projectRoot, plan, kg, options = {}) {
253
504
  "Certified tiles must reference existing certified blocks.",
254
505
  ],
255
506
  caveats: plan.caveats,
256
- visibility: "shared",
507
+ visibility: plan.mode === "personal" ? "private" : "shared",
508
+ ownerDomain: plan.domain,
509
+ usesDomains: usedDomains,
510
+ requiredExports,
257
511
  domain: plan.domain,
258
512
  audience: plan.audience,
259
513
  lifecycle: plan.lifecycle,
@@ -311,16 +565,16 @@ export function generateAppFromPlan(projectRoot, plan, kg, options = {}) {
311
565
  const dashboards = plan.pages.map((page, pageIndex) => ({
312
566
  version: 1,
313
567
  id: page.id,
314
- // Story layout on the primary page when a narration was supplied.
315
568
  ...(options.narration && pageIndex === 0
316
569
  ? { sections: buildStorySections(page.tiles, options.narration) }
317
570
  : {}),
571
+ ...(pageIndex === 0 ? { story: plan.storyEvidencePlan } : {}),
318
572
  metadata: {
319
573
  title: page.title,
320
574
  description: page.description ?? plan.planning.displayStrategy,
321
575
  domain: plan.domain,
322
576
  audience: plan.audience,
323
- visibility: "shared",
577
+ visibility: plan.mode === "personal" ? "private" : "shared",
324
578
  lifecycle: "draft",
325
579
  tags: plan.tags,
326
580
  businessOutcome: plan.planning.normalizedGoal,
@@ -990,16 +1244,26 @@ function filterBindingsForCertifiedNode(node, globalFilters) {
990
1244
  return uniqueTileFilterBindings(bindings);
991
1245
  }
992
1246
  function parameterBindingsForCertifiedNode(node, filterBindings) {
993
- const dynamicParams = (node.parameterPolicy ?? [])
994
- .filter((entry) => entry.policy === "dynamic")
995
- .map((entry) => entry.name);
1247
+ const definitions = new Map((node.parameters ?? []).map((entry) => [entry.name, entry]));
1248
+ const runtimePolicies = new Set(["dynamic", "optional"]);
1249
+ const dynamicParams = Array.from(new Set([
1250
+ ...(node.parameters ?? []).filter((entry) => runtimePolicies.has(entry.policy)).map((entry) => entry.name),
1251
+ ...(node.parameterPolicy ?? []).filter((entry) => runtimePolicies.has(entry.policy)).map((entry) => entry.name),
1252
+ ]));
996
1253
  return dynamicParams.map((param) => {
997
1254
  const filterBinding = filterBindings.find((entry) => entry.paramNames?.includes(param) || entry.filter === param);
1255
+ const definition = definitions.get(param);
998
1256
  return {
999
1257
  param,
1000
1258
  source: filterBinding ? "dashboard_filter" : "variable",
1001
1259
  ...(filterBinding?.filter ? { filter: filterBinding.filter } : {}),
1002
1260
  field: filterBinding?.binding ?? param,
1261
+ ...(definition ? {
1262
+ parameterType: definition.type,
1263
+ required: definition.required,
1264
+ ...(definition.default !== undefined ? { default: definition.default } : {}),
1265
+ policy: definition.policy,
1266
+ } : {}),
1003
1267
  };
1004
1268
  });
1005
1269
  }
@@ -1155,9 +1419,12 @@ function buildLayoutItems(tiles) {
1155
1419
  trustState: tile.trustState ?? tile.display?.trustState ?? (tile.certification === "certified" ? "certified" : "draft_ready"),
1156
1420
  reviewStatus: tile.reviewStatus,
1157
1421
  };
1158
- if (isDashboardTile(tile)) {
1422
+ if (tile.kind === "certified_block" && tile.blockId) {
1159
1423
  item.block = { blockId: tile.blockId };
1160
1424
  }
1425
+ else if (tile.kind === "semantic_query" && tile.semantic) {
1426
+ item.semantic = tile.semantic;
1427
+ }
1161
1428
  else {
1162
1429
  item.text = { markdown: markdownForGeneratedPlanTile(tile) };
1163
1430
  }
@@ -1187,7 +1454,8 @@ function markdownForGeneratedPlanTile(tile) {
1187
1454
  ].filter(Boolean).join("\n");
1188
1455
  }
1189
1456
  function isDashboardTile(tile) {
1190
- return tile.kind === "certified_block" && tile.certification === "certified" && Boolean(tile.blockId);
1457
+ return (tile.kind === "certified_block" && tile.certification === "certified" && Boolean(tile.blockId))
1458
+ || (tile.kind === "semantic_query" && Boolean(tile.semantic));
1191
1459
  }
1192
1460
  function appPlanReadme(plan, validation) {
1193
1461
  const reviewDashboardItems = plan.pages.flatMap((page) => page.tiles
@@ -1688,12 +1956,7 @@ function slugify(input) {
1688
1956
  .replace(/^-+|-+$/g, "")
1689
1957
  .replace(/-{2,}/g, "-");
1690
1958
  }
1691
- function resolveAppPackageDir(projectRoot, domain, appId) {
1692
- const domainSlug = slugify(domain);
1693
- const domainDir = domainSlug ? join(projectRoot, "domains", domainSlug) : "";
1694
- if (domainDir && existsSync(domainDir)) {
1695
- return join(domainDir, "apps", appId);
1696
- }
1959
+ function resolveAppPackageDir(projectRoot, appId) {
1697
1960
  return join(projectRoot, "apps", appId);
1698
1961
  }
1699
1962
  function normalizeToken(input) {