Skip to content

feat(dashboards): chart Google Cloud metrics live with PromQL - #1480

Closed
JeremyFunk wants to merge 38 commits into
feat/gcp-live-query-accessfrom
feat/gcp-promql-widget
Closed

JeremyFunk wants to merge 38 commits into
feat/gcp-live-query-accessfrom
feat/gcp-promql-widget

Conversation

@JeremyFunk

@JeremyFunk JeremyFunk commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #1477 (feat/gcp-live-query-access), which sits on #1475 (feat/gcp-promql-query). #1478 (MCP tools) sits on this branch. Merge the stack bottom-up.

What

Dashboard widgets can chart a PromQL query over the organization's Google Cloud Monitoring metrics. The query is read live from Google through #1475's GcpPromQlService; nothing is ingested.

  • Data source kind promql (packages/widgets/src/dashboard/v3/data-source.ts): { kind: "promql", query, projects?, legend?, transform? }. The public v2 dashboards API mirrors it (V2PromQlDataSource, query capped at 4,000 characters like the read).
  • Request path: toWidgetRequest lowers the source to endpoint gcp_promql with params { promql, projects?, legend? }. planWidgetRequest then fills the window and substitutes dashboard variables. A tile with no query stays at promql_query_empty: it renders "No query yet" and sends no request. The browser's server function (apps/web/src/api/warehouse/gcp-promql.ts, loaded on first use) and the share resolver (DashboardWidgetDataService) share the same pure helpers in packages/query-engine/src/gcp-promql.ts. They build the range request with step = the chart bucket for the window, never under 60 s, and turn the answer into chart rows.
  • Chart rows: one row per timestamp, { bucket, [series]: value }. A series is named by the legend template ({{label}}). Without a legend it is named by its labels, minus __name__, and minus project_id when a single project answered. A missing point leaves its key out of that row.
  • Variables: substituted plainly into the query. All becomes a regex alternation (a|b) for a =~ matcher.
  • Editor: a Google Cloud source tab for line, area, bar, stat and gauge panels. In this mode the type picker offers only those types.
    • Metric search uses gcpMetricSearch. Picking a metric replaces the query with a starter query from gcpMetricDescribe: a rate for DELTA/CUMULATIVE, avg for GAUGE, histogram_quantile(0.95, …_bucket) for distributions. It uses the UTF-8 selector form and adds a monitored_resource matcher when the metric has several resource types.
    • Projects and legend fields, and Run preview, as with raw SQL, since Google bills every read. Edits made after the last run are marked as such.
    • Stat and gauge tiles reduce the series by the rail's Aggregate.
  • States:
    • Google's refusals (invalid query, more than 500 series) show on the tile and in the editor.
    • A partial answer adds a one-line notice under the chart (notice on the ready state).
    • Not connected, no Google identity on the deployment, and more than 20 projects show the muted setup state with the next step.
  • Shared boards:
    • The share resolver runs only the stored query. The viewer can change the window and the variable values, nothing else.
    • A query variable that a PromQL tile references must now also pass the free-text charset, including every option of an All. Its options are telemetry values, and PromQL takes them unescaped.
    • redactForShare drops query, projects and legend.
    • Google's error text stays off the share, and so does any project id: the partial-answer notice only counts the failed scopes.
  • Docs: a Live queries section in integrations/gcp.md, and the new source tab in dashboards/build-dashboards.md.

Decisions taken without the owner

  • Projects is a text field (comma- or space-separated ids), not a multi-select. No endpoint lists the projects the connections reach, and the brief said to keep it simple. Empty = every project, up to 20.
  • Step = the chart policy bucket (computeBucketSecondsForRange, ~100 points), floored at 60 s. It does not follow the tile-width model, so the step does not change with tile width and Google's edge cache sees fewer distinct keys.
  • The stat/gauge "Value field" select is hidden outside the query builder. It lists query-builder series, and neither raw SQL nor PromQL read it. For raw SQL it already had no effect.
  • Share-side GCP setup errors (not connected, too many projects) still reach the viewer as the generic retryable "couldn't be loaded" and are logged at error level, like every other share failure.
  • Retries unchanged: a widget fetch is still retried twice. Google does not bill a refused query.
  • No MCP changes here: describe_dashboard_schema takes its kinds from WIDGET_DATA_SOURCE_KINDS, so it already lists promql. feat(mcp): query Google Cloud metrics live with PromQL #1478 has the tools.

Verify

Scoped vitest only; CI runs the rest:

  • packages/widgets: src/dashboard/promql-source.test.ts, plus the package's typecheck
  • packages/domain: src/http/v2/dashboard-widget-parity.test.ts
  • packages/query-engine: src/gcp-promql.test.ts, src/widget-request.test.ts, src/dashboard-variables/interpolate.test.ts
  • packages/backend: DashboardWidgetDataService.test.ts, share-variables.test.ts, widget-data-parity.test.ts
  • apps/web: data-source-registry.test.ts, share-support.test.ts, use-widget-data.test.ts, dashboard-summary.test.ts
  • bun run ios:openapi:check: up to date

Not verified without a live system; this needs a visual pass at the top of the stack:

  • Editor: the Google Cloud tab on a line widget, metric search (including its searching and failure states), a starter query written on pick, Run preview drawing the chart, and the stale-edit marker.
  • Tile states:
    • invalid query: red, Google's message, Details
    • more than 500 series
    • not connected, unavailable, or more than 20 projects: muted, with the next step
    • no query yet
    • partial-answer notice under the chart
    • empty window
  • Stat and gauge tiles from a PromQL query.
  • A shared board and a single-chart share with a PromQL tile, including a dashboard variable set to All.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

A widget data source kind `promql` stores a PromQL query, optional projects and a legend template. The browser and the share API both run it through GcpPromQlService as a range query with the chart's bucket as step, and draw the answer as wide chart rows. The editor gets a Google Cloud source tab with a metric search that writes a starter query.
Share links hold a query variable a PromQL tile reads to the free-text charset and count failed scopes instead of naming them. Variables reach PromQL under a `promql` param key, with All as a regex alternation. Browser and share path share the request and row helpers; a request the author can fix is a fix_request error. The editor marks a stale preview, shows search progress and failures, and reduces stat and gauge tiles by the rail's Aggregate.
@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 74bf83cb-34ea-49aa-bf75-c16ca922249e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 4/10 · risky as written
The share charset guard misses sparkline PromQL sources and two stored shapes fail at view time; the edge cases behind both were read in the code, not assumed.
quality 70/100 · 3 warnings · tests partial · risk medium · 2/2 new units observable

Adds a promql widget data source that charts the org's Google Cloud metrics live through #1475/#1477, with an editor tab, share redaction and variable handling. The shape is sound but the share's variable guard misses sparkline queries, and two stored shapes (empty projects, a stat reduction) fail or misreport at view time. The open WarehouseInvalidInputError issue in warehouse-query-atoms.ts is not addressed: the new atom's failures come from runWarehouseQuery's WarehouseApiError | BackendError channel, not from the query-set clients that raise it.

Findings

🟠 Warning · F1 · promQlQueries skips sparkline sources, so a variable can escape the guard

security · apps/api/src/routes/v2/share.http.ts:305-313

The share executes widget.display?.sparkline?.dataSource when the request asks for source: "sparkline" (DashboardWidgetDataService.ts:267, and the source is whatever the unauthenticated caller sent at share.http.ts:349), but this list walks widget.dataSource alone, so a variable referenced only by a sparkline's PromQL never enters promQlNames and promQlUnsafe stays false (share-variables.ts:150). The viewer's value then needs only to be one of the options the warehouse listed, and a telemetry value containing a quote reaches the promql branch of interpolateString unescaped — plainly, or as every option of an All joined with | — rewriting the author's matcher on a public link. Collect both data sources here.

Build the list from `widget.dataSource` and `widget.display?.sparkline?.dataSource` alike before mapping out the query strings, and add a sparkline-only reference to the `share-variables.test.ts` case.
🟠 Warning · F2 · dataSourcePromQl forwards an empty projects list every read rejects

