Skip to content

refactor: consolidate peer cohorts, roster-scope team view - #155

Merged
aleksdotbar merged 10 commits into
mainfrom
peer-cohort-consolidation
Jun 21, 2026
Merged

refactor: consolidate peer cohorts, roster-scope team view#155
aleksdotbar merged 10 commits into
mainfrom
peer-cohort-consolidation

Conversation

@il10241024

@il10241024 il10241024 commented Jun 5, 2026

Copy link
Copy Markdown

🔗 Mirrored PR cyberfabric/cyber-insight-front#88 | Author: aleksdotbar | Opened: 2026-06-05T05:14:37Z | Status: open
GitHub API does not allow setting PR author or timestamps — attribution preserved here.


  • 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.

Summary by CodeRabbit

  • New Features

    • Member-focused metrics (per-person delivery/collab/code measures) and peer-aware KPI ordering; provenance tags shown where available.
    • Richer peer-distribution visuals (quartile zones, median labels) and improved KPI value formatting.
  • Bug Fixes

    • Empty sections no longer fabricate catalog zeros; peer computations default safely when stats are missing.
    • More robust peer/cohort handling when deriving comparisons from displayed team members.
  • UI/UX Updates

    • Title‑case peer labels, tooltip provider at root, "Team range" header, refined drilldown and hero/heatmap displays.
  • Removals

    • Executive summary view and several org-level widgets removed.

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

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@aleksdotbar, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 10a8dad9-0cd1-4eb4-8055-b008dd2f980b

📥 Commits

Reviewing files that changed from the base of the PR and between 580a27a and 084c477.

📒 Files selected for processing (57)
  • src/api/index.ts
  • src/api/metric-registry.ts
  • src/api/odata.ts
  • src/api/raw-types.ts
  • src/api/transforms.test.ts
  • src/api/transforms.ts
  • src/api/view-configs.test.tsx
  • src/api/view-configs.ts
  • src/components/widgets/metric-card.tsx
  • src/components/widgets/metric-info.tsx
  • src/components/widgets/org-health-radar.tsx
  • src/components/widgets/org-kpi-cards.tsx
  • src/components/widgets/team-metrics-bar.tsx
  • src/components/widgets/teams-table.tsx
  • src/components/widgets/v2/counters-block.test.tsx
  • src/components/widgets/v2/counters-block.tsx
  • src/components/widgets/v2/distribution-strip.test.tsx
  • src/components/widgets/v2/distribution-strip.tsx
  • src/components/widgets/v2/ic-needs-attention.test.tsx
  • src/components/widgets/v2/ic-needs-attention.tsx
  • src/components/widgets/v2/kpi-tile.test.tsx
  • src/components/widgets/v2/kpi-tile.tsx
  • src/components/widgets/v2/members-heatmap/index.test.tsx
  • src/components/widgets/v2/members-heatmap/index.tsx
  • src/components/widgets/v2/section-card.test.tsx
  • src/components/widgets/v2/section-card.tsx
  • src/components/widgets/v2/section-drilldown-sheet.tsx
  • src/components/widgets/v2/section-status.tsx
  • src/components/widgets/v2/summary-with-breakdown.tsx
  • src/components/widgets/v2/team-members-attention.test.tsx
  • src/components/widgets/v2/team-members-attention.tsx
  • src/lib/insight/v2/derivations.ts
  • src/lib/insight/v2/kpi-defs.ts
  • src/lib/insight/v2/peer-status.test.ts
  • src/lib/insight/v2/peer-status.ts
  • src/lib/insight/v2/sections.ts
  • src/lib/insight/v2/team-member-status.test.ts
  • src/lib/insight/v2/team-member-status.ts
  • src/lib/peers.ts
  • src/lib/status.ts
  • src/mocks/factories.ts
  • src/mocks/handlers.ts
  • src/mocks/registry.ts
  • src/mocks/v2/factories.ts
  • src/queries/executive-view.ts
  • src/queries/team-view.ts
  • src/queries/v2/ic-extras.ts
  • src/queries/v2/team-extras.ts
  • src/routeTree.gen.ts
  • src/routes/__root.tsx
  • src/routes/ic.$person.exec.tsx
  • src/screens/executive-view.test.tsx
  • src/screens/executive-view.tsx
  • src/screens/ic-dashboard/engineering-dashboard-v2.tsx
  • src/screens/team-view-v2.tsx
  • src/screens/team-view.tsx
  • src/types/insight.ts
📝 Walkthrough

Walkthrough

This 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.

Changes

Peer cohort computation and exec feature removal

Layer / File(s) Summary
Data model: raw types and registry
src/api/raw-types.ts, src/api/metric-registry.ts, src/types/insight.ts
Raw KPI aggregates gain optional peer median/cohort fields; raw bullet rows gain percentile fields; metric registry removes EXEC_SUMMARY, V2_PEER_COHORT_STATS, V2_IC_KPI_PEER_MEDIAN and adds V2_MEMBER_VALUES_* and V2_MEMBER_PRS; BulletMetric and IcKpi types add peer/source fields.
Transform logic: KPI and bullet metrics
src/api/transforms.ts, src/api/transforms.test.ts
Adds formatKpiValue helper; extracts peer median and cohort size into IcKpi objects; computes scaled peer quartile stats and embeds as peer on BulletMetric; backfills honest-zero rows only when backend returns data; propagates source_tags.
Peer status catalog-driven
src/lib/insight/v2/peer-status.ts, src/lib/insight/v2/peer-status.test.ts
Removes cohortStats parameter; simplifies peerStatusForRow and rowStatus to use only catalog and row.peer; updates tests to inject peer stats on row objects.
Query layer: roster scoping and member values
src/queries/team-view.ts, src/queries/v2/team-extras.ts, src/mocks/handlers.ts, src/mocks/factories.ts
Team queries accept optional roster for person_id filtering; team-extras fetches member values per section with client-side range computation; handlers and factories support V2_MEMBER_VALUES and V2_MEMBER_PRS metrics with peer quartile/percentile mock data.
KPI ordering and sections
src/lib/insight/v2/kpi-defs.ts, src/lib/insight/v2/sections.ts
Adds IC_KPI_ORDER allowlist and orderIcKpis helper for tile ordering; removes code_quality from IC_SECTIONS.
Composition and collaboration
src/lib/insight/v2/derivations.ts
CompositionRow adds sources field; introduces collectSources helper; deriveCollabActivities populates sources using metric-key constants.
CountersBlock and HeroTile
src/components/widgets/v2/counters-block.tsx, src/components/widgets/v2/counters-block.test.tsx
Removes cohortStats prop; derives stats from row.peer; introduces PeerZoneBar and HeroRangeBar with peer distribution visualization; refactors InPackFold into bordered panel with desktop/mobile layouts; updates text capitalization.
DistributionStrip and peer positioning
src/components/widgets/v2/distribution-strip.tsx, src/components/widgets/v2/distribution-strip.test.tsx
Removes cohortStats prop; computes peer stats from row.peer; updates position text to title case ("Top 25%"/"No peer data"); uses STATUS_SURFACE_CLASS.
IcNeedsAttention
src/components/widgets/v2/ic-needs-attention.tsx, src/components/widgets/v2/ic-needs-attention.test.tsx
Removes cohortStats prop; derives stats from row.peer; changes median label to "Median".
KpiTile with embedded peer median
src/components/widgets/v2/kpi-tile.tsx, src/components/widgets/v2/kpi-tile.test.tsx
Removes median prop and KpiPeerMedian type; uses kpi.peer_median; computes valueStatus and renders peer-median footer or "No peer data"; adds source_tags and trend badge.
MembersHeatmap client cohort computation
src/components/widgets/v2/members-heatmap/index.tsx, src/components/widgets/v2/members-heatmap/index.test.tsx
Removes cohortStats prop; adds teamPeerStatus helper using client-computed quartiles; splits cohort state into statsByColumn and cohortByMetric; removes two columns; updates legend/popover text capitalization.
SectionCard status without cohorts
src/components/widgets/v2/section-card.tsx
Removes cohortStats prop; calls rowStatus with only byMetricKey; updates styling to use STATUS_BG_CLASS/STATUS_TEXT_CLASS; adjusts hover opacity and badge capitalization.
SectionDrilldownSheet with DrilldownPanel
src/components/widgets/v2/section-drilldown-sheet.tsx
Introduces DrilldownPanel abstraction for dialog/sheet layout switching; removes cohortStats prop; adds sources to SummaryWithBreakdown for collaboration.
SummaryWithBreakdown source provenance
src/components/widgets/v2/summary-with-breakdown.tsx
Adds optional sources prop; introduces SOURCE_LABELS and formatSources; conditionally renders provenance instead of status label; updates styling to STATUS_TEXT_CLASS.
TeamMembersAttention local cohort
src/components/widgets/v2/team-members-attention.tsx, src/components/widgets/v2/team-members-attention.test.tsx
Removes cohortStats and cohortSize props; builds local cohortByMetric from members; uses peerStatsFor for quartile computation; updates subtitle to use members.length.
EngineeringDashboardV2 KPI and drilldown
src/screens/ic-dashboard/engineering-dashboard-v2.tsx
Uses orderIcKpis for tile ordering; computes peerCount/hasKpiData from data.kpis; removes KPI median queries; replaces effect-based drilldown reset with render-time state tracking; maps SectionDrilldownSheet per section.
TeamViewV2Screen roster and drilldown
src/screens/team-view-v2.tsx
Passes roster to useTeamBulletSections; removes useIcCohortStats; replaces effect-based state reset with render-time check; maps SectionDrilldownSheet per section; simplifies member ID derivation.
TeamViewScreen roster passing
src/screens/team-view.tsx
Passes computed roster to useTeamBulletSection calls; removes drill-click handler.
UI polish and class renames
src/lib/peers.ts, src/lib/status.ts, src/components/widgets/metric-card.tsx, src/components/widgets/metric-info.tsx, src/routes/__root.tsx, src/routes/ic.$person.exec.tsx, src/routeTree.gen.ts, src/api/odata.ts, src/api/view-configs.ts
PEER_LABEL strings capitalized; STATUS_BG/TEXT/SURFACE renamed to _CLASS variants; metric card header text simplified; TooltipProvider moved to root; exec route removed; odata andFilters helper added.
Executive view feature removal
src/queries/executive-view.ts, src/screens/executive-view.tsx, src/screens/executive-view.test.tsx, src/api/view-configs.ts, src/api/view-configs.test.tsx, src/mocks/factories.ts, src/mocks/registry.ts, src/mocks/v2/factories.ts, src/queries/v2/ic-extras.ts, src/types/insight.ts
Removes useExecSummary hook and ExecSummary type; removes ExecutiveViewScreen component and tests; removes exec view-config hook and tests; removes exec mock factories; removes peer cohort and KPI peer median mocks/queries; removes exec-related type definitions.

🎯 4 (Complex) | ⏱️ ~60 minutes

🐰 A rabbit hops through peer data fields with glee,
Embedding quartiles for all to see,
No more cohorts passed around,
Client computation, locally bound,
Exec's curtain call takes a bow,
Capitalized labels dance now!

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch peer-cohort-consolidation

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 and usage tips.

@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: 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 win

Handle zero peer median as valid data.

Line 31 and Line 52 currently treat peer_median = 0 as missing. Per the IcKpi contract, missing cohort is null, 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 win

Keep decimal1 delta formatting consistent with KPI value formatting.

With decimal1 now shown as one decimal, formatDelta still rounds to integers on Line 96, which can display 0 while 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 win

Use section-scoped metric keys for cohort aggregation.

cohortByMetric is keyed by b.metric_key, but status direction is resolved via bulletCatalogKey(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 win

Stabilize sources ordering for deterministic UI output.

collectSources currently 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

📥 Commits

Reviewing files that changed from the base of the PR and between 53d77ad and 65627d6.

📒 Files selected for processing (54)
  • src/api/index.ts
  • src/api/metric-registry.ts
  • src/api/odata.ts
  • src/api/raw-types.ts
  • src/api/transforms.test.ts
  • src/api/transforms.ts
  • src/api/view-configs.test.tsx
  • src/api/view-configs.ts
  • src/components/widgets/metric-card.tsx
  • src/components/widgets/metric-info.tsx
  • src/components/widgets/org-health-radar.tsx
  • src/components/widgets/org-kpi-cards.tsx
  • src/components/widgets/team-metrics-bar.tsx
  • src/components/widgets/teams-table.tsx
  • src/components/widgets/v2/counters-block.test.tsx
  • src/components/widgets/v2/counters-block.tsx
  • src/components/widgets/v2/distribution-strip.test.tsx
  • src/components/widgets/v2/distribution-strip.tsx
  • src/components/widgets/v2/ic-needs-attention.test.tsx
  • src/components/widgets/v2/ic-needs-attention.tsx
  • src/components/widgets/v2/kpi-tile.test.tsx
  • src/components/widgets/v2/kpi-tile.tsx
  • src/components/widgets/v2/members-heatmap/index.test.tsx
  • src/components/widgets/v2/members-heatmap/index.tsx
  • src/components/widgets/v2/section-card.tsx
  • src/components/widgets/v2/section-drilldown-sheet.tsx
  • src/components/widgets/v2/section-status.tsx
  • src/components/widgets/v2/summary-with-breakdown.tsx
  • src/components/widgets/v2/team-members-attention.test.tsx
  • src/components/widgets/v2/team-members-attention.tsx
  • src/lib/insight/v2/derivations.ts
  • src/lib/insight/v2/kpi-defs.ts
  • src/lib/insight/v2/peer-status.test.ts
  • src/lib/insight/v2/peer-status.ts
  • src/lib/insight/v2/sections.ts
  • src/lib/peers.ts
  • src/lib/status.ts
  • src/mocks/factories.ts
  • src/mocks/handlers.ts
  • src/mocks/registry.ts
  • src/mocks/v2/factories.ts
  • src/queries/executive-view.ts
  • src/queries/team-view.ts
  • src/queries/v2/ic-extras.ts
  • src/queries/v2/team-extras.ts
  • src/routeTree.gen.ts
  • src/routes/__root.tsx
  • src/routes/ic.$person.exec.tsx
  • src/screens/executive-view.test.tsx
  • src/screens/executive-view.tsx
  • src/screens/ic-dashboard/engineering-dashboard-v2.tsx
  • src/screens/team-view-v2.tsx
  • src/screens/team-view.tsx
  • src/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

Comment thread src/components/widgets/v2/summary-with-breakdown.tsx
Comment thread src/mocks/factories.ts
Comment thread src/mocks/factories.ts Outdated
Comment thread src/mocks/handlers.ts
Comment thread src/mocks/handlers.ts
Comment thread src/queries/team-view.ts Outdated
Comment thread src/queries/team-view.ts
Comment thread src/queries/team-view.ts
Comment thread src/queries/v2/team-extras.ts Outdated
Comment thread src/routes/__root.tsx
aleksdotbar and others added 4 commits June 8, 2026 15:20
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>
@aleksdotbar
aleksdotbar requested a review from a team as a code owner June 16, 2026 10:32
aleksdotbar and others added 5 commits June 16, 2026 12:35
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>
@aleksdotbar
aleksdotbar force-pushed the peer-cohort-consolidation branch from 67a0c59 to 084c477 Compare June 18, 2026 17:54
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.

4 participants