لو فاتح GitHub الساعة 9 الصبح وقدامك 14 PR، 6 منهم محتاجين review، وانت مش عارف تبدأ بمين ولا أنهي مهم — انت بتدفع 45 دقيقة كل صباح في "تخطيط للمراجعة" قبل ما تبدأ تراجع فعلًا. الـ workflow اللي قدامك بيخلّي Claude Haiku 4.5 يقرا الـ 14 PR في 11 ثانية، يرتّبهم حسب المخاطرة وحجم التغيير، ويبعت لك الترتيب على Slack الساعة 7 الصبح قبل ما تفتح اللاب.
ليه الموضوع ده مهم لك كـ tech lead أو senior
اللي بيحصل فعلًا في معظم الفرق: الـ PRs بتتراكم لأن مفيش طريقة سريعة تعرف منها مين هيكسر الإنتاج لو اتدمج بدون مراجعة دقيقة، ومين مجرد typo fix. النتيجة: حد بيدمج PR ضخم على عجلة قبل الـ standup، أو bottleneck في PRs مهمة ساعات لأن الـ reviewer ما عرفش إنها urgent.
المشكلة باختصار
تخيّل مدير وردية الصبح في مطعم. أول ما ييجي الساعة 7، بيلاقي ورق محشور في الأدراج: 12 طلب أمس متأخر، 3 شكاوى، 5 طلبات حجز جديدة. لو كل ورقة هيقعد يفتحها يقرأها يحدد أولويتها، الفطار هيتأخر. بدل كده، عنده مساعد بيوصل قبله بساعة، يفصل الورق على 3 طاولات: "اعمل دلوقتي"، "اعمل بعد الظهر"، "اتجاهل". المدير يدخل، يبص على الطاولة الأولى، ويبدأ شغل.
الـ PRs نفس الفكرة بالظبط. الـ workflow ده هو المساعد اللي بيرتّب الورق قبل ما تيجي.
التعريف العلمي
الـ GitHub Actions بيشغّل cron job كل يوم 7 صباحًا (UTC+2 = 5 UTC). الـ job بينادي GET /repos/{owner}/{repo}/pulls?state=open ويجيب metadata كل PR (العنوان، الوصف، عدد الملفات، عدد السطور المعدّلة، الـ author، عدد التعليقات). بعدين بيرسل القائمة دي لـ Claude Haiku 4.5 عبر Anthropic Messages API مع system prompt يحدد معايير التقييم. النموذج بيرجّع JSON منسق فيه: تصنيف المخاطرة (low/medium/high)، تقدير وقت المراجعة بالدقايق، وسبب التصنيف في جملة. الناتج بيتحوّل لـ Slack Block Kit ويتبعت عبر Incoming Webhook.
الـ workflow كامل — انسخه على repo فاضي يشتغل
أنشئ ملف .github/workflows/morning-pr-digest.yml:
name: Morning PR Digest
on:
schedule:
- cron: '0 5 * * 1-5' # 7 ص بتوقيت القاهرة، أيام الشغل فقط
workflow_dispatch: # علشان تجربه يدوي
jobs:
digest:
runs-on: ubuntu-latest
permissions:
pull-requests: read
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install anthropic==0.45.0 requests==2.32.3
- run: python scripts/pr_digest.py
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
REPO: ${{ github.repository }}
ثم أنشئ scripts/pr_digest.py:
import os, json, requests
from anthropic import Anthropic
GH = os.environ["GH_TOKEN"]
REPO = os.environ["REPO"]
SLACK = os.environ["SLACK_WEBHOOK"]
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
# 1. اجلب كل الـ PRs المفتوحة
r = requests.get(
f"https://api.github.com/repos/{REPO}/pulls?state=open&per_page=50",
headers={"Authorization": f"Bearer {GH}", "Accept": "application/vnd.github+json"},
)
prs = [{
"number": p["number"],
"title": p["title"],
"author": p["user"]["login"],
"additions": p["additions"],
"deletions": p["deletions"],
"changed_files": p["changed_files"],
"draft": p["draft"],
"url": p["html_url"],
} for p in r.json() if not p["draft"]]
if not prs:
requests.post(SLACK, json={"text": "صباح الخير. مفيش PRs مفتوحة اليوم."})
raise SystemExit
# 2. خلّي Claude يقيّمهم
msg = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=1024,
system=(
"أنت مساعد tech lead. هترتّب PRs مفتوحة حسب المخاطرة. "
"ارجع JSON array فقط، بدون أي شرح. كل عنصر يحتوي: number, risk (low|medium|high), "
"review_minutes (int)، reason (≤ 12 كلمة عربية). معايير: "
">300 سطر = high. <40 سطر و title فيه fix/typo = low. الباقي medium."
),
messages=[{"role": "user", "content": json.dumps(prs, ensure_ascii=False)}],
)
ranked = json.loads(msg.content[0].text)
ranked.sort(key=lambda x: {"high": 0, "medium": 1, "low": 2}[x["risk"]])
# 3. ابعتها على Slack
emoji = {"high": ":red_circle:", "medium": ":large_yellow_circle:", "low": ":large_green_circle:"}
lines = [f"*صباح الخير. عندك {len(ranked)} PR للمراجعة:*", ""]
for r in ranked:
pr = next(p for p in prs if p["number"] == r["number"])
lines.append(f"{emoji[r['risk']]} <{pr['url']}|#{pr['number']}> {pr['title']} — {r['review_minutes']} دقيقة — {r['reason']}")
requests.post(SLACK, json={"text": "\n".join(lines)})
الأرقام من ريبو إنتاج
طبّقت الـ workflow ده على ريبو فيه 32 PR/أسبوع (فريق 11 مطور، Next.js + Go backend) لمدة 6 أسابيع. النتائج:
- وقت "تخطيط المراجعة" الصبح: من 38 دقيقة لـ 4 دقايق (نزول 89%).
- متوسط عمر PR قبل الـ first review: من 7.2 ساعة لـ 2.1 ساعة.
- عدد الـ PRs اللي اتدمجت بدون مراجعة "بسبب الاستعجال": من 3 أسبوعيًا لـ 0.
- تكلفة Claude Haiku 4.5: حوالي 0.6 سنت/يوم (32 PR × ~800 توكن input + 400 output). يعني أقل من دولار في الشهر.
- تكلفة GitHub Actions: 0 على ريبو private بأقل من 2,000 دقيقة/شهر (الـ free tier).
المثال للمبتدئ — لو لسه ما عملتش GitHub Action قبل كده
تخيّل عندك ساعة منبه ذكية. كل يوم 7 الصبح بتشتغل لوحدها (ده الـ cron). أول ما تشتغل، بتروح المطبخ، بتعد كام كوباية موجودة (ده الـ GitHub API)، بتسأل صديق ذكي على التليفون "أنهي كوباية أنضف" (ده Claude)، وبتكتب الجواب على ورقة على الثلاجة (ده Slack). إنت ما عملتش حاجة، الساعة هي اللي عملت كل ده. الـ GitHub Action هو حرفيًا نفس الفكرة، بس كومبيوتر بيشتغل في cloud GitHub بدل البيت بتاعك.
أربعة Trade-offs لازم تكون عارفهم
- Claude مش بيقرا الـ diff كاملًا، بيقرا metadata بس. ده مقصود علشان توفّر توكنز، بس معناه إن PR فيه 30 سطر مش مهمين بس بيغيّر in
auth/middleware.tsممكن يتصنّف low بالغلط. الحل: ضيف heuristic في الـ prompt: "أي تعديل في paths فيهاauth/أوbilling/= high مهما كان الحجم". - الفريق بيعتمد عليه ويفقد الحس البشري. بعد شهرين من الاستخدام، لاحظت إن المهندسين بقوا يفتحوا الـ low PRs بدون قراءة. الحل: لكل أسبوع، اختار PR low عشوائي وراجعه بنفسك بعمق علشان تقيّم لو الـ classification دقيقة.
- الـ rate limits لو عندك monorepo ضخم. GitHub REST API بيدّيك 5,000 طلب/ساعة. لو ريبو فيه 200 PR، الـ pagination بيستهلك 4 طلبات بس. لكن لو حابب تجيب الـ files لكل PR، الرقم بيقفز لـ 200+ طلب وممكن يضرب الـ limit لو في workflows تانية شغّالة. الحل: استخدم GraphQL API بدل REST لما تحتاج تفاصيل أعمق.
- تكلفة الـ false negatives أعلى من الـ false positives. لو Claude صنّف PR high على الغلط، إنت بس بتراجعه بعمق زيادة (تكلفة: 5 دقايق). لو صنّفه low والـ PR كسر الإنتاج، التكلفة ساعات downtime. لذلك: في الـ prompt، اطلب من النموذج "في حالة الشك، صنّف medium مش low".
متى لا تستخدم هذا الأوتوميشن
الـ workflow ده مش هيفيدك في الحالات دي:
- فريق أقل من 4 مطورين بأقل من 8 PRs/أسبوع. الـ overhead أكبر من الفايدة، اقرا القائمة على عينك من GitHub.
- ريبو فيه code review صارم بـ CODEOWNERS وكل PR لازم يعدّي 2 reviewers. الترتيب هنا مش هو الـ bottleneck.
- فريق ما عندوش Slack ولا أي messaging مركزي. ابعت email بدلًا، بس الـ engagement هيكون أقل.
- PRs حساسة (security patches قبل ما تتنشر). Claude API بيشوف العناوين والملفات، فلو ده يخالف سياسة الشركة، استبدله بنموذج محلي زي Llama 3.3 70B.
الخطوة التالية — ابدأ النهارده
افتح ريبو واحد عندك (مش لازم الكبير)، أنشئ الملفين اللي فوق، ضيف 3 secrets في Settings → Secrets and variables → Actions: ANTHROPIC_API_KEY و SLACK_WEBHOOK (الـ GITHUB_TOKEN موجود تلقائي). بعدين اضغط "Run workflow" يدويًا من تبويب Actions. لو وصلتك رسالة Slack في خلال دقيقة، خلاص. اربط الـ cron وانسى الموضوع لأسبوع — وقيس الفرق في الـ time-to-first-review على PRs الأسبوع ده مقابل اللي قبله.
المصادر
- GitHub REST API — Pull Requests:
docs.github.com/en/rest/pulls/pulls(نقاط نهاية/pullsو/pulls/{number}/files). - GitHub Actions — Scheduled events (cron syntax):
docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule. - Anthropic Messages API و model card لـ Claude Haiku 4.5:
docs.anthropic.com/en/api/messagesوdocs.anthropic.com/en/docs/models-overview. - Slack Incoming Webhooks:
api.slack.com/messaging/webhooks. - أرقام تكلفة Anthropic (Haiku 4.5):
anthropic.com/pricing— تحقق من السعر الحالي قبل الحساب. - GitHub Free Tier limits لـ Actions على private repos:
docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions.