Skip to content

fix: scope repo-filtered health/lifecycle pipes to calling project (IN-1253) - #4528

Merged
gaspergrom merged 5 commits into
mainfrom
feat/IN-1253-repo-filtered-health-score
Aug 31, 2026
Merged

fix: scope repo-filtered health/lifecycle pipes to calling project (IN-1253)#4528
gaspergrom merged 5 commits into
mainfrom
feat/IN-1253-repo-filtered-health-score

Conversation

@gaspergrom

Copy link
Copy Markdown
Contributor

Summary

Adds two new Tinybird pipes, repo_lifecycle_v2 and repo_health_score_v2_breakdown, that recompute Lifecycle and the Health Score breakdown live for a caller-supplied set of repo URLs — powering the Overview page's per-repo filter (IN-1253). Both pipes join insightsProjects and require the caller's slug to match, so a repos value from a different project can never leak that project's data.

  • repo_health_score_v2_breakdown.pipe: mirrors project_insights_health_breakdown_copy.pipe's per-column aggregation, scoped to repos; HAVING count() > 0 returns zero rows when every selected repo is archived/excluded.
  • repo_lifecycle_v2.pipe: mirrors the project-level lifecycle logic, scoped to repos; deliberately has no HAVING guard, so an all-archived/excluded selection collapses to one row with lifecycleLabel: null.

Already deployed and validated in staging + production (cross-project leakage test against a foreign repo URL confirmed zero data returned; happy-path validated against OpenStack and project-jupyter).

Deploy order: this PR must be live in production before the paired insights PR (linuxfoundation/insights#TBD) is merged, since the insights server routes call these pipes by name.

Test plan

  • tb check passes on both pipes
  • Deployed to prod via crowd-tinybird-manager; cross-project leakage test (foreign repo URL) returns empty/null, not leaked data
  • Happy-path validated against real projects (OpenStack, project-jupyter)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
…N-1253)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Copilot AI balanced review requested due to automatic review settings August 28, 2026 12:47
@cursor

cursor Bot commented Aug 28, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
New analytics endpoints gate cross-project access on slug, but incorrect joins or parameter handling could leak repo-level health data; insights routing depends on deploy order.

Overview
Adds two live Tinybird pipes so the Insights Overview repo filter can recompute Health Score v2 breakdown and lifecycle for a caller-supplied repos list instead of the full project rollup.

repo_health_score_v2_breakdown mirrors project_insights_health_breakdown_copy aggregation (signal columns, category medians, total score + label bands) for enabled, non-excluded repos in the selection. It requires slug via insightsProjects so foreign repo URLs cannot return another project’s data, and uses HAVING count() > 0 so an all-archived/excluded selection returns no rows.

repo_lifecycle_v2 mirrors the project-level best-state-wins lifecycle rollup over the same repos + slug guard, but does not filter enabled/excluded so archived lifecycle states still roll up; empty selections yield lifecycleLabel: null in one row.

Deploy these pipes before the paired insights server PR that calls them by name.

Reviewed by Cursor Bugbot for commit 58fcdcc. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds project-scoped Tinybird endpoints for live repository-filtered lifecycle and Health Score breakdown calculations.

Changes:

  • Adds repository-filtered lifecycle aggregation.
  • Adds repository-filtered Health Score signal aggregation.
  • Restricts results using the calling project slug.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
repo_lifecycle_v2.pipe Adds scoped lifecycle rollup.
repo_health_score_v2_breakdown.pipe Adds scoped health breakdown rollup.
Suppressed comments (1)

services/libs/tinybird/pipes/repo_lifecycle_v2.pipe:48

  • This does not actually mirror the project lifecycle population: project_insights_copy_health_v2_project filters rep.enabled = true AND rep.excluded = false, while health_score_v2_repo_copy_ds still contains disabled repositories. A caller-supplied disabled URL can therefore contribute (and potentially win) the lifecycle rollup. Filtering these flags still preserves archived labels because archived is a separate field.
    WHERE
        hs.repoUrl
        IN {{ Array(repos, 'String', description="Selected repo URLs", required=True) }}
        AND ip.slug = {{ String(slug, description="Calling project's slug", required=True) }}

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +96 to +98
AND rep.enabled = true
AND rep.excluded = false
AND ip.slug = {{ String(slug, description="Calling project's slug", required=True) }}
Comment on lines +6 to +9
- Deliberately does NOT filter on `enabled`/`excluded` — `archived` is itself a valid
`lifecycleLabelV2` value (per the spec's decision tree: archived flag > abandoned > inert >
declining > stable > active, first match wins), so an all-archived/excluded selection must still
produce a real lifecycle label rather than being dropped from the rollup.
@gaspergrom
gaspergrom requested a review from joanagmaia August 28, 2026 13:19
…(IN-1253)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Copilot AI review requested due to automatic review settings August 28, 2026 17:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (3)

services/libs/tinybird/pipes/repo_lifecycle_v2.pipe:47

  • The PR contract says an all-archived/excluded selection must collapse to NULL, and the project-level counterpart restricts its population. This query only scopes by URL/project, so an archived repository remains in groupArray and returns archived. Filter to enabled, non-archived, non-excluded repositories so the empty aggregate produces the documented NULL.
    WHERE
        hs.repoUrl IN {{ Array(repos, 'String', description="Selected repo URLs", required=True) }}
        AND ip.slug = {{ String(slug, description="Calling project's slug", required=True) }}

services/libs/tinybird/pipes/repo_health_score_v2_breakdown.pipe:120

  • enabled and archived are independent flags (repository upserts can set archived while forcing enabled = true), so this does not actually drop every archived repo. health_score_v2_signal_detail_ds retains a base row for archived repos with NULL category signals; therefore count() > 0 passes and returns a NULL-filled row instead of the promised zero rows. Explicitly exclude archived repositories.
        AND rep.enabled = true
        AND rep.excluded = false
        AND ip.slug = {{ String(slug, description="Calling project's slug", required=True) }}
    HAVING count() > 0

services/libs/tinybird/pipes/repo_lifecycle_v2.pipe:10

  • This description contradicts both the PR contract and the SQL: it says archived/excluded selections produce a real label and that there is no repositories join, while the stated endpoint behavior requires NULL for that population and lines 43–44 perform both joins. Update the metadata together with the population-filter fix so operators are not given the opposite contract.
    - Deliberately does NOT filter on `enabled`/`excluded` — `archived` is itself a valid
    `lifecycleLabelV2` value (per the spec's decision tree: archived flag > abandoned > inert >
    declining > stable > active, first match wins), so an all-archived/excluded selection must still
    produce a real lifecycle label rather than being dropped from the rollup.
    - Reads `health_score_v2_repo_copy_ds` directly, no join with `repositories`.

Copilot AI review requested due to automatic review settings August 31, 2026 10:02
@gaspergrom
gaspergrom merged commit b453134 into main Aug 31, 2026
15 checks passed
@gaspergrom
gaspergrom deleted the feat/IN-1253-repo-filtered-health-score branch August 31, 2026 10:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Suppressed comments (5)

services/libs/tinybird/pipes/repo_health_score_v2_breakdown.pipe:118

  • Archived repositories are not excluded here. archived is independent of enabled/excluded, and health_score_v2_signal_detail.pipe materializes every non-deleted, non-excluded repo, so an enabled archived repo survives the inner join and makes HAVING count() > 0 return a mostly empty aggregate row. This contradicts the documented zero-row behavior for an all-archived selection; filter rep.archived = false explicitly.
        AND rep.enabled = true
        AND rep.excluded = false

services/libs/tinybird/pipes/repo_health_score_v2_breakdown.pipe:106

  • This median also needs quantileExactOrNull; otherwise an all-NULL security category becomes 0 rather than remaining unavailable.
        quantileExact(0.5)
        (hv2.securitySupplyChainScoreV2) AS securitySupplyChainScoreV2,

services/libs/tinybird/pipes/repo_health_score_v2_breakdown.pipe:108

  • This median also needs quantileExactOrNull; otherwise an all-NULL development category becomes 0 rather than remaining unavailable.
        quantileExact(0.5)
        (hv2.developmentActivityScoreV2) AS developmentActivityScoreV2,

services/libs/tinybird/pipes/repo_health_score_v2_breakdown.pipe:110

  • The raw total must preserve an empty/all-NULL input as NULL. With quantileExact, that case becomes 0, so the endpoint's IS NULL branch is bypassed and it returns healthScoreV2: 0 with a critical label for repos whose health score is unavailable.
        quantileExact(0.5)
        (hv2.healthScoreV2) AS healthScoreV2Raw

services/libs/tinybird/pipes/repo_lifecycle_v2.pipe:9

  • This contract conflicts with both the PR description and the upstream data: the PR says an all-archived selection returns NULL, while these lines say it returns archived; additionally, health_score_v2.pipe excludes excluded = true rows before materializing health_score_v2_repo_copy_ds, so an all-excluded selection cannot produce a real label here. Please decide the intended archived behavior and align the SQL and descriptions.
    - Deliberately does NOT filter on `enabled`/`excluded` — `archived` is itself a valid
    `lifecycleLabelV2` value (per the spec's decision tree: archived flag > abandoned > inert >
    declining > stable > active, first match wins), so an all-archived/excluded selection must still
    produce a real lifecycle label rather than being dropped from the rollup.

Comment on lines +103 to +104
quantileExact(0.5)
(hv2.maintainerHealthScoreV2) AS maintainerHealthScoreV2,
`lifecycleLabelV2` value (per the spec's decision tree: archived flag > abandoned > inert >
declining > stable > active, first match wins), so an all-archived/excluded selection must still
produce a real lifecycle label rather than being dropped from the rollup.
- Reads `health_score_v2_repo_copy_ds` directly, no join with `repositories`.

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 58fcdcc. Configure here.

quantileExact(0.5)
(hv2.developmentActivityScoreV2) AS developmentActivityScoreV2,
quantileExact(0.5)
(hv2.healthScoreV2) AS healthScoreV2Raw

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Null health totals become critical

Medium Severity

quantileExact on healthScoreV2 returns 0 when every selected repo is NULL (IN-1248: fewer than two categories), so the later IS NULL check never runs and the endpoint emits healthScoreV2: 0 with healthLabel: critical. A single-repo selection that should reduce to that repo's own NULL score is shown as a failing zero instead of unavailable.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 58fcdcc. Configure here.

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.

3 participants