لماذا لا يُعدّ خطأ Cursor خطأً عاديًا

الشخص الذي يكتب التغيير نفسه يحمل سياقًا لا يراه المحرر أبدًا. فهو يتذكر أن لوحة الإعدادات تقرأ من ذاكرة تخزين مؤقت يجب إبطالها، وأن زر الرفع معطّل إلى أن يُختار ملف، وأن نقطة نهاية بعينها تعيد مصفوفة فارغة لا خطأ 404 حين لا يوجد تطابق. أما Cursor فيعمل انطلاقًا من الملفات التي فتحتها والنص الذي كتبته، وكل ما يقع خارج هذه النافذة تخمين.

لذا فإن نمط الإخفاق ليس صياغة خاطئة، بل تغيير صحيح محليًا وخاطئ على مستوى النظام. الدالة تفعل تمامًا ما تقول إنها تفعله، لكنها تُستدعى في التوقيت الخطأ، أو تترك خلفها جزءًا من الحالة، أو تفترض بنية توقّف API عن إعادتها منذ دورتَي تطوير.

ولا تلتقط اختبارات الوحدة هذا، لأنها تُكتب وفق الافتراضات نفسها التي كُتب الكود وفقها. وإذا كتب الوكيل الاثنين معًا، فسيتفقان مع بعضهما ويختلفان مع منتجك.

من أين تأتي أخطاء Cursor فعليًا

ثلاثة مواضع تفسّر معظمها.

حالة الواجهة

  • يُرسَل نموذج لكن القائمة خلفه لا تتحدّث.

  • تُغلق نافذة منبثقة وتترك قفل التمرير مفعّلًا على الصفحة.

  • يبقى زر مفعّلًا بينما هناك طلب قيد التنفيذ، فيُنشئ النقر المزدوج سجلّين.

التدفقات غير المتزامنة

  • تُعرض الشاشة قبل وصول البيانات ولا يُعاد عرضها أبدًا.

  • تبدأ مهمة في الخلفية دون أن ينتظرها شيء، فتقرأ الخطوة التالية قيمًا قديمة.

  • ينتهي مسار خطأ بصمت، فيرى المستخدم رسالة نجاح لعمل قد أخفق.

حدود التكامل

  • تطابق الحمولة تعريف النوع لكنها لا تطابق ما تقبله الخدمة فعليًا.

  • يُفترض وجود المصادقة لأنها كانت موجودة في الجلسة التي رآها الوكيل.

  • التقسيم إلى صفحات والحالات الفارغة وحدود المعدل تُعالَج في نظام الأنواع ولا مكان غيره.

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

كيف تكتشف أخطاء Cursor قبل دمجها

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

ثلاثة شروط يجب أن تتحقق. لا صعوبة في أي منها، لكن إغفال واحد منها هو ما يجعل الحلقة تتسرّب.

يجب أن يعرف الوكيل كيف يتحقق

يثبّت الإعداد مهارة تحقق داخل وكيل البرمجة نفسه، فيعرف كيف يُنشئ الاختبارات ويشغّلها ويفرز نتائجها بدلًا من التخمين من ملف README. وهو يكتب ملف تعليمات في المكان الذي يبحث فيه محررك عنه أصلًا، ما يعني أن Cursor يلتقطه من .cursor/rules/ تمامًا كما يقرأ Claude Code دليل المهارات الخاص به. وتُدعم ثمانية محررات، وهو ما يهمّ في فريق لا يستخدم جميع أفراده المحرر نفسه.

الطرفية

npm install -g @testsprite/testsprite-cli
testsprite setup

ويمكنك فعل الشيء نفسه من لوحة تحكم TestSprite إن كنت تفضّل ألا تثبّت شيئًا محليًا. وكل ما يستطيع CLI فعله غير ذلك تجده في مستودع CLI.

يجب أن يستهدف الفحص التطبيق المنشور لا نسخة وهمية

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

يجب أن يعود الإخفاق في صورة يستطيع الوكيل استخدامها

هذا هو الجزء الذي يحدد ما إذا كانت الحلقة ستُغلق. فالإخفاق الذي يصل على هيئة لقطة شاشة في لوحة تحكم يعني أن على إنسان أن يقرأها ويفسّرها ويكتب موجّهًا. أما الإخفاق الذي يصل كحزمة واحدة متسقة تضم ما جرت محاولته، وما فعله التطبيق، وأين حاد عن المسار، فهو شيء يستطيع الوكيل التقاطه والتصرف بناءً عليه مباشرة. وتبدأ الدورة التالية من الدليل لا من وصفك للدليل.

اجعله يحدث دون أن يتذكره أحد

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

  • GitHub App هو webhook تُعدّه من لوحة تحكم TestSprite. يستمع إلى حدث النشر الذي ينتجه مسار النشر لديك أصلًا، فلا يتغير شيء في مستودعك.

  • GitHub Actions يضع الخطوة داخل سير عملك الخاص، ويُضبط من الطرفية.

يستحق هذا النموذج أن تفهمه حتى لو لم تلمس الإعدادات أبدًا. فالتكامل لا يبني تطبيقك ولا ينشره، ولا يضيف ملفات سير عمل ولا يعدّلها. إنه يستمع إلى حدث النشر الذي ينتجه مسار النشر لديك أصلًا، ويتعامل مع «البناء الجديد متاح على هذا الرابط» بوصفه إشارة بدء التشغيل. وتعود النتائج إلى حيث يجري العمل، كتعليق على طلب السحب أو كفحص على الـ commit.

