refactor: consolidate peer cohorts, roster-scope team view - #155
Conversation
- peer cohort rides on each bullet/KPI row (`row.peer`, ic_kpis median); heatmap + needs-attention compute their cohort client-side from the displayed roster. Drop the peer-cohort-stats / ic-kpi-peer-median hooks, metrics, and all cohortStats plumbing. - team view scopes by `person_id in (roster)`: members in one query, sections aggregated over the roster — fixes empty sections (org_unit_id never matched a manager email). Retire supervisor_email/org_unit_id scoping; drop the broken team drill (ic_drill has no supervisor_email). - members heatmap reads per-person "member values" + "member PRs" (one query per section over the roster, not one per member); drop Cycle and Build% columns; PRs sourced from period weekly git. - drilldown shows data-source provenance (source_tags → "M365 · Zoom") instead of "no data"; on-par fold restyled to rows. - remove the executive/org view: screens, route, widgets, query, and the org_unit_id filter/view-config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
|
Warning Review limit reached
More reviews will be available in 59 minutes and 59 seconds. Learn how PR review limits work. To continue reviewing without waiting, enable usage-based billing in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (57)
📝 WalkthroughWalkthroughThis PR migrates peer cohort statistics computation from server-provided aggregates to client-embedded quartile data; removes the executive view feature; refactors data models, transforms, queries, and UI components to support the new peer data flow; and applies UI polish including text capitalization and CSS class naming standardization. ChangesPeer cohort computation and exec feature removal
🎯 4 (Complex) | ⏱️ ~60 minutes
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/components/widgets/v2/kpi-tile.tsx (1)
31-33:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winHandle zero peer median as valid data.
Line 31 and Line 52 currently treat
peer_median = 0as missing. Per theIcKpicontract, missing cohort isnull, so zero is valid and should still render median/status logic.Suggested fix
function peerStatusVsMedian( value: number, median: number, higherIsBetter: boolean ): Status { - if (!Number.isFinite(value) || !Number.isFinite(median) || median === 0) { + if (!Number.isFinite(value) || !Number.isFinite(median)) { return "neutral"; } const meetsTarget = higherIsBetter ? value >= median : value <= median; return meetsTarget ? "good" : "bad"; } @@ - const hasMedian = - peerMedian != null && Number.isFinite(peerMedian) && peerMedian > 0; + const hasMedian = peerMedian != null && Number.isFinite(peerMedian);Also applies to: 51-53
🤖 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 `@src/components/widgets/v2/kpi-tile.tsx` around lines 31 - 33, The current checks incorrectly treat a zero peer median as missing by using "median === 0" (and similar for peer_median); update the logic in the KPI tile where the status is computed (the conditional that returns "neutral" when validating value and median) to only treat null/undefined as missing per the IcKpi contract and keep Number.isFinite(value) and Number.isFinite(median) checks but remove the "median === 0" (and analogous "peer_median === 0") check so zero is accepted as valid; apply this change to both occurrences that currently use that three-part condition.src/api/transforms.ts (1)
72-90:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winKeep
decimal1delta formatting consistent with KPI value formatting.With
decimal1now shown as one decimal,formatDeltastill rounds to integers on Line 96, which can display0while the KPI value visibly changed.🐛 Proposed fix
function formatDelta(delta: number, fmt: IcKpiFormat): string { const sign = delta > 0 ? '+' : ''; switch (fmt) { case 'integer': return `${sign}${Math.round(delta)}`; - case 'decimal1': return `${sign}${Math.round(delta)}`; + case 'decimal1': return `${sign}${Math.round(delta * 10) / 10}`; case 'percent': return `${sign}${Math.round(delta)}%`; case 'hours': return `${sign}${Math.round(delta)}h`; } }🤖 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 `@src/api/transforms.ts` around lines 72 - 90, formatDelta is still rounding deltas to integers while formatValue/formatKpiValue supports 'decimal1'; change formatDelta to use the same formatting path: accept the same fmt (string | undefined), convert it with asIcKpiFormat(fmt) and call formatValue(deltaRaw, asIcKpiFormat(fmt)) (or reuse formatKpiValue) instead of integer rounding so 'decimal1' deltas show one decimal; update references to formatDelta callers if they need to pass the fmt through.
🧹 Nitpick comments (2)
src/components/widgets/v2/team-members-attention.tsx (1)
34-50: ⚡ Quick winUse section-scoped metric keys for cohort aggregation.
cohortByMetricis keyed byb.metric_key, but status direction is resolved viabulletCatalogKey(b). If the same metric key appears in multiple sections, this can merge unrelated cohorts and skew “below peers” counts.Proposed fix
- const cohortByMetric = new Map<string, PeerStats>(); + const cohortByMetric = new Map<string, PeerStats>(); { const valuesByMetric = new Map<string, number[]>(); for (const m of members) { for (const b of bulletsByPerson?.get(m.person_id.toLowerCase()) ?? []) { if (b.schema_error) continue; const v = Number(b.value); if (!Number.isFinite(v)) continue; - const arr = valuesByMetric.get(b.metric_key); + const metricScopeKey = bulletCatalogKey(b); + const arr = valuesByMetric.get(metricScopeKey); if (arr) arr.push(v); - else valuesByMetric.set(b.metric_key, [v]); + else valuesByMetric.set(metricScopeKey, [v]); } } for (const [k, vals] of valuesByMetric) { const stats = peerStatsFor(vals); if (stats) cohortByMetric.set(k, stats); } } @@ - const stats = cohortByMetric.get(b.metric_key); + const stats = cohortByMetric.get(bulletCatalogKey(b));Also applies to: 61-63
🤖 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 `@src/components/widgets/v2/team-members-attention.tsx` around lines 34 - 50, The cohort aggregation currently groups by raw metric_key which can merge metrics from different sections; change grouping and lookup to use the section-scoped catalog key returned by bulletCatalogKey(b) so cohorts are computed per-section. Specifically, when building valuesByMetric and cohortByMetric (symbols: valuesByMetric, cohortByMetric, peerStatsFor) replace uses of b.metric_key with bulletCatalogKey(b) (and do the same for the later code around the block referenced at lines 61-63 where cohort lookups occur) to ensure peers are aggregated per section; keep the same filtering (schema_error, Number.isFinite) and only change the grouping/lookup key.src/lib/insight/v2/derivations.ts (1)
41-52: ⚡ Quick winStabilize
sourcesordering for deterministic UI output.
collectSourcescurrently preserves first-seen order from incoming rows, which can vary by backend row order. Sorting avoids label-order jitter (e.g.,"M365 · Zoom"vs"Zoom · M365").♻️ Proposed change
function collectSources( rows: BulletMetric[], keys: ReadonlyArray<string>, ): string[] { const set = new Set<string>(); for (const r of rows) { if (keys.includes(r.metric_key)) { for (const t of r.source_tags ?? []) set.add(t); } } - return [...set]; + return [...set].sort((a, b) => a.localeCompare(b)); }🤖 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 `@src/lib/insight/v2/derivations.ts` around lines 41 - 52, collectSources builds a Set of source tags but returns them in insertion order which depends on backend row ordering; change collectSources to return a deterministically sorted array (e.g., convert the set to an array and call sort or use localeCompare) so UI labels are stable. Update the return in function collectSources (and any callers expecting sources) to return [...set].sort((a,b)=>a.localeCompare(b)) or equivalent deterministic comparator.
🤖 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 `@src/components/widgets/v2/summary-with-breakdown.tsx`:
- Around line 114-117: The provenance labels are being forced to all-caps by the
Tailwind `uppercase` utility in the span that renders formatSources(sources)
inside the SummaryWithBreakdown component; remove the `uppercase` class (or
replace it with a neutral class like `normal-case`) so the curated casing from
SOURCE_LABELS/formatSources is preserved when rendering the provenance string.
In `@src/mocks/factories.ts`:
- Around line 184-193: The peer median fields in src/mocks/factories.ts (e.g.,
loc_median, prs_merged_median, tasks_closed_median, bugs_fixed_median,
ai_sessions_median) are hard-coded 30‑day totals but src/mocks/handlers.ts
scales IC_KPIS by periodDays, causing mismatched comparisons; update the factory
to scale those absolute-count medians by the same period factor (periodDays /
30) when constructing peer medians so they match the scaled IC_KPIS returned by
handlers.ts, while leaving percentage/ratio medians (ai_loc_share_pct_median,
focus_time_pct_median, build_success_pct_median, pr_cycle_time_h_median where
appropriate) unchanged.
- Around line 379-381: The mock quartiles p25 and p75 are computed from the
30-day baseline (d0.range_min, d0.median, d0.range_max) but the returned row's
value is period-scaled, so the peer band can drift; update p25 and p75 to be
scaled by the same period factor used for value (i.e., apply the same
scaling/normalization logic used when computing value) so p25 and p75 remain in
the same units as value — modify the expressions that set p25 and p75 (currently
using d0.range_min/d0.median/d0.range_max) to mirror the period-scaling applied
elsewhere in this factory so cohort coloring matches the bar.
In `@src/mocks/handlers.ts`:
- Around line 117-123: The mock currently seeds prs_merged inside the member
metrics object (the prs_merged property in src/mocks/handlers.ts), which
conflicts with the new contract that V2_MEMBER_PRS is the sole source of PR
counts; remove the prs_merged property (or set it to null to mirror the live
TEAM_MEMBER placeholder) from the mock object so tests/handlers rely on
V2_MEMBER_PRS for merged-PR data instead of this seeded value.
- Around line 210-218: The V2_MEMBER_PRS mock currently ignores date range and
always returns seedOf(id)%20; update the handler (METRIC_REGISTRY.V2_MEMBER_PRS)
to read period constraints from parseFilter(body) (use period_days if present or
compute days from start_date/end_date) and scale the returned prs_merged by the
requested period (e.g., normalize to weeks or use multiplier = Math.max(1,
Math.round(period_days/7))). Replace the constant prs_merged value with a
period-aware value (e.g., base = seedOf(id) % 20 then prs_merged = Math.max(0,
Math.round(base * multiplier))) so tests that pass different period_days/date
bounds observe different counts.
In `@src/queries/team-view.ts`:
- Around line 73-80: The hook's enabled flag and queryFn must guard against an
empty roster to avoid building "person_id in ()": change the enabled expression
from Boolean(roster) to Boolean(teamId) && (roster?.length ?? 0) > 0 (or
roster?.length > 0) and add an explicit empty-length check in the queryFn (e.g.,
if (!roster || roster.length === 0) return []) before mapping to ids so you
never construct the filter `person_id in ()` in useTeamMembers.
- Around line 97-123: The code currently ignores a non-ok "members" batch and
synthesizes members; change the logic in the function that calls
queryBatchWithRange (the resp/results processing) to detect when an item with id
=== "members" has status !== "ok" and surface that failure instead of skipping
it — e.g., throw or return an error/flag so callers (and membersQ.isError) can
see the failure; keep the existing behavior for "prs" (skip non-ok) but do not
synthesize real roster entries when "members" failed (use the existing
transformTeamMembers and buildSyntheticMember only when the "members" result is
ok).
- Around line 83-95: The team-member and PR batch queries in the items array are
being capped with `$top: 200`, which drops roster-scoped members and causes
buildSyntheticMember(...) to emit incorrect data; remove the `$top: 200`
property (or replace it with no limit) from both BatchQueryItem entries that use
METRIC_REGISTRY.TEAM_MEMBER and METRIC_REGISTRY.V2_MEMBER_PRS so the filter
returns all roster members (keep the existing `filter` unchanged).
In `@src/queries/v2/team-extras.ts`:
- Around line 57-60: In the loop that processes resp.results returned by
queryBatchWithRange (BatchQueryResult<RawMemberValueRow>), don't silently skip
results where r.status !== "ok" or r.id is missing; instead collect these
failures (include r.id and r.error/message) and surface them after the loop —
either by throwing a descriptive error or returning a failed result object — so
callers (e.g., the team-view-v2 consumer) can detect partial failures; update
the logic around resp.results, the for (const r of ...) loop, and the code that
currently builds byMember to fail-fast or propagate an error when any batch
result is not "ok".
In `@src/routes/__root.tsx`:
- Around line 39-48: The root TooltipProvider currently omits the delay setting
so tooltips default to 0ms and break the previous hover behavior (MetricInfo
expected delay={200}); update the root provider by passing the same delay value
used before (e.g., add delay={200} to the <TooltipProvider> declaration) so the
global tooltip delay matches MetricInfo's previous behavior and restores
consistent hover timing across the app.
---
Outside diff comments:
In `@src/api/transforms.ts`:
- Around line 72-90: formatDelta is still rounding deltas to integers while
formatValue/formatKpiValue supports 'decimal1'; change formatDelta to use the
same formatting path: accept the same fmt (string | undefined), convert it with
asIcKpiFormat(fmt) and call formatValue(deltaRaw, asIcKpiFormat(fmt)) (or reuse
formatKpiValue) instead of integer rounding so 'decimal1' deltas show one
decimal; update references to formatDelta callers if they need to pass the fmt
through.
In `@src/components/widgets/v2/kpi-tile.tsx`:
- Around line 31-33: The current checks incorrectly treat a zero peer median as
missing by using "median === 0" (and similar for peer_median); update the logic
in the KPI tile where the status is computed (the conditional that returns
"neutral" when validating value and median) to only treat null/undefined as
missing per the IcKpi contract and keep Number.isFinite(value) and
Number.isFinite(median) checks but remove the "median === 0" (and analogous
"peer_median === 0") check so zero is accepted as valid; apply this change to
both occurrences that currently use that three-part condition.
---
Nitpick comments:
In `@src/components/widgets/v2/team-members-attention.tsx`:
- Around line 34-50: The cohort aggregation currently groups by raw metric_key
which can merge metrics from different sections; change grouping and lookup to
use the section-scoped catalog key returned by bulletCatalogKey(b) so cohorts
are computed per-section. Specifically, when building valuesByMetric and
cohortByMetric (symbols: valuesByMetric, cohortByMetric, peerStatsFor) replace
uses of b.metric_key with bulletCatalogKey(b) (and do the same for the later
code around the block referenced at lines 61-63 where cohort lookups occur) to
ensure peers are aggregated per section; keep the same filtering (schema_error,
Number.isFinite) and only change the grouping/lookup key.
In `@src/lib/insight/v2/derivations.ts`:
- Around line 41-52: collectSources builds a Set of source tags but returns them
in insertion order which depends on backend row ordering; change collectSources
to return a deterministically sorted array (e.g., convert the set to an array
and call sort or use localeCompare) so UI labels are stable. Update the return
in function collectSources (and any callers expecting sources) to return
[...set].sort((a,b)=>a.localeCompare(b)) or equivalent deterministic comparator.
🪄 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
Run ID: 260f8c83-220d-4c0c-8b6e-c4e6011ceed2
📒 Files selected for processing (54)
src/api/index.tssrc/api/metric-registry.tssrc/api/odata.tssrc/api/raw-types.tssrc/api/transforms.test.tssrc/api/transforms.tssrc/api/view-configs.test.tsxsrc/api/view-configs.tssrc/components/widgets/metric-card.tsxsrc/components/widgets/metric-info.tsxsrc/components/widgets/org-health-radar.tsxsrc/components/widgets/org-kpi-cards.tsxsrc/components/widgets/team-metrics-bar.tsxsrc/components/widgets/teams-table.tsxsrc/components/widgets/v2/counters-block.test.tsxsrc/components/widgets/v2/counters-block.tsxsrc/components/widgets/v2/distribution-strip.test.tsxsrc/components/widgets/v2/distribution-strip.tsxsrc/components/widgets/v2/ic-needs-attention.test.tsxsrc/components/widgets/v2/ic-needs-attention.tsxsrc/components/widgets/v2/kpi-tile.test.tsxsrc/components/widgets/v2/kpi-tile.tsxsrc/components/widgets/v2/members-heatmap/index.test.tsxsrc/components/widgets/v2/members-heatmap/index.tsxsrc/components/widgets/v2/section-card.tsxsrc/components/widgets/v2/section-drilldown-sheet.tsxsrc/components/widgets/v2/section-status.tsxsrc/components/widgets/v2/summary-with-breakdown.tsxsrc/components/widgets/v2/team-members-attention.test.tsxsrc/components/widgets/v2/team-members-attention.tsxsrc/lib/insight/v2/derivations.tssrc/lib/insight/v2/kpi-defs.tssrc/lib/insight/v2/peer-status.test.tssrc/lib/insight/v2/peer-status.tssrc/lib/insight/v2/sections.tssrc/lib/peers.tssrc/lib/status.tssrc/mocks/factories.tssrc/mocks/handlers.tssrc/mocks/registry.tssrc/mocks/v2/factories.tssrc/queries/executive-view.tssrc/queries/team-view.tssrc/queries/v2/ic-extras.tssrc/queries/v2/team-extras.tssrc/routeTree.gen.tssrc/routes/__root.tsxsrc/routes/ic.$person.exec.tsxsrc/screens/executive-view.test.tsxsrc/screens/executive-view.tsxsrc/screens/ic-dashboard/engineering-dashboard-v2.tsxsrc/screens/team-view-v2.tsxsrc/screens/team-view.tsxsrc/types/insight.ts
💤 Files with no reviewable changes (16)
- src/screens/executive-view.test.tsx
- src/components/widgets/v2/section-status.tsx
- src/screens/executive-view.tsx
- src/routes/ic.$person.exec.tsx
- src/components/widgets/org-health-radar.tsx
- src/components/widgets/org-kpi-cards.tsx
- src/api/index.ts
- src/queries/executive-view.ts
- src/components/widgets/teams-table.tsx
- src/lib/insight/v2/sections.ts
- src/mocks/registry.ts
- src/api/odata.ts
- src/mocks/v2/factories.ts
- src/routeTree.gen.ts
- src/queries/v2/ic-extras.ts
- src/components/widgets/team-metrics-bar.tsx
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Color each roster member on the members-heatmap and needs-attention
against their OWN department's metric distribution, and read the team
section cards' comparison off the backend's blended-department
expectation. Replaces the client-side cohort computed over the displayed
roster, which is role-blind for a team spanning departments.
- thread org_unit_id onto TeamMember (raw-types / types / transforms).
- useDeptDistributions: fetch the dept-distribution metrics scoped
org_unit_id IN (roster depts) into a source-split {kpi, bullet} map
(org_unit_id -> metric_key -> PeerStats). The kpi family backs the
team_row heatmap columns, the bullet family backs member bullet
comparisons — kept separate because both emit prs_merged from
different attribution sources.
- members-heatmap / team-members-attention: color each member against
their department; a degenerate cohort (n < MIN_DEPT_COHORT_N) renders
neutral ("no peer data").
- statsToDisplayUnit: scale raw-hour dept stats to a day-displayed bullet
before comparing (the bullet transform auto-scales hours to days).
- section cards: cohort label -> department; the backend folds the
blended expectation onto row.peer, so the scoring path is unchanged.
- mocks + tests (heatmap, attention, section-card, transforms).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
…ption Team sections now mirror the IC dashboard's set: Task delivery, Git output, Collaboration, AI adoption — replacing the team-only Code quality section (which had no data) with Git output, and naming the AI section "AI adoption" in both views. - sections.ts: TEAM_SECTIONS code_quality -> git_output; AI label is "AI adoption" on both IC and team. - TEAM_BULLET_GIT registry id (…0007); TEAM_BULLET_SECTIONS maps git_output to it. code_quality stays in the map for the legacy team view. - mock handler serves the team git bullet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
- Heatmap cells use soft tints of the semantic accents (success/30, destructive/20) instead of the solid button/alert fills, so a dense grid reads as a calm data view; green gets more alpha to offset its lower chroma so the two hues match in weight. - Column-header hint is now a hover Tooltip (was a click Popover), so the header's click does only one thing — sort. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Resolve src/queries/v2/ic-extras.ts: keep this branch's removal of the standalone cohort-stat hooks (useIcCohortStats / useIcKpiPeerMedians and their PeerCohortStat / KpiPeerMedianRow types). The peer cohort now rides on the bullet rows, and the V2_PEER_COHORT_STATS / V2_IC_KPI_PEER_MEDIAN metrics they queried were dropped — main's pagination fix on those hooks (c1e733c) targets code this branch deletes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Team-aggregate distributions have no per-person histogram to chart, so render them as a value-vs-expectation row list (new CountersBlock list layout) instead of per-person peer-strip cards. The IC drilldown is unchanged and keeps its histograms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
Member names in the team needs-attention list were non-interactive buttons wired to discarded state. Render each as a router Link to the member's IC dashboard. Remove the resulting dead onMemberClick callback chain from the team screen and members heatmap; the heatmap keeps its own member detail sheet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
…nt standings The team section card scored a team aggregate against an individual department distribution — a variance mismatch, since an average of N members regresses to the middle of an individual band. Roll each metric up from per-member-vs-own-department standings (plurality direction) instead; the team aggregate stays the descriptive headline. The IC card is unchanged (no override). Extracts a shared memberMetricPeerStatus primitive (also used by the attention list, with the heatmap sharing its threshold), and wires the AI section through its department-distribution + member-values metrics so it scores like the other three. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
V2_MEMBER_VALUES_AI was registered at …0043 — the Member PRs metric id — so the AI member-values fetch clobbered the heatmap's PRs source. Repoint to …0049 to match the relocated backend metric. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Aleksandr Barkhatov <pm@aleks.bar>
67a0c59 to
084c477
Compare
row.peer, ic_kpis median); heatmap + needs-attention compute their cohort client-side from the displayed roster. Drop the peer-cohort-stats / ic-kpi-peer-median hooks, metrics, and all cohortStats plumbing.person_id in (roster): members in one query, sections aggregated over the roster — fixes empty sections (org_unit_id never matched a manager email). Retire supervisor_email/org_unit_id scoping; drop the broken team drill (ic_drill has no supervisor_email).Summary by CodeRabbit
New Features
Bug Fixes
UI/UX Updates
Removals