Skip to content

feat: add Delivery Insights (DORA metrics) page to the sidebar - #729

Draft
LakshanSS wants to merge 12 commits into
openchoreo:mainfrom
LakshanSS:laki-dora-2
Draft

feat: add Delivery Insights (DORA metrics) page to the sidebar#729
LakshanSS wants to merge 12 commits into
openchoreo:mainfrom
LakshanSS:laki-dora-2

Conversation

@LakshanSS

@LakshanSS LakshanSS commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a Delivery Insights page showing the four DORA metrics, reached from the
sidebar and scoped by a Namespace → Project → Component breadcrumb.

This supersedes #724, which put the same metrics behind an Insights tab on the
namespace/project/component entity pages. Delivery performance is read by
engineering leadership looking across an organisation, while entity pages are a
developer's view of a single component — so this follows the placement
Cost Insights (#723) established. Entity pages are left byte-identical to
main
by this PR.

#724 stays open as a draft for now so the two placements can be compared; only
one of them should merge.

What's in it

  • KPI tiles — Deployment Frequency, Lead Time for Changes, Change Failure Rate,
    MTTR, each with its DORA classification, delta vs the previous equal-length
    window, and a sparkline.
  • Trend charts — one per metric, at daily/weekly/monthly granularity (lead
    time as p50/p75/p95).
  • Breakdown table — one level down (namespace → projects, project →
    components, component → environments), sorted by deployment frequency. Each row
    carries its own metrics plus an overall rating (the scope's weakest tier).
    Project/component rows drill the page scope down; environment rows apply the
    environment filter.
  • Per-environment cards for the current scope, an environment filter, and a
    "how these metrics are calculated" footnote.
  • Bookmarkable views — scope, range, granularity and environment all live in
    the URL, so a given view can be shared or saved.
  • Shared scope picker — the namespace/project/component breadcrumb is
    extracted as ScopeBreadcrumb and now backs both Delivery and Cost Insights
    (CostInsightsBreadcrumb becomes a thin wrapper, ~290 lines de-duplicated).

Data layer

ObservabilityClient gains getDoraMetrics / getDoraDeployments against the
observer's POST /api/v1alpha1/insights/dora/query and
.../insights/dora/deployments/query, called directly like the other
observability APIs.

Observer URL resolution gains namespace-level support: /resolve-urls now works
without an environmentName by resolving through the namespace's environments
(new resolveForNamespace in the client-node resolver) — this is what the
org-wide scope needs.

Testing

  • yarn tsc — clean
  • yarn prettier --check . — clean
  • Package lint — 0 errors (only the repo's pre-existing no-mixed-plugin-imports
    test-utils warnings)
  • Tests green across all four changed packages — 124 suites / 1122 tests:
    observability 94/839, portal-app 23/200, client-node 4/65,
    observability-backend 3/18. New suites cover the page's URL↔state wiring
    (scope/level derivation, filter persistence, drill-down, clearing the env
    filter across a namespace switch), the shared breadcrumb, and the breakdown
    table's drill-vs-filter behaviour.

Depends on

The observer-side endpoints land in openchoreo/openchoreo#4248, which is held
open until the full Delivery Insights feature is complete — so this is a
draft until that merges.

Open question for reviewers

Delivery Insights and Cost Insights are now two sidebar items. A single
Insights entry with Delivery / Cost tabs inside may be the better
long-term information architecture. Kept as separate items here to avoid
reworking #723 so soon after it landed — happy to unify in a follow-up if
that's preferred.

Summary by CodeRabbit

  • New Features
    • Added Delivery Insights with DORA dashboards, trend charts, metric summaries, environment cards, and drill-down tables.
    • Added filtering by time range, granularity, environment, and delivery scope.
    • Added bookmarkable URLs that preserve selected views and filters.
    • Added observability APIs for DORA metrics and deployment data.
    • Added namespace-level observability URL resolution.
    • Added reusable scope breadcrumbs for navigating namespaces, projects, and components.
  • Bug Fixes
    • Improved isolation of cached observability URLs across access tokens.

LakshanSS added 10 commits July 10, 2026 16:16
…and component levels

Adds an Insights tab to the domain (Namespace), system (Project), and
component entity pages, backed by the observer's new DORA read API
(openchoreo#3668). The tab hosts two inner views per the Insights design:

- Delivery Insights: four DORA KPI tiles (value, DORA classification,
  delta vs previous window, sparkline), trend charts with range
  (7d/30d/90d/12mo) and granularity (daily/weekly/monthly) controls, an
  environment filter, a one-level-down breakdown table (namespace: by
  project, project: by component, component: by environment) with row
  drill-down into the child's Insights tab (environment rows apply the
  env filter instead), per-environment metric cards, and a
  how-it-is-calculated footnote.
- Cost Insights: embeds the existing FinOps cost analysis at project
  level (drill-down preserved under /insights/cost); other levels point
  to the project pages until cost lands there.

Supporting changes: getDoraMetrics/getDoraDeployments on the
observability API client, and namespace-level observer URL resolution
(resolve-urls without environmentName resolves through the namespace's
environments) for the cross-environment scopes.

Signed-off-by: LakshanSS <lakshan230897@gmail.com>
The sparkline is absolutely positioned in the tile's bottom-right corner,
so long footer text (e.g. the lead-time coverage line) flowed underneath
it. Reserve the sparkline's width as footer padding and let the text wrap.

Signed-off-by: LakshanSS <lakshan230897@gmail.com>
Signed-off-by: LakshanSS <lakshan230897@gmail.com>
The test asserted the pre-namespace-resolution error message
('namespaceName and environmentName are required'), but environmentName
has been optional since resolve-urls gained namespace-level resolution.
Also adds coverage for the omitted-environmentName success path.

Signed-off-by: LakshanSS <lakshan230897@gmail.com>
resolveForNamespace cached results keyed only by namespaceName, so a
namespace result resolved for one user could be served to a different
user who cannot access that namespace or its environments. Scope the
cache key to the caller's token, matching per-request authorization.

Signed-off-by: LakshanSS <lakshan230897@gmail.com>
InsightsContent's environment filter narrowed the KPI tiles/charts but
not the project/component breakdown table: useDoraBreakdown rebuilt each
child scope without scope.environment, so the table always showed all
environments. Also include environment in scopeKey so the breakdown
effect refires when only the environment filter changes.

Signed-off-by: LakshanSS <lakshan230897@gmail.com>
An unrounded rate (e.g. 1.1428571428571428) printed the raw float in
the KPI tile instead of a formatted value like 1.14/day.

Signed-off-by: LakshanSS <lakshan230897@gmail.com>
Resolves conflicts with the cost insights view (openchoreo#723): both PRs
independently added new types/methods to ObservabilityApi.ts and
types.ts, kept both sides.

Signed-off-by: LakshanSS <lakshan230897@gmail.com>
Surface the four DORA metrics as a standalone Delivery Insights page
reached from the sidebar, scoped by a namespace/project/component
breadcrumb, instead of as an Insights tab on entity pages. The audience
for delivery performance is engineering leadership looking across an
organisation, whereas entity pages are a developer's view of one
component — so this follows the placement Cost Insights established.

- KPI tiles for Deployment Frequency, Lead Time for Changes, Change
  Failure Rate and MTTR, each with its DORA classification, delta vs the
  previous equal window, and a sparkline; one trend chart per metric at
  daily/weekly/monthly granularity (lead time as p50/p75/p95).
- A one-level-down breakdown table (namespace to projects, project to
  components, component to environments) sorted by deployment frequency,
  each row carrying its own metrics and an overall rating (the scope's
  weakest tier). Project/component rows drill the page scope down;
  environment rows apply the environment filter.
- Per-environment metric cards, an environment filter, and a
  "how these metrics are calculated" footnote.
- Scope, range, granularity and environment all live in the URL, so a
  view can be bookmarked or shared.
- The namespace/project/component picker is extracted as a shared
  ScopeBreadcrumb, now used by both Delivery and Cost Insights.

Data comes from the observer's insights endpoints via new getDoraMetrics
/ getDoraDeployments client methods. Observer URL resolution gains
namespace-level support so the org-wide scope can resolve without an
environment.

Signed-off-by: LakshanSS <lakshan230897@gmail.com>
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

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: Pro Plus

Run ID: 2069d069-c8be-4e3b-be81-24ec474a8ede

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
📝 Walkthrough

Walkthrough

The PR adds Delivery Insights with DORA metrics, scoped navigation, drill-down views, environment filtering, URL-persisted state, observability APIs, namespace-level URL resolution, and portal routing. Cost Insights now uses the shared ScopeBreadcrumb.

Changes

Delivery Insights

Layer / File(s) Summary
DORA contracts and observability data access
plugins/openchoreo-observability/src/types.ts, plugins/openchoreo-observability/src/api/*, packages/openchoreo-client-node/src/*, plugins/openchoreo-observability-backend/src/*
Adds typed DORA responses, DORA API methods, namespace-level observer URL resolution, token-partitioned caching, and optional environment handling.
Shared scope breadcrumb
plugins/openchoreo-observability/src/components/ScopeBreadcrumb/*, plugins/openchoreo-observability/src/components/CostInsights/CostInsightsBreadcrumb.tsx
Adds catalog-backed namespace, project, and component navigation. Cost Insights delegates to ScopeBreadcrumb.
DORA hooks and visualizations
plugins/openchoreo-observability/src/components/DeliveryInsights/*
Adds DORA data hooks, filters, metric tiles, trend charts, breakdown tables, environment cards, formatting utilities, loading states, and error states.
URL-driven page and portal wiring
plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsPage.tsx, packages/portal-app/*, plugins/openchoreo-observability/src/index.ts, .changeset/*
Adds URL state parsing, drill-down behavior, the /delivery-insights route, sidebar navigation, public exports, and release metadata.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Portal
  participant DeliveryInsightsPage
  participant ScopeBreadcrumb
  participant DeliveryInsightsContent
  participant ObservabilityApi
  Portal->>DeliveryInsightsPage: render /delivery-insights
  DeliveryInsightsPage->>ScopeBreadcrumb: provide URL-derived scope
  ScopeBreadcrumb-->>DeliveryInsightsPage: return scope selection or drill-down
  DeliveryInsightsPage->>DeliveryInsightsContent: provide scope and filters
  DeliveryInsightsContent->>ObservabilityApi: query DORA metrics and deployments
  ObservabilityApi-->>DeliveryInsightsContent: return typed DORA data
Loading

Possibly related PRs

Suggested reviewers: stefinie123, kaviththiranga

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the feature and testing, but it omits most required template sections, including purpose, goals, user stories, release note, documentation, security, and test environment. Complete the required template sections and provide the missing release, documentation, security, training, certification, marketing, sample, migration, and test-environment details.
Docstring Coverage ⚠️ Warning Docstring coverage is 63.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: adding a Delivery Insights page with DORA metrics to the sidebar.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@LakshanSS

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 8

🧹 Nitpick comments (7)
plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsPage.tsx (2)

152-161: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Guard onDrill at component level.

At component level the breakdown rows are environments, and the content applies them through onEnvFilterChange. onDrill has no guard for that level. If it is ever called there, it replaces component with an environment name.

♻️ Proposed change
   const onDrill = useCallback(
     (childName: string) => {
+      // Component-level rows are environments; the content handles those.
+      if (component) return;
       if (project) {
         onScopeChange({ namespace, project, component: childName });
       } else {
         onScopeChange({ namespace, project: childName });
       }
     },
-    [namespace, project, onScopeChange],
+    [namespace, project, component, onScopeChange],
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsPage.tsx`
around lines 152 - 161, Update the onDrill callback to guard against
component-level scope before applying namespace/project drill-down changes; at
that level, route the selected environment through onEnvFilterChange instead of
assigning it to component, while preserving the existing project and namespace
behavior.

95-102: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use the functional updater form of setSearchParams.

update passes next back to React instead of returning it, so the closure still reads the render-time searchParams. Use the functional form to build from the latest params before calling the mutator.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsPage.tsx`
around lines 95 - 102, Update the useCallback named update to use
setSearchParams’s functional updater: receive the latest URLSearchParams, clone
them, apply mutator, and return the updated params. Remove the render-time
searchParams dependency if it is no longer referenced, while preserving replace:
true.
plugins/openchoreo-observability/src/components/DeliveryInsights/DoraBreakdownTable.test.tsx (2)

95-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

This test cannot fail, and the worst-tier rule has no coverage.

The assertion re-checks the text that the earlier test already checks, so the test passes whether or not the row is interactive. Assert the observable property instead: a non-clickable row carries no cursor: pointer.

overallRating is the only non-trivial logic in the component, and no test covers its worst-tier selection.

💚 Proposed tests
   it('does not act on a row click when no handler is supplied', async () => {
     // The component level passes neither handler for entity-backed rows; the
     // row must simply not be interactive rather than navigating anywhere.
     await renderTable({ onDrill: undefined });
-    fireEvent.click(screen.getByText('checkout'));
-    expect(screen.getByText('checkout')).toBeInTheDocument();
+    const row = screen.getByText('checkout').closest('tr')!;
+    expect(row).not.toHaveStyle('cursor: pointer');
   });
+
+  it('rates a row by its weakest known tier', async () => {
+    const mixed = summary(30, 'Elite');
+    mixed!.changeFailureRate!.classification = 'Low';
+    mixed!.mttr!.classification = 'Unknown';
+    await renderTable({ rows: [{ ...projectRow, summary: mixed }] });
+    expect(screen.getByText('Low')).toBeInTheDocument();
+    expect(screen.queryByText('Elite')).not.toBeInTheDocument();
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DoraBreakdownTable.test.tsx`
around lines 95 - 101, Strengthen the no-handler test around renderTable by
asserting the clicked row does not expose cursor: pointer, rather than
rechecking its text. Also add coverage for overallRating’s worst-tier selection,
verifying the component chooses the lowest applicable rating when multiple tiers
are present.

6-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Type the fixture so the as any casts disappear.

classification is typed as string, so lines 42 and 48 need as any. Those casts remove the type check that would catch a drift between the fixture and DoraMetricsResponse['summary'].

♻️ Proposed change
-import type { DoraBreakdownRow } from './useDoraBreakdown';
+import type { DoraBreakdownRow } from './useDoraBreakdown';
+import type { DoraClassification } from '../../types';
 
-const summary = (total: number, classification: string) => ({
+const summary = (
+  total: number,
+  classification: DoraClassification,
+): DoraBreakdownRow['summary'] => ({

Then drop as any on both rows:

-  summary: summary(30, 'Elite') as any,
+  summary: summary(30, 'Elite'),
-  summary: summary(10, 'High') as any,
+  summary: summary(10, 'High'),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DoraBreakdownTable.test.tsx`
around lines 6 - 49, Update the summary fixture helper to type its
classification parameter and returned object against
DoraMetricsResponse['summary'], using the existing classification type rather
than string. Ensure summary(30, 'Elite') and summary(10, 'High') satisfy that
type, then remove the as any casts from projectRow.summary and envRow.summary.
plugins/openchoreo-observability/src/components/DeliveryInsights/DoraBreakdownTable.tsx (1)

221-230: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The delta in the rating column is frequency-only, and its colors bypass the theme.

Two points in this block:

  1. delta comes from df?.deltaPct at Line 162, so the percentage sits in the "DORA rating" column while it describes deployment frequency. Add a title so the meaning is explicit.
  2. The colors are hardcoded hex values. They do not follow the theme, so contrast can drop in dark mode. Use theme.palette.success.main and theme.palette.error.main.
♻️ Proposed change
                   {delta !== null && delta !== 0 && (
                     <Typography
                       component="span"
                       className={classes.miniDelta}
-                      style={{ color: delta > 0 ? '`#1e7e34`' : '`#c62828`' }}
+                      title="Change in deployment frequency vs the previous window"
+                      color={delta > 0 ? 'primary' : 'error'}
                     >

Define the positive color through the theme in useStyles if primary is not the wanted tone.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DoraBreakdownTable.tsx`
around lines 221 - 230, Update the delta Typography rendering in
DoraBreakdownTable to add a title clarifying that the percentage represents
deployment frequency, not the DORA rating. Replace the hardcoded positive and
negative hex colors with theme-derived success and error palette values,
defining any needed color in useStyles.
packages/openchoreo-client-node/src/observability-url-resolver.test.ts (1)

46-84: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

The isolation test passes for a reason unrelated to token partitioning.

User B returns an empty environment list, so the code throws before it reaches any env: cache entry. The test therefore proves only that the ns: key is token-specific. Add a case where user B lists the same environment name dev but its plane lookup fails. That case exercises the env:${namespace}/${envName} cache entry that user A populated, which is the path described in the comment on resolveForNamespace.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openchoreo-client-node/src/observability-url-resolver.test.ts`
around lines 46 - 84, Update the isolation test around resolveForNamespace so
user B returns the same “dev” environment while its plane lookup fails, rather
than returning an empty list. Configure getB to provide the environment listing
and then return a failed response for the subsequent plane lookup, and assert
the failure reflects user B’s lookup instead of user A’s cached observer URL.
Keep the existing user A setup and token-routed client creation, ensuring the
env:${namespace}/${envName} cache path is exercised.
plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsContent.tsx (1)

100-104: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Skip environment-card queries when the cards are hidden.

When envFilter is set at the domain or system level, this call passes effectiveScope, but Lines 358-369 hide breakdown.envRows. The supplied useDoraBreakdown.ts context at Lines 37-196 still creates one environment summary request for every namespace environment. Add a loadEnvironmentRows input and disable those requests while an environment filter is active.

Also applies to: 358-369

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsContent.tsx`
around lines 100 - 104, Update useDoraBreakdown to accept a loadEnvironmentRows
option and guard the environment summary requests with it. In
DeliveryInsightsContent, pass false whenever envFilter is active so
domain/system filtered views do not fetch hidden breakdown.envRows cards, while
preserving environment-row loading when no filter is applied.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/openchoreo-client-node/src/observability-url-resolver.ts`:
- Around line 152-203: The environment-level cache is not partitioned by token,
allowing users to reuse each other’s cached results. In
packages/openchoreo-client-node/src/observability-url-resolver.ts lines 152-203,
update resolveForEnvironment’s env: cache key to include the token; in
packages/openchoreo-client-node/src/observability-url-resolver.test.ts lines
46-84, add a case where user B lists the same dev environment but its plane
lookup fails, ensuring the test exercises user A’s populated env: entry.

In
`@plugins/openchoreo-observability-backend/src/services/ObservabilityService.ts`:
- Around line 55-57: Update the namespace-scoped branch in ObservabilityService
to avoid returning only the first observer URL when environments in the
namespace may use different observers: either validate and enforce a single
observer URL per namespace, or aggregate requests across every namespace
observer. Add coverage for multiple environments resolving to different
observers and preserve the existing environment-specific behavior.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsContent.tsx`:
- Around line 169-176: Update the refresh flow in DeliveryInsightsContent so the
Refresh button’s refetch handler reloads both headline metrics and the
useDoraBreakdown data. Expose and invoke the breakdown refetch function or pass
an equivalent reload key, ensuring tables and environment cards refresh with the
KPI tiles and charts.
- Around line 223-232: Update the Change Failure Rate value expression in the
DoraMetricTile to display an em dash unless cfr exists and cfr.total is greater
than zero; only call formatPercent(cfr.rate) for nonzero deployment totals,
matching the policy used by DoraEnvironmentCards.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DoraTrendChart.tsx`:
- Around line 47-75: Update DoraTrendChart’s chartData construction and axis
configuration so sparse leadTime/mttr buckets are retained in chronological
order with null metric values, causing Recharts to render gaps instead of
connecting across unmeasured periods. Preserve existing bucketLabel formatting
for populated points and add a regression test covering missing daily or monthly
buckets.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraBreakdown.ts`:
- Around line 154-176: Limit concurrent DORA metric requests in the fetchSummary
flow instead of starting every children and envChildren request at once. Add or
reuse a bounded-concurrency mechanism around the two Promise.all mappings,
preserving each row’s summary on success and returning the original child on
failure; ensure the combined project and environment fan-out cannot exceed the
configured limit.
- Around line 68-78: Update the environment query in useDoraBreakdown to filter
by metadata.annotations[CHOREO_ANNOTATIONS.NAMESPACE] using scope.namespace,
matching the System and Component queries. Remove the metadata.namespace filter
while preserving the Environment kind and environment name mapping.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraInsights.ts`:
- Around line 27-30: Update useDoraInsights so each DORA response is associated
with the current scope, range, and granularity query key, and only expose data
when those keys match the active query. Clear or suppress prior data for a new
query so failed requests cannot return an older response. Preserve the existing
behavior of setting loading to false for stale scopes while ensuring new scopes
only return responses belonging to the current query.

---

Nitpick comments:
In `@packages/openchoreo-client-node/src/observability-url-resolver.test.ts`:
- Around line 46-84: Update the isolation test around resolveForNamespace so
user B returns the same “dev” environment while its plane lookup fails, rather
than returning an empty list. Configure getB to provide the environment listing
and then return a failed response for the subsequent plane lookup, and assert
the failure reflects user B’s lookup instead of user A’s cached observer URL.
Keep the existing user A setup and token-routed client creation, ensuring the
env:${namespace}/${envName} cache path is exercised.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsContent.tsx`:
- Around line 100-104: Update useDoraBreakdown to accept a loadEnvironmentRows
option and guard the environment summary requests with it. In
DeliveryInsightsContent, pass false whenever envFilter is active so
domain/system filtered views do not fetch hidden breakdown.envRows cards, while
preserving environment-row loading when no filter is applied.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsPage.tsx`:
- Around line 152-161: Update the onDrill callback to guard against
component-level scope before applying namespace/project drill-down changes; at
that level, route the selected environment through onEnvFilterChange instead of
assigning it to component, while preserving the existing project and namespace
behavior.
- Around line 95-102: Update the useCallback named update to use
setSearchParams’s functional updater: receive the latest URLSearchParams, clone
them, apply mutator, and return the updated params. Remove the render-time
searchParams dependency if it is no longer referenced, while preserving replace:
true.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DoraBreakdownTable.test.tsx`:
- Around line 95-101: Strengthen the no-handler test around renderTable by
asserting the clicked row does not expose cursor: pointer, rather than
rechecking its text. Also add coverage for overallRating’s worst-tier selection,
verifying the component chooses the lowest applicable rating when multiple tiers
are present.
- Around line 6-49: Update the summary fixture helper to type its classification
parameter and returned object against DoraMetricsResponse['summary'], using the
existing classification type rather than string. Ensure summary(30, 'Elite') and
summary(10, 'High') satisfy that type, then remove the as any casts from
projectRow.summary and envRow.summary.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/DoraBreakdownTable.tsx`:
- Around line 221-230: Update the delta Typography rendering in
DoraBreakdownTable to add a title clarifying that the percentage represents
deployment frequency, not the DORA rating. Replace the hardcoded positive and
negative hex colors with theme-derived success and error palette values,
defining any needed color in useStyles.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f7556576-8d60-4388-b571-f836a6b56da7

📥 Commits

Reviewing files that changed from the base of the PR and between 9130de9 and fee9ce7.

📒 Files selected for processing (28)
  • .changeset/delivery-insights-dora-ui.md
  • packages/openchoreo-client-node/src/observability-url-resolver.test.ts
  • packages/openchoreo-client-node/src/observability-url-resolver.ts
  • packages/portal-app/src/components/Root/Root.tsx
  • packages/portal-app/src/createPortalApp.tsx
  • plugins/openchoreo-observability-backend/src/router.test.ts
  • plugins/openchoreo-observability-backend/src/router.ts
  • plugins/openchoreo-observability-backend/src/services/ObservabilityService.ts
  • plugins/openchoreo-observability/src/api/ObservabilityApi.ts
  • plugins/openchoreo-observability/src/api/ObserverUrlCache.ts
  • plugins/openchoreo-observability/src/components/CostInsights/CostInsightsBreadcrumb.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsContent.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsPage.test.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsPage.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DoraBreakdownTable.test.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DoraBreakdownTable.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DoraEnvironmentCards.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DoraMetricTile.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DoraTrendChart.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/index.ts
  • plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraBreakdown.ts
  • plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraInsights.ts
  • plugins/openchoreo-observability/src/components/DeliveryInsights/utils.ts
  • plugins/openchoreo-observability/src/components/ScopeBreadcrumb/ScopeBreadcrumb.test.tsx
  • plugins/openchoreo-observability/src/components/ScopeBreadcrumb/ScopeBreadcrumb.tsx
  • plugins/openchoreo-observability/src/components/ScopeBreadcrumb/index.ts
  • plugins/openchoreo-observability/src/index.ts
  • plugins/openchoreo-observability/src/types.ts

Comment thread packages/openchoreo-client-node/src/observability-url-resolver.ts Outdated
Comment on lines +55 to +57
if (!environmentName) {
return this.resolver.resolveForNamespace(namespaceName, userToken);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline packages/openchoreo-client-node/src/observability-url-resolver.ts --items all

rg -n --type ts -C 8 \
  '\bresolveForNamespace\b|\bresolveForEnvironment\b|\bobserverUrl\b' \
  packages/openchoreo-client-node/src/observability-url-resolver.ts \
  packages/openchoreo-client-node/src/observability-url-resolver.test.ts

rg -n --type ts -C 6 \
  '\bgetDoraMetrics\b|\bresolveUrls\b|\bobserverUrl\b' \
  plugins/openchoreo-observability/src \
  plugins/openchoreo-observability-backend/src

Repository: openchoreo/backstage-plugins

Length of output: 50385


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Relevant source slices"
sed -n '40,75p' plugins/openchoreo-observability-backend/src/services/ObservabilityService.ts
sed -n '148,215p' packages/openchoreo-client-node/src/observability-url-resolver.ts
sed -n '240,365p' packages/openchoreo-client-node/src/observability-url-resolver.ts

echo
echo "## Behavioral probe of resolver loop semantics"
node - <<'JS'
async function fakeResolveForEnvironment(namespaces) {
  return (input) => new Promise((resolve) => {
    const ns = input.url.match(/namespaces\/([^/]+)\b/)?.[1];
    const env = input.url.match(/\benvironments\/([^?]+)/)?.[1];
    const planes = namespaces[ns]?.planes?.[env] ?? {};
    resolve({
      ok: true,
      status: 200,
      response: {},
      data: planes
    });
  });
}

async function resolveForNamespace(namespaces, envOrder) {
  let lastError;
  for (const envName of envOrder) {
    try {
      const result = await fakeResolveForEnvironment(namespaces)({ url: `/namespaces/n1/environments/${envName}` });
      if (result.data.observabilityPlaneRef) {
        const op = await fakeResolveForEnvironment(namespaces)({ url: `/namespaces/n1/observabilityPlanes/${result.data.observabilityPlaneRef.name}` });
        if (op.data?.spec?.observerURL) {
          return { observerUrl: op.data.spec.observerURL };
        }
      }
    } catch (error) {
      lastError = new Error(String(error));
    }
  }
  throw lastError || new Error('No observer found');
}

(async () => {
  const cases = [
    {
      name: 'environments share one observer',
      namespaces: { n1: { planes: { dev: { observabilityPlaneRef: { name: 'obs-common' } }, prod: { observabilityPlaneRef: { name: 'obs-common' } } } } },
    },
    {
      name: 'environments point to different observers',
      namespaces: { n1: { planes: { dev: { observabilityPlaneRef: { name: 'obs-dev' } }, prod: { observabilityPlaneRef: { name: 'obs-prod' } } } } },
    },
  ];
  for (const c of cases) {
    let first, second;
    try {
      first = await resolveForNamespace(c.namespaces, ['dev', 'prod']);
      try { second = await resolveForNamespace(c.namespaces, ['prod', 'dev']); } catch (e) {}
    } catch (e) {
      first = e.message;
    }
    console.log(c.name, 'first=', first.observerUrl || first, 'second=', second.observerUrl || second);
  }
})();
JS

Repository: openchoreo/backstage-plugins

Length of output: 7507


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Behavioral probe of resolver loop semantics"
node - <<'JS'
function flattenEnvOrder(envs) {
  // Mirrors the resolver code: first pick an environment, then pick an observer plane.
  const order = [];
  for (const environment of envs) {
    for (const observerEnv of environment) {
      order.push({ environment, observerEnv });
    }
  }
  return order;
}

async function simulateResolve(namespaces, envOrder) {
  for (const { environment, observerEnv } of envOrder) {
    const observerUrl = namespaces[environment]?.observerURLForEnvironment?.[observerEnv];
    if (observerUrl) {
      return { observerUrl };
    }
  }
  throw new Error('No observer found');
}

(async () => {
  const namespaces = {
    n1: {
      observerURLForEnvironment: {
        dev: 'https://observer-dev.example.com',
        prod: 'https://observer-prod.example.com',
      },
    },
  };
  for (const inputOrder of [
    [['dev'], ['prod']],
    [['prod'], ['dev']],
  ]) {
    const order = flattenEnvOrder(inputOrder);
    console.log('input order:', JSON.stringify(inputOrder));
    for (const e of order) {
      console.log('attempt', e.environment, '<-', e.observerEnv, '=>', namespaces.n1.observerURLForEnvironment[e.observerEnv]);
    }
    try {
      const first = await simulateResolve(namespaces, order);
      console.log('resolved observer=', first.observerUrl);
      console.log('metrics from another environment appear missing when request targets', order[order.length - 1].environment);
    } catch (error) {
      console.log('resolved observer=', error.message);
    }
  }
})();
JS

echo
echo "## Additional search for multi-entry behavior / tests"
rg -n --type ts -C 4 "environments?:|environment?:|observerURLForEnvironment|ObservabilityPlaneRef|observabilityPlaneRef" packages/openchoreo-client-node/src/observability-url-resolver.ts packages/openchoreo-client-node/src/observability-url-resolver.test.ts

Repository: openchoreo/backstage-plugins

Length of output: 11199


Enforce one observer URL per namespace, or aggregate cross-environment calls.

resolveForNamespace returns the first environment’s observer URL it can resolve. If environments in the same namespace can point to different observers, namespace-scoped requests may call an observer that does not have the requested environment’s metrics. Add invariant validation and a test for this case, or aggregate calls across all namespace observers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/openchoreo-observability-backend/src/services/ObservabilityService.ts`
around lines 55 - 57, Update the namespace-scoped branch in ObservabilityService
to avoid returning only the first observer URL when environments in the
namespace may use different observers: either validate and enforce a single
observer URL per namespace, or aggregate requests across every namespace
observer. Add coverage for multiple environments resolving to different
observers and preserve the existing environment-specific behavior.

Comment on lines +68 to +78
const { items: envEntities } = await catalogApi.getEntities({
filter: {
kind: 'Environment',
'metadata.namespace': scope.namespace,
},
fields: ['metadata.name'],
});
const envNames = envEntities.map(e => e.metadata.name);
if (!cancelled) {
setEnvironments(envNames);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find where Environment entities are created and how namespace is recorded.
rg -nP -C6 "kind:\s*['\"]?Environment" --type=ts -g '!**/*.test.*'

# Inspect the annotation constants.
rg -nP -C2 'NAMESPACE\s*[:=]' --type=ts -g '**/*annotation*'

# Compare other Environment catalog queries in the repo.
rg -nP -C6 "getEntities\(" --type=ts | rg -n -C6 'Environment'

Repository: openchoreo/backstage-plugins

Length of output: 166


🏁 Script executed:

#!/bin/bash
set -u

echo "== repo files matching useDoraBreakdown.ts =="
fd -a 'useDoraBreakdown\.ts$' . || true

file="$(fd 'useDoraBreakdown\.ts$' . | head -n 1 || true)"
if [ -n "${file:-}" ]; then
  echo "== file: $file =="
  wc -l "$file"
  sed -n '1,140p' "$file"
fi

echo "== search Environment kind strings =="
rg -nP -C6 "Environment['\"]|kind: ['\"]?Environment|['\"].*Environment" -t ts -g '!**/*.test.*' . || true

echo "== search annotation NAMESPACE constants =="
rg -nP -C4 "NAMESPACE|CHOREO_ANNOTATIONS|CHOREO_NAMESPACE|namespace" -t ts -g '**/*annotation*' . || true

echo "== catalog queries with Environment =="
rg -nP -C4 "getEntities|ApiRef|CatalogApi" -t ts . || true

echo "== search openchoreo namespace annotation =="
rg -nP -C4 "choreo|OpenChoreo|NAMESPACE|openchoreo" -t ts . || true

Repository: openchoreo/backstage-plugins

Length of output: 50385


🏁 Script executed:

#!/bin/bash
set -u

echo "== candidate files =="
git ls-files | rg 'useDoraBreakdown\.ts|environment|Environment|annotations|catalog|ingest|CHOREO|openchoreo\.io' | head -n 200 || true

echo "== exact useDoraBreakdown file location =="
use_dora="$(git ls-files | rg '(^|/)useDoraBreakdown\.ts$' | head -n 1 || true)"
if [ -f "${use_dora:-}" ]; then
  echo "FILE=$use_dora"
  wc -l "$use_dora"
  sed -n '1,180p' "$use_dora"
fi

echo "== Environment entity kind exact =="
rg -n "kind:\s*['\"]Environment|['\"]Environment['\"]" -g '*.ts' -g '*.tsx' -g '*.yaml' -g '*.yml' . | head -n 200 || true

echo "== annotation NAMESPACE exact =="
rg -n "CHOREO_ANNOTATIONS|ANNOTATIONS|NAMESPACE|openchoreo\.io" -g '*.ts' -g '*.tsx' . | head -n 300 || true

echo "== targeted getEntities calls =="
rg -n "getEntities\(" -g '*.ts' -g '*.tsx' . | head -n 300 || true

Repository: openchoreo/backstage-plugins

Length of output: 50384


🏁 Script executed:

#!/bin/bash
set -u

echo "== env processors =="
for f in plugins/catalog-backend-module-openchoreo/src/processors/EnvironmentEntityProcessor.ts plugins/catalog-backend-module-openchoreo/src/utils/entityTranslation.ts plugins/catalog-backend-module-openchoreo/src/kinds/EnvironmentEntityV1alpha1.ts; do
  echo "--- $f ---"
  if [ -f "$f" ]; then
    wc -l "$f"
    sed -n '1,220p' "$f"
  fi
done

echo "== annotation constants =="
for f in plugins/openchoreo-backstage-plugin-common/src/annotations.ts plugins/openchoreo-react/src/utils/platformOverviewConstants.ts; do
  echo "--- $f ---"
  if [ -f "$f" ];  then
    wc -l "$f"
    sed -n '1,120p' "$f"
  fi
done

echo "== targeted entityTranslation lines 380-480 =="
sed -n '380,485p' plugins/catalog-backend-module-openchoreo/src/utils/entityTranslation.ts

Repository: openchoreo/backstage-plugins

Length of output: 20974


🏁 Script executed:

#!/bin/bash
set -u

echo "== common annotation constants file =="
fd -a 'annotations\.ts$' . | while read -r f; do
  echo "--- $f ---"
  grep -n -C3 "CHOREO_ANNOTATIONS\|NAMESPACE\|ENVIRONMENT" "$f" || true
done

echo "== exact annotation key occurrences =="
rg -n "openchoreo\.io/namespace|CHOREO_ANNOTATIONS\.NAMESPACE|ENVIRONMENT" plugins/openchoreo-backstage-plugin-common/src packages/openchoreo-client-node/src/generated/openchoreo/types.ts | head -n 200 || true

echo "== translation namespace call sites =="
rg -n -C4 "translateEnvironmentToEntity|translate.*Environment|createEnvironment" plugins/catalog-backend-module-openchoreo/src plugins/openchoreo-backend/src plugins/scaffolder-backend-module-openchoreo/src | head -n 240 || true

Repository: openchoreo/backstage-plugins

Length of output: 23154


Filter environments by the OpenChoreo namespace annotation.

translateEnvironmentToEntity stores the OpenChoreo namespace in metadata.annotations[CHOREO_ANNOTATIONS.NAMESPACE], so the environment catalog query must use that annotation like the System and Component queries do. Filtering by metadata.namespace makes non-default namespaces return no envNames, leaving the environment filter and per-environment DORA cards empty.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraBreakdown.ts`
around lines 68 - 78, Update the environment query in useDoraBreakdown to filter
by metadata.annotations[CHOREO_ANNOTATIONS.NAMESPACE] using scope.namespace,
matching the System and Component queries. Remove the metadata.namespace filter
while preserving the Environment kind and environment name mapping.

- Refresh now reloads the breakdown as well as the headline metrics.
  useDoraBreakdown issues its own requests and had no reload path, so the
  table and environment cards could show an older snapshot than the tiles
  and charts.
- useDoraInsights stores the query key alongside its response and only
  returns data whose key still matches, so a failed or in-flight request
  for a newly selected scope can no longer leave the previous scope's
  numbers on screen. A failed refresh of the *same* scope still keeps its
  last good data, which is intended.
- Change Failure Rate tile now shows an em dash when the window has no
  deployments, matching DoraEnvironmentCards and DoraBreakdownTable
  instead of reporting a misleading 0.0%.
- Lead time and MTTR trend lines no longer bridge across buckets that had
  no measurement. Both series omit empty buckets, so they are aligned to
  the zero-filled deployment-frequency buckets with nulls for the gaps
  (recharts renders those as gaps).
- Breakdown metric requests are capped at 6 in flight instead of firing
  one per project plus one per environment in the same tick.
- Documented that resolveForNamespace assumes one observability plane per
  namespace, and corrected its cache comment: the namespace entry is
  token-partitioned but the pre-existing per-environment entry it
  delegates to is not.

Signed-off-by: LakshanSS <lakshan230897@gmail.com>
@LakshanSS

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraInsights.ts`:
- Around line 94-97: Update the error state in useDoraInsights so each error is
stored with the queryKey that produced it, and expose it only when that key
matches the active queryKey; preserve current-data behavior for successful
results and null scopes. Add a regression test covering a failed query followed
by a scope change, including changing to a null scope, and verify the stale
error is not rendered by DeliveryInsightsContent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 394d0f4a-332b-4442-aecb-0113b4a24d59

📥 Commits

Reviewing files that changed from the base of the PR and between fee9ce7 and 7672c49.

📒 Files selected for processing (8)
  • packages/openchoreo-client-node/src/observability-url-resolver.ts
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsContent.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DoraTrendChart.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraBreakdown.ts
  • plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraInsights.test.ts
  • plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraInsights.ts
  • plugins/openchoreo-observability/src/components/DeliveryInsights/utils.test.ts
  • plugins/openchoreo-observability/src/components/DeliveryInsights/utils.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DoraTrendChart.tsx
  • packages/openchoreo-client-node/src/observability-url-resolver.ts
  • plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsContent.tsx
  • plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraBreakdown.ts

Comment on lines +94 to +97
// A stale result (a scope/window that has since changed) is withheld rather
// than shown under the current selection. A failed *refresh* of the current
// key keeps its last good data alongside the error, which is intended.
const data = result?.key === queryKey ? result.data : null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Scope error to the active query.

Line 97 suppresses stale data, but error has no queryKey. After a failed request, changing to a null scope leaves the old error exposed. Changing to another scope can also render the prior error before the effect clears it. Store the error with its query key and return it only when the key matches. Add a regression test for a failed query followed by a scope change.

Proposed change
-  const [error, setError] = useState<string | null>(null);
+  const [errorResult, setErrorResult] = useState<{
+    key: string;
+    message: string;
+  } | null>(null);

-        setError(null);
+        setErrorResult(null);

-          setError(
-            err instanceof Error ? err.message : 'Failed to fetch DORA metrics',
-          );
+          setErrorResult({
+            key: queryKey,
+            message:
+              err instanceof Error
+                ? err.message
+                : 'Failed to fetch DORA metrics',
+          });

   const data = result?.key === queryKey ? result.data : null;
+  const error =
+    errorResult?.key === queryKey ? errorResult.message : null;

plugins/openchoreo-observability/src/components/DeliveryInsights/DeliveryInsightsContent.tsx renders the hook error directly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// A stale result (a scope/window that has since changed) is withheld rather
// than shown under the current selection. A failed *refresh* of the current
// key keeps its last good data alongside the error, which is intended.
const data = result?.key === queryKey ? result.data : null;
// A stale result (a scope/window that has since changed) is withheld rather
// than shown under the current selection. A failed *refresh* of the current
// key keeps its last good data alongside the error, which is intended.
const data = result?.key === queryKey ? result.data : null;
const error =
errorResult?.key === queryKey ? errorResult.message : null;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@plugins/openchoreo-observability/src/components/DeliveryInsights/useDoraInsights.ts`
around lines 94 - 97, Update the error state in useDoraInsights so each error is
stored with the queryKey that produced it, and expose it only when that key
matches the active queryKey; preserve current-data behavior for successful
results and null scopes. Add a regression test covering a failed query followed
by a scope change, including changing to a null scope, and verify the stale
error is not rendered by DeliveryInsightsContent.

useDoraBreakdown hardcoded weekly, so the breakdown table and environment
cards described a different window shape than the headline tiles above
them. That was visible as a headline of 263 deployments over a breakdown
summing to 313, because summary totals used to depend on the requested
granularity (fixed in the observer alongside this).

Only `summary` is read here, and summaries now cover the exact requested
window regardless of granularity, so this no longer changes any number —
but every request on the page now describes the same view, and the magic
'weekly' no longer invites the question.

Signed-off-by: LakshanSS <lakshan230897@gmail.com>
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