correctness · packages/widgets/src/dashboard/access.ts:187-189

projects: [] is the documented "every project" form and V2PromQlDataSource stores it, but PromQlProjects requires at least one id (packages/domain/src/http/gcp-promql.ts:31), so promQlRangeInput (packages/query-engine/src/gcp-promql.ts:28) hands the empty array to GcpPromQlRangeRequest and the decode fails: the tile shows a request error on every view, signed in and on a share, instead of reading all connected projects. The editor already omits the field when nothing is typed (widget-query-builder-page.tsx:177).

export const dataSourcePromQl = (dataSource: unknown): PromQlDataSource | null => {
	if (!isRecord(dataSource) || dataSource.kind !== "promql") return null
	// An empty list means "every project", but `PromQlProjects` wants at least one id.
	const projects = Array.isArray(dataSource.projects) ? dataSource.projects.filter(isString) : []
	return {
		query: isString(dataSource.query) ? dataSource.query : "",
		...(projects.length > 0 ? { projects } : undefined),
		...(isString(dataSource.legend) ? { legend: dataSource.legend } : undefined),
	}
}
🟠 Warning · F3 · Stat/gauge PromQL tiles reduce a value field the rows never carry

correctness · apps/web/src/components/dashboard-builder/config/widget-query-builder-page.tsx:172

promQlChartRows emits wide rows { bucket, [series]: value } (packages/query-engine/src/gcp-promql.ts:68), so reduceToValue.field: "value" never matches and applyTransform falls back to the first numeric column of rows[0] (use-widget-data.ts:206): a stat over a query with several series sums, averages, mins or maxes one arbitrary series, and count returns the number of time buckets rather than the series count. Reduce the row's value columns for a PromQL source instead of naming a value field.

Emit a `value` column from `promQlChartRows` (or reshape with a `flattenSeries`-style transform) so the rail's Aggregate applies to every series rather than the first one.
🤖 Prompt to fix all 3 findings with an AI agent
Findings from an automated review of commit 7c45912f90649a16a32a4a4b19b941697791c54c. Verify each one against the current code before changing anything, fix only those that still apply, and keep each fix to the lines it names.

---

F1 · Warning · security · apps/api/src/routes/v2/share.http.ts:305-313
`promQlQueries` skips sparkline sources, so a variable can escape the guard
The share executes `widget.display?.sparkline?.dataSource` when the request asks for `source: "sparkline"` (`DashboardWidgetDataService.ts:267`, and the source is whatever the unauthenticated caller sent at `share.http.ts:349`), but this list walks `widget.dataSource` alone, so a variable referenced only by a sparkline's PromQL never enters `promQlNames` and `promQlUnsafe` stays false (`share-variables.ts:150`). The viewer's value then needs only to be one of the options the warehouse listed, and a telemetry value containing a quote reaches the `promql` branch of `interpolateString` unescaped — plainly, or as every option of an All joined with `|` — rewriting the author's matcher on a public link. Collect both data sources here.
Suggested fix: Build the list from `widget.dataSource` and `widget.display?.sparkline?.dataSource` alike before mapping out the query strings, and add a sparkline-only reference to the `share-variables.test.ts` case.

---

F2 · Warning · correctness · packages/widgets/src/dashboard/access.ts:187-189
`dataSourcePromQl` forwards an empty `projects` list every read rejects
`projects: []` is the documented "every project" form and `V2PromQlDataSource` stores it, but `PromQlProjects` requires at least one id (`packages/domain/src/http/gcp-promql.ts:31`), so `promQlRangeInput` (`packages/query-engine/src/gcp-promql.ts:28`) hands the empty array to `GcpPromQlRangeRequest` and the decode fails: the tile shows a request error on every view, signed in and on a share, instead of reading all connected projects. The editor already omits the field when nothing is typed (`widget-query-builder-page.tsx:177`).
Replace those lines with:
export const dataSourcePromQl = (dataSource: unknown): PromQlDataSource | null => {
	if (!isRecord(dataSource) || dataSource.kind !== "promql") return null
	// An empty list means "every project", but `PromQlProjects` wants at least one id.
	const projects = Array.isArray(dataSource.projects) ? dataSource.projects.filter(isString) : []
	return {
		query: isString(dataSource.query) ? dataSource.query : "",
		...(projects.length > 0 ? { projects } : undefined),
		...(isString(dataSource.legend) ? { legend: dataSource.legend } : undefined),
	}
}

---

F3 · Warning · correctness · apps/web/src/components/dashboard-builder/config/widget-query-builder-page.tsx:172
Stat/gauge PromQL tiles reduce a `value` field the rows never carry
`promQlChartRows` emits wide rows `{ bucket, [series]: value }` (`packages/query-engine/src/gcp-promql.ts:68`), so `reduceToValue.field: "value"` never matches and `applyTransform` falls back to the first numeric column of `rows[0]` (`use-widget-data.ts:206`): a stat over a query with several series sums, averages, mins or maxes one arbitrary series, and `count` returns the number of time buckets rather than the series count. Reduce the row's value columns for a PromQL source instead of naming a `value` field.
Suggested fix: Emit a `value` column from `promQlChartRows` (or reshape with a `flattenSeries`-style transform) so the rail's Aggregate applies to every series rather than the first one.

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 3 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 3 DashboardWidgetDataService.resolve 3/day, 23.8% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
getGcpPromQlChart / metric search / describe server functions (apps/web) server function + outbound read yes runWarehouseQuery wraps each in Effect.withSpan(operation) (apps/web/src/api/warehouse/effect-utils.ts:236)
Share PromQL tile execution (apps/api share widgetData) inbound endpoint + outbound Google read yes resolve annotates maple.widget.* (DashboardWidgetDataService.ts:249); GcpPromQlService.queryRange annotates orgId (GcpPromQlService.ts:400)

7c45912 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 inline notes from Maple's review. The score and summary are in the review comment above.

})
})

