> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# مراجعة كود الذكاء الاصطناعي دون أن تصبح شكلية
- URL: https://arabroot.io/articles/مراجعة-كود-الذكاء-الاصطناعي-دون-ان-تصبح-شكلية/
- Published: 2026-10-13T07:00:00.000Z
- Updated: 2026-10-13T06:33:24.000Z
- Description: كيف تراجع طلبات الدمج التي يكتبها الذكاء الاصطناعي بحسب خطرها، وتترك للبوابات الآلية ما تستطيع قياسه.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, DevOps والتشغيل, DevOps وCI/CD, الاستضافة الذاتية

يفتح الوكيل الذكي AI Agent طلب دمج Pull Request فيه 250 سطراً خلال دقائق، ثم يفتح طلباً آخر بعد ساعة، ويصل الطلب الثالث قبل أن تنتهي من قراءة الأول، فتبدأ بقراءة الوصف، وتنظر إلى العلامة الخضراء بجانب الاختبارات، ثم تضغط Approve لأن كل شيء يبدو مرتباً. وهنا تصبح المراجعة شكلية، أي أنها موجودة في الإجراءات وغائبة في الواقع، والسبب أن كتابة الكود صارت أرخص بكثير بينما قراءته بتركيز ما زالت تكلف الوقت نفسه.

وقد ناقشنا في مقال [جودة الكود](https://arabroot.io/articles/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AC%D9%88%D8%AF%D8%A9-%D8%A7%D9%84%D9%83%D9%88%D8%AF-code-quality-%D9%88%D9%83%D9%8A%D9%81-%D8%AA%D9%82%D9%8A%D8%B3%D9%87%D8%A7/#ai-code) لماذا يحتاج كود الذكاء الاصطناعي انتباهاً خاصاً، وفي مقال [SonarQube عملياً](https://arabroot.io/articles/sonarqube-%D8%B9%D9%85%D9%84%D9%8A%D8%A7-%D8%A8%D9%88%D8%A7%D8%A8%D8%A9-%D8%AC%D9%88%D8%AF%D8%A9-%D9%84%D9%83%D9%88%D8%AF-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/) كيف تعطي بوابة الجودة Quality Gate نتيجتها للوكيل حتى يصلح كوده، وفي مقال [متى يكون الذكاء الاصطناعي واثقاً ومخطئاً؟](https://arabroot.io/articles/%D9%85%D8%AA%D9%89-%D9%8A%D9%83%D9%88%D9%86-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-%D9%88%D8%A7%D8%AB%D9%82%D8%A7-%D9%88%D9%85%D8%AE%D8%B7%D8%A6%D8%A7/) أنواع الأخطاء التي يكتبها النموذج بثقة. وهذا المقال يجيب عن السؤال الذي يأتي بعدها: كيف يراجع الفريق هذا الكم من الكود دون أن يتحول الضغط على Approve إلى عادة؟

وسوف نناقش في هذا المقال ما يلي:

- مشكلة الحجم Review Volume: لماذا لا يكفي أن تطلب من المراجع أن يقرأ أكثر.
- المراجعة حسب الخطر Risk-based Review: أي التغييرات تحتاج مراجعة بشرية عميقة، وأيها تحملها البوابات الآلية.
- قائمة تحقق خاصة بكود الذكاء الاصطناعي: الاعتماديات الجديدة، والاختبارات التي ضعفت، وابتلاع الأخطاء، والتغيير خارج المطلوب Scope Creep، والأسرار.
- البوابات الآلية Automated Gates: الاختبارات، وتغطية الكود الجديد، وSAST، وSCA، وفحص الأسرار، وحد حجم التغيير.
- قالب طلب الدمج PR Template الذي يطلب الدليل بدل الوصف.
- تجربة عملية على ثلاثة طلبات دمج بأسلوب الوكيل، نرى فيها ما أوقفته البوابات، وما لم تجده إلا قائمة المراجع.
- إرهاق المراجع Reviewer Fatigue وكيف تحمي انتباهه.

## مشكلة الحجم Review Volume

في تقرير [Octoverse 2025](https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/?ref=arabroot.io) من GitHub دمج المطورون في المتوسط 43.2 مليون طلب دمج في الشهر، بزيادة 23% عن السنة التي قبلها، ودفعوا قرابة مليار Commit خلال 2025 بزيادة 25%. ولاحظ أن هذه الزيادة لها أسباب كثيرة، منها دخول مطورين جدد إلى المنصة، ولا يصح أن تنسبها كلها للذكاء الاصطناعي، ولكنها تعطيك اتجاهاً واضحاً: عدد التغييرات التي تنتظر من يقرأها يكبر.

أما الطرف الآخر من المعادلة، أي قدرة الإنسان على المراجعة، فلم يتغير. وفي [دراسة SmartBear على فريق برمجة في Cisco](https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/?ref=arabroot.io) كانت التوصية ألا يراجع المطور أكثر من 200 إلى 400 سطر في المرة الواحدة، وأن مراجعة هذا الحجم خلال 60 إلى 90 دقيقة تكشف 70% إلى 90% من العيوب، وأن كثافة العيوب المكتشفة تنخفض بوضوح عندما تزيد سرعة القراءة عن 500 سطر في الساعة. ولاحظ أنها دراسة من شركة تبيع أداة مراجعة، ولكنها من أشهر ما نشر بأرقام عن المراجعة، ورسالتها متسقة مع خبرة أي مطور: المراجع الذي يقرأ 250 سطراً في خمس دقائق لا يراجع، وإنما يتصفح.

والصورة التالية تبين المشكلة:

![وكيل ذكاء اصطناعي يكتب مئات الأسطر في دقائق، فيخرج منه طابور طويل من طلبات الدمج يمر من فتحة ضيقة إلى مراجع واحد بانتباه محدود، وتحتها رقمان: زيادة 23% في طلبات الدمج الشهرية في Octoverse 2025، و200 إلى 400 سطر لكل جلسة مراجعة في دراسة SmartBear على فريق في Cisco](https://arabroot.io/content/images/2026/10/ai-code-review-01-volume.webp)

الوكيل يكتب أسرع، والمراجع يقرأ بالسرعة نفسها، فيكبر الطابور أمامه

وقد يتساءل البعض: إذا كانت المراجعة بطيئة فلماذا لا نلغيها ونكتفي بالاختبارات؟ والإجابة أن المراجعة لم تكن أصلاً لكشف الأخطاء فقط، ففي دراسة Microsoft التي نشرها [Bacchelli وBird في مؤتمر ICSE سنة 2013](https://www.microsoft.com/en-us/research/publication/expectations-outcomes-and-challenges-of-modern-code-review/?ref=arabroot.io) بعد ملاحظة المطورين ومقابلتهم وتصنيف مئات التعليقات، كان كشف العيوب هو الدافع الأول للمراجعة، ولكن المراجعات في الواقع كانت أقل تعلقاً بالعيوب مما توقعوا، وأكثر فائدة في نقل المعرفة ومعرفة الفريق بما يتغير، ووجدت الدراسة أن فهم التغيير هو أصعب جزء في المراجعة. وهذا بالضبط ما يضيع مع كود الذكاء الاصطناعي، لأن كاتب الطلب نفسه قد لا يفهم كل سطر فيه، فإذا لم يفهمه المراجع أيضاً فلا يوجد إنسان واحد في الفريق يعرف ماذا دخل إلى الإنتاج Production.

ومن الجهة الأخرى لا يمكن أن تكون المراجعة اختيارية، ففي [تقرير Veracode لسنة 2025](https://www.veracode.com/blog/genai-code-security-report/?ref=arabroot.io) عن أمان الكود المولد، الذي اختبر أكثر من 100 نموذج لغة على أربع لغات، فشلت 45% من العينات في اختبارات الأمان وأدخلت ثغرات من قائمة OWASP Top 10، وكانت النسبة 72% في Java و38% في Python، ولم يتحسن الأمان في النماذج الأحدث والأكبر مع أن الكود صار أصح من ناحية الوظيفة. والحل إذن ليس أن تقرأ أكثر ولا أن تقرأ أقل، وإنما أن توزع الانتباه: الآلة تفحص ما يمكن قياسه في كل طلب، والإنسان يصرف وقته حيث يكون الخطأ مكلفاً ولا تستطيع الأداة أن تراه.

## المراجعة حسب الخطر Risk-based Review

الفكرة بسيطة: ليست كل الأسطر متساوية، فتعديل نص رسالة في الواجهة لا يحتاج ما يحتاجه تعديل دالة الاسترداد Refund. والطريقة البسيطة أن يقرر المراجع بنفسه في كل طلب هل هو مهم، وهذه الطريقة تفشل مع الإرهاق، لأن المراجع المتعب يرى كل طلب بسيطاً. والطريقة الأصح أن يكون القرار مكتوباً مسبقاً في المستودع، بحسب الملفات التي تغيرت، فلا يعتمد على مزاج المراجع في نهاية اليوم.

والجدول التالي يبين المناطق التي تحتاج مراجعة بشرية عميقة دائماً، حتى لو نجحت كل البوابات:

| المنطقة                                  | لماذا لا تكفي البوابات؟                                                                      | مثال من نوع الأخطاء                                                                                                                                                                                            |
| ---------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| المصادقة والصلاحيات Auth and Permissions | الأداة لا تعرف من يحق له ماذا، فهذه قاعدة عمل وليست نمطاً في الكود                           | دالة تجلب الطلب بالمعرف دون أن تتحقق من مالكه، وهي ثغرة BOLA التي شرحناها في [اختبار API](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-api-%D9%85%D8%B9-bruno-%D9%88-postman/)            |
| المال Money                              | الاختبار يتحقق مما كتبه كاتب الاختبار، فإذا كانت قاعدة التقريب خاطئة في الاثنين نجح الاختبار | التقريب، والضريبة، والاسترداد أكثر من المدفوع، والعملات                                                                                                                                                        |
| حذف البيانات Data Deletion               | الخطأ هنا لا يمكن التراجع عنه بإعادة النشر                                                   | شرط WHERE ناقص، أو حذف متسلسل Cascade لم يقصده أحد                                                                                                                                                             |
| تعديلات قاعدة البيانات Migrations        | تعمل على بيانات الإنتاج وحجمها، وليس على بيانات الاختبار                                     | قفل جدول كبير أثناء التعديل، أو عمود يحذف قبل أن يتوقف الكود القديم عن قراءته                                                                                                                                  |
| التزامن Concurrency                      | الاختبارات العادية تعمل بخيط واحد، فلا تظهر فيها حالة السباق Race Condition                  | رصيد يقرأ ثم يكتب دون قفل، كما شرحنا في [بناء المحفظة الرقمية](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D9%85%D8%AD%D9%81%D8%B8%D8%A9-%D8%B1%D9%82%D9%85%D9%8A%D8%A9-ledger/) |
| حدود الثقة Trust Boundaries              | أدوات SAST تجد الأنماط المعروفة فقط                                                          | مدخل من المستخدم يصل إلى أمر نظام أو مسار ملف أو استعلام                                                                                                                                                       |

وما عدا ذلك، مثل تعديلات الواجهة التي لا تلمس البيانات، والتوثيق، وإضافة اختبارات جديدة، وإعادة الترتيب الداخلي Refactoring التي تنجح فيها كل البوابات، يكفيه أن تنجح البوابات ثم يقرأه مراجع قراءة سريعة. والصورة التالية تبين الترتيب:

![أربع خطوات: طلب دمج من إنسان أو وكيل، ثم البوابات الآلية وهي الاختبارات وتغطية الكود الجديد وSAST وSCA والأسرار وحد حجم التغيير والاختبارات الضعيفة، وإذا فشلت لا يبدأ أي إنسان، ثم سؤال ماذا لمس التغيير: الصلاحيات والمال والحذف والتعديلات والتزامن وحدود الثقة، وملف CODEOWNERS يحدد المراجع، ثم مراجعة عميقة بالقائمة للطلبات الحساسة ومراجعة سريعة للبقية](https://arabroot.io/content/images/2026/10/ai-code-review-02-risk.webp)

البوابات أولاً حتى لا يضيع وقت الإنسان على ما تجده الأداة، ثم الملفات التي تغيرت تحدد من يراجع وبأي عمق

والأداة التي تنفذ هذا القرار في أغلب المنصات هي ملف `CODEOWNERS`، فكل سطر فيه نمط مسار وأسماء المسؤولين عنه، وعندما يلمس طلب الدمج ملفاً يطابق النمط يطلب منهم المراجعة تلقائياً. وفي [Gitea](https://docs.gitea.com/usage/code-owners?ref=arabroot.io) يقرأ الملف من `./CODEOWNERS` أو `./docs/CODEOWNERS` أو `./.gitea/CODEOWNERS`، وإذا فعلت شرط موافقة صاحب الكود في حماية الفرع فلن يدمج الطلب حتى يوافق واحد منهم على كل قاعدة تنطبق عليه، والفكرة نفسها في [GitHub](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners?ref=arabroot.io). والمثال التالي لمستودع التجربة:

```text
# .gitea/CODEOWNERS
# Changes here always get a deep review from the owners, whoever wrote them.
shop/auth\.py        @security-team
shop/refunds\.py     @payments-team
shop/money\.py       @payments-team
migrations/.*        @dba-team
```

ولاحظ أن الأنماط في Gitea تعبيرات نمطية Regex وليست أنماط Glob كما في GitHub، لذلك كتبنا `\.` و`.*`، وإذا لم تكن تعرف التعبيرات النمطية فقد شرحناها في [مقال Regex للمبتدئين](https://arabroot.io/articles/%D8%A7%D9%84%D8%AA%D8%B9%D8%A8%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regex-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/). وطريقة حماية الفرع وطلبات الدمج نفسها شرحناها في [مقال Git في العمل اليومي](https://arabroot.io/articles/git-merge-%D9%88-rebase-%D9%88-cherry-pick/#pull-requests).

## قائمة تحقق خاصة بكود الذكاء الاصطناعي

قوائم المراجعة العامة تسأل عن التسمية والتصميم والاختبارات، وهي مفيدة، ولكن كود الذكاء الاصطناعي يخطئ بطرق متكررة يمكن أن تبحث عنها مباشرة. والسبب أن النموذج يحاول أن يجعل المهمة تنجح كما طلبت منه، فإذا كان الاختبار يفشل فأسهل طريق هو تعديل الاختبار، وإذا كان الخطأ يظهر فأسهل طريق هو إخفاؤه، وإذا احتاج دالة فأسهل طريق هو إضافة مكتبة. والجدول التالي هو القائمة التي سوف نطبقها في التجربة:

| البند                               | ما الذي تبحث عنه                                                                                                                | أمر يساعدك                                                                                                                                                                                                                                                                                                                 |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| الاعتماديات المضافة Dependencies    | كل مكتبة جديدة: هل هي موجودة فعلاً، وهل اسمها صحيح، ولماذا هذه النسخة القديمة، وما رخصتها، وهل نحتاجها أصلاً                    | git diff main... -- requirements.txt package.json                                                                                                                                                                                                                                                                          |
| الاختبارات التي ضعفت Weakened Tests | اختبار حذف، أو تخطي Skip، أو قيمة متوقعة تغيرت، أو Assertion صار يقبل أكثر من جواب                                              | git diff main... -- tests/                                                                                                                                                                                                                                                                                                 |
| ابتلاع الأخطاء Error Swallowing     | except: pass، وcatch يسجل ثم يكمل، ودالة ترجع نجاحاً بعد أن فشلت كل المحاولات                                                   | git diff main... \| grep -nE 'except|catch|return True'                                                                                                                                                                                                                                                                    |
| التغيير خارج المطلوب Scope Creep    | ملفات لا علاقة لها بعنوان الطلب، مثل تعديل في الصلاحيات داخل طلب عن الاسترداد                                                   | git diff --stat main...                                                                                                                                                                                                                                                                                                    |
| الأسرار Secrets                     | مفاتيح وكلمات مرور في الكود أو في ملفات الإعداد أو في أمثلة التوثيق                                                             | فحص الأسرار في البوابات، ثم عينك على ملفات الإعداد                                                                                                                                                                                                                                                                         |
| مسار الفشل Failure Path             | ماذا يحدث إذا كانت الخدمة الخارجية متوقفة، أو القيمة فارغة، أو المستخدم ليس المالك؟ النموذج يكتب المسار السعيد Happy Path جيداً | اقرأ كل if وexcept واسأل: من يعرف أن هذا حدث؟                                                                                                                                                                                                                                                                              |
| الواجهات المخترعة Hallucinated APIs | دالة أو خيار لا يوجد في نسخة المكتبة التي تستخدمها                                                                              | تشغيل الاختبارات يكشف أغلبها، والباقي في التوثيق، كما في [المقال السابق](https://arabroot.io/articles/%D9%85%D8%AA%D9%89-%D9%8A%D9%83%D9%88%D9%86-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-%D9%88%D8%A7%D8%AB%D9%82%D8%A7-%D9%88%D9%85%D8%AE%D8%B7%D8%A6%D8%A7/#detect) |

ولاحظ أن القائمة قصيرة عن قصد، والسبب أن القائمة الطويلة تتحول بدورها إلى إجراء شكلي يضع عليه المراجع علامة صح دون أن يقرأ.

## البوابات الآلية Automated Gates

البوابة Gate فحص آلي يعمل على كل طلب دمج ويمنع الدمج إذا فشل، والفرق بينها وبين الفحص الذي ينبه فقط أن البوابة لا تحتاج أن يتذكرها أحد. وقد شرحنا في مقال [خط CI/CD آمن](https://arabroot.io/articles/%D8%AE%D8%B7-ci-cd-%D8%A7%D9%85%D9%86-%D9%85%D9%86-%D8%A7%D9%84%D8%A8%D8%AF%D8%A7%D9%8A%D8%A9-%D9%84%D9%84%D9%86%D9%87%D8%A7%D9%8A%D8%A9/#gate-policy) سياسة البوابات كاملة، أي ما الذي يوقف وما الذي ينبه، وكيف تكتب الاستثناء بسبب وصاحب وتاريخ انتهاء، وهنا نختار منها ما يناسب كود الذكاء الاصطناعي تحديداً:

- **الاختبارات Tests:** كل الاختبارات تعمل وتنجح، وهي أبسط بوابة وأهمها، ولكنها لا تقول شيئاً عن الكود الذي لا يختبره أحد.
- **تغطية الكود الجديد Coverage on New Code:** نسبة الأسطر الجديدة أو المعدلة التي نفذتها الاختبارات، وليس تغطية المشروع كله، والسبب أن مشروعاً تغطيته 85% يقبل ملفاً جديداً تغطيته 20% دون أن يتحرك الرقم الكلي كثيراً. وفي [SonarQube](https://arabroot.io/articles/sonarqube-%D8%B9%D9%85%D9%84%D9%8A%D8%A7-%D8%A8%D9%88%D8%A7%D8%A8%D8%A9-%D8%AC%D9%88%D8%AF%D8%A9-%D9%84%D9%83%D9%88%D8%AF-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/#gates) تسمى الفكرة الكود الجديد New Code، وفي التجربة نستخدم أداة [diff-cover](https://github.com/Bachmann1234/diff%5Fcover?ref=arabroot.io) التي تقرأ تقرير التغطية والفرق في Git.
- **التحليل الثابت SAST:** يبحث في الكود عن أنماط معروفة الخطورة، مثل تعطيل التحقق من الشهادة أو قراءة YAML بطريقة غير آمنة، وقد شرحناه في [مقال SAST](https://arabroot.io/articles/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D9%83%D9%88%D8%AF-%D8%A7%D9%84%D8%AB%D8%A7%D8%A8%D8%AA-sast-%D9%85%D8%B9-semgrep-%D9%88-sonarqube/)، وفي التجربة نستخدم [Bandit](https://bandit.readthedocs.io/?ref=arabroot.io) للغة Python.
- **تحليل الاعتماديات SCA:** يقارن نسخ المكتبات بقواعد الثغرات المعروفة، وهو مهم مع الذكاء الاصطناعي لأن النموذج يقترح النسخ التي رآها في بيانات تدريبه، وهي غالباً قديمة. وفي التجربة نستخدم [pip-audit](https://github.com/pypa/pip-audit?ref=arabroot.io)، والصورة الكاملة في [مقال سلسلة التوريد](https://arabroot.io/articles/%D8%A7%D9%85%D9%86-%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%B1%D9%8A%D8%AF-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).
- **فحص الأسرار Secrets Scanning:** يبحث عن المفاتيح وكلمات المرور في الـ Commits الجديدة، وفي التجربة نستخدم [Gitleaks](https://github.com/gitleaks/gitleaks?ref=arabroot.io) الذي شرحناه في [مقال اكتشاف الأسرار](https://arabroot.io/articles/%D8%A7%D9%83%D8%AA%D8%B4%D8%A7%D9%81-%D8%A7%D9%84%D8%A7%D8%B3%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D9%85%D8%B3%D8%B1%D8%A8%D8%A9-%D9%85%D8%B9-gitleaks-%D9%88-trufflehog/).
- **حد حجم التغيير Diff Size Limit:** عدد الأسطر المضافة والمحذوفة خارج ملفات الاختبار، فإذا زاد عن الحد يرفض الطلب ويقسم. وفي [إرشادات Google للمراجعة](https://google.github.io/eng-practices/review/developer/small-cls.html?ref=arabroot.io) أن 100 سطر حجم معقول عادة، و1000 سطر كبير جداً عادة، والقرار للمراجع. واخترنا في التجربة 200 سطر، وهو قرار فريق وليس رقماً علمياً.
- **الاختبارات التي ضعفت:** فحص صغير كتبناه بأنفسنا، يعد علامات التخطي `skip` و`xfail` الجديدة، ويقارن عدد أسطر `assert` المحذوفة بالمضافة. وهو فحص تقريبي، وسوف ترى في التجربة ما الذي يفوته.

أما بوابة الجودة Quality Gate في SonarQube فهي تجمع أغلب هذه الشروط في مكان واحد مع لوحة ونتائج تاريخية، وقد شرحناها في [مقالها](https://arabroot.io/articles/sonarqube-%D8%B9%D9%85%D9%84%D9%8A%D8%A7-%D8%A8%D9%88%D8%A7%D8%A8%D8%A9-%D8%AC%D9%88%D8%AF%D8%A9-%D9%84%D9%83%D9%88%D8%AF-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/)، ولم نستخدمها هنا حتى تبقى التجربة خفيفة وترى كل بوابة وحدها.

## قالب طلب الدمج الذي يطلب الدليل

الوكيل يكتب أوصافاً جميلة لطلبات الدمج، مثل «أضفنا معالجة قوية للأخطاء واختبارات شاملة»، والوصف الجميل يقلل انتباه المراجع بدل أن يزيده. والحل أن يطلب القالب دليلاً يمكن التحقق منه بدل الوصف: مخرج الاختبار الحقيقي، والأمر الذي شغله الكاتب، وهل لمس منطقة حساسة. وGitea يقرأ القالب من مسارات منها `.gitea/pull_request_template.md` و`.github/pull_request_template.md`، ويجب أن يكون في الفرع الافتراضي كما في [توثيق القوالب](https://docs.gitea.com/usage/issue-pull-request-templates?ref=arabroot.io). وهذا القالب الذي وضعناه في مستودع التجربة:

```markdown
## What changed and why
<!-- One paragraph. Link the issue. -->

## Risk
- [ ] Touches auth, permissions or roles
- [ ] Touches money (prices, VAT, refunds, balances)
- [ ] Deletes or migrates data
- [ ] Concurrency, retries or background jobs
- [ ] New dependency (name, version, why, licence)
- [ ] None of the above

## Evidence
<!-- Paste real output, not a summary: the test command and its result,
     a screenshot or curl for behaviour changes, the migration dry run. -->

## AI assistance
- [ ] AI wrote part of this change (tool and what it wrote)
- [ ] I have read every line of the diff and can explain it
- [ ] Tests were written or checked against the requirement, not against the code

## Out of scope
<!-- Anything you noticed and did NOT change in this PR. -->
```

في الإعداد أعلاه لاحظ التالي:

- قسم الخطر Risk يربط القالب بالمراجعة حسب الخطر، فإذا وضع الكاتب علامة على المال أو الصلاحيات عرف المراجع أن هذا الطلب يحتاج قراءة كاملة، وإذا وضع «None» وغير ملف `refunds.py` فهذه وحدها ملاحظة مراجعة.
- قسم الدليل Evidence يطلب المخرج الحقيقي، والسبب أن جملة «كل الاختبارات تنجح» لا تقول أي اختبارات، ولا هل تخطى بعضها.
- البند «I have read every line» ليس شكلياً كما يبدو، فهو يحمل كاتب الطلب مسؤولية الكود الذي ولده، ولا يتركها للوكيل.
- قسم «Out of scope» يعطي الكاتب مكاناً يكتب فيه ما لاحظه ولم يغيره، فلا يجمع إصلاحات متفرقة في طلب واحد.

## تجربة عملية: ثلاثة طلبات دمج بأسلوب الوكيل

في هذه التجربة سوف نبني مستودعاً صغيراً لمتجر فيه الطلبات والضريبة والصلاحيات واختباراتها، ثم ثلاثة فروع، كل فرع طلب دمج فيه أخطاء من الأنواع التي يكتبها الذكاء الاصطناعي، ثم نشغل البوابات على كل طلب، ثم نطبق قائمة المراجع ونرى ماذا وجدت وحدها. ولاحظ أننا كتبنا الطلبات الثلاثة بأنفسنا تقليداً لأنماط موثقة في الأبحاث وفي [تجربة المقال السابق](https://arabroot.io/articles/%D9%85%D8%AA%D9%89-%D9%8A%D9%83%D9%88%D9%86-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-%D9%88%D8%A7%D8%AB%D9%82%D8%A7-%D9%88%D9%85%D8%AE%D8%B7%D8%A6%D8%A7/#lab)، ولم يكتبها نموذج، والسبب أننا نريد أن نعرف مسبقاً ما الخطأ في كل طلب حتى نحكم على البوابات. واستخدمنا مستودع Git عادياً مع سكربت يشغل البوابات بدل Gitea وRunner، لأن ما نريد قياسه هو الفحوص نفسها، وطريقة ربطها في Gitea Actions موجودة في [مقال الخط الآمن](https://arabroot.io/articles/%D8%AE%D8%B7-ci-cd-%D8%A7%D9%85%D9%86-%D9%85%D9%86-%D8%A7%D9%84%D8%A8%D8%AF%D8%A7%D9%8A%D8%A9-%D9%84%D9%84%D9%86%D9%87%D8%A7%D9%8A%D8%A9/#stage-pr).

### تجهيز بيئة التجربة

كل ملفات التجربة في مجلد `lab` مع المقال: المجلد `base` هو الفرع الرئيسي `main`، وكل مجلد `pr*` فيه الملفات التي يضيفها أو يغيرها طلب الدمج. ونشغل كل شيء في Container واحد بلغة Python حتى لا نثبت أي شيء على الجهاز:

```bash
cd lab
docker run -d --name lab-aiq-gate -v "$PWD":/lab python:3.13.16-slim sleep infinity
docker exec lab-aiq-gate sh -c '
  apt-get update -qq && apt-get install -y -qq git curl
  pip install -q --root-user-action=ignore pytest==9.1.1 pytest-cov diff-cover==10.6.0 \
      bandit==1.9.4 pip-audit==2.10.1 requests pyyaml
  curl -sSL https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz \
      | tar -xz -C /usr/local/bin gitleaks'
```

ثم السكربت `build-repo.sh` الذي يبني المستودع داخل الـ Container، فيضع `base` في الفرع الرئيسي، ثم يصنع فرعاً لكل طلب دمج:

```bash
#!/bin/sh
# Builds the lab repository in /work/shop: main from base/, then one branch per pull request.
# Each pr*/ folder holds the files that pull request adds or replaces.
set -e
rm -rf /work/shop && mkdir -p /work/shop && cp -r /lab/base/. /work/shop/
cd /work/shop
git init -q -b main
git config user.name "lab" && git config user.email "lab@example.com"
git add -A && git commit -q -m "shop: orders, VAT and tests"
for pr in pr1-csv-export pr2-simplify-vat pr3-refunds; do
  git switch -q -c "$pr" main
  cp -r "/lab/$pr/." .
  git add -A && git commit -q -m "$(cat "/lab/$pr.msg")"
  git switch -q main
done
git log --all --oneline --graph
```

```bash
docker exec lab-aiq-gate sh /lab/build-repo.sh
```

والمخرج سوف يكون كما يلي:

```text
* d3224f7 feat: export orders to CSV and send them to accounting
| * dfadbee refactor: simplify vat() and fix its test
|/
| * d853a46 feat: refunds for customers and support
|/
* d034fd6 shop: orders, VAT and tests
```

والفرع الرئيسي فيه ثلاثة ملفات صغيرة: `money.py` يحسب ضريبة القيمة المضافة 15% ويقرب نصف الهللة للأعلى، و`auth.py` فيه المستخدم ودوره، و`orders.py` يرجع الطلب لمالكه أو للمدير فقط. وهذا جزء الضريبة:

```python
VAT_RATE = Decimal("0.15")
CENT = Decimal("0.01")

def vat(amount: Decimal) -> Decimal:
    """VAT on a net amount, rounded half up to the nearest halala."""
    return (amount * VAT_RATE).quantize(CENT, rounding=ROUND_HALF_UP)
```

### سكربت البوابات

السكربت التالي `gates.sh` يشغل البوابات السبع على فرع واحد مقارنة بالفرع الرئيسي، ويطبع سطراً لكل بوابة، ويرجع رمز خروج غير الصفر إذا فشلت أي واحدة، وهذا ما يحتاجه أي نظام CI حتى يمنع الدمج:

```bash
#!/bin/sh
# Runs the automated gates on one pull request branch against main.
# Usage: sh /lab/gates.sh <branch>     Exit code 0 only if every gate passes.
BR="$1"; cd /work/shop || exit 2
git switch -q "$BR" || exit 2
MAX_LINES=200        # changed lines of non-test code allowed in one pull request
MIN_NEW_COVERAGE=80  # percent of changed lines that tests must execute
fail=0
gate() { printf '%-26s %s\n' "$1" "$2"; [ "$2" = PASS ] || fail=1; }

# 1. Diff size: added + deleted lines outside tests/
lines=$(git diff --numstat main..."$BR" -- . ':(exclude)tests/' | awk '{s+=$1+$2} END {print s+0}')
[ "$lines" -le "$MAX_LINES" ] && r=PASS || r=FAIL; gate "diff size ($lines lines)" $r

# 2. Tests with coverage
python -m pytest -q -p no:cacheprovider --cov=shop --cov-report=xml:/tmp/cov.xml > /tmp/pytest.txt 2>&1 && r=PASS || r=FAIL
gate "tests ($(tail -1 /tmp/pytest.txt | tr -d '=' | sed 's/^ *//;s/ in .*//'))" $r

# 3. Coverage on new code only (diff-cover reads the coverage report and the git diff)
diff-cover /tmp/cov.xml --compare-branch=main --fail-under=$MIN_NEW_COVERAGE > /tmp/diffcover.txt 2>&1 && r=PASS || r=FAIL
gate "new-code coverage ($(grep -m1 '^Coverage:' /tmp/diffcover.txt | awk '{print $2}'))" $r

# 4. SAST on the changed Python files, medium severity and above
files=$(git diff --name-only --diff-filter=AM main..."$BR" -- '*.py' ':(exclude)tests/')
: > /tmp/bandit.txt
if [ -n "$files" ]; then bandit -q -ll $files > /tmp/bandit.txt 2>&1 && r=PASS || r=FAIL; else r=PASS; fi
gate "SAST bandit ($(grep -c '>> Issue' /tmp/bandit.txt 2>/dev/null) issues)" $r

# 5. SCA on the pinned requirements
pip-audit -r requirements.txt --no-deps --disable-pip --progress-spinner off > /tmp/audit.txt 2>&1 && r=PASS || r=FAIL
gate "SCA pip-audit ($(grep -cE '^[A-Za-z].* (PYSEC|GHSA|CVE)-' /tmp/audit.txt) vulns)" $r

# 6. Secrets in the commits of this branch
gitleaks git --no-banner --log-opts="main..$BR" . > /tmp/gitleaks.txt 2>&1 && r=PASS || r=FAIL
gate "secrets gitleaks" $r

# 7. Test weakening: new skip/xfail markers, or fewer assert lines in tests/ than before
d=$(git diff main..."$BR" -- tests/)
skips=$(echo "$d" | grep -cE '^\+.*pytest\.mark\.(skip|xfail)')
removed=$(echo "$d" | grep -cE '^-\s*assert '); added=$(echo "$d" | grep -cE '^\+\s*assert ')
weak=$(( skips + (removed > added ? removed - added : 0) ))
[ "$weak" -eq 0 ] && r=PASS || r=FAIL; gate "test weakening ($weak hits)" $r

git switch -q main
exit $fail
```

في الإعداد أعلاه لاحظ التالي:

- كل بوابة تقارن بالفرع الرئيسي `main`، أي أنها تحكم على ما أضافه هذا الطلب فقط، ولا تفشل بسبب مشاكل قديمة في المشروع، وهذه نفس فكرة الكود الجديد في SonarQube.
- حد الحجم يستثني مجلد `tests/`، والسبب أننا لا نريد أن نعاقب من يكتب اختبارات أكثر.
- الخيار `-ll` في Bandit يعني أن البوابة تفشل على الخطورة المتوسطة Medium وما فوقها فقط، والملاحظات المنخفضة Low تبقى للمراجع، وسوف ترى بعد قليل ماذا يعني ذلك.
- pip-audit يعمل بالخيارين `--no-deps --disable-pip`، أي أنه يقرأ النسخ المثبتة في الملف ويسأل قاعدة الثغرات عنها، دون أن يحاول تثبيتها، والسبب أن النسخ القديمة جداً قد لا تبنى أصلاً على Python 3.13.
- Gitleaks يفحص الـ Commits التي في الفرع وليست في `main` فقط، عبر `--log-opts="main..$BR"`.

### الطلب الأول: تصدير الطلبات إلى المحاسبة

عنوان الطلب «feat: export orders to CSV and send them to accounting»، وهو ملف جديد `shop/export.py` من 251 سطراً كتبناه كما يكتبه وكيل طلبنا منه «add CSV export of orders and send it to the accounting API, make it robust»: دالة تنسيق مستقلة لكل عمود مع `try` في كل واحدة، ومكتبتان جديدتان بنسختين قديمتين، ومفتاح API في الكود، واختباران للمسار السعيد. وهذا الجزء الذي يرسل الملف:

```python
def send_to_accounting(csv_text: str, retries: int = 3) -> bool:
    """Upload the CSV to the accounting API, retrying on failure."""
    for attempt in range(retries):
        try:
            response = requests.post(
                ACCOUNTING_URL,
                data=csv_text.encode("utf-8"),
                headers={"Authorization": f"Bearer {ACCOUNTING_API_KEY}", "Content-Type": "text/csv"},
                verify=False,
            )
            if response.status_code == 200:
                return True
        except Exception:
            pass
        time.sleep(2 ** attempt)
    return True
```

الآن نشغل البوابات:

```bash
docker exec lab-aiq-gate sh /lab/gates.sh pr1-csv-export
```

والمخرج سوف يكون كما يلي:

```text
diff size (253 lines)      FAIL
tests (10 passed)          PASS
new-code coverage (57%)    FAIL
SAST bandit (3 issues)     FAIL
SCA pip-audit (12 vulns)   FAIL
secrets gitleaks           FAIL
test weakening (0 hits)    PASS
exit code: 1
```

خمس بوابات من سبع أوقفت الطلب، ولاحظ أن الاختبارات نجحت، فلو كانت هي البوابة الوحيدة لدخل الطلب. وهذه تفاصيل ما وجدته كل أداة، من تشغيلها مباشرة على الفرع نفسه. أولاً Bandit:

```text
>> Issue: [B506:yaml_load] Use of unsafe yaml load. Allows instantiation of arbitrary objects. Consider yaml.safe_load().
   Severity: Medium   Confidence: High
   Location: ./shop/export.py:28:19
>> Issue: [B501:request_with_no_cert_validation] Call to requests with verify=False disabling SSL certificate checks, security issue.
   Severity: High   Confidence: High
   Location: ./shop/export.py:236:23
>> Issue: [B113:request_without_timeout] Call to requests without timeout
   Severity: Medium   Confidence: Low
   Location: ./shop/export.py:232:23
```

وهذا المخرج مختصر، حيث أبقينا من كل ملاحظة سطر العنوان والخطورة والمكان، وحذفنا رابط CWE ومقتطف الكود. وعند تشغيل Bandit بالخيار `-l`، أي بكل المستويات، ظهرت ثلاث ملاحظات أخرى بخطورة Low، وهي `B110:try_except_pass` مرتين و`B112:try_except_continue` مرة، وهي بالضبط ابتلاع الأخطاء، ولكن البوابة لم تفشل بسببها لأننا وضعنا الحد عند Medium. أما دوال التنسيق الاثنتا عشرة التي تبتلع الخطأ وتسجله بمستوى `debug` ثم ترجع نصاً فارغاً فلم تظهر في أي مستوى، والسبب أن فيها سطر تسجيل، فهي ليست `pass` في نظر الأداة.

ثانياً pip-audit:

```text
Found 12 known vulnerabilities in 2 packages
Name     Version ID              Fix Versions
-------- ------- --------------- ------------
requests 2.19.0  PYSEC-2018-28   2.20.0
requests 2.19.0  PYSEC-2018-28   2.20.0
requests 2.19.0  PYSEC-2023-74   2.31.0
requests 2.19.0  PYSEC-2023-74   2.31.0
requests 2.19.0  PYSEC-2026-1873 2.32.0
requests 2.19.0  PYSEC-2026-1872 2.32.4
requests 2.19.0  PYSEC-2026-2275 2.33.0
requests 2.19.0  PYSEC-2026-1873 2.32.0
requests 2.19.0  PYSEC-2026-1872 2.32.4
requests 2.19.0  PYSEC-2026-2275 2.33.0
pyyaml   5.3.1   PYSEC-2021-142  5.4
pyyaml   5.3.1   PYSEC-2021-142  5.4
```

وقبله سطرا تحذير من pip-audit ينصحان بتثبيت الـ Hash لكل اعتمادية، حذفناهما من المخرج. ولاحظ أن كل معرف ظهر مرتين في تشغيلنا، فالعدد 12 في سطر البوابة يعني 6 ثغرات مختلفة، خمس في requests وواحدة في PyYAML، لذلك كتبنا 6 في الصورة الأخيرة من المقال.

ثالثاً Gitleaks مع الخيار `-v`:

```text
Secret:      sk_live_51NzQk2LmT8vXp3RfYw9HcJd4Ka7
RuleID:      stripe-access-token
Entropy:     5.003258
File:        shop/export.py
Line:        20
WRN leaks found: 1
```

والمفتاح مخترع للتجربة، ولكنه بشكل مفاتيح Stripe، فعرفته الأداة بقاعدة `stripe-access-token`. وهذا المخرج مختصر أيضاً، حيث حذفنا سطر Finding وأسطر الـ Commit والكاتب والتاريخ وأسطر المعلومات الأخرى، ووقت السطر الأخير وألوان الطرفية. وأخيراً diff-cover:

```text
shop/export.py (57.2%): Missing lines 26-31,39,41-43,51,53-55,63,65-67,75,78-80,88,91-93,101,104-106,114,117-119,127,130-132,140,142-144,152,154-156,164,166-168,176,178-180,204-205,213-214,223-224,230-232,238-243,247-251
-------------
Total:   173 lines
Missing: 74 lines
Coverage: 57%
-------------

Failure. Coverage is below 80%.
```

ولاحظ أن الأسطر غير المغطاة هي فروع `except` كلها، والدالة `send_to_accounting` كاملة، أي أن الاختبارات لم تجرب أي مسار فشل.

والآن ما الذي لم تجده أي بوابة؟ اقرأ آخر سطر في `send_to_accounting` مرة أخرى: بعد أن تفشل المحاولات الثلاث ترجع الدالة `True`. والسكربت `probe_pr1.py` يوجه الإرسال إلى عنوان لا يستمع عليه أحد ويطبع النتيجة:

```text
export_and_send returned: True
```

أي أن نظامك سوف يسجل أن التصدير نجح، ولن تصل الفواتير إلى المحاسبة، ولن يظهر أي خطأ في أي سجل، والسبب أن `except Exception: pass` ابتلع الخطأ و`return True` أعلن النجاح. وهذا ما وجده بند «مسار الفشل» في القائمة، وهو أخطر ما في الطلب، مع أنه سطر واحد.

### الطلب الثاني: تبسيط دالة الضريبة

عنوان الطلب «refactor: simplify vat() and fix its test»، وهو طلب صغير من 7 أسطر، والفرق كاملاً كما يلي:

```diff
-from decimal import Decimal, ROUND_HALF_UP
+from decimal import Decimal

 VAT_RATE = Decimal("0.15")
-CENT = Decimal("0.01")

 def vat(amount: Decimal) -> Decimal:
-    """VAT on a net amount, rounded half up to the nearest halala."""
-    return (amount * VAT_RATE).quantize(CENT, rounding=ROUND_HALF_UP)
+    """VAT on a net amount, rounded to the nearest halala."""
+    return round(amount * VAT_RATE, 2)

-def test_vat_rounds_half_up():
-    # 0.30 * 0.15 = 0.045, which rounds half up to 0.05
-    assert vat(Decimal("0.30")) == Decimal("0.05")
+def test_vat_rounds():
+    # 0.30 * 0.15 = 0.045, either halala is acceptable
+    assert vat(Decimal("0.30")) in (Decimal("0.04"), Decimal("0.05"))
```

وهذا المخرج مختصر من `git diff`، حيث حذفنا رؤوس الملفين وأسطر `@@` وجمعنا الملفين في كتلة واحدة. والقصة التي يحكيها الفرق معروفة: الوكيل بسط الدالة باستخدام `round`، ففشل الاختبار لأن `round` على Decimal يقرب نصف الهللة إلى الرقم الزوجي Banker's Rounding وليس للأعلى، فعدل الوكيل الاختبار حتى يقبل الجوابين. ونتيجة البوابات:

```text
diff size (7 lines)        PASS
tests (8 passed)           PASS
new-code coverage (100%)   PASS
SAST bandit (0 issues)     PASS
SCA pip-audit (0 vulns)    PASS
secrets gitleaks           PASS
test weakening (0 hits)    PASS
exit code: 0
```

كل البوابات نجحت، حتى فحص الاختبارات الضعيفة، والسبب أن سطر `assert` حذف وأضيف مكانه سطر `assert` آخر، فالعدد لم يتغير. والسكربت `probe_pr2.py` يحسب الضريبة على أربعة مبالغ تنتهي بنصف هللة، على الفرعين:

```text
## main
0.30 -> 0.05
0.70 -> 0.11
1.10 -> 0.17
2.30 -> 0.35
## pr2-simplify-vat
0.30 -> 0.04
0.70 -> 0.10
1.10 -> 0.16
2.30 -> 0.34
```

هللة واحدة في كل مرة، على كل مبلغ من هذا النوع، في كل فاتورة. وهذا ما وجده بند «الاختبارات التي ضعفت»، لأن المراجع الذي يرى Assertion صار يقبل جوابين يسأل مباشرة: أيهما الصحيح؟ وإذا أردت أن تعرف لماذا تقرب اللغات نصف الرقم إلى الزوجي فقد شرحنا ذلك في [مقال الأرقام العشرية](https://arabroot.io/articles/%D8%A7%D9%84%D8%A3%D8%B1%D9%82%D8%A7%D9%85-%D8%A7%D9%84%D8%B9%D8%B4%D8%B1%D9%8A%D8%A9-floating-point-%D9%85%D9%86-%D8%A7%D9%84%D8%AF%D8%A7%D8%AE%D9%84/#rounding). ولاحظ أن هذا الطلب كان سيدخل في المراجعة البسيطة لو لم يكن `money.py` في ملف `CODEOWNERS`، فهو صغير وكل شيء فيه أخضر.

### الطلب الثالث: الاسترداد Refund

عنوان الطلب «feat: refunds for customers and support»، وفيه ملف جديد `refunds.py` مع أربعة اختبارات، وتعديل من سطرين في `auth.py`، وسطر في ملف اختبار قديم. وهذه دالة الاسترداد:

```python
def refund_order(user: User, order_id: int, amount: Decimal) -> Order:
    """Refund part or all of an order and return the updated order."""
    order = ORDERS.get(order_id)
    if order is None:
        raise NotFound(order_id)
    if amount <= 0:
        raise ValueError("refund amount must be positive")
    order.refunded += amount
    if order.refunded >= order.total:
        order.status = "refunded"
    return order
```

وهذا التعديل في `auth.py`:

```python
def is_admin(user: User) -> bool:
    # support staff issue refunds, so they need admin access too
    return user.role in ("admin", "support")
```

ونتيجة البوابات:

```text
diff size (21 lines)       PASS
tests (11 passed, 1 skipped) PASS
new-code coverage (100%)   PASS
SAST bandit (0 issues)     PASS
SCA pip-audit (0 vulns)    PASS
secrets gitleaks           PASS
test weakening (1 hits)    FAIL
exit code: 1
```

البوابة الوحيدة التي فشلت هي فحص الاختبارات الضعيفة، لأن الطلب أضاف `@pytest.mark.skip(reason="flaky on CI")` على الاختبار `test_support_cannot_read_other_orders`. وهذا الاختبار ليس متقلباً Flaky أصلاً، وإنما فشل لأن تعديل `is_admin` جعل موظف الدعم مديراً يقرأ كل الطلبات، فتخطاه الوكيل بدل أن يسأل. والبوابة أوقفت الطلب، ولكنها لا تعرف لماذا، والذي يعرف هو المراجع. والسكربت `probe_pr3.py` يسأل ما لم تسأله الاختبارات:

```text
bob refunded alice's order: 1000.00 of 115.00 status refunded
support reads alice's order: 1
```

أي أن أي عميل يستطيع أن يسترد طلب عميل آخر، لأن الدالة تجلب الطلب بالمعرف دون أن تتحقق من مالكه، وبمبلغ 1000 ريال على طلب قيمته 115، لأنه لا يوجد حد أعلى للاسترداد، وأن موظف الدعم صار يقرأ طلبات كل العملاء. والتغطية 100% هنا لا تعني شيئاً، فكل سطر نفذ، ولكن لم يكتب أحد اختباراً لمستخدم ليس المالك. وهذه ثلاثة أخطاء وجدتها بنود المراجعة حسب الخطر: الصلاحيات، والمال، والتغيير خارج المطلوب، لأن طلباً عنوانه الاسترداد غير تعريف المدير في النظام كله.

### النتيجة في صورة واحدة

الصورة التالية تجمع النتائج:

![جدول من ثلاثة صفوف وثمانية أعمدة: الطلب الأول فشل في حجم التغيير 253 سطراً وتغطية الكود الجديد 57% وBandit بثلاث ملاحظات وpip-audit بست ثغرات وGitleaks بمفتاح واحد، ونجحت فيه الاختبارات، والمراجع وجد أنه يرجع True حتى لو فشل الإرسال. الطلب الثاني نجح في كل البوابات، والمراجع وجد أن التقريب تغير وأن الاختبار صار يقبل الجوابين. الطلب الثالث نجح في كل البوابات إلا الاختبارات الضعيفة بسبب Skip واحد، والمراجع وجد أن أي عميل يسترد طلب غيره وأن الدعم صار مديراً](https://arabroot.io/content/images/2026/10/ai-code-review-03-results.webp)

البوابات أوقفت أغلب مشاكل الطلب الأول، وأخطر خطأ في كل طلب لم تجده إلا قائمة المراجع

والدرس من التجربة أن البوابات ممتازة في الأشياء التي لها نمط ثابت: مفتاح بشكل معروف، ونسخة لها ثغرة مسجلة، ودالة خطيرة معروفة، وأسطر لم تنفذ، وطلب كبير. وهي ضعيفة في المعنى: هل هذا المستخدم يملك هذا الطلب؟ هل التقريب للأعلى أم للزوجي؟ هل النجاح نجاح فعلاً؟ ولو أنفق المراجع وقته في الطلب الأول على المفتاح والنسخ القديمة لكان ذلك هدراً، لأن الأدوات وجدتها قبله، والذي يحتاجه منه هو السطر الأخير في `send_to_accounting`.

### تنظيف بيئة التجربة

```bash
docker rm -f lab-aiq-gate
```

والمستودع نفسه كان داخل الـ Container في `/work/shop`، فيحذف معه، وتبقى ملفات `lab` كما هي.

## إرهاق المراجع Reviewer Fatigue

المراجع المتعب لا يقول «أنا متعب»، وإنما يكتب «LGTM» على طلب لم يفهمه، وهذا أخطر من عدم المراجعة، لأن الفريق يظن أن الكود روجع. ومع كود الذكاء الاصطناعي يزيد الإرهاق لسببين: الحجم أكبر كما رأينا، والكود يبدو مرتباً ومنسقاً ومعلقاً عليه، فيقل الشك الذي يدفعك لقراءة السطر مرتين. والإجراءات التالية تحمي انتباه المراجع:

- **البوابات قبل الإنسان:** لا يطلب من أحد أن يراجع طلباً فشلت فيه البوابات، والسبب أن كل دقيقة يصرفها المراجع على ما تجده الأداة دقيقة ناقصة من ما لا تجده.
- **طلبات صغيرة:** حد الحجم في البوابات يجبر الكاتب، إنساناً كان أو وكيلاً، على تقسيم العمل، ونتيجة [SmartBear](https://smartbear.com/learn/code-review/best-practices-for-peer-code-review/?ref=arabroot.io) أن المراجعة تفقد فعاليتها بعد 60 إلى 90 دقيقة متواصلة.
- **المراجعة الذاتية أولاً:** الكاتب يقرأ الفرق كاملاً قبل أن يطلب المراجعة، ويملأ قسم الدليل في القالب، فلا يصل إلى المراجع طلب لم يقرأه كاتبه.
- **أدوات المراجعة بالذكاء الاصطناعي تمر أولاً وليست موافقة:** أداة تعلق على الطلب قبل الإنسان تفيد في الملاحظات المتكررة، ولكن الموافقة Approve تبقى لإنسان يستطيع أن يشرح الكود، والسبب أن الأداة يمكن أن تخطئ بثقة مثل الوكيل الذي كتب الكود.
- **توزيع الطلبات الحساسة:** ملف `CODEOWNERS` يرسل طلبات المال والصلاحيات لأشخاص محددين، فلا يقع كل شيء على أكثر المراجعين خبرة، ولا يراجع المبتدئ تعديلاً في الصلاحيات وحده.
- **الموافقة تعني أنك تستطيع الشرح:** وفي [معيار المراجعة في Google](https://google.github.io/eng-practices/review/reviewer/standard.html?ref=arabroot.io) أن يوافق المراجع عندما يحسن الطلب صحة الكود عموماً حتى لو لم يكن كاملاً، وهذا يعني أنه يعرف ماذا تغير ولماذا. فإذا لم تستطع أن تشرح لزميل ما يفعله الطلب فلا تضغط Approve.

## أخطاء شائعة

- **الاعتماد على الاختبارات الخضراء وحدها:** في التجربة نجحت الاختبارات في الطلبات الثلاثة، وكان في كل طلب خطأ يصل إلى العميل أو إلى المال.
- **تغطية المشروع بدل تغطية الكود الجديد:** الرقم الكلي يخفي الملف الجديد غير المختبر، فقس الأسطر التي تغيرت في الطلب.
- **رفع حد الخطورة حتى تنجح البوابة:** إذا كانت ملاحظات Bandit المنخفضة كثيرة فاجعلها تظهر للمراجع، ولا تحذفها، فهي في التجربة كانت ابتلاع الأخطاء نفسه.
- **قبول Skip بسبب «flaky»:** اسأل لماذا فشل الاختبار قبل أن تقبل تخطيه، ففي التجربة كان الاختبار يحرس قاعدة صلاحيات.
- **قائمة تحقق من خمسين بنداً:** تتحول إلى علامات صح بلا قراءة، فاجعلها قصيرة ومرتبطة بالأخطاء التي تتكرر عندكم فعلاً.
- **ترك قرار العمق للمراجع:** اكتبه في `CODEOWNERS` وفي قسم الخطر في القالب، حتى لا يعتمد على مزاج نهاية اليوم.
- **مراجعة الكود دون تشغيله:** الأسئلة الصعبة في التجربة أجاب عنها سكربت من عشرة أسطر شغلناه على الفرع، فإذا شككت في سلوك فشغله.

## الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

- الذكاء الاصطناعي زاد عدد التغييرات وحجمها، وقدرة الإنسان على المراجعة بتركيز بقيت كما هي، لذلك يجب أن توزع الانتباه بدل أن تطلب من المراجع قراءة أكثر.
- المراجعة حسب الخطر تعني أن الصلاحيات والمال والحذف والتعديلات على قاعدة البيانات والتزامن وحدود الثقة تحتاج مراجعة عميقة دائماً، وأن القرار مكتوب في `CODEOWNERS` وليس في رأس المراجع.
- البوابات الآلية، أي الاختبارات وتغطية الكود الجديد وSAST وSCA وفحص الأسرار وحد الحجم، أوقفت في التجربة الطلب الأول بخمس بوابات من سبع، ولم توقف الطلب الثاني إطلاقاً.
- أخطر خطأ في كل طلب لم تجده إلا قائمة المراجع: دالة ترجع نجاحاً بعد الفشل، وتقريب تغير مع اختبار يقبل الجوابين، واسترداد دون تحقق من المالك.
- قالب طلب الدمج يطلب الدليل والخطر ومسؤولية الكاتب، والموافقة تعني أنك تستطيع شرح الطلب لزميل.

[![](https://arabroot.io/content/images/2026/10/ai-confidently-wrong-cover-cover.webp)دليلمتى يكون الذكاء الاصطناعي واثقاً ومخطئاً؟أنماط الأخطاء التي يكتبها مساعد البرمجة بثقة كاملة، ولماذا تحدث، وكيف تكشف كل واحد منها قبل أن يصل إلى الإنتاج.](https://arabroot.io/articles/%D9%85%D8%AA%D9%89-%D9%8A%D9%83%D9%88%D9%86-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A-%D9%88%D8%A7%D8%AB%D9%82%D8%A7-%D9%88%D9%85%D8%AE%D8%B7%D8%A6%D8%A7/)[![](https://arabroot.io/content/images/2026/10/code-quality-cover-cover.webp)دليلما هي جودة الكود Code Quality وكيف تقيسها؟ما الذي يجعل الكود سهل التغيير، وماذا تقيس أدوات الفحص فعلاً، ولماذا يحتاج كود الوكلاء الأذكياء إلى بوابة جودة.](https://arabroot.io/articles/%D9%85%D8%A7-%D9%87%D9%8A-%D8%AC%D9%88%D8%AF%D8%A9-%D8%A7%D9%84%D9%83%D9%88%D8%AF-code-quality-%D9%88%D9%83%D9%8A%D9%81-%D8%AA%D9%82%D9%8A%D8%B3%D9%87%D8%A7/)[![](https://arabroot.io/content/images/2026/10/sonarqube-cover-cover.webp)دليلSonarQube عملياً: بوابة جودة لكود الذكاء الاصطناعيكيف تثبت SonarQube مع PostgreSQL وتضبط قواعده وبوابته، ثم تجعل الوكيل الذكي يقرأ نتيجتها ويصلح كوده حتى تنجح.](https://arabroot.io/articles/sonarqube-%D8%B9%D9%85%D9%84%D9%8A%D8%A7-%D8%A8%D9%88%D8%A7%D8%A8%D8%A9-%D8%AC%D9%88%D8%AF%D8%A9-%D9%84%D9%83%D9%88%D8%AF-%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/)[![](https://arabroot.io/content/images/2026/10/secure-pipeline-cover-cover.webp)دليلخط CI/CD آمن من البداية للنهاية على Gitea Actionsكيف تجمع فحوص الأمن في خط واحد لا يستطيع طلب الدمج تجاوزه، ثم ترسل إلى الإنتاج نفس ال Docker Image الموقعة التي فحصتها.](https://arabroot.io/articles/%D8%AE%D8%B7-ci-cd-%D8%A7%D9%85%D9%86-%D9%85%D9%86-%D8%A7%D9%84%D8%A8%D8%AF%D8%A7%D9%8A%D8%A9-%D9%84%D9%84%D9%86%D9%87%D8%A7%D9%8A%D8%A9/)[![](https://arabroot.io/content/images/2026/10/ai-testing-cover-cover.webp)دليلاختبارات يكتبها الذكاء الاصطناعي: متى تثق بها؟أين يساعدك الذكاء الاصطناعي في كتابة الاختبارات وأين يخدعك، ولماذا يبقى قرار ما هو الصحيح بيد الإنسان.](https://arabroot.io/articles/%D8%A7%D8%AA%D9%85%D8%AA%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A8%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/)

## سجل التحديثات

- أكتوبر 2026: كتابة المقال، وتنفيذ التجربة على Docker Engine 29.8.0 مع Python 3.13.16 (python:3.13.16-slim) وGit 2.47.3 وpytest 9.1.1 وpytest-cov 7.1.0 وcoverage 7.16.2 وdiff-cover 10.6.0 وBandit 1.9.4 وpip-audit 2.10.1 وGitleaks 8.30.1.