قراران يحددان مدى فائدته، وكلاهما مسألة تقدير لا مسألة إعدادات.

  • أي لحظة تُعدّ لحظة الجاهزية. اختر الحدث الذي يُطلَق بعد أن يصبح النشر حيًّا، لا الحدث الذي يُطلَق عند بدء البناء. فالحدث الذي يصل مبكرًا جدًا يوجّه التشغيل إلى رابط لم يعمل بعد، فتفشل كل الاختبارات لسبب لا علاقة له بكودك. وهذه أكثر الطرق شيوعًا لخطأ الإعداد.

  • هل يكون الفحص إرشاديًا أم مُلزِمًا. التعليق على طلب السحب يُعلِم فحسب، أما الفحص المطلوب فيمنع الدمج. ابدأ إرشاديًا لأسبوع لترى ما الذي يلتقطه، ثم قرّر. أما جعله مُلزِمًا من اليوم الأول، قبل أن يثق به أحد، فهو الطريق إلى تعطيل فحص مفيد.

ملاحظة عملية إن كانت معايناتك تعمل على منصة مثل Vercel أو Netlify: تختلف تسمية روابط المعاينة من مستضيف لآخر، لذا يأخذ التكامل نمطًا بدلًا من عنوان ثابت، ويملأ الفرع أو طلب السحب في كل تشغيل.

أين يقع هذا بجانب اختباراتك الحالية

هذا تحقق سلوكي على المنتج وهو يعمل، ولذلك فهو يكمّل اختبارات الوحدة المحلية السريعة لا يحلّ محلها. أبقِ اختبارات الوحدة للمنطق، ودع هذا يغطي ما لا تستطيع رؤيته بحكم بنيتها: هل تعمل الميزة حين يقودها مستخدم حقيقي.

ملاحظة عن النمط الأوسع

لا شيء من هذا خاص بـ Cursor. فالفجوة نفسها تظهر مع كل مساعد يكتب الكود أسرع مما يستطيع الإنسان قراءته، ولهذا تستحق خطوة التحقق أن تُبنى مرة واحدة وتُعاد عبر المحررات. وفي لوحة صدارة مفتوحة بنى فيها الوكلاء التطبيق نفسه، قدّم أرخص نموذج في الميدان أصحّ تطبيق حين كانت حلقة التحقق هذه قائمة، وبنصف تكلفة أغلى المشاركات. ولم يكن الدرس أن نموذجًا ما أفضل من غيره، بل أن نموذجًا يملك إشارة تغذية راجعة فعّالة يتفوق على نموذج أقوى بلا إشارة.

أين يقع TestSprite في حلقة Cursor

TestSprite هو نصف التحقق. فـ Cursor يكتب التغيير، وTestSprite يفتح التطبيق المنشور، ويمرّ بالسلوك كما يفعل المستخدم، ويبلّغ بما حدث فعلًا. ولا يحاول أي منهما أداء عمل الآخر، وهذا الفصل هو بيت القصيد: صاحب التغيير هو الطرف الخطأ لتأكيده.

ويترتب على ذلك ثلاثة أمور لفريق يطلق كودًا كتبه Cursor. تتوقف التغطية عن الاعتماد على أن يجد أحدهم وقتًا لكتابة الاختبارات، لأن الحالات تُولَّد من منتجك وتُنقَّح بلغة طبيعية. وتتوقف عمليات إعادة التصميم الشكلية عن تحويل مجموعة الاختبارات إلى اللون الأحمر، لأن الخطوة نيّة لا مسار عبر الـ DOM. ويعود الإخفاق كحزمة واحدة يستطيع الوكيل التصرف بناءً عليها، فتبدأ الدورة التالية من الدليل لا من وصفك له.

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

لماذا تنجح اختبارات Cursor نفسها بينما الميزة معطّلة؟

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

هل أحتاج إلى فريق QA لإعداد هذا؟

لا. الإعداد يتكوّن من أمر في CLI وتثبيت GitHub App. وهو مصمم لفرق يكون فيها المطورون هم الوحيدين الذين سينظرون يومًا في نتيجة اختبار.

هل يحتاج إلى الوصول إلى كودي المصدري؟

يجري التحقق على تطبيقك المنشور من خلال واجهته. أما صلاحية الكتابة في GitHub فتُستخدم لنشر النتائج على طلبات السحب والـ commits، لا لدفع كود أو تعديل ملفات سير العمل.

ماذا يحدث للاختبارات حين تتغير الواجهة عن قصد؟

الاختبار المرتبط بتدفق عمل لا بعنصر بعينه ينجو من إعادة تصميم لا تغيّر التدفق. وحين يتغير التدفق نفسه، فهذا سلوك جديد يحتاج إلى حالة جديدة أو محدَّثة، ويستطيع الوكيل توليدها من التغيير.

هل يمكنني تشغيل هذا على فرع دون التأثير في المجموعة الرئيسية؟

الاختبارات تتبع التطبيق لا فرع git، لذا توجد مجموعة اختبارات مرجعية واحدة. اختر مُشغِّل pull request إن أردت فحوصًا لكل فرع، ومُشغِّل push إن أردت بيئة مشتركة واحدة يجري التحقق منها بعد كل دمج.

الخلاصة المختصرة

توقّف عن قراءة الفرق. شغّل التطبيق.

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