Skip to content

fix: resolve missing lead time breakdown when review events are absen…#697

Open
THEAbhishekjoshi wants to merge 1 commit into
middlewarehq:mainfrom
THEAbhishekjoshi:fix-lead-time-breakdown
Open

fix: resolve missing lead time breakdown when review events are absen…#697
THEAbhishekjoshi wants to merge 1 commit into
middlewarehq:mainfrom
THEAbhishekjoshi:fix-lead-time-breakdown

Conversation

@THEAbhishekjoshi
Copy link
Copy Markdown

@THEAbhishekjoshi THEAbhishekjoshi commented May 21, 2026

Summary

Fixes #696
Fixes incorrect Lead Time breakdown calculations when PRs are merged without review and/or approval events.

Previously, if a PR skipped parts of the review chain, portions of the elapsed lifecycle time were not attributed to any breakdown component, causing the breakdown totals to not reconcile with the overall lead/cycle time.


Problem

The existing implementation assumes the following review chain always exists:

Ready for Review → Review → Approval → Merge

However, some PRs may be:

  • merged without reviews
  • reviewed but merged without approval

In those cases:

  • first_review_time
  • rework_time
  • merge_time

were set to -1 / None, causing parts of the elapsed time to disappear from the Lead Time breakdown.

Example:

Scenario Missing Time
No review + no approval ready → merge
Review exists, no approval review → merge

As a result, the total cycle time remained correct, but the breakdown components no longer summed to the total.


Solution

Implemented fallback timing behavior so elapsed time is attributed to the next logical stage when review events are missing.

Fallback behavior:

Scenario Response Time Rework Time Merge Time
Review + Approval ready → review review → approval approval → merge
Review, no Approval ready → review review → merge 0
No Review, no Approval ready → merge 0 0

This ensures:

  • no elapsed lifecycle time is lost
  • breakdown components reconcile with total lead/cycle time
  • analytics remain meaningful even for unreviewed PRs

Summary by CodeRabbit

  • Bug Fixes
    • Improved pull request metrics accuracy by excluding bot-created events from analytics calculations
    • Enhanced rework time and merge time calculations to provide meaningful values instead of placeholder indicators when review data is unavailable
    • Fixed first review time computation to derive from actual response timestamps

Review Change Stack

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 21, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Walkthrough

The PR updates lead time metric calculations in etl_code_analytics.py to account for missing review/approval events by introducing fallback anchors. Bot-filtered events are passed to metric computation, and first_response_end_time is introduced to enable rework_time calculation even when no approved reviews exist, replacing hardcoded sentinel values with real elapsed durations.

Changes

Lead Time Breakdown Components

Layer / File(s) Summary
Bot event filtering at call site
backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py
create_pr_metrics filters out bot-created PR events into non_bot_pr_events and passes the filtered list to get_pr_performance instead of the raw event list.
First response and rework time calculation
backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py
get_pr_performance introduces first_response_end_time (derived from first_review.created_at when available, otherwise pr.state_changed_at) and uses it to compute rework_time (seconds) when there are no approved reviews, replacing the prior -1 sentinel with an actual elapsed duration.
Merge time sentinel logic update
backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py
get_pr_performance updates merge_time handling: unmerged PRs retain -1, but merged PRs with no approved reviews now get merge_time = 0 instead of -1.
First review time calculation update
backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py
get_pr_performance changes first_review_time to be derived from first_response_end_time (falling back to pr.state_changed_at when first_review is missing) instead of using first_review.created_at with an -1 fallback.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A metric dance, when reviews don't show,
Time falls through gaps, yet code must flow.
Now anchors catch what silence left behind,
And lead times sum with truth refined.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Title check ✅ Passed The title accurately describes the main fix: resolving missing lead time breakdown calculations when review events are absent, which aligns with the core changeset.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering the linked issue, problem explanation, and solution with clear fallback behavior tables.
Linked Issues check ✅ Passed The code changes fully implement the requested fallback timing behavior from #696: introducing first_response_end_time to handle missing reviews, setting rework_time based on available events, and reconciling merge_time to ensure all lifecycle time is attributed.
Out of Scope Changes check ✅ Passed All changes are directly focused on fixing the lead time breakdown calculation logic as specified in #696; no unrelated modifications are present in the PR.

✏️ 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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py (1)

98-108: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against negative rework_time for parity with merge_time.

The merge_time branch on line 119 already clamps negatives to -1 to prevent garbage when events arrive out of expected order (e.g., approval recorded after merge, clock skew, post-close reviews). The new rework_time = (pr.state_changed_at - first_response_end_time).total_seconds() path on lines 99–101 has no such guard, so a first_review.created_at later than pr.state_changed_at would yield a negative rework duration that flows through to pr.rework_time (line 33–35 only filters -1, not negatives).

🛡️ Suggested guard
         if not approved_reviews:
-            rework_time = (
-                pr.state_changed_at - first_response_end_time
-            ).total_seconds()
+            rework_time = (
+                pr.state_changed_at - first_response_end_time
+            ).total_seconds()
+            # Prevent garbage state when review is recorded after state change
+            rework_time = -1 if rework_time < 0 else rework_time
🤖 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 `@backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py` around
lines 98 - 108, The computed rework_time in etl_code_analytics.py can be
negative when using the branch that sets rework_time = (pr.state_changed_at -
first_response_end_time).total_seconds(); update that branch to clamp negative
values the same way merge_time does (set rework_time to -1 when the computed
value is < 0) so pr.rework_time never receives a raw negative duration. Locate
the variables/expressions rework_time, pr.state_changed_at,
first_response_end_time, first_review and approved_reviews and apply the guard
there to match the merge_time behavior.
🧹 Nitpick comments (1)
backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py (1)

131-138: 💤 Low value

Dead fallback branch in first_review_time.

first_response_end_time is now first_review.created_at if first_review else pr.state_changed_at (lines 92–96). Since create_pr_metrics returns early for PullRequestState.OPEN (line 24–25), pr.state_changed_at is always set when this code runs, making first_response_end_time unconditionally truthy. The if first_response_end_time else -1 ternary on lines 136–137 is therefore unreachable and can be simplified.

♻️ Suggested simplification
         return PRPerformance(
             first_review_time=(
-                (
-                    first_response_end_time - pull_request_ready_for_review_time
-                ).total_seconds()
-                if first_response_end_time
-                else -1
+                first_response_end_time - pull_request_ready_for_review_time
+            ).total_seconds(),
-            ),
             rework_time=rework_time,
🤖 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 `@backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py` around
lines 131 - 138, The fallback branch in PRPerformance.first_review_time is dead
because first_response_end_time is always truthy; simplify the expression by
removing the ternary and unconditionally computing (first_response_end_time -
pull_request_ready_for_review_time).total_seconds() when constructing
PRPerformance in create_pr_metrics (use the existing symbols
first_response_end_time and pull_request_ready_for_review_time and the
PRPerformance constructor) so the unreachable "else -1" branch is eliminated.
🤖 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.

Outside diff comments:
In `@backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py`:
- Around line 98-108: The computed rework_time in etl_code_analytics.py can be
negative when using the branch that sets rework_time = (pr.state_changed_at -
first_response_end_time).total_seconds(); update that branch to clamp negative
values the same way merge_time does (set rework_time to -1 when the computed
value is < 0) so pr.rework_time never receives a raw negative duration. Locate
the variables/expressions rework_time, pr.state_changed_at,
first_response_end_time, first_review and approved_reviews and apply the guard
there to match the merge_time behavior.

---

Nitpick comments:
In `@backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py`:
- Around line 131-138: The fallback branch in PRPerformance.first_review_time is
dead because first_response_end_time is always truthy; simplify the expression
by removing the ternary and unconditionally computing (first_response_end_time -
pull_request_ready_for_review_time).total_seconds() when constructing
PRPerformance in create_pr_metrics (use the existing symbols
first_response_end_time and pull_request_ready_for_review_time and the
PRPerformance constructor) so the unreachable "else -1" branch is eliminated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f10b82b8-8ce9-4f3e-8c61-2ece778f78ee

📥 Commits

Reviewing files that changed from the base of the PR and between 5c3f32d and bd0edb3.

📒 Files selected for processing (1)
  • backend/analytics_server/mhq/service/code/sync/etl_code_analytics.py

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.

Lead Time breakdown missing time when review events are absent

2 participants