fix: render signal-scores heatmap as a true percentile-band distribution IN-1304 - #2213
Conversation
…ion IN-1304 The previous heatmap only plotted top-20% vs median per signal - two data points recolored as heatmap cells, not an actual distribution. Switch to the full p10-p100 decile spread (matching a backend pipe change that extends health_score_report_signal_scores to return 10 percentile columns instead of p80/median), so each row shows where a signal genuinely spreads repositories apart across the whole range, not just at two fixed points. Depends on linuxfoundation/crowd.dev#4637 deploying to production before this can go live - the pipe currently still serves the old p80_pct/median_pct shape, so this frontend change must not deploy ahead of that pipe push, or the widget will render undefined values. Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
There was a problem hiding this comment.
Pull request overview
Updates the signal-scores widget to visualize ten percentile points per signal, dependent on the companion Tinybird schema change.
Changes:
- Adds percentile types and mapping.
- Expands the heatmap and tooltip to ten columns.
- Updates mapper tests.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
health-score-coverage-signal-scores.types.ts |
Defines percentile data shapes. |
health-score-coverage-signal-scores.ts |
Maps Tinybird percentile fields. |
health-score-coverage-signal-scores.test.ts |
Updates mapper fixtures and assertions. |
signal-scores.vue |
Renders the expanded heatmap. |
Note
Copilot is running an experiment and ran this review at Balanced.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| percentiles: Object.fromEntries( | ||
| SIGNAL_SCORE_PERCENTILES.map((p) => [ | ||
| p, | ||
| row[`p${p}_pct` as keyof HealthScoreCoverageSignalScoreRow] as number, | ||
| ]), |
| const percentileLabel = (p: (typeof SIGNAL_SCORE_PERCENTILES)[number]): string => (p === 100 ? 'Max' : `${p}th`); | ||
| const columns = SIGNAL_SCORE_PERCENTILES.map(percentileLabel); |
| const percentile = SIGNAL_SCORE_PERCENTILES[columnIndex]; | ||
| if (!signal || percentile === undefined) return ''; | ||
| const columnLabel = percentile === 100 ? 'Highest scoring repositories' : `${percentile}th percentile`; | ||
| return `${rowLabel(signal)}<br/>${columnLabel}: ${percent}%`; |
| percentiles: Object.fromEntries( | ||
| SIGNAL_SCORE_PERCENTILES.map((p) => [ | ||
| p, | ||
| row[`p${p}_pct` as keyof HealthScoreCoverageSignalScoreRow] as number, |
There was a problem hiding this comment.
per the PR's own deploy order (frontend first, pipe second), this reads the old-shape row until #4637 ships - row['p10_pct'] etc. don't exist on that row, so 8 of 10 percentiles come back undefined and the chart renders NaN cells in prod. /api/report/health-score-coverage/** is cached in redis for a day, so that stretches past the pipe deploy too unless the cache gets purged. worth making this additive (fall back to the old columns when the new ones are missing) or purging the cache as part of the deploy step - or is the plan to accept a short NaN window and purge manually?
Summary
The "Where the strongest repositories pull ahead" heatmap (added in #2204) only plotted two data points per signal — top-20% and median — recolored as heatmap cells instead of bars. That doesn't deliver an actual percentile distribution, just the same two-point comparison in a different chart type.
This switches the widget to a genuine percentile-band heatmap: 11 signals × 10 columns (10th through 100th percentile, in steps of 10), so each row shows the real spread of repository scores across the whole range.
types/report/health-score-coverage-signal-scores.types.ts— row/data shapes now carry all 10 percentile columns (p10_pct..p100_pct) instead ofp80_pct/median_pctserver/data/tinybird/report/health-score-coverage-signal-scores.ts— mapper updated to the new shapesignal-scores.vue— heatmap now renders 10 columns with "10th"..."Max" labels; tooltip shows the exact percentile and percentage on hoverDeploy ordering — read before merging
This depends on a companion pipe change: linuxfoundation/crowd.dev#4637, which extends
health_score_report_signal_scoresto return the 10 percentile columns instead ofp80_pct/median_pct.That pipe has NOT been deployed to production yet — it's a breaking schema change and deploying it before this frontend PR is live would break the widget silently (rendering
undefinedvalues with no error). Deploy order must be:crowd.dev#4637's pipe to productionDo not push the pipe to production ahead of this PR going live, and don't merge this PR expecting it to auto-deploy days before the pipe does — coordinate the two.
Test plan
pnpm tsc-check— cleanpnpm lint:fix— 0 errors (pre-existing unrelated warnings only)crowd.dev#4637deploys