tech-hub-skills 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -0
  3. package/bin/cli.js +241 -0
  4. package/bin/copilot.js +182 -0
  5. package/bin/postinstall.js +42 -0
  6. package/package.json +46 -0
  7. package/tech_hub_skills/roles/ai-engineer/skills/01-prompt-engineering/README.md +252 -0
  8. package/tech_hub_skills/roles/ai-engineer/skills/02-rag-pipeline/README.md +448 -0
  9. package/tech_hub_skills/roles/ai-engineer/skills/03-agent-orchestration/README.md +599 -0
  10. package/tech_hub_skills/roles/ai-engineer/skills/04-llm-guardrails/README.md +735 -0
  11. package/tech_hub_skills/roles/ai-engineer/skills/05-vector-embeddings/README.md +711 -0
  12. package/tech_hub_skills/roles/ai-engineer/skills/06-llm-evaluation/README.md +777 -0
  13. package/tech_hub_skills/roles/azure/skills/01-infrastructure-fundamentals/README.md +264 -0
  14. package/tech_hub_skills/roles/azure/skills/02-data-factory/README.md +264 -0
  15. package/tech_hub_skills/roles/azure/skills/03-synapse-analytics/README.md +264 -0
  16. package/tech_hub_skills/roles/azure/skills/04-databricks/README.md +264 -0
  17. package/tech_hub_skills/roles/azure/skills/05-functions/README.md +264 -0
  18. package/tech_hub_skills/roles/azure/skills/06-kubernetes-service/README.md +264 -0
  19. package/tech_hub_skills/roles/azure/skills/07-openai-service/README.md +264 -0
  20. package/tech_hub_skills/roles/azure/skills/08-machine-learning/README.md +264 -0
  21. package/tech_hub_skills/roles/azure/skills/09-storage-adls/README.md +264 -0
  22. package/tech_hub_skills/roles/azure/skills/10-networking/README.md +264 -0
  23. package/tech_hub_skills/roles/azure/skills/11-sql-cosmos/README.md +264 -0
  24. package/tech_hub_skills/roles/azure/skills/12-event-hubs/README.md +264 -0
  25. package/tech_hub_skills/roles/code-review/skills/01-automated-code-review/README.md +394 -0
  26. package/tech_hub_skills/roles/code-review/skills/02-pr-review-workflow/README.md +427 -0
  27. package/tech_hub_skills/roles/code-review/skills/03-code-quality-gates/README.md +518 -0
  28. package/tech_hub_skills/roles/code-review/skills/04-reviewer-assignment/README.md +504 -0
  29. package/tech_hub_skills/roles/code-review/skills/05-review-analytics/README.md +540 -0
  30. package/tech_hub_skills/roles/data-engineer/skills/01-lakehouse-architecture/README.md +550 -0
  31. package/tech_hub_skills/roles/data-engineer/skills/02-etl-pipeline/README.md +580 -0
  32. package/tech_hub_skills/roles/data-engineer/skills/03-data-quality/README.md +579 -0
  33. package/tech_hub_skills/roles/data-engineer/skills/04-streaming-pipelines/README.md +608 -0
  34. package/tech_hub_skills/roles/data-engineer/skills/05-performance-optimization/README.md +547 -0
  35. package/tech_hub_skills/roles/data-governance/skills/01-data-catalog/README.md +112 -0
  36. package/tech_hub_skills/roles/data-governance/skills/02-data-lineage/README.md +129 -0
  37. package/tech_hub_skills/roles/data-governance/skills/03-data-quality-framework/README.md +182 -0
  38. package/tech_hub_skills/roles/data-governance/skills/04-access-control/README.md +39 -0
  39. package/tech_hub_skills/roles/data-governance/skills/05-master-data-management/README.md +40 -0
  40. package/tech_hub_skills/roles/data-governance/skills/06-compliance-privacy/README.md +46 -0
  41. package/tech_hub_skills/roles/data-scientist/skills/01-eda-automation/README.md +230 -0
  42. package/tech_hub_skills/roles/data-scientist/skills/02-statistical-modeling/README.md +264 -0
  43. package/tech_hub_skills/roles/data-scientist/skills/03-feature-engineering/README.md +264 -0
  44. package/tech_hub_skills/roles/data-scientist/skills/04-predictive-modeling/README.md +264 -0
  45. package/tech_hub_skills/roles/data-scientist/skills/05-customer-analytics/README.md +264 -0
  46. package/tech_hub_skills/roles/data-scientist/skills/06-campaign-analysis/README.md +264 -0
  47. package/tech_hub_skills/roles/data-scientist/skills/07-experimentation/README.md +264 -0
  48. package/tech_hub_skills/roles/data-scientist/skills/08-data-visualization/README.md +264 -0
  49. package/tech_hub_skills/roles/devops/skills/01-cicd-pipeline/README.md +264 -0
  50. package/tech_hub_skills/roles/devops/skills/02-container-orchestration/README.md +264 -0
  51. package/tech_hub_skills/roles/devops/skills/03-infrastructure-as-code/README.md +264 -0
  52. package/tech_hub_skills/roles/devops/skills/04-gitops/README.md +264 -0
  53. package/tech_hub_skills/roles/devops/skills/05-environment-management/README.md +264 -0
  54. package/tech_hub_skills/roles/devops/skills/06-automated-testing/README.md +264 -0
  55. package/tech_hub_skills/roles/devops/skills/07-release-management/README.md +264 -0
  56. package/tech_hub_skills/roles/devops/skills/08-monitoring-alerting/README.md +264 -0
  57. package/tech_hub_skills/roles/devops/skills/09-devsecops/README.md +265 -0
  58. package/tech_hub_skills/roles/finops/skills/01-cost-visibility/README.md +264 -0
  59. package/tech_hub_skills/roles/finops/skills/02-resource-tagging/README.md +264 -0
  60. package/tech_hub_skills/roles/finops/skills/03-budget-management/README.md +264 -0
  61. package/tech_hub_skills/roles/finops/skills/04-reserved-instances/README.md +264 -0
  62. package/tech_hub_skills/roles/finops/skills/05-spot-optimization/README.md +264 -0
  63. package/tech_hub_skills/roles/finops/skills/06-storage-tiering/README.md +264 -0
  64. package/tech_hub_skills/roles/finops/skills/07-compute-rightsizing/README.md +264 -0
  65. package/tech_hub_skills/roles/finops/skills/08-chargeback/README.md +264 -0
  66. package/tech_hub_skills/roles/ml-engineer/skills/01-mlops-pipeline/README.md +566 -0
  67. package/tech_hub_skills/roles/ml-engineer/skills/02-feature-engineering/README.md +655 -0
  68. package/tech_hub_skills/roles/ml-engineer/skills/03-model-training/README.md +704 -0
  69. package/tech_hub_skills/roles/ml-engineer/skills/04-model-serving/README.md +845 -0
  70. package/tech_hub_skills/roles/ml-engineer/skills/05-model-monitoring/README.md +874 -0
  71. package/tech_hub_skills/roles/mlops/skills/01-ml-pipeline-orchestration/README.md +264 -0
  72. package/tech_hub_skills/roles/mlops/skills/02-experiment-tracking/README.md +264 -0
  73. package/tech_hub_skills/roles/mlops/skills/03-model-registry/README.md +264 -0
  74. package/tech_hub_skills/roles/mlops/skills/04-feature-store/README.md +264 -0
  75. package/tech_hub_skills/roles/mlops/skills/05-model-deployment/README.md +264 -0
  76. package/tech_hub_skills/roles/mlops/skills/06-model-observability/README.md +264 -0
  77. package/tech_hub_skills/roles/mlops/skills/07-data-versioning/README.md +264 -0
  78. package/tech_hub_skills/roles/mlops/skills/08-ab-testing/README.md +264 -0
  79. package/tech_hub_skills/roles/mlops/skills/09-automated-retraining/README.md +264 -0
  80. package/tech_hub_skills/roles/platform-engineer/skills/01-internal-developer-platform/README.md +153 -0
  81. package/tech_hub_skills/roles/platform-engineer/skills/02-self-service-infrastructure/README.md +57 -0
  82. package/tech_hub_skills/roles/platform-engineer/skills/03-slo-sli-management/README.md +59 -0
  83. package/tech_hub_skills/roles/platform-engineer/skills/04-developer-experience/README.md +57 -0
  84. package/tech_hub_skills/roles/platform-engineer/skills/05-incident-management/README.md +73 -0
  85. package/tech_hub_skills/roles/platform-engineer/skills/06-capacity-management/README.md +59 -0
  86. package/tech_hub_skills/roles/product-designer/skills/01-requirements-discovery/README.md +407 -0
  87. package/tech_hub_skills/roles/product-designer/skills/02-user-research/README.md +382 -0
  88. package/tech_hub_skills/roles/product-designer/skills/03-brainstorming-ideation/README.md +437 -0
  89. package/tech_hub_skills/roles/product-designer/skills/04-ux-design/README.md +496 -0
  90. package/tech_hub_skills/roles/product-designer/skills/05-product-market-fit/README.md +376 -0
  91. package/tech_hub_skills/roles/product-designer/skills/06-stakeholder-management/README.md +412 -0
  92. package/tech_hub_skills/roles/security-architect/skills/01-pii-detection/README.md +319 -0
  93. package/tech_hub_skills/roles/security-architect/skills/02-threat-modeling/README.md +264 -0
  94. package/tech_hub_skills/roles/security-architect/skills/03-infrastructure-security/README.md +264 -0
  95. package/tech_hub_skills/roles/security-architect/skills/04-iam/README.md +264 -0
  96. package/tech_hub_skills/roles/security-architect/skills/05-application-security/README.md +264 -0
  97. package/tech_hub_skills/roles/security-architect/skills/06-secrets-management/README.md +264 -0
  98. package/tech_hub_skills/roles/security-architect/skills/07-security-monitoring/README.md +264 -0
  99. package/tech_hub_skills/roles/system-design/skills/01-architecture-patterns/README.md +337 -0
  100. package/tech_hub_skills/roles/system-design/skills/02-requirements-engineering/README.md +264 -0
  101. package/tech_hub_skills/roles/system-design/skills/03-scalability/README.md +264 -0
  102. package/tech_hub_skills/roles/system-design/skills/04-high-availability/README.md +264 -0
  103. package/tech_hub_skills/roles/system-design/skills/05-cost-optimization-design/README.md +264 -0
  104. package/tech_hub_skills/roles/system-design/skills/06-api-design/README.md +264 -0
  105. package/tech_hub_skills/roles/system-design/skills/07-observability-architecture/README.md +264 -0
  106. package/tech_hub_skills/roles/system-design/skills/08-process-automation/PROCESS_TEMPLATE.md +336 -0
  107. package/tech_hub_skills/roles/system-design/skills/08-process-automation/README.md +521 -0
  108. package/tech_hub_skills/skills/README.md +336 -0
  109. package/tech_hub_skills/skills/ai-engineer.md +104 -0
  110. package/tech_hub_skills/skills/azure.md +149 -0
  111. package/tech_hub_skills/skills/code-review.md +399 -0
  112. package/tech_hub_skills/skills/compliance-automation.md +747 -0
  113. package/tech_hub_skills/skills/data-engineer.md +113 -0
  114. package/tech_hub_skills/skills/data-governance.md +102 -0
  115. package/tech_hub_skills/skills/data-scientist.md +123 -0
  116. package/tech_hub_skills/skills/devops.md +160 -0
  117. package/tech_hub_skills/skills/docker.md +160 -0
  118. package/tech_hub_skills/skills/enterprise-dashboard.md +613 -0
  119. package/tech_hub_skills/skills/finops.md +184 -0
  120. package/tech_hub_skills/skills/ml-engineer.md +115 -0
  121. package/tech_hub_skills/skills/mlops.md +187 -0
  122. package/tech_hub_skills/skills/optimization-advisor.md +329 -0
  123. package/tech_hub_skills/skills/orchestrator.md +497 -0
  124. package/tech_hub_skills/skills/platform-engineer.md +102 -0
  125. package/tech_hub_skills/skills/process-automation.md +226 -0
  126. package/tech_hub_skills/skills/process-changelog.md +184 -0
  127. package/tech_hub_skills/skills/process-documentation.md +484 -0
  128. package/tech_hub_skills/skills/process-kanban.md +324 -0
  129. package/tech_hub_skills/skills/process-versioning.md +214 -0
  130. package/tech_hub_skills/skills/product-designer.md +104 -0
  131. package/tech_hub_skills/skills/project-starter.md +443 -0
  132. package/tech_hub_skills/skills/security-architect.md +135 -0
  133. package/tech_hub_skills/skills/system-design.md +126 -0
@@ -0,0 +1,540 @@
1
+ # cr-05: Review Analytics
2
+
3
+ Metrics and insights for review process optimization and bottleneck detection.
4
+
5
+ ## Overview
6
+
7
+ Review analytics provides visibility into your code review process, helping identify bottlenecks, balance workloads, and continuously improve team velocity. Track SLOs, measure cycle time, and make data-driven decisions.
8
+
9
+ ## Capabilities
10
+
11
+ ### Time Metrics
12
+ - Time to first review
13
+ - Time to approval
14
+ - Total cycle time (open to merge)
15
+ - Time in each review state
16
+
17
+ ### Load Metrics
18
+ - Reviews per developer
19
+ - Review comments given/received
20
+ - Approval rate
21
+ - Change request rate
22
+
23
+ ### Quality Metrics
24
+ - Defect escape rate
25
+ - Review iteration count
26
+ - PR size distribution
27
+ - Security findings per review
28
+
29
+ ### Process Insights
30
+ - Bottleneck identification
31
+ - Trend analysis
32
+ - SLO compliance
33
+ - Prediction analytics
34
+
35
+ ## Implementation
36
+
37
+ ### Review Analytics Dashboard
38
+
39
+ ```python
40
+ #!/usr/bin/env python3
41
+ """Review analytics and metrics collection."""
42
+
43
+ from dataclasses import dataclass, field
44
+ from datetime import datetime, timedelta
45
+ from typing import List, Dict, Optional, Tuple
46
+ from collections import defaultdict
47
+ import json
48
+
49
+ @dataclass
50
+ class ReviewEvent:
51
+ """A single review event."""
52
+ pr_number: int
53
+ event_type: str # opened, review_requested, reviewed, approved, merged
54
+ timestamp: datetime
55
+ actor: str
56
+ details: Dict = field(default_factory=dict)
57
+
58
+ @dataclass
59
+ class PRMetrics:
60
+ """Metrics for a single PR."""
61
+ pr_number: int
62
+ author: str
63
+ opened_at: datetime
64
+ merged_at: Optional[datetime] = None
65
+ first_review_at: Optional[datetime] = None
66
+ approved_at: Optional[datetime] = None
67
+ lines_added: int = 0
68
+ lines_removed: int = 0
69
+ files_changed: int = 0
70
+ review_iterations: int = 0
71
+ reviewers: List[str] = field(default_factory=list)
72
+ comments_count: int = 0
73
+
74
+ @property
75
+ def time_to_first_review(self) -> Optional[float]:
76
+ """Hours until first review."""
77
+ if self.first_review_at:
78
+ return (self.first_review_at - self.opened_at).total_seconds() / 3600
79
+ return None
80
+
81
+ @property
82
+ def time_to_approval(self) -> Optional[float]:
83
+ """Hours until approval."""
84
+ if self.approved_at:
85
+ return (self.approved_at - self.opened_at).total_seconds() / 3600
86
+ return None
87
+
88
+ @property
89
+ def cycle_time(self) -> Optional[float]:
90
+ """Hours from open to merge."""
91
+ if self.merged_at:
92
+ return (self.merged_at - self.opened_at).total_seconds() / 3600
93
+ return None
94
+
95
+ @property
96
+ def pr_size(self) -> str:
97
+ """Categorize PR size."""
98
+ total_lines = self.lines_added + self.lines_removed
99
+ if total_lines < 50:
100
+ return "XS"
101
+ elif total_lines < 200:
102
+ return "S"
103
+ elif total_lines < 500:
104
+ return "M"
105
+ elif total_lines < 1000:
106
+ return "L"
107
+ else:
108
+ return "XL"
109
+
110
+
111
+ class ReviewAnalytics:
112
+ """Analyze review metrics and generate insights."""
113
+
114
+ # SLO definitions
115
+ SLOS = {
116
+ "time_to_first_review": 4, # hours
117
+ "time_to_approval": 24, # hours
118
+ "cycle_time": 48, # hours
119
+ "max_review_iterations": 3,
120
+ "max_pr_size": 400, # lines
121
+ }
122
+
123
+ def __init__(self):
124
+ self.prs: Dict[int, PRMetrics] = {}
125
+ self.events: List[ReviewEvent] = []
126
+
127
+ def add_pr(self, pr: PRMetrics) -> None:
128
+ """Add PR metrics."""
129
+ self.prs[pr.pr_number] = pr
130
+
131
+ def add_event(self, event: ReviewEvent) -> None:
132
+ """Add review event."""
133
+ self.events.append(event)
134
+
135
+ def calculate_team_metrics(
136
+ self,
137
+ start_date: datetime,
138
+ end_date: datetime
139
+ ) -> Dict:
140
+ """Calculate team-wide metrics for date range."""
141
+ prs_in_range = [
142
+ pr for pr in self.prs.values()
143
+ if start_date <= pr.opened_at <= end_date
144
+ ]
145
+
146
+ if not prs_in_range:
147
+ return {"error": "No PRs in date range"}
148
+
149
+ merged_prs = [pr for pr in prs_in_range if pr.merged_at]
150
+
151
+ return {
152
+ "summary": {
153
+ "total_prs": len(prs_in_range),
154
+ "merged_prs": len(merged_prs),
155
+ "merge_rate": f"{len(merged_prs) / len(prs_in_range) * 100:.1f}%",
156
+ },
157
+ "time_metrics": {
158
+ "avg_time_to_first_review": self._avg([
159
+ pr.time_to_first_review for pr in merged_prs
160
+ if pr.time_to_first_review
161
+ ]),
162
+ "avg_time_to_approval": self._avg([
163
+ pr.time_to_approval for pr in merged_prs
164
+ if pr.time_to_approval
165
+ ]),
166
+ "avg_cycle_time": self._avg([
167
+ pr.cycle_time for pr in merged_prs
168
+ if pr.cycle_time
169
+ ]),
170
+ "p90_cycle_time": self._percentile([
171
+ pr.cycle_time for pr in merged_prs
172
+ if pr.cycle_time
173
+ ], 90),
174
+ },
175
+ "quality_metrics": {
176
+ "avg_review_iterations": self._avg([
177
+ pr.review_iterations for pr in merged_prs
178
+ ]),
179
+ "avg_comments_per_pr": self._avg([
180
+ pr.comments_count for pr in merged_prs
181
+ ]),
182
+ },
183
+ "size_distribution": self._size_distribution(prs_in_range),
184
+ "slo_compliance": self._calculate_slo_compliance(merged_prs),
185
+ }
186
+
187
+ def calculate_reviewer_metrics(self) -> Dict[str, Dict]:
188
+ """Calculate per-reviewer metrics."""
189
+ reviewer_stats = defaultdict(lambda: {
190
+ "reviews_given": 0,
191
+ "comments_given": 0,
192
+ "approvals": 0,
193
+ "change_requests": 0,
194
+ "avg_response_time": [],
195
+ })
196
+
197
+ for event in self.events:
198
+ if event.event_type == "reviewed":
199
+ reviewer = event.actor
200
+ reviewer_stats[reviewer]["reviews_given"] += 1
201
+
202
+ state = event.details.get("state", "")
203
+ if state == "APPROVED":
204
+ reviewer_stats[reviewer]["approvals"] += 1
205
+ elif state == "CHANGES_REQUESTED":
206
+ reviewer_stats[reviewer]["change_requests"] += 1
207
+
208
+ # Convert to final format
209
+ return {
210
+ reviewer: {
211
+ "reviews_given": stats["reviews_given"],
212
+ "approval_rate": (
213
+ f"{stats['approvals'] / stats['reviews_given'] * 100:.1f}%"
214
+ if stats["reviews_given"] > 0 else "N/A"
215
+ ),
216
+ "change_request_rate": (
217
+ f"{stats['change_requests'] / stats['reviews_given'] * 100:.1f}%"
218
+ if stats["reviews_given"] > 0 else "N/A"
219
+ ),
220
+ }
221
+ for reviewer, stats in reviewer_stats.items()
222
+ }
223
+
224
+ def identify_bottlenecks(self) -> List[Dict]:
225
+ """Identify review process bottlenecks."""
226
+ bottlenecks = []
227
+
228
+ # Check for slow first reviews
229
+ slow_first_review = [
230
+ pr for pr in self.prs.values()
231
+ if pr.time_to_first_review and
232
+ pr.time_to_first_review > self.SLOS["time_to_first_review"]
233
+ ]
234
+ if len(slow_first_review) > len(self.prs) * 0.2:
235
+ bottlenecks.append({
236
+ "type": "slow_first_review",
237
+ "severity": "high",
238
+ "message": f"{len(slow_first_review)} PRs ({len(slow_first_review)/len(self.prs)*100:.0f}%) "
239
+ f"exceed first review SLO of {self.SLOS['time_to_first_review']}h",
240
+ "recommendation": "Add more reviewers or enable auto-assignment"
241
+ })
242
+
243
+ # Check for too many iterations
244
+ high_iteration_prs = [
245
+ pr for pr in self.prs.values()
246
+ if pr.review_iterations > self.SLOS["max_review_iterations"]
247
+ ]
248
+ if high_iteration_prs:
249
+ bottlenecks.append({
250
+ "type": "high_iterations",
251
+ "severity": "medium",
252
+ "message": f"{len(high_iteration_prs)} PRs required >{self.SLOS['max_review_iterations']} review iterations",
253
+ "recommendation": "Improve PR guidelines and automated checks"
254
+ })
255
+
256
+ # Check for oversized PRs
257
+ large_prs = [
258
+ pr for pr in self.prs.values()
259
+ if pr.lines_added + pr.lines_removed > self.SLOS["max_pr_size"]
260
+ ]
261
+ if len(large_prs) > len(self.prs) * 0.3:
262
+ bottlenecks.append({
263
+ "type": "large_prs",
264
+ "severity": "medium",
265
+ "message": f"{len(large_prs)} PRs exceed {self.SLOS['max_pr_size']} lines",
266
+ "recommendation": "Encourage smaller, focused PRs"
267
+ })
268
+
269
+ # Check reviewer load imbalance
270
+ reviewer_loads = self.calculate_reviewer_metrics()
271
+ if reviewer_loads:
272
+ loads = [r["reviews_given"] for r in reviewer_loads.values()]
273
+ if max(loads) > 3 * min(loads) if min(loads) > 0 else False:
274
+ bottlenecks.append({
275
+ "type": "unbalanced_load",
276
+ "severity": "medium",
277
+ "message": "Review load is unbalanced across team",
278
+ "recommendation": "Enable load-balanced reviewer assignment"
279
+ })
280
+
281
+ return bottlenecks
282
+
283
+ def _calculate_slo_compliance(self, prs: List[PRMetrics]) -> Dict:
284
+ """Calculate SLO compliance rates."""
285
+ if not prs:
286
+ return {}
287
+
288
+ first_review_compliant = sum(
289
+ 1 for pr in prs
290
+ if pr.time_to_first_review and
291
+ pr.time_to_first_review <= self.SLOS["time_to_first_review"]
292
+ )
293
+
294
+ approval_compliant = sum(
295
+ 1 for pr in prs
296
+ if pr.time_to_approval and
297
+ pr.time_to_approval <= self.SLOS["time_to_approval"]
298
+ )
299
+
300
+ cycle_compliant = sum(
301
+ 1 for pr in prs
302
+ if pr.cycle_time and
303
+ pr.cycle_time <= self.SLOS["cycle_time"]
304
+ )
305
+
306
+ return {
307
+ "first_review_slo": f"{first_review_compliant / len(prs) * 100:.1f}%",
308
+ "approval_slo": f"{approval_compliant / len(prs) * 100:.1f}%",
309
+ "cycle_time_slo": f"{cycle_compliant / len(prs) * 100:.1f}%",
310
+ }
311
+
312
+ def _size_distribution(self, prs: List[PRMetrics]) -> Dict[str, int]:
313
+ """Calculate PR size distribution."""
314
+ dist = defaultdict(int)
315
+ for pr in prs:
316
+ dist[pr.pr_size] += 1
317
+ return dict(dist)
318
+
319
+ def _avg(self, values: List[float]) -> Optional[float]:
320
+ """Calculate average, handling empty lists."""
321
+ if not values:
322
+ return None
323
+ return round(sum(values) / len(values), 2)
324
+
325
+ def _percentile(self, values: List[float], p: int) -> Optional[float]:
326
+ """Calculate percentile."""
327
+ if not values:
328
+ return None
329
+ sorted_values = sorted(values)
330
+ idx = int(len(sorted_values) * p / 100)
331
+ return round(sorted_values[min(idx, len(sorted_values) - 1)], 2)
332
+
333
+ def generate_report(self, start_date: datetime, end_date: datetime) -> str:
334
+ """Generate markdown analytics report."""
335
+ metrics = self.calculate_team_metrics(start_date, end_date)
336
+ bottlenecks = self.identify_bottlenecks()
337
+
338
+ report = f"""# Code Review Analytics Report
339
+
340
+ **Period**: {start_date.strftime('%Y-%m-%d')} to {end_date.strftime('%Y-%m-%d')}
341
+
342
+ ## Summary
343
+ - Total PRs: {metrics['summary']['total_prs']}
344
+ - Merged PRs: {metrics['summary']['merged_prs']}
345
+ - Merge Rate: {metrics['summary']['merge_rate']}
346
+
347
+ ## Time Metrics
348
+ | Metric | Value | SLO | Status |
349
+ |--------|-------|-----|--------|
350
+ | Avg Time to First Review | {metrics['time_metrics']['avg_time_to_first_review']}h | {self.SLOS['time_to_first_review']}h | {'✅' if metrics['time_metrics']['avg_time_to_first_review'] and metrics['time_metrics']['avg_time_to_first_review'] <= self.SLOS['time_to_first_review'] else '❌'} |
351
+ | Avg Time to Approval | {metrics['time_metrics']['avg_time_to_approval']}h | {self.SLOS['time_to_approval']}h | {'✅' if metrics['time_metrics']['avg_time_to_approval'] and metrics['time_metrics']['avg_time_to_approval'] <= self.SLOS['time_to_approval'] else '❌'} |
352
+ | Avg Cycle Time | {metrics['time_metrics']['avg_cycle_time']}h | {self.SLOS['cycle_time']}h | {'✅' if metrics['time_metrics']['avg_cycle_time'] and metrics['time_metrics']['avg_cycle_time'] <= self.SLOS['cycle_time'] else '❌'} |
353
+ | P90 Cycle Time | {metrics['time_metrics']['p90_cycle_time']}h | - | - |
354
+
355
+ ## SLO Compliance
356
+ {chr(10).join(f"- {k}: {v}" for k, v in metrics['slo_compliance'].items())}
357
+
358
+ ## PR Size Distribution
359
+ {chr(10).join(f"- {size}: {count} PRs" for size, count in sorted(metrics['size_distribution'].items()))}
360
+
361
+ ## Identified Bottlenecks
362
+ """
363
+
364
+ if bottlenecks:
365
+ for b in bottlenecks:
366
+ report += f"""
367
+ ### {b['type'].replace('_', ' ').title()}
368
+ - **Severity**: {b['severity']}
369
+ - **Issue**: {b['message']}
370
+ - **Recommendation**: {b['recommendation']}
371
+ """
372
+ else:
373
+ report += "\nNo significant bottlenecks identified.\n"
374
+
375
+ return report
376
+
377
+
378
+ # GitHub Action for weekly metrics
379
+ WEEKLY_METRICS_ACTION = """
380
+ name: Weekly Review Metrics
381
+ on:
382
+ schedule:
383
+ - cron: '0 9 * * 1' # Monday 9 AM
384
+ workflow_dispatch:
385
+
386
+ jobs:
387
+ metrics:
388
+ runs-on: ubuntu-latest
389
+ steps:
390
+ - uses: actions/checkout@v4
391
+
392
+ - name: Collect PR metrics
393
+ uses: actions/github-script@v7
394
+ id: metrics
395
+ with:
396
+ script: |
397
+ const oneWeekAgo = new Date();
398
+ oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
399
+
400
+ const { data: prs } = await github.rest.pulls.list({
401
+ owner: context.repo.owner,
402
+ repo: context.repo.repo,
403
+ state: 'all',
404
+ sort: 'created',
405
+ direction: 'desc',
406
+ per_page: 100
407
+ });
408
+
409
+ const weeklyPRs = prs.filter(pr =>
410
+ new Date(pr.created_at) >= oneWeekAgo
411
+ );
412
+
413
+ const metrics = {
414
+ total: weeklyPRs.length,
415
+ merged: weeklyPRs.filter(pr => pr.merged_at).length,
416
+ open: weeklyPRs.filter(pr => pr.state === 'open').length
417
+ };
418
+
419
+ return metrics;
420
+
421
+ - name: Post to Slack
422
+ uses: slackapi/slack-github-action@v1
423
+ with:
424
+ payload: |
425
+ {
426
+ "text": "Weekly Review Metrics",
427
+ "blocks": [
428
+ {
429
+ "type": "section",
430
+ "text": {
431
+ "type": "mrkdwn",
432
+ "text": "*Weekly Code Review Metrics*\\n• Total PRs: ${{ fromJSON(steps.metrics.outputs.result).total }}\\n• Merged: ${{ fromJSON(steps.metrics.outputs.result).merged }}\\n• Open: ${{ fromJSON(steps.metrics.outputs.result).open }}"
433
+ }
434
+ }
435
+ ]
436
+ }
437
+ env:
438
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
439
+ """
440
+ ```
441
+
442
+ ### Grafana Dashboard Configuration
443
+
444
+ ```json
445
+ {
446
+ "dashboard": {
447
+ "title": "Code Review Analytics",
448
+ "panels": [
449
+ {
450
+ "title": "Time to First Review (hours)",
451
+ "type": "stat",
452
+ "targets": [
453
+ {
454
+ "expr": "avg(github_pr_time_to_first_review_hours)",
455
+ "legendFormat": "Avg"
456
+ }
457
+ ],
458
+ "thresholds": {
459
+ "steps": [
460
+ {"color": "green", "value": null},
461
+ {"color": "yellow", "value": 4},
462
+ {"color": "red", "value": 8}
463
+ ]
464
+ }
465
+ },
466
+ {
467
+ "title": "Cycle Time Trend",
468
+ "type": "timeseries",
469
+ "targets": [
470
+ {
471
+ "expr": "avg(github_pr_cycle_time_hours) by (week)",
472
+ "legendFormat": "Cycle Time"
473
+ }
474
+ ]
475
+ },
476
+ {
477
+ "title": "Reviewer Load",
478
+ "type": "barchart",
479
+ "targets": [
480
+ {
481
+ "expr": "sum(github_reviews_given) by (reviewer)",
482
+ "legendFormat": "{{reviewer}}"
483
+ }
484
+ ]
485
+ },
486
+ {
487
+ "title": "PR Size Distribution",
488
+ "type": "piechart",
489
+ "targets": [
490
+ {
491
+ "expr": "count(github_pr_lines_changed) by (size_category)"
492
+ }
493
+ ]
494
+ },
495
+ {
496
+ "title": "SLO Compliance",
497
+ "type": "gauge",
498
+ "targets": [
499
+ {
500
+ "expr": "github_review_slo_compliance_percent"
501
+ }
502
+ ],
503
+ "thresholds": {
504
+ "steps": [
505
+ {"color": "red", "value": null},
506
+ {"color": "yellow", "value": 80},
507
+ {"color": "green", "value": 95}
508
+ ]
509
+ }
510
+ }
511
+ ]
512
+ }
513
+ }
514
+ ```
515
+
516
+ ## Key Metrics Reference
517
+
518
+ | Metric | Formula | Target | Why It Matters |
519
+ |--------|---------|--------|----------------|
520
+ | Time to First Review | first_review_at - opened_at | < 4 hours | Fast feedback enables iteration |
521
+ | Time to Approval | approved_at - opened_at | < 24 hours | Prevents context switching |
522
+ | Cycle Time | merged_at - opened_at | < 48 hours | Overall delivery speed |
523
+ | Review Iterations | count(change_requests) | < 3 | Quality of initial submission |
524
+ | Review Load | reviews_per_person / team_avg | 0.8 - 1.2 | Balanced workload |
525
+ | Defect Escape Rate | bugs_in_reviewed_code / total_bugs | < 1% | Review effectiveness |
526
+
527
+ ## Connections
528
+
529
+ - **Inputs from**: All review activities (cr-01 to cr-04)
530
+ - **Outputs to**: Dashboards, reports, alerts
531
+ - **Integrates with**: Platform (pe-05 SLOs), FinOps (cost of delays)
532
+
533
+ ## Best Practices
534
+
535
+ 1. Track metrics weekly, analyze monthly, set goals quarterly
536
+ 2. Focus on trends, not individual data points
537
+ 3. Share metrics transparently with team
538
+ 4. Use data to improve process, not blame individuals
539
+ 5. Set realistic SLOs based on baseline, then improve
540
+ 6. Correlate review metrics with production quality