const promQlQueries = document.widgets.flatMap((widget) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

promQlQueries skips sparkline sources, so a variable can escape the guard

F1 · Warning · security

The share executes widget.display?.sparkline?.dataSource when the request asks for source: "sparkline" (DashboardWidgetDataService.ts:267, and the source is whatever the unauthenticated caller sent at share.http.ts:349), but this list walks widget.dataSource alone, so a variable referenced only by a sparkline's PromQL never enters promQlNames and promQlUnsafe stays false (share-variables.ts:150). The viewer's value then needs only to be one of the options the warehouse listed, and a telemetry value containing a quote reaches the promql branch of interpolateString unescaped — plainly, or as every option of an All joined with | — rewriting the author's matcher on a public link. Collect both data sources here.

Build the list from `widget.dataSource` and `widget.display?.sparkline?.dataSource` alike before mapping out the query strings, and add a sparkline-only reference to the `share-variables.test.ts` case.
🤖 Prompt to fix with an AI agent
In `apps/api/src/routes/v2/share.http.ts:305-313`: `promQlQueries` skips sparkline sources, so a variable can escape the guard.

The share executes `widget.display?.sparkline?.dataSource` when the request asks for `source: "sparkline"` (`DashboardWidgetDataService.ts:267`, and the source is whatever the unauthenticated caller sent at `share.http.ts:349`), but this list walks `widget.dataSource` alone, so a variable referenced only by a sparkline's PromQL never enters `promQlNames` and `promQlUnsafe` stays false (`share-variables.ts:150`). The viewer's value then needs only to be one of the options the warehouse listed, and a telemetry value containing a quote reaches the `promql` branch of `interpolateString` unescaped — plainly, or as every option of an All joined with `|` — rewriting the author's matcher on a public link. Collect both data sources here.

Suggested fix: Build the list from `widget.dataSource` and `widget.display?.sparkline?.dataSource` alike before mapping out the query strings, and add a sparkline-only reference to the `share-variables.test.ts` case.

Verify the problem exists at that location before changing it, and keep the fix to those lines.

Comment on lines +187 to +189
...(Array.isArray(dataSource.projects)
? { projects: dataSource.projects.filter(isString) }
: undefined),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

dataSourcePromQl forwards an empty projects list every read rejects

F2 · Warning · correctness

projects: [] is the documented "every project" form and V2PromQlDataSource stores it, but PromQlProjects requires at least one id (packages/domain/src/http/gcp-promql.ts:31), so promQlRangeInput (packages/query-engine/src/gcp-promql.ts:28) hands the empty array to GcpPromQlRangeRequest and the decode fails: the tile shows a request error on every view, signed in and on a share, instead of reading all connected projects. The editor already omits the field when nothing is typed (widget-query-builder-page.tsx:177).

Suggested change
...(Array.isArray(dataSource.projects)
? { projects: dataSource.projects.filter(isString) }
: undefined),
export const dataSourcePromQl = (dataSource: unknown): PromQlDataSource | null => {
if (!isRecord(dataSource) || dataSource.kind !== "promql") return null
// An empty list means "every project", but `PromQlProjects` wants at least one id.
const projects = Array.isArray(dataSource.projects) ? dataSource.projects.filter(isString) : []
return {
query: isString(dataSource.query) ? dataSource.query : "",
...(projects.length > 0 ? { projects } : undefined),
...(isString(dataSource.legend) ? { legend: dataSource.legend } : undefined),
}
}
🤖 Prompt to fix with an AI agent
In `packages/widgets/src/dashboard/access.ts:187-189`: `dataSourcePromQl` forwards an empty `projects` list every read rejects.

`projects: []` is the documented "every project" form and `V2PromQlDataSource` stores it, but `PromQlProjects` requires at least one id (`packages/domain/src/http/gcp-promql.ts:31`), so `promQlRangeInput` (`packages/query-engine/src/gcp-promql.ts:28`) hands the empty array to `GcpPromQlRangeRequest` and the decode fails: the tile shows a request error on every view, signed in and on a share, instead of reading all connected projects. The editor already omits the field when nothing is typed (`widget-query-builder-page.tsx:177`).

Replace those lines with:

export const dataSourcePromQl = (dataSource: unknown): PromQlDataSource | null => {
	if (!isRecord(dataSource) || dataSource.kind !== "promql") return null
	// An empty list means "every project", but `PromQlProjects` wants at least one id.
	const projects = Array.isArray(dataSource.projects) ? dataSource.projects.filter(isString) : []
	return {
		query: isString(dataSource.query) ? dataSource.query : "",
		...(projects.length > 0 ? { projects } : undefined),
		...(isString(dataSource.legend) ? { legend: dataSource.legend } : undefined),
	}
}

Verify the problem exists at that location before changing it, and keep the fix to those lines.

const existingTransform = dataSourceTransform(widget.dataSource)
const transform =
widgetTypeByVisualization(state.visualization)?.isScalar === true
? { ...existingTransform, reduceToValue: { field: "value", aggregate: state.statAggregate } }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Stat/gauge PromQL tiles reduce a value field the rows never carry

F3 · Warning · correctness

promQlChartRows emits wide rows { bucket, [series]: value } (packages/query-engine/src/gcp-promql.ts:68), so reduceToValue.field: "value" never matches and applyTransform falls back to the first numeric column of rows[0] (use-widget-data.ts:206): a stat over a query with several series sums, averages, mins or maxes one arbitrary series, and count returns the number of time buckets rather than the series count. Reduce the row's value columns for a PromQL source instead of naming a value field.

Emit a `value` column from `promQlChartRows` (or reshape with a `flattenSeries`-style transform) so the rail's Aggregate applies to every series rather than the first one.
🤖 Prompt to fix with an AI agent
In `apps/web/src/components/dashboard-builder/config/widget-query-builder-page.tsx:172`: Stat/gauge PromQL tiles reduce a `value` field the rows never carry.

`promQlChartRows` emits wide rows `{ bucket, [series]: value }` (`packages/query-engine/src/gcp-promql.ts:68`), so `reduceToValue.field: "value"` never matches and `applyTransform` falls back to the first numeric column of `rows[0]` (`use-widget-data.ts:206`): a stat over a query with several series sums, averages, mins or maxes one arbitrary series, and `count` returns the number of time buckets rather than the series count. Reduce the row's value columns for a PromQL source instead of naming a `value` field.

Suggested fix: Emit a `value` column from `promQlChartRows` (or reshape with a `flattenSeries`-style transform) so the rail's Aggregate applies to every series rather than the first one.

Verify the problem exists at that location before changing it, and keep the fix to those lines.

Starter queries group by project_id, and default series keys keep metrics_scope, which tells a scope's projects apart behind one answering project.
@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🟡 Confidence 5/10 · needs attention
The hunks since 7c45912 are narrow and additive, pinned by tests, but the three earlier PromQL warnings still hold at this head.
quality 70/100 · 3 warnings · tests covered · risk medium · 3/3 new units observable

Dashboard tiles can chart a live PromQL query over Google Cloud metrics, with a Google Cloud source tab, a share path that runs only the stored query, and starter queries now grouped per project. The three earlier warnings (F1, F2, F3) still hold at this head; the WarehouseInvalidInputError in warehouse-query-atoms.ts is a breakdown-query error this change does not touch.

  • promQlStarterQuery now groups every starter query by project_id, so projects no longer merge
  • New promql widget data source, lowered to the gcp_promql endpoint and redacted on shares
  • Share variables a PromQL tile references are held to the free-text charset
  • Partial Google answers surface a notice under the chart

Before merge

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 3 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 3 DashboardWidgetDataService.resolve 3/day, 23.8% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • Step can never exceed Google's 86 400 s cap: computeBucketSecondsForRange picks from AUTO_BUCKET_LADDER (max 86 400, datetime.ts:750)
  • Legend collisions stay apart via the used set suffixing (2) (gcp-promql.ts:66)
  • Share PromQL reads are spanned: runWarehouseQuery (effect-utils.ts:236) plus GcpPromQlService.queryRange
Observability coverage: 3 of 3 changes observable
Change Kind Observable Evidence
Browser gcp_promql widget read (server function) outbound yes runWarehouseQuery("gcpPromQlQueryRange", …) wraps it in a span (apps/web/src/api/warehouse/effect-utils.ts:236)
Share resolution of a PromQL tile outbound yes DashboardWidgetDataService.resolve is an Effect.fn span and GcpPromQlService.queryRange annotates it (DashboardWidgetDataService.ts:242, GcpPromQlService.ts:396)
Editor metric search / describe outbound yes getGcpMetricSearch and getGcpMetricDescriptor both go through runWarehouseQuery (gcp-promql.ts:49, 61)

3372d50 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 4/10 · risky as written
The share and interpolation paths are unit-tested and the new endpoint is traced; the All-alternation splice is the one unescaped edge left.
quality 60/100 · 4 warnings · tests covered · risk medium · 3/3 new units observable

Adds a promql dashboard source that charts live Google Cloud Monitoring reads in the tile, the editor and shared boards, with share variables held to the free-text charset. One defect: an All selection splices an unescaped alternation into the matcher. The open WarehouseInvalidInputError in warehouse-query-atoms.ts is not addressed by this diff.

  • promql data source lowers to endpoint gcp_promql and renders wide chart rows
  • The share resolver runs the stored query through GcpPromQlService, redacting query, projects and legend
  • A Google Cloud source tab with metric search, starter queries and Run preview
  • All of a query variable becomes a | alternation inside a PromQL matcher

Findings

🟠 Warning · F4 · An All selection splices a bare a|b alternation into the matcher

correctness · packages/query-engine/src/dashboard-variables/interpolate.ts:106

variable.options.join("|") is substituted at the reference's position, so a matcher that is more than the bare variable describes the wrong set: with options ["dev","prod"], {env=~"api-$env"} becomes api-dev|prod, which (PromQL anchors =~ over the whole expression) also matches a series named prod. An option holding a regex metacharacter — a hostname or a project id with a ., a value with + — matches values it does not name, on every member view as well as the share. Group and escape each alternative the way the sql branch escapes its literals, e.g. "(" + options.map(escapePromQlRegex).join("|") + ")".

Add a small PromQL-regex escaper next to `escapeClickHouseString` usage and return `(${variable.options.map(escapePromQlRegex).join("|")})` for an All, so the alternation is grouped at the reference's position and each option is matched literally. Grafana's Prometheus datasource does the same.
🤖 Prompt to fix this finding with an AI agent
Findings from an automated review of commit 24cd5c76d1c61806f1e16e2a1fc5853e774a9772. Verify each one against the current code before changing anything, fix only those that still apply, and keep each fix to the lines it names.

---

F4 · Warning · correctness · packages/query-engine/src/dashboard-variables/interpolate.ts:106
An All selection splices a bare `a|b` alternation into the matcher
`variable.options.join("|")` is substituted at the reference's position, so a matcher that is more than the bare variable describes the wrong set: with options `["dev","prod"]`, `{env=~"api-$env"}` becomes `api-dev|prod`, which (PromQL anchors `=~` over the whole expression) also matches a series named `prod`. An option holding a regex metacharacter — a hostname or a project id with a `.`, a value with `+` — matches values it does not name, on every member view as well as the share. Group and escape each alternative the way the `sql` branch escapes its literals, e.g. `"(" + options.map(escapePromQlRegex).join("|") + ")"`.
Suggested fix: Add a small PromQL-regex escaper next to `escapeClickHouseString` usage and return `(${variable.options.map(escapePromQlRegex).join("|")})` for an All, so the alternation is grouped at the reference's position and each option is matched literally. Grafana's Prometheus datasource does the same.

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 3 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 3 DashboardWidgetDataService.resolve 3/day, 23.8% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • Share redaction drops query, projects and legend (packages/widgets/src/dashboard/redact.ts:90, pinned by promql-source.test.ts:60)
  • Share variable values stay inside TEXTBOX_ALLOWED, All included (packages/backend/src/services/dashboards/share-variables.ts:150)
  • promQlRangeInput trims the query and floors the step at 60 s (packages/query-engine/src/gcp-promql.ts:24)
Observability coverage: 3 of 3 changes observable
Change Kind Observable Evidence
gcp_promql range read from a dashboard tile / share outbound call yes Effect.fn("GcpPromQlService.queryRange") with orgId annotation and maple.gcp.projects (packages/backend/src/services/integrations/GcpPromQlService.ts:495); resolve annotates org/widget/source (DashboardWidgetDataService.ts:249)
browser server function getGcpPromQlChart / getGcpMetricSearch outbound call yes runWarehouseQuery("gcpPromQlQueryRange") wraps the /internal client call (apps/web/src/api/warehouse/gcp-promql.ts:34)
new tile failure path (Google refusal, partial answer) error path yes asWidgetOutcomeFailure(request.widgetId, "promql") logs the cause and maps to a tagged failure (DashboardWidgetDataService.ts:445)

24cd5c7 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 inline note from Maple's review. The score and summary are in the review comment above.

return replaceRefs(input, (name) => {
const variable = values[name]
if (variable === undefined) return null
return variable.isAll ? variable.options.join("|") : variable.value

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

An All selection splices a bare a|b alternation into the matcher

F4 · Warning · correctness

variable.options.join("|") is substituted at the reference's position, so a matcher that is more than the bare variable describes the wrong set: with options ["dev","prod"], {env=~"api-$env"} becomes api-dev|prod, which (PromQL anchors =~ over the whole expression) also matches a series named prod. An option holding a regex metacharacter — a hostname or a project id with a ., a value with + — matches values it does not name, on every member view as well as the share. Group and escape each alternative the way the sql branch escapes its literals, e.g. "(" + options.map(escapePromQlRegex).join("|") + ")".

Add a small PromQL-regex escaper next to `escapeClickHouseString` usage and return `(${variable.options.map(escapePromQlRegex).join("|")})` for an All, so the alternation is grouped at the reference's position and each option is matched literally. Grafana's Prometheus datasource does the same.
🤖 Prompt to fix with an AI agent
In `packages/query-engine/src/dashboard-variables/interpolate.ts:106`: An All selection splices a bare `a|b` alternation into the matcher.

`variable.options.join("|")` is substituted at the reference's position, so a matcher that is more than the bare variable describes the wrong set: with options `["dev","prod"]`, `{env=~"api-$env"}` becomes `api-dev|prod`, which (PromQL anchors `=~` over the whole expression) also matches a series named `prod`. An option holding a regex metacharacter — a hostname or a project id with a `.`, a value with `+` — matches values it does not name, on every member view as well as the share. Group and escape each alternative the way the `sql` branch escapes its literals, e.g. `"(" + options.map(escapePromQlRegex).join("|") + ")"`.

Suggested fix: Add a small PromQL-regex escaper next to `escapeClickHouseString` usage and return `(${variable.options.map(escapePromQlRegex).join("|")})` for an All, so the alternation is grouped at the reference's position and each option is matched literally. Grafana's Prometheus datasource does the same.

Verify the problem exists at that location before changing it, and keep the fix to those lines.

@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
All four earlier warnings are still live at this head, one of them in the share's PromQL variable guard, and none has a failing test.
quality 60/100 · 4 warnings · tests partial · risk high · 3/3 new units observable

Adds a promql data source that charts Google Cloud Monitoring live, through the editor, the share resolver and the v2 API. Mostly additive and sound, but four earlier warnings still stand, including a share-link variable guard that a variable can escape. The open WarehouseInvalidInputError ("No query data found in selected time range") is the pre-existing empty-result state in the warehouse atoms; this diff does not fix it and adds one more way to reach it (an empty PromQL answer).

  • promql sources lower to the gcp_promql endpoint with { promql, projects?, legend? }
  • Share resolver holds query variables used inside PromQL to the free-text charset
  • redactForShare strips a PromQL tile's query, projects and legend
  • Editor gains a Google Cloud tab with metric search and starter queries

Before merge

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 3 DashboardWidgetDataService.resolve 3/day, 11.1% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • A stored projects: [] rides dataSourcePromQl into GcpPromQlRangeRequest, which rejects it (access.ts:187, gcp-promql.ts:45)
  • The share resolver runs the stored, unredacted document while the browser gets the redacted one (share.http.ts:346 vs :421)
  • The new server function and outbound reads carry spans via runWarehouseQuery and GcpPromQlService.queryRange (effect-utils.ts:236, GcpPromQlService.ts:495)
Observability coverage: 3 of 3 changes observable
Change Kind Observable Evidence
getGcpPromQlChart widget server function inbound server function yes runWarehouseQuery("gcpPromQlChart", …) applies Effect.withSpan(operation) (apps/web/src/api/warehouse/effect-utils.ts:236)
PromQL tile read in DashboardWidgetDataService.resolve outbound call yes GcpPromQlService.queryRange is Effect.fn and annotates orgId (packages/backend/src/services/integrations/GcpPromQlService.ts:495)
Metric search and describe from the editor outbound call yes runWarehouseQuery wrappers in apps/web/src/api/warehouse/gcp-promql.ts over GcpPromQlService.searchMetrics / describeMetric

d512231 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
Four earlier warnings (share variable guard, empty projects, stat reducer field, All alternation) are unfixed at this head; the new lowering, redaction and row-shaping code reads correctly.
quality 60/100 · 4 warnings · tests partial · risk high · 2/2 new units observable

Adds a promql widget data source kind that charts live Google Cloud Monitoring PromQL reads, with an editor tab, share redaction and variable interpolation. The new code is sound, but the four earlier findings are unaddressed at this head; the open WarehouseInvalidInputError is not fixed by the diff and the new decode path can still raise it.

  • promql sources lower to endpoint gcp_promql and chart rows live from Google
  • Share redaction drops a promql source's query, projects and legend
  • An All selection is spliced into a PromQL matcher as a|b
  • The editor offers a Google Cloud tab for line, area, bar, stat and gauge

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 3 DashboardWidgetDataService.resolve 3/day, 11.1% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • promQlRangeInput floors the step at 60 s and the read caps it (gcp-promql.ts:24)
  • Only the member notice carries scope and cause; the share notice counts scopes (gcp-promql.ts:95)
  • redactDataSource is an allowlist and drops the promql query, projects and legend (redact.ts:90)
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
PromQL widget read from the editor's server function getGcpPromQlChart client call to the API's PromQL proxy yes runWarehouseQuery("gcpPromQlQueryRange", …) drives a client span (apps/web/src/api/warehouse/gcp-promql.ts:34)
Share resolver reads Google through GcpPromQlService.queryRange client call to Google Cloud Monitoring yes Effect.fn("GcpPromQlService.queryRange") span, pendingReads/PendingScopes annotation (packages/backend/src/services/integrations/GcpPromQlService.ts:100-110)

bc3e84b · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
Four open warnings survive at this head: the share variable guard misses sparkline sources, and an empty projects list breaks every read it is stored on.
quality 60/100 · 4 warnings · tests partial · risk high · 2/2 new units observable

Adds a promql widget data source that charts live Google Cloud metrics, from the editor and from the public v2 API, with the pure row-shaping helpers shared by the browser server function and the share resolver. The four open findings are all still present in this head, so it needs attention before merge. The open @maple/web/errors/WarehouseInvalidInputError issue (message "No query data found in selected time range", last seen 2026-08-11) is not addressed by this diff; it only adds a new call site for genuinely malformed input.

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 3 DashboardWidgetDataService.resolve 3/day, 11.1% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • Every hunk of the 40-file diff was read; the 13 files the kickoff lists as changed since bc3e84b are not in this PR's base..head diff (git diff --stat vs 356069a)
  • promQlQueries still walks widget.dataSource only (share.http.ts:305) while the share executes display.sparkline.dataSource (DashboardWidgetDataService.ts:267) — F1 confirmed open
  • PromQlProjects requires isMinLength(1) (packages/domain/src/http/gcp-promql.ts:45-48) and both decoders accept projects: [], so F2 confirmed open
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
gcp_promql widget data through the browser server function client yes runWarehouseQuery wraps the internal client call with a span (apps/web/src/api/warehouse/effect-utils.ts:230); the function is registered in data-source-registry.ts:77
Share-resolver PromQL read (DashboardWidgetDataService.resolve → GcpPromQlService.queryRange) client yes runs inside the existing DashboardWidgetDataService.resolve span (DashboardWidgetDataService.ts:433)

830d90a · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 3/10 · risky as written
All four earlier findings still reproduce at this head; the new lowering, rows, share guard and service branch are pinned by tests.
quality 60/100 · 4 warnings · tests partial · risk medium · 2/2 new units observable

Adds a live promql dashboard data source: chart rows from Google PromQL, an editor tab with metric search, and the share path. The shared primitives are tested, but the four earlier findings all still reproduce here, so it is not ready as-is. The open WarehouseInvalidInputError in warehouse-query-atoms.ts is untouched by this diff.

  • toWidgetRequest lowers a promql source to endpoint gcp_promql
  • promQlWidgetData turns an answer into one keyed row per timestamp
  • The browser runs it lazily through getGcpPromQlChart, not the warehouse
  • resolveShareVariables holds PromQL-referenced query variables to the free-text charset

Before merge

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 3 DashboardWidgetDataService.resolve 3/day, 11.1% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • Empty projects survives to the request: PromQlProjects requires at least one (packages/domain/src/http/gcp-promql.ts:45)
  • Share failures log the cause and return a generic message, not Google's text (DashboardWidgetDataService.ts:87)
  • Disabled promql_query_empty plans are handled on both hosts (widget-request.ts:168, DashboardWidgetDataService.ts:309)
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
Browser server function getGcpPromQlChart / getGcpMetricSearch / getGcpMetricDescriptor (outbound call to the internal API) client yes each goes through runWarehouseQuery + MapleInternalAtomClient (apps/web/src/api/warehouse/gcp-promql.ts:34), the pattern every other warehouse fn on this page uses
GCP_PROMQL_ENDPOINT branch of the batch/share resolver consumer yes inside Effect.fn("DashboardWidgetDataService.resolve"), which reports live (DashboardWidgetDataService.resolve, p95 11100 ms); failures log via executionFailed (DashboardWidgetDataService.ts:445)

b4105f3 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
Four warnings unresolved on the share and stat paths; the sparkline branch and the base-branch configure/lab files sit outside this PR's diff.
quality 60/100 · 4 warnings · tests partial · risk high · 2/2 new units observable

Adds a promql widget data source that charts Google Cloud metrics live, with its editor, share guard and redaction. The four earlier warnings are unchanged at this head, and the open WarehouseInvalidInputError issue is on another path and untouched.

  • promql data source lowers to the gcp_promql endpoint and charts live Google metrics.
  • promQlRangeInput/promQlChartRows are shared by the browser server function and the share resolver.
  • The editor gained a Google Cloud tab with metric search and starter queries.
  • Share resolves only the stored query and redacts query, projects and legend.

Before merge

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 3 DashboardWidgetDataService.resolve 3/day, 11.1% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • The share guard keeps a PromQL-referenced query variable to the free-text charset, All included (share-variables.ts:126).
  • stepSeconds floors at 60 s and the ladder caps at 86,400 s, inside GcpPromQlRangeRequest's bounds (datetime.ts:750).
  • The 8 files changed since the last review are base-branch content; pr_file_diff refuses them, so they were only read at head.
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
Dashboard share resolver's live Google Cloud PromQL read outbound call yes Effect.fn("GcpPromQlService.queryRange") at packages/backend/src/services/integrations/GcpPromQlService.ts:495, called from DashboardWidgetDataService.resolve
Browser server functions getGcpPromQlChart / getGcpMetricSearch / getGcpMetricDescriptor outbound call yes runWarehouseQuery wraps the call in Effect.withSpan(operation) (apps/web/src/api/warehouse/effect-utils.ts:236)

b30cd15 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 3/10 · risky as written
Thin traffic (~3 share resolves/day) but the four open findings all still reproduce at this head; no test covers the empty-projects or sparkline guard paths.
quality 60/100 · 4 warnings · tests partial · risk medium · 2/2 new units observable

Adds a promql widget data source that charts live Google Cloud Monitoring metrics through a PromQL proxy, on the board, in the editor and on share links. The lowering, redaction and notice plumbing are sound, but the four earlier findings all reproduce at this head, so it should not merge as is. The web WarehouseInvalidInputError issue is not addressed: this change adds a new decode site that raises it (apps/web/src/api/warehouse/gcp-promql.ts:33), for a query the read refuses or an empty projects list.

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 3 DashboardWidgetDataService.resolve 3/day, 11.1% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
getGcpPromQlChart browser server function → API gcp_promql range read (live Google) outbound call yes Server-side GcpPromQlService.queryRange is an Effect.fn with annotateCurrentSpan({ orgId }) (GcpPromQlService.ts:506-510)
share resolver executing a PromQL tile background request work yes Runs inside the existing DashboardWidgetDataService.resolve span (DashboardWidgetDataService.ts:433)

7a655ed · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 3/10 · risky as written
Only a bundle-budget constant changed since the last review, but all four earlier warnings still reproduce at this head.
quality 60/100 · 4 warnings · tests partial · risk medium · 2/2 new units observable

Adds a live PromQL data source that charts Google Cloud Monitoring metrics on dashboard tiles, plus an editor tab, share-path redaction and docs. The only change since the last review is the web bundle budget; the four open warnings are unchanged, so this is not ready to merge. The open WarehouseInvalidInputError ("Breakdown query too broad without filters") is on the breakdown-query path this diff does not touch, so it is not addressed here.

  • Widgets gain a promql v3 data source read live from Google, with no ingest
  • check-bundle-budget.ts raises maxGzipBytes from 707.5 KB to 710 KB
  • New Google Cloud source tab and PromQlEditorPanel for time-series panels
  • redactForShare drops query, projects and legend for shared boards

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 3 DashboardWidgetDataService.resolve 3/day, 11.1% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • Only edit since the last review is the budget comment and maxGzipBytes constant (apps/web/perf/check-bundle-budget.ts:113), no logic change
  • interpolate.ts:106, access.ts:188, widget-query-builder-page.tsx:172 and share.http.ts:305 re-read at the head; all four open warnings still reproduce unchanged
  • promQlChartRows emits wide { bucket, [series]: value } rows (gcp-promql.ts:68), confirming the open stat/gauge finding is not fixed
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
widget PromQL range read (getGcpPromQlChart → internal atom client) client yes Effect.fn("Integrations.getGcpPromQlChart") span, apps/web/src/api/warehouse/gcp-promql.ts:24
GCP metric search and describe reads (getGcpMetricSearch, getGcpMetricDescriptor) client yes Effect.fn("Integrations.getGcpMetricSearch" / "getGcpMetricDescriptor"), apps/web/src/api/warehouse/gcp-promql.ts:44

bac2dcc · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
The share-variable guard and the editor panel hunks in four delegated groups were left partly unread; the four previously raised findings all still stand at this head.
quality 60/100 · 4 warnings · tests partial · risk high · 1/1 new units observable

Adds a promql widget data source that charts Google Cloud metrics read live through the API's PromQL proxy, with an editor tab, share redaction and a variable charset guard. The four earlier findings are all still present at this head; the unauthenticated share path is the part to look at carefully.

  • PromQlWidgetDataSource and makePromQlDataSource add the promql kind
  • toWidgetRequest lowers it to gcp_promql with { promql, projects, legend }
  • promQlChartRows/promQlWidgetData shape wide chart rows plus a partial-answer notice
  • resolveShareVariables now takes promQlTemplates and holds a referenced query variable to the free-text charset

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 5.9% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • promQlChartRows key collision and ordering (gcp-promql.ts:61-74)
  • redactDataSource drops query, projects and legend (redact.ts:90)
  • Empty projects are omitted by the editor (widget-query-builder-page.tsx:177), not by dataSourcePromQl
Observability coverage: 1 of 1 changes observable
Change Kind Observable Evidence
getGcpPromQlChart / getGcpMetricSearch / getGcpMetricDescriptor server functions outbound client call yes runWarehouseQuery wraps each call (apps/web/src/api/warehouse/gcp-promql.ts:34, 49, 61)

2a59dad · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

# Conflicts:
#	apps/web/perf/check-bundle-budget.ts
@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 3/10 · risky as written
All four earlier findings are still in the code at this head, including the share guard's blind spot, and the editor's stat/gauge reduction has no test.
quality 60/100 · 4 warnings · tests partial · risk medium · 3/3 new units observable

Adds a promql widget source that charts an org's Google Cloud metrics live, with an editor tab, share redaction and a share-variable guard. All four earlier findings are unchanged at this head, F1 (the sparkline blind spot in the share guard) included; the open WarehouseInvalidInputError issue the change sits next to is not addressed.

  • kind: "promql" lowers to the gcp_promql endpoint with promql/projects/legend
  • promQlChartRows builds wide rows; scalar tiles reduce by field: "value"
  • A query variable a PromQL tile reads is held to the free-text charset
  • The editor gains a Google Cloud tab, metric search and Run preview

Before merge

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 5.9% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • promQlChartRows emits ISO bucket values (gcp-promql.ts:69), the same shape decoded warehouse rows carry (error-timestamps.test.ts:78)
  • The preview fetch moved out of WidgetTimeRangeProvider, but useWidgetData passes widget.timeRange explicitly (use-widget-data.ts:693), so the window is unchanged
  • redactForShare drops query, projects and legend (redact.ts:90), and a share's partial-answer notice carries only a count (DashboardWidgetDataService.ts:444)
Observability coverage: 3 of 3 changes observable
Change Kind Observable Evidence
Browser gcp_promql chart read (getGcpPromQlChart) outbound call yes same runWarehouseQuery + MapleInternalAtomClient path as every other warehouse server fn (apps/web/src/api/warehouse/gcp-promql.ts:34)
Share-side PromQL read in DashboardWidgetDataService.resolve outbound call yes runs inside the widget-data request span and calls #1477's instrumented GcpPromQlService (DashboardWidgetDataService.ts:443)
Metric search / describe from the editor outbound call yes gcpMetricSearchResultAtom through the same atom client (warehouse-query-atoms.ts:727)

eb432e6 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 3/10 · risky as written
The share path leaks project ids, all four earlier findings are still live at this head, and the query-engine group's test files went unread.
quality 50/100 · 5 warnings · tests covered · risk medium · 1/1 new units observable

Adds a promql dashboard data source that reads Google Cloud Monitoring live through GcpPromQlService, in the browser and on public shares. The plumbing is sound but the share path serves project ids to unauthenticated viewers, and the four earlier findings (F1–F4) are all still present at this head; the open WarehouseInvalidInputError issue is not addressed by the diff.

  • promql data source lowered to endpoint gcp_promql in toWidgetRequest
  • planWidgetRequest disables an empty PromQL query as promql_query_empty
  • Share resolver executes PromQL through GcpPromQlService with sanitized failures
  • Editor gains a Google Cloud tab with metric search and starter queries

Before merge

Findings

🟠 Warning · F5 · Shared PromQL rows carry project_id keys an unauthenticated viewer can read

security · packages/backend/src/services/dashboards/DashboardWidgetDataService.ts:444

promQlWidgetData(result, params.legend, false) builds row keys from the series labels, and GcpPromQlService stamps each series with project_id (or metrics_scope when one scope monitors several projects) at packages/backend/src/services/integrations/GcpPromQlService.ts:466-472. seriesKey strips project_id only when exactly one project answered (packages/query-engine/src/gcp-promql.ts:47), so a widget reading two projects — or one scope covering several — serves project_id=shop-prod / metrics_scope=... as a chart series name on the public share. redact.ts:91 deliberately drops projects from the same shared document for exactly this reason, and the partial-answer notice is sanitized to hide scopes, so the project ids reach a viewer with no account.

Pass a flag through `promQlWidgetData`/`promQlChartRows` that also drops `project_id` and `metrics_scope` from the derived series names for shares (e.g. fall back to an ordinal series name), and extend the share test with a two-project answer so the row keys are asserted.
🤖 Prompt to fix this finding with an AI agent
Findings from an automated review of commit f32f981d6d6c511ea96b9af3c9437a296a9d0742. Verify each one against the current code before changing anything, fix only those that still apply, and keep each fix to the lines it names.

---

F5 · Warning · security · packages/backend/src/services/dashboards/DashboardWidgetDataService.ts:444
Shared PromQL rows carry `project_id` keys an unauthenticated viewer can read
`promQlWidgetData(result, params.legend, false)` builds row keys from the series labels, and `GcpPromQlService` stamps each series with `project_id` (or `metrics_scope` when one scope monitors several projects) at `packages/backend/src/services/integrations/GcpPromQlService.ts:466-472`. `seriesKey` strips `project_id` only when exactly one project answered (`packages/query-engine/src/gcp-promql.ts:47`), so a widget reading two projects — or one scope covering several — serves `project_id=shop-prod` / `metrics_scope=...` as a chart series name on the public share. `redact.ts:91` deliberately drops `projects` from the same shared document for exactly this reason, and the partial-answer notice is sanitized to hide scopes, so the project ids reach a viewer with no account.
Suggested fix: Pass a flag through `promQlWidgetData`/`promQlChartRows` that also drops `project_id` and `metrics_scope` from the derived series names for shares (e.g. fall back to an ordinal series name), and extend the share test with a two-project answer so the row keys are asserted.

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 5.9% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • promQlChartRows dedupes clashing series keys and sorts rows (packages/query-engine/src/gcp-promql.ts:61-74)
  • Share notice counts failed scopes without causes or ids (gcp-promql.ts:95-98)
  • GcpPromQlService stamps project_id/metrics_scope onto series labels (grep at head)
Observability coverage: 1 of 1 changes observable
Change Kind Observable Evidence
PromQL widget read (browser server function and share resolver) outbound third-party read yes both go through runWarehouseQuery and GcpPromQlService.queryRange (apps/web/src/api/warehouse/gcp-promql.ts:34, DashboardWidgetDataService.ts:443)

f32f981 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot maple-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 inline note from Maple's review. The score and summary are in the review comment above.

promQlRangeInput(params, executed.window),
)
const result = yield* gcpPromQl.queryRange(orgId, rangeRequest)
return promQlWidgetData(result, params.legend, false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Shared PromQL rows carry project_id keys an unauthenticated viewer can read

F5 · Warning · security

promQlWidgetData(result, params.legend, false) builds row keys from the series labels, and GcpPromQlService stamps each series with project_id (or metrics_scope when one scope monitors several projects) at packages/backend/src/services/integrations/GcpPromQlService.ts:466-472. seriesKey strips project_id only when exactly one project answered (packages/query-engine/src/gcp-promql.ts:47), so a widget reading two projects — or one scope covering several — serves project_id=shop-prod / metrics_scope=... as a chart series name on the public share. redact.ts:91 deliberately drops projects from the same shared document for exactly this reason, and the partial-answer notice is sanitized to hide scopes, so the project ids reach a viewer with no account.

Pass a flag through `promQlWidgetData`/`promQlChartRows` that also drops `project_id` and `metrics_scope` from the derived series names for shares (e.g. fall back to an ordinal series name), and extend the share test with a two-project answer so the row keys are asserted.
🤖 Prompt to fix with an AI agent
In `packages/backend/src/services/dashboards/DashboardWidgetDataService.ts:444`: Shared PromQL rows carry `project_id` keys an unauthenticated viewer can read.

`promQlWidgetData(result, params.legend, false)` builds row keys from the series labels, and `GcpPromQlService` stamps each series with `project_id` (or `metrics_scope` when one scope monitors several projects) at `packages/backend/src/services/integrations/GcpPromQlService.ts:466-472`. `seriesKey` strips `project_id` only when exactly one project answered (`packages/query-engine/src/gcp-promql.ts:47`), so a widget reading two projects — or one scope covering several — serves `project_id=shop-prod` / `metrics_scope=...` as a chart series name on the public share. `redact.ts:91` deliberately drops `projects` from the same shared document for exactly this reason, and the partial-answer notice is sanitized to hide scopes, so the project ids reach a viewer with no account.

Suggested fix: Pass a flag through `promQlWidgetData`/`promQlChartRows` that also drops `project_id` and `metrics_scope` from the derived series names for shares (e.g. fall back to an ordinal series name), and extend the share test with a two-project answer so the row keys are asserted.

Verify the problem exists at that location before changing it, and keep the fix to those lines.

@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
All five open findings still reproduce in the head code; the files listed as changed since the last review sit in the base commit, outside this PR's diff.
quality 50/100 · 5 warnings · tests partial · risk high · 2/2 new units observable

Dashboard tiles can now chart a live Google Cloud PromQL query: new promql data source, editor tab, chart rows and the share resolver. Five earlier findings still reproduce at this head, so it needs attention. The open WarehouseInvalidInputError (breakdown query too broad) is untouched by this diff.

  • kind: "promql" lower to the gcp_promql endpoint with { promql, projects?, legend? }
  • Share resolver runs the stored PromQL query and builds rows via promQlWidgetData
  • Empty query disables the tile (promql_query_empty); partial answers add a notice

Before merge

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 5.9% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • Share executes widget.display.sparkline.dataSource when the caller sends source: "sparkline" (DashboardWidgetDataService.ts:267), so F1 stands
  • PromQlProjects requires one id (domain/src/http/gcp-promql.ts:45) while dataSourcePromQl forwards projects: [] into promQlRangeInput, so F2 stands
  • promQlChartRows keeps project_id in row keys unless one project answered (query-engine/src/gcp-promql.ts:47), so F5 stands
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
Browser gcp_promql server function (getGcpPromQlChart) and the metric search/describe atoms outbound client call yes Every adapter runs through runWarehouseQuery, which spans the operation (apps/web/src/api/warehouse/effect-utils.ts:236)
Share resolver PromQL branch outbound client call yes Effect.fn("DashboardWidgetDataService.resolve") span (DashboardWidgetDataService.ts:242) plus gcpPromQl.queryRange from #1477

507b4d1 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 3/10 · risky as written
The earlier share-path security and stat-value warnings still hold at this head and no hunk fixes them.
quality 50/100 · 5 warnings · tests covered · risk medium · 3/3 new units observable

Charts Google Cloud metrics live through a promql widget: editor tab, server function, shared-board guard and redaction, all tested. The five open warnings (share guard skipping sparklines, empty projects, stat value field, All alternation, project_id on shared rows) are unfixed, and the open WarehouseInvalidInputError issue is untouched.

  • promql data-source kind reads a Google Cloud PromQL query live on every view
  • promQlChartRows names series by legend or labels, value when none remain
  • Shared boards gate variables a PromQL tile references to the free-text charset
  • Editor gains a Google Cloud tab with metric search and starter queries

Before merge

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 5.9% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • promQlQueries is the fifth argument resolveShareVariables expects (share.http.ts:322 vs share-variables.ts:41)
  • GcpMetricSearchResponse is { metrics: string[] }, so the editor's cast at promql-editor-panel.tsx:85 holds
  • Read promQlStarterQuery: the distribution branch uses selector(...), so the monitored_resource matcher is not dropped
Observability coverage: 3 of 3 changes observable
Change Kind Observable Evidence
getGcpPromQlChart server function (tile preview, member dashboards) server yes wrapped by runWarehouseQuery, which pipes Effect.withSpan(operation) (apps/web/src/api/warehouse/effect-utils.ts:236)
Outbound integrations.gcpPromQlQueryRange call to the internal API client yes same span, operation gcpPromQlRangeRequest (apps/web/src/api/warehouse/gcp-promql.ts)
Share resolver's live Google read in the promql branch server yes Effect.fn("DashboardWidgetDataService.resolve") with maple.dashboard.id/maple.widget.id annotations (DashboardWidgetDataService.ts:242-254)

a08a452 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
The head adds only a 20-metric validation guard; the five open PromQL findings are untouched at this head and still need fixing.
quality 50/100 · 5 warnings · tests partial · risk medium

The only change since the last review is a metric-variable guard in variables-manager-dialog.tsx: a metric-sourced query variable is now refused above 20 metric names, matching the schema cap. It is correct and safe; the five open PromQL findings from earlier passes are unchanged at this head. The open WarehouseInvalidInputError in warehouse-query-atoms.ts is not addressed by this diff, which only adds the gcpMetricSearchResultAtom atom.

Before merge

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 5.9% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • The new 20-metric cap matches the schema: Schema.isMaxLength(20) at shared/variables.ts:50 and http/v2/dashboards.ts:556
  • Re-read all five open findings' lines (share.http.ts:305, access.ts:187, widget-query-builder-page.tsx:172, interpolate.ts:106, DashboardWidgetDataService.ts:444); none is touched, so none resolves
  • applyTransform's resolveField fallback (use-widget-data.ts:196) confirms the stat/gauge field: "value" mismatch for wide PromQL rows

59ae1a0 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
No new code since the prior review, but the share-path warnings (sparkline promql bypass, project_id on shared rows) are all still in the head.
quality 50/100 · 5 warnings · tests partial · risk high · 2/2 new units observable

Nothing in this PR changed since the prior review: the only file that moved is options.test.ts, which the base branch owns and this PR does not touch. The five open warnings all still stand at this head, so the share path is not safe to merge yet.

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 5.9% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • Only options.test.ts differs from 59ae1a0, and it is absent from this PR's diff (git diff e5e0270...a239184)
  • Sparkline dataSource is a full v3 source (shared/display.ts:98), so a promql sparkline bypass is reachable
  • PromQlProjects requires at least one id (packages/domain/src/http/gcp-promql.ts:45), so a stored projects: [] fails decode
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
DashboardWidgetDataService.resolve promql branch server yes Effect.fn("DashboardWidgetDataService.resolve") with annotateCurrentSpan at DashboardWidgetDataService.ts:242-254
GcpPromQlService.queryRange per read client yes called through GcpPromQlService.layer, the same instrumented path as the other integrations

a239184 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
All five earlier warnings still stand at this head, including the shared-row project_id leak and the All-selection matcher splice; nothing new since a239184.
quality 50/100 · 5 warnings · tests covered · risk high · 2/2 new units observable

The Google Cloud PromQL dashboard widget code is unchanged since the earlier review, and the 18 files listed as changed since then carry no diff against this PR's base (they arrive from feat/gcp-live-query-access), so there was nothing new to read. All five open findings still reproduce at this head; the open WarehouseInvalidInputError ("Breakdown query too broad without filters") is unrelated and untouched.

  • promql v3 data source and matching v2 API schema lower to endpoint gcp_promql
  • Web server function getGcpPromQlChart reads Google live and returns wide chart rows plus a notice
  • Share resolver holds any query variable a PromQL tile references to the free-text charset
  • redactForShare drops a PromQL source's query, projects and legend

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 0.0% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • git diff 6555809...58a6a9b on the 18 "changed since" files is empty: they are base-branch content, not part of this PR
  • promQlRangeInput step is max(60 s, chart bucket); computeBucketSecondsForRange caps the ladder at 86,400 s, inside GcpPromQlRangeRequest's limits
  • gcpMetricSearch really returns { metrics } (packages/domain/src/http/gcp-promql.ts:138), so the editor's cast is safe
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
gcp_promql widget read (web server function → API → Google) server + outbound client yes GcpPromQlService.queryRange span at packages/backend/src/services/integrations/GcpPromQlService.ts:506; http.client span with traceparent in apps/web/src/lib/services/common/telemetry.ts:170
PromQL editor metric search / describe outbound client yes same http.client span; GcpPromQlService.searchMetrics/describeMetric spans at GcpPromQlService.ts:553,585

58a6a9b · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
Only the rail's traced gating changed and it reads correctly, but five earlier PromQL warnings (share guard, empty projects, stat reduction, All alternation, project_id keys) sit unresolved at this …
quality 50/100 · 5 warnings · tests partial · risk medium · 1/1 new units observable

Since the last review only the GCP workload rail changed: traced is now withheld until the service overview answers, which reads correctly. The five earlier PromQL findings are unchanged at this head and still open; the open WarehouseInvalidInputError is not addressed here.

  • GCP workload rail hides "Traced as" until the service overview resolves (route $service/$name.tsx:307).

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 0.0% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • git diff 58a6a9b..93a5d83 touches only apps/web/src/routes/infra/gcp/$service/$name.tsx, so the five open findings keep their lines
  • Rail traced group and its Traces/Service-map links render only when defined (gcp-workload-rail.tsx:140)
  • Open WarehouseInvalidInputError stacks in warehouse-query-atoms.ts; no validation path is changed here
Observability coverage: 1 of 1 changes observable
Change Kind Observable Evidence
Live Google Cloud PromQL read (widget + share data source) outbound call yes GcpPromQlService.queryRange is an Effect.fn span annotated with orgId (GcpPromQlService.ts:506-519)

93a5d83 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
The share path's PromQL variable guard, project-id row keys and empty-projects decode are all still wrong at this head.
quality 50/100 · 5 warnings · tests partial · risk high · 2/2 new units observable

Adds a promql widget data source, editor tab and share path for live Google Cloud Monitoring charts. All five earlier findings still stand at this head, and the open WarehouseInvalidInputError (a breakdown-query guard) is untouched.

  • New promql widget data source reads Google Cloud Monitoring live
  • planWidgetRequest disables a PromQL tile whose query is blank
  • Share variables referenced by PromQL queries must pass the free-text charset
  • PromQL chart rows carry a partial-answer notice under the chart

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 0.0% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • Two files named as changed since the last review are unchanged between base and head (git diff 9047677 29f6d3a)
  • promQlRangeInput floors the step at GCP_PROMQL_MIN_STEP_SECONDS; GcpPromQlRangeRequest caps points
  • Both new PromQL calls open spans (runWarehouseQuery, GcpPromQlService.queryRange)
Observability coverage: 2 of 2 changes observable
Change Kind Observable Evidence
gcp_promql widget data server function outbound HTTP to the API yes apps/web/src/api/warehouse/gcp-promql.ts:34 wraps the call in runWarehouseQuery, which the effect-utils.ts:229 comment documents as span-normalizing
PromQL range read from Google Cloud Monitoring outbound third-party read yes DashboardWidgetDataService.ts:443 calls GcpPromQlService.queryRange, an Effect.fn span (GcpPromQlService.ts:506), inside the DashboardWidgetDataService.resolve span

29f6d3a · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

@maple-review-bot

maple-review-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Maple review

🔴 Confidence 2/10 · do not merge
Five earlier warnings (share guard, empty projects, stat reduction, All splicing, project_id on shares) are unchanged at this head and none has a test.
quality 50/100 · 5 warnings · tests partial · risk high · 3/3 new units observable

Adds a live Google Cloud PromQL source for dashboard widgets across the editor, the share resolver and the v2 API. The five earlier warnings all still stand at this head; the open WarehouseInvalidInputError is a breakdown query this change neither causes nor fixes.

  • PromQlWidgetDataSource (kind: "promql") joins the v3 and v2 data sources
  • toWidgetRequest lowers it to the gcp_promql endpoint under a promql param key
  • Share links run the stored PromQL and guard referenced variables to the free-text charset
  • A queryless tile is promql_query_empty, rendering "No query yet"

Still open from earlier reviews

Production impact

Open errors in the changed files
Issue Service Occurrences File
@maple/web/errors/WarehouseInvalidInputError maple-web 9 apps/web/src/lib/services/atoms/warehouse-query-atoms.ts

After this merges, Maple checks whether they stop.

Production traffic of the changed files (last 7 days)
File Calls/day Busiest operations
packages/backend/src/services/dashboards/DashboardWidgetDataService.ts 2 DashboardWidgetDataService.resolve 2/day, 0.0% err
Telemetry this change adds and removes (3)
  • ➕ span name Integrations.getGcpPromQlChart · apps/web/src/api/warehouse/gcp-promql.ts:24
  • ➕ span name Integrations.getGcpMetricSearch · apps/web/src/api/warehouse/gcp-promql.ts:44
  • ➕ span name Integrations.getGcpMetricDescriptor · apps/web/src/api/warehouse/gcp-promql.ts:58
What was checked
  • A 4,000-character query, a 60 s step floor and the 11,000-point cap are enforced on the range request (packages/query-engine/src/gcp-promql.ts:24, packages/domain/src/http/gcp-promql.ts:79)
  • Shared documents drop query, projects and legend before render (packages/widgets/src/dashboard/redact.ts:90)
  • The new gcp_promql server function and GcpPromQlService.queryRange both run under named Effect.fn spans
Observability coverage: 3 of 3 changes observable
Change Kind Observable Evidence
PromQL widget read from the browser server function getGcpPromQlChart (apps/web/src/api/warehouse/gcp-promql.ts) client yes Effect.fn("Integrations.getGcpPromQlChart") at line 24, wrapped in runWarehouseQuery
PromQL read on the share/batch path in DashboardWidgetDataService.resolve server yes Effect.fn("DashboardWidgetDataService.resolve") with Effect.annotateCurrentSpan({ orgId, "maple.widget.id": … }) at line 249
Google Cloud Monitoring range query reached through GcpPromQlService.queryRange client yes Effect.fn("GcpPromQlService.queryRange") with annotateCurrentSpan({ orgId }) (service added by the base branch #1477)

cea3674 · Updated on every push. Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant