Skip to content

feat(#4586): add enabled/disabled support for metric providers and metrics - #4589

Open
fullsend-ai-coder[bot] wants to merge 3 commits into
mainfrom
agent/4586-metric-provider-enabled-flag
Open

feat(#4586): add enabled/disabled support for metric providers and metrics#4589
fullsend-ai-coder[bot] wants to merge 3 commits into
mainfrom
agent/4586-metric-provider-enabled-flag

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Allow MetricProviders and individual Metrics to be optionally disabled by default. This introduces:

  • enabled field on the Metric type (scorecard-common)
  • isEnabled method on MetricProvider interface (scorecard-node)
  • Config-level enabled attribute for providers and metrics (scorecard-backend)
  • Resolution chain: config metric > code metric > config provider > code
    provider > default (true)

Disabled metrics are excluded from:

  • Scheduled data collection (PullMetricsByProviderTask)
  • Provider task initialization (scheduler)
  • API responses (router GET /metrics endpoints)
  • Scaffolder actions (listMetrics)
  • CatalogMetricService queries

Old data for disabled metrics remains in the database.

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com


Closes #4586

Post-script verification

  • Branch is not main/master (agent/4586-metric-provider-enabled-flag)
  • Secret scan passed (gitleaks — d0f1a88d5157a5d50cff9b7da9bf36eae1ff8742..HEAD)
  • Pre-commit hooks passed (authoritative run on runner)
  • Tests ran inside sandbox

…trics

Allow MetricProviders and individual Metrics to be optionally disabled by
default. This introduces:

- enabled field on the Metric type (scorecard-common)
- isEnabled method on MetricProvider interface (scorecard-node)
- Config-level enabled attribute for providers and metrics (scorecard-backend)
- Resolution chain: config metric > code metric > config provider > code
  provider > default (true)

Disabled metrics are excluded from:
- Scheduled data collection (PullMetricsByProviderTask)
- Provider task initialization (scheduler)
- API responses (router GET /metrics endpoints)
- Scaffolder actions (listMetrics)
- CatalogMetricService queries

Old data for disabled metrics remains in the database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rhdh-gh-app

rhdh-gh-app Bot commented Sep 4, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-scorecard-backend workspaces/scorecard/plugins/scorecard-backend minor v4.2.0
@red-hat-developer-hub/backstage-plugin-scorecard-common workspaces/scorecard/plugins/scorecard-common minor v4.2.0
@red-hat-developer-hub/backstage-plugin-scorecard-node workspaces/scorecard/plugins/scorecard-node minor v4.2.0

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 4, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:56 PM UTC · Completed 6:30 PM UTC

Commit: 602bac1 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $6.14

@codecov

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.50575% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.39%. Comparing base (d0f1a88) to head (dacd837).
⚠️ Report is 3 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4589      +/-   ##
==========================================
+ Coverage   62.37%   62.39%   +0.01%     
==========================================
  Files        2607     2607              
  Lines      104756   104838      +82     
  Branches    29439    29449      +10     
==========================================
+ Hits        65346    65418      +72     
- Misses      37560    37570      +10     
  Partials     1850     1850              
Flag Coverage Δ *Carryforward flag
adoption-insights 84.77% <ø> (ø) Carriedforward from d0f1a88
ai-integrations 78.80% <ø> (ø) Carriedforward from d0f1a88
app-defaults 56.22% <ø> (ø) Carriedforward from d0f1a88
augment 46.67% <ø> (ø) Carriedforward from d0f1a88
boost 80.51% <ø> (ø) Carriedforward from d0f1a88
bulk-import 73.12% <ø> (ø) Carriedforward from d0f1a88
cost-management 13.35% <ø> (ø) Carriedforward from d0f1a88
dcm 73.47% <ø> (ø) Carriedforward from d0f1a88
e2e-adoption-insights 60.00% <ø> (ø) Carriedforward from d0f1a88
e2e-extensions 62.31% <ø> (ø) Carriedforward from d0f1a88
e2e-global-header 50.35% <ø> (ø) Carriedforward from d0f1a88
e2e-homepage 61.11% <ø> (ø) Carriedforward from d0f1a88
e2e-intelligent-assistant 47.04% <ø> (ø) Carriedforward from d0f1a88
e2e-orchestrator 49.52% <ø> (ø) Carriedforward from d0f1a88
e2e-orchestrator-plugin 49.51% <ø> (ø) Carriedforward from d0f1a88
e2e-quickstart 55.21% <ø> (ø) Carriedforward from d0f1a88
e2e-scorecard 50.21% <ø> (ø) Carriedforward from d0f1a88
e2e-theme 16.36% <ø> (ø) Carriedforward from d0f1a88
extensions 57.37% <ø> (ø) Carriedforward from d0f1a88
global-floating-action-button 71.18% <ø> (ø) Carriedforward from d0f1a88
global-header 68.09% <ø> (ø) Carriedforward from d0f1a88
homepage 48.39% <ø> (ø) Carriedforward from d0f1a88
install-dynamic-plugins 71.31% <ø> (ø) Carriedforward from d0f1a88
intelligent-assistant 76.43% <ø> (ø) Carriedforward from d0f1a88
konflux 91.98% <ø> (ø) Carriedforward from d0f1a88
lightspeed 69.02% <ø> (ø) Carriedforward from d0f1a88
mcp-integrations 84.14% <ø> (ø) Carriedforward from d0f1a88
orchestrator 71.13% <ø> (ø) Carriedforward from d0f1a88
quickstart 63.74% <ø> (ø) Carriedforward from d0f1a88
sandbox 79.56% <ø> (ø) Carriedforward from d0f1a88
scorecard 87.95% <88.50%> (-0.01%) ⬇️
theme 87.91% <ø> (ø) Carriedforward from d0f1a88
translations 5.12% <ø> (ø) Carriedforward from d0f1a88
x2a 77.10% <ø> (ø) Carriedforward from d0f1a88

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d0f1a88...dacd837. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 4, 2026

Copy link
Copy Markdown

Review — approve

PR: feat(#4586): add enabled/disabled support for metric providers and metrics
Provenance: Prior review discarded (unverifiable-wrong-app); this is treated as a first review. Severity anchoring skipped.

Summary

This PR adds an opt-in/opt-out mechanism for scorecard metric providers and individual metrics. It introduces:

  • enabled?: boolean on the Metric type (scorecard-common)
  • isEnabled?(): boolean on the MetricProvider interface (scorecard-node)
  • Config-level enabled attribute at provider and metric levels (scorecard-backend config.d.ts)
  • A five-level precedence chain: config metric > config provider > code metric > code provider > default (true)

Disabled metrics are excluded from scheduling, API listing responses, individual metric endpoints, and scaffolder actions. The implementation correctly returns NotFoundError for disabled metrics on single-metric endpoints, preventing information leakage about disabled metrics.

Correctness

Precedence chain implementation — The isMetricEnabled() function correctly implements the five-level resolution chain. Config overrides always win over code-level defaults, and within each tier, metric-level (more specific) wins over provider-level (less specific). Unit tests exhaustively cover all 14 precedence combinations, including cross-tier interactions.

API endpoint coverage — All relevant routes are protected:

  • GET /metrics — listing uses filterEnabledMetrics to exclude disabled metrics
  • GET /metrics/:metricId/collectorsassertMetricEnabled guard
  • GET /metrics/catalog/:kind/:namespace/:nameCatalogMetricService.getLatestEntityMetrics uses filterEnabledMetrics
  • GET /metrics/catalog/:kind/:namespace/:name/time-seriesCatalogMetricService.getEntityMetricTimeSeries checks isMetricEnabled
  • GET /metrics/:metricId/catalog/aggregations (deprecated) — assertMetricEnabled guard
  • GET /metrics/:metricId/catalog/aggregations/entitiesCatalogMetricService.getEntityMetricDetails checks isMetricEnabled
  • GET /aggregations/:aggregationIdassertMetricEnabled on aggregationConfig.metricId
  • GET /aggregations/:aggregationId/metadata — same guard
  • GET /aggregations/:aggregationId/time-series — same guard
  • Scaffolder listMetrics action — uses filterEnabledMetrics
  • Scheduler — skips providers with all metrics disabled; PullMetricsByProviderTask filters individual metrics

Scheduler belt-and-suspenders — The scheduler check (initializeTasksByProviders) avoids creating task runners for fully-disabled providers. PullMetricsByProviderTask.pullProviderMetrics also filters disabled metrics at execution time, providing defense-in-depth.

Error handlingfilterEnabledMetrics treats unresolvable providers as enabled (avoids hiding broken registrations, logged at debug level).

Backward compatibility — All new fields and methods are optional. Omitting enabled on Metric and isEnabled() on MetricProvider preserves the existing default-enabled behavior. Changeset correctly uses minor bumps for all three packages.

Security

No security concerns. Disabled metrics return NotFoundError("Metric not found: ...") — identical to the error for non-existent metrics, preventing enumeration of disabled metric IDs. The enabled/disabled state is controlled by config and code, not by user input.

Intent & coherence

The implementation matches all three requirements from issue #4586:

  1. Optional isEnabled() on MetricProvider and enabled on Metric
  2. Config-level enabled attribute for providers and metrics
  3. Backend ignores disabled metrics; old data remains in the database

The change is well-scoped to the scorecard workspace with no unrelated modifications. The report.api.md changes in scorecard/report.api.md are property reordering artifacts from API Extractor, not behavioral changes.

Documentation

  • README.md updated with config example and cross-references
  • providers.md updated with provider authoring guidance and code examples
  • disabled-metrics-logic.md updated with the enabled-by-default system and precedence chain
  • config.d.ts JSDoc comments clearly document override semantics

Style / conventions

Follows existing codebase patterns consistently:

  • Config path resolution mirrors existing resolveThresholdsConfigPath / resolveScheduleFromConfig patterns
  • Error handling follows existing NotFoundError / NotAllowedError conventions
  • Test structure matches existing test patterns with mockServices

Notes

  • [provenance-warning] Prior review provenance: unverifiable-wrong-app. Severity anchoring was skipped for this run.
Previous run

Review

Verdict: Request Changes

This PR implements enabled/disabled support for metric providers and metrics (issue #4586). The core resolution function isMetricEnabledByDefault is well-designed with a clear five-level precedence chain, the type/interface additions are backward-compatible (optional fields), and the changeset correctly bumps all three packages as minor. The 14 unit tests for the resolution logic cover the important combinations. Test integrity is clean — no existing assertions were weakened.

However, the implementation has gaps in endpoint enforcement that directly contradict issue requirement #3, and existing documentation is not updated for the new feature.

Incomplete Endpoint Enforcement (medium)

Five router endpoints still serve data for disabled metrics, violating the issue requirement that "calls that return metrics just based on the database should check if the metric is enabled or not":

  • GET /metrics/:metricId/collectors — serves collector metadata for disabled metrics
  • GET /metrics/:metricId/catalog/aggregations — deprecated but still active, returns aggregation data
  • GET /aggregations/:aggregationId — resolves metricId from aggregation config without an enabled check
  • GET /aggregations/:aggregationId/metadata — returns metric metadata without checking enabled state
  • GET /aggregations/:aggregationId/time-series — returns aggregation time-series without checking enabled state

Both the correctness and security reviews independently flagged this gap. Remediation: Add an isMetricEnabledByDefault check (or reuse filterEnabledMetrics) in each handler before serving data.

Documentation Not Updated (high)

Three existing documentation files are now stale:

  • docs/providers.md — The MetricProvider authoring guide shows a full implementation example but does not document the new isEnabled?() method or the enabled field on Metric. Provider authors who want to ship a disabled-by-default provider will not discover this capability.
  • README.md — The backend config reference documents schedule, thresholds, and disabledMetrics but does not document the new enabled config keys at provider and metric levels.
  • docs/disabled-metrics-logic.md — Covers the disabledMetrics list and entity annotation mechanism but does not mention the new enabled-by-default system or how the two mechanisms interact.

Resolution Chain Priority Concern (medium)

The resolution chain places code-level metric.enabled (step 2) above config provider enabled (step 3). This means a plugin author who sets enabled: true on a Metric prevents an administrator from disabling the entire provider via app-config — the admin would need to disable each metric individually. The config.d.ts doc comment ("all its metrics are disabled unless individual metrics are re-enabled") is misleading about this behavior. Consider reordering so config provider enabled is evaluated before code metric enabled, or document this precedence explicitly.

Code Duplication (medium)

The try { getProvider(); isMetricEnabledByDefault(); } catch { return true; } pattern is copy-pasted in three locations (listMetrics.ts, CatalogMetricService.getLatestEntityMetrics, and router.ts). The router already extracts this into filterEnabledMetrics. Extract a shared utility to prevent drift.

Function Naming (medium)

isMetricEnabledByDefault is misleading — it resolves a multi-layered enabled state including config overrides, not merely a default. A name like isMetricEnabled would be more accurate and parallel to the existing isMetricIdDisabled.

Error Handling Idiom (medium)

The bare catch { return true; } in three locations silently swallows all errors and diverges from the codebase's dominant catch (error) pattern. At minimum, log at warn/debug level so operators can detect misconfiguration.

Information Leakage (low)

NotFoundError("Metric '<metricId>' is disabled") in CatalogMetricService distinguishes disabled from non-existent metrics, enabling metric ID enumeration. Use a generic "Metric not found" message.

Test Gaps (low)

  • No test covers config provider enabled: false + code metric enabled: true — the exact priority inversion scenario.
  • listMetrics.test.ts uses empty config, so the new filtering logic is never exercised at the action integration level.

Scheduler Startup Asymmetry (low)

The provider-skip check in initializeTasks runs only at startup. Enabling a fully-disabled provider later requires a restart, unlike partially-disabled providers which adapt at runtime via PullMetricsByProviderTask. Document this or rely solely on per-metric filtering.

Unrelated Frontend Changes (low)

scorecard/report.api.md contains cosmetic field-order swaps unrelated to issue #4586, likely from API Extractor regeneration. Consider reverting to reduce diff noise.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 6:32 PM UTC · Completed 6:55 PM UTC

Commit: 602bac1 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $7.69

- Rename isMetricEnabledByDefault to isMetricEnabled for clarity
- Reorder resolution chain: config overrides now always take
  precedence over code defaults (config metric > config provider >
  code metric > code provider > default)
- Add enabled checks to 5 router endpoints that were serving disabled
  metrics (collectors, deprecated aggregations, aggregations,
  aggregations metadata, aggregations time-series)
- Extract filterEnabledMetrics utility to eliminate try/catch
  duplication across router, CatalogMetricService, and listMetrics
- Add error logging in catch blocks instead of silently swallowing
- Fix information leakage: use generic "Metric not found" message
  instead of revealing disabled state
- Add tests for config provider overriding code metric enabled and
  for disabled metrics on router endpoints
- Update docs: providers.md, disabled-metrics-logic.md, README.md

Addresses review feedback on #4589
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 4, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 6:56 PM UTC · Ended 7:24 PM UTC

Commit: 52801b1 · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review September 4, 2026 19:24

Superseded by updated review

const providers = this.metricProvidersRegistry.listProviders();

for (const provider of providers) {
const hasEnabledMetric = provider

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] test-coverage

The scheduler provider-skip logic (skip providers where all metrics are disabled) lacks a dedicated unit test. The scheduler tests do not exercise the new hasEnabledMetric guard.

Suggested fix: Add a unit test to initializeTasksByProviders that registers a provider with all metrics disabled by config and asserts no task is created for it.

* is disabled unless the administrator explicitly enables it in
* `app-config.yaml`. Omitting this field (or setting it to `true`)
* means the metric is enabled by default.
*/

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] api-surface

The enabled field on the Metric type is included in API listing responses. When a metric has enabled: false in code but is re-enabled via config, API consumers see enabled: false on an active metric, which could be misleading.

Suggested fix: Consider stripping internal-only fields (like the code-level enabled default) from API responses, or documenting in the API contract that enabled reflects the code-level default, not the effective state.

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Sep 4, 2026
@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:56 PM UTC · Completed 7:24 PM UTC

Commit: 52801b1 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $1.74

@christoph-jerolimov

Copy link
Copy Markdown
Member

/fs-fix Update api reports to resolve ci issues

@fullsend-ai-coder

fullsend-ai-coder Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 10:11 AM UTC · Completed 10:24 AM UTC

Commit: 52801b1 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $2.04

Regenerate report.api.md for the scorecard frontend plugin to match
API Extractor output. The property ordering in the committed report
diverged from what API Extractor generates, causing CI failures.

Addresses review feedback on #4589
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 5, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 10:25 AM UTC · Ended 11:07 AM UTC

Commit: dacd837 · View workflow run →

@sonarqubecloud

sonarqubecloud Bot commented Sep 5, 2026

Copy link
Copy Markdown

@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ❌ Failure (validation failed after 2 iteration(s)) · Started 10:25 AM UTC · Completed 11:07 AM UTC

Commit: dacd837 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6

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

Labels

ready-for-merge All reviewers approved — ready to merge ready-for-review Agent PR ready for human review workspace/scorecard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extend scorecard-backend so that MetricsProviders and Metrics can be optionally disabled by default

1 participant