Skip to content

feat: implement PR velocity metric in trend analyzer#473

Open
ANU5565 wants to merge 3 commits into
agnivo988:mainfrom
ANU5565:feature/trend-analysis-phase-1
Open

feat: implement PR velocity metric in trend analyzer#473
ANU5565 wants to merge 3 commits into
agnivo988:mainfrom
ANU5565:feature/trend-analysis-phase-1

Conversation

@ANU5565

@ANU5565 ANU5565 commented Jun 7, 2026

Copy link
Copy Markdown

This PR completes Phase 1 of the Trend Analysis feature.

Added PRVelocity field to MonthlyMetric struct.

Implemented CalculatePRVelocity helper function in trends.go.

Integrated PR velocity calculation into the AnalyzeTrends pipeline.

Added unit test in trends_test.go.

Summary by CodeRabbit

  • New Features

    • Trend reports now include a pull request velocity metric (average PR merge time in hours) tracked per month and surfaced in trend summaries; shows 0 when no qualifying PRs exist.
  • Tests

    • Added unit test validating the PR velocity calculation produces expected average merge durations.

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 79d0aa35-51a3-4b0f-bb40-c36685087e8b

📥 Commits

Reviewing files that changed from the base of the PR and between 4721f8d and ffb785a.

📒 Files selected for processing (2)
  • internal/analyzer/trends.go
  • internal/analyzer/trends_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/analyzer/trends.go

📝 Walkthrough

Walkthrough

Adds PR velocity metrics: new PRVelocity fields on monthly and overall trend structs, a CalculatePRVelocity function that averages merged PR durations in hours, integrates per-month velocity assignment in AnalyzeTrends, and adds a unit test verifying a 24-hour case.

Changes

PR Velocity Metrics

Layer / File(s) Summary
PR Velocity data structures
internal/analyzer/trends.go
Adds PRVelocity float64 fields to MonthlyMetric and TrendMetrics for JSON-serializable PR merge-duration metrics.
PR Velocity calculation
internal/analyzer/trends.go
Adds CalculatePRVelocity(prs []github.PullRequest) float64 which averages merge durations (hours) for PRs with MergedAt != nil and CreatedAt < MergedAt, returning 0 when none qualify.
Integration into trend analysis
internal/analyzer/trends.go
AnalyzeTrends() filters PRs per month (by CreatedAt in [monthStart, monthEnd)) and sets each month's MonthlyMetric.PRVelocity using CalculatePRVelocity; includes a minor whitespace edit.
PR velocity metric tests
internal/analyzer/trends_test.go
Adds TestCalculatePRVelocity that creates a PR with a 24-hour Created→Merged interval and asserts CalculatePRVelocity returns approximately 24.0.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

nsoc26, level3

Poem

🐰 I hopped through code with nimble paws,
Counting hours between create and merge,
Twenty-four ticks in a tidy line,
Metrics bloom and numbers sing,
A rabbit cheers: the trends emerge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: implementing a PR velocity metric in the trend analyzer, which aligns with the primary objectives.
Linked Issues check ✅ Passed All coding requirements from issue #472 are met: data structures (MonthlyMetric, TrendMetrics) include PRVelocity field, CalculatePRVelocity function is implemented, AnalyzeTrends integrates the calculation, and unit tests verify the logic.
Out of Scope Changes check ✅ Passed All changes are directly within scope: additions to trends.go and trends_test.go only implement PR velocity functionality as specified in issue #472 with no extraneous modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@internal/analyzer/trends_test.go`:
- Around line 7-17: The test in trends_test.go is a placeholder and never
exercises the PR velocity logic; replace the dummy assertions with a real unit
test that sets up sample PR/commit data, invokes CalculatePRVelocity (or the
higher-level AnalyzeTrends wrapper) and asserts the expected monthly PR velocity
outputs; locate the test function TestMonthlyMetricCalculation and: create
deterministic input fixtures for PR open/close/merge times, call
CalculatePRVelocity (or AnalyzeTrends) with those fixtures, and assert the
returned metrics (counts/rates) against expected values so the test fails on
regressions.

In `@internal/analyzer/trends.go`:
- Line 67: TrendMetrics.PRVelocity is declared but never set by AnalyzeTrends,
so consumers get 0; update AnalyzeTrends (and any helper e.g.,
computeTrendMetrics or accumulatePRStats) to calculate and assign PRVelocity on
the returned TrendMetrics struct by computing the desired velocity metric from
PR-related data (for example average PRs per period or slope over the window)
using the same source the function uses for PR counts; ensure you set
TrendMetrics.PRVelocity before returning and update any code paths that build
TrendMetrics in lines ~148-157 to also populate PRVelocity consistently.
- Around line 154-156: The loop currently sets every
metrics.MonthlyData[i].PRVelocity to CalculatePRVelocity(prs) using the full PR
list; instead, for each metrics.MonthlyData entry call CalculatePRVelocity with
only the PRs belonging to that month (filter prs by the MonthlyData[i] date
range or month identifier) and assign that result to
metrics.MonthlyData[i].PRVelocity so each month gets a month-specific velocity;
update the loop around metrics.MonthlyData and use the filtered slice when
invoking CalculatePRVelocity.
🪄 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: 9b774672-416f-4996-900a-092afedc48bc

📥 Commits

Reviewing files that changed from the base of the PR and between 6d6fa89 and 945cafb.

📒 Files selected for processing (2)
  • internal/analyzer/trends.go
  • internal/analyzer/trends_test.go

Comment thread internal/analyzer/trends_test.go Outdated
Comment thread internal/analyzer/trends.go
Comment thread internal/analyzer/trends.go

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
internal/analyzer/trends.go (1)

67-67: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Top-level TrendMetrics.PRVelocity is still never populated.

The struct field was added but AnalyzeTrends does not assign it anywhere (lines 96-178), so consumers always receive the zero value even when monthly PR velocity data exists.

🔧 Suggested fix

Add this assignment after line 152 (after PR trend analysis):

 	// Analyze PR trends
 	metrics.PRTrend, metrics.PRMergeRate = AnalyzePRTrends(prs, metrics.MonthlyData)
+	metrics.PRVelocity = CalculatePRVelocity(prs)
 	for _, m := range metrics.MonthlyData {
🤖 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 `@internal/analyzer/trends.go` at line 67, TrendMetrics.PRVelocity was added
but never set in AnalyzeTrends; update AnalyzeTrends (the function that computes
trends and calls the PR trend analysis) to assign the computed PR velocity value
into the TrendMetrics.PRVelocity field after the PR trend analysis completes
(i.e., once you have the monthly PR velocity value from the PR trend
computation), ensuring the code that constructs or returns TrendMetrics includes
PRVelocity so consumers receive the populated value.
🧹 Nitpick comments (1)
internal/analyzer/trends_test.go (1)

8-23: ⚡ Quick win

Consider adding test cases for edge conditions.

The current test covers the basic happy path with a single merged PR. Adding test cases for the following scenarios would improve confidence in the implementation:

  • Multiple PRs to verify averaging logic
  • Unmerged PRs (nil MergedAt) to confirm they're excluded
  • Empty PR slice to verify zero return
  • PRs where CreatedAt >= MergedAt to confirm exclusion
📋 Example additional test cases
func TestCalculatePRVelocity_MultiplePRs(t *testing.T) {
    now := time.Now()
    prs := []github.PullRequest{
        {CreatedAt: now.Add(-24 * time.Hour), MergedAt: &now}, // 24h
        {CreatedAt: now.Add(-48 * time.Hour), MergedAt: &now}, // 48h
    }
    velocity := CalculatePRVelocity(prs)
    expected := 36.0 // (24 + 48) / 2
    if velocity < expected-0.01 || velocity > expected+0.01 {
        t.Errorf("Expected ~36.0 hours, got %f", velocity)
    }
}

func TestCalculatePRVelocity_UnmergedPRs(t *testing.T) {
    prs := []github.PullRequest{
        {CreatedAt: time.Now(), MergedAt: nil}, // unmerged
    }
    velocity := CalculatePRVelocity(prs)
    if velocity != 0 {
        t.Errorf("Expected 0 for unmerged PRs, got %f", velocity)
    }
}
🤖 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 `@internal/analyzer/trends_test.go` around lines 8 - 23, Add edge-case unit
tests for CalculatePRVelocity: (1) a Multiple PRs test that supplies several
merged PRs (use differing CreatedAt/MergedAt) and asserts the average hours
(allow small float tolerance); (2) an Unmerged PRs test that includes PRs with
nil MergedAt and asserts the result is 0; (3) an Empty slice test asserting 0 is
returned for no PRs; and (4) a CreatedAfterMerged test that provides PRs where
CreatedAt >= MergedAt and asserts those PRs are excluded (result behaves like
excluding them). Reference the CalculatePRVelocity function and use
github.PullRequest structs with controlled time values in
internal/analyzer/trends_test.go.
🤖 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 `@internal/analyzer/trends_test.go`:
- Around line 20-22: The test in trends_test.go checks float equality with "if
velocity != 24.0" which is fragile; change it to a tolerance-based comparison
using an epsilon (e.g. 1e-9) and fail the test when math.Abs(velocity - 24.0) >
epsilon. Import math if needed and update the error message in the failing
branch to show the expected value, actual value, and the used epsilon for
clarity.

---

Duplicate comments:
In `@internal/analyzer/trends.go`:
- Line 67: TrendMetrics.PRVelocity was added but never set in AnalyzeTrends;
update AnalyzeTrends (the function that computes trends and calls the PR trend
analysis) to assign the computed PR velocity value into the
TrendMetrics.PRVelocity field after the PR trend analysis completes (i.e., once
you have the monthly PR velocity value from the PR trend computation), ensuring
the code that constructs or returns TrendMetrics includes PRVelocity so
consumers receive the populated value.

---

Nitpick comments:
In `@internal/analyzer/trends_test.go`:
- Around line 8-23: Add edge-case unit tests for CalculatePRVelocity: (1) a
Multiple PRs test that supplies several merged PRs (use differing
CreatedAt/MergedAt) and asserts the average hours (allow small float tolerance);
(2) an Unmerged PRs test that includes PRs with nil MergedAt and asserts the
result is 0; (3) an Empty slice test asserting 0 is returned for no PRs; and (4)
a CreatedAfterMerged test that provides PRs where CreatedAt >= MergedAt and
asserts those PRs are excluded (result behaves like excluding them). Reference
the CalculatePRVelocity function and use github.PullRequest structs with
controlled time values in internal/analyzer/trends_test.go.
🪄 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: 6ffe9ada-b024-4787-bc58-29658ebf046a

📥 Commits

Reviewing files that changed from the base of the PR and between 945cafb and 4721f8d.

📒 Files selected for processing (2)
  • internal/analyzer/trends.go
  • internal/analyzer/trends_test.go

Comment thread internal/analyzer/trends_test.go Outdated
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.

[Feature]: Implement Trend Analysis Phase 1 (Data Structures & Analyzer)

1 participant