From b3ac6d788f0af77041cff6b0db3b15a6aea77284 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Sirois Date: Wed, 25 Mar 2026 13:55:16 +0400 Subject: [PATCH] feat(ci): include explain plans in site API payload Thread baseExplainPlan and optimizedExplainPlan through the CI payload so the site can render explain plan visualizations for CI queries. - Add explainPlan/optimizedExplainPlan to CiOptimization type - Map explain plans from QueryProcessResult in all result states - Add explainPlan to no_improvement QueryProcessResult variant Companion to query-doctor-monorepo PR that adds the explain plan renderer to the CI query detail panel. Closes query-doctor/query-doctor-monorepo#2586 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/reporters/github/github.test.ts | 8 ++++---- src/reporters/site-api.ts | 9 +++++++++ src/runner.ts | 6 ++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/reporters/github/github.test.ts b/src/reporters/github/github.test.ts index 36fa2a7..af82541 100644 --- a/src/reporters/github/github.test.ts +++ b/src/reporters/github/github.test.ts @@ -147,7 +147,7 @@ describe("buildViewModel", () => { hash: "new-query-1", query: "SELECT 1", formattedQuery: "SELECT 1", - optimization: { state: "no_improvement_found", cost: 10, indexesUsed: [] }, + nudges: [], optimization: { state: "no_improvement_found", cost: 10, indexesUsed: [] }, }, ], }), @@ -236,13 +236,13 @@ describe("buildViewModel", () => { hash: "new-1", query: "SELECT 1", formattedQuery: "SELECT 1", - optimization: { state: "no_improvement_found", cost: 10, indexesUsed: [] }, + nudges: [], optimization: { state: "no_improvement_found", cost: 10, indexesUsed: [] }, }, { hash: "new-2", query: "SELECT 2", formattedQuery: "SELECT 2", - optimization: { state: "no_improvement_found", cost: 10, indexesUsed: [] }, + nudges: [], optimization: { state: "no_improvement_found", cost: 10, indexesUsed: [] }, }, ], }), @@ -269,7 +269,7 @@ describe("buildViewModel", () => { hash: "new-1", query: "SELECT 1", formattedQuery: "SELECT 1", - optimization: { state: "no_improvement_found", cost: 10, indexesUsed: [] }, + nudges: [], optimization: { state: "no_improvement_found", cost: 10, indexesUsed: [] }, }, ], }), diff --git a/src/reporters/site-api.ts b/src/reporters/site-api.ts index 4767140..59cae0c 100644 --- a/src/reporters/site-api.ts +++ b/src/reporters/site-api.ts @@ -29,11 +29,14 @@ export type CiOptimization = costReductionPercentage: number; indexRecommendations: CiIndexRecommendation[]; indexesUsed: string[]; + explainPlan?: object; + optimizedExplainPlan?: object; } | { state: "no_improvement_found"; cost: number; indexesUsed: string[]; + explainPlan?: object; } | { state: "error"; @@ -129,6 +132,8 @@ function mapResultToQuery(result: QueryProcessResult): CiQueryPayload | null { : 0, indexRecommendations: result.indexRecommendations.map(mapIndexRecommendation), indexesUsed: result.recommendation.existingIndexes, + explainPlan: result.recommendation.baseExplainPlan, + optimizedExplainPlan: result.recommendation.explainPlan, }, }; @@ -142,6 +147,7 @@ function mapResultToQuery(result: QueryProcessResult): CiQueryPayload | null { state: "no_improvement_found", cost: result.cost, indexesUsed: result.existingIndexes, + explainPlan: result.explainPlan, }, }; @@ -155,6 +161,7 @@ function mapResultToQuery(result: QueryProcessResult): CiQueryPayload | null { state: "no_improvement_found", cost: 0, indexesUsed: [], + explainPlan: result.explainPlan, }, }; @@ -181,11 +188,13 @@ function mapResultToQuery(result: QueryProcessResult): CiQueryPayload | null { state: "no_improvement_found", cost: result.warning.baseCost, indexesUsed: result.warning.optimization.existingIndexes, + explainPlan: result.warning.explainPlan, } : { state: "no_improvement_found", cost: result.warning.baseCost, indexesUsed: [], + explainPlan: result.warning.explainPlan, }, }; diff --git a/src/runner.ts b/src/runner.ts index 26d3a1f..b8d20fc 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -337,6 +337,7 @@ export class Runner { formattedQuery, cost: out.baseCost, existingIndexes: existingIndexesForQuery, + nudges, explainPlan: out.baseExplainPlan, }; } this.queryStats.optimized++; @@ -398,6 +399,7 @@ export class Runner { cost: out.baseCost, existingIndexes: existingIndexesForQuery, nudges, + explainPlan: out.baseExplainPlan, }; } } else if (out.kind === "zero_cost_plan") { @@ -482,8 +484,8 @@ export type QueryProcessResult = cost: number; existingIndexes: string[]; nudges: Nudge[]; - } - | { + explainPlan?: object; + } | { kind: "error"; error: Error; fingerprint: string;