Free

تصحيح الأخطاء الصامتة مع Claude

ثلاثة أخطاء حقيقية نقرة فيها لا تُحدث شيئاً — أخطأ فيها Claude كل مرة حتى أغلقت جملة إضافية في البرومبت القضية.


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

صادفت ثلاثة من هذه الأخطاء متتالية أثناء بناء تدفق الدفع في how2claude. إليك تحليل ما بعد الحادثة، وأنماط البرومبت التي أستخدمها الآن للأخطاء الصامتة.

الخطأ 1: علامة استفهام بالعرض الكامل مختبئة في عنوان محفظة

وصّلت مدفوعات الكريبتو عبر x402. اشتغلت محلياً. أول نقرة في الإنتاج: invalid_string at payTo في الـ console. تدفق التوقيع لم يبدأ حتى — Zod schema الخاص بـ facilitator رفض الطلب قبلها.

المحفظة عنوان 0x... من 42 حرفاً، بدت سليمة بالعين. طلبت من Claude أن يفحص حقل wallet في config/credentials/production.yml.enc:

w = Rails.application.credentials.dig(:x402, :wallet_address).to_s
puts "length: #{w.length}"
# => 43

43 حرفاً. عناوين EVM يجب أن تكون 42. الحرف الـ43 كان علامة استفهام صينية بالعرض الكامل (U+FF1F) دخلت أثناء نسخ ولصق من طريقة إدخال صينية.

المسح الأول من Claude لم يرفع أي علم — بالنسبة له كانت سلسلة تبدأ بـ 0x وتبدو صحيحة. لم يعد الطول من تلقاء نفسه. أضف هذه إلى البرومبت: "هذا العنوان أطول بحرف واحد من المتوقع — اطبع كل codepoint على حدة". يظهر 0xFF1F، تُحل القضية.

الخطأ 2: زر Stripe Checkout لم يتفاعل مع النقر

زر Subscribe في صفحة التسعير — تنقر، الصفحة لا تتحرك. لا أخطاء. تبويب network يُظهر POST خارجاً، Stripe يُعيد 302 إلى checkout.stripe.com — ثم... لا شيء.

طلبت من Claude أن يفحص الـ controller أولاً. المنطق سليم: redirect_to session.url, allow_other_host: true. JS — لا يوجد listener ذو صلة.

أخيراً لاحظت header الاستجابة: Content-Type: text/vnd.turbo-stream.html. Turbo كان يلتقط submit الخاص بـ button_to كطلب Turbo Stream، وTurbo Stream لا يتبع 302 عبر الأصول المختلفة — فابتُلع التحويل وبقيت الصفحة صامتة في مكانها.

الإصلاح:

<%= button_to "Subscribe", ..., data: { turbo: false } %>

الخطأ نفسه عاد ضربني بعد شهر في زر Google OAuth. المعترضات على مستوى الإطار أرض خصبة للأخطاء الصامتة — Claude افتراضياً يستنتج خطياً طلب/استجابة ولن يذهب باحثاً عن طبقة وسيطة أعادت كتابة الدلالات. أضف إلى البرومبت: "مرّ على كل معترض على مستوى الإطار تمر به هذه النقرة — اسرد كل middleware/طبقة JS تعالج هذا الطلب في المسار browser → server → browser."

الخطأ 3: زر التبديل Monthly/Yearly لم يتفاعل

Stimulus controller الخاص بالتبديل بين الشهري/السنوي في صفحة التسعير — تنقر على الزر، لا شيء يتبدل. دالة الـ controller أُطلقت (مؤكدة بـ console.log)، لكن this.monthlyTarget كان undefined.

تخمين Claude الأول: خطأ مطبعي في اسم الـ target. لم يكن كذلك. data-pricing-target="monthly" موجود في الـ DOM.

المشكلة في الـ scope. data-controller="pricing" كان على حاوية زر التبديل، لكن قسمَي الـ grid كانا خارج تلك الحاوية. Stimulus لا يبحث عن الـ targets إلا داخل الشجرة الفرعية لعنصر الـ controller؛ ما في الخارج غير موجود بالنسبة له. نقلت data-controller إلى <section> الذي يغلّف كل شيء — اشتغل.

هذا الخطأ يصرخ "الكود صحيح" — كل الأسماء مطابقة، كل الخصائص موجودة، الميزة فقط معطوبة. Claude افتراضياً يقرأ الكود سطراً سطراً؛ لن يتصور بنية الـ DOM من تلقاء نفسه. أضف إلى البرومبت: "ارسم شجرة الأسلاف والأحفاد للعنصر الذي يحمل data-controller='pricing' — حدد أي data-pricing-target يقع داخل الشجرة الفرعية وأيها خارجها."

ثلاثة أنماط برومبت للأخطاء الصامتة

الأخطاء الثلاثة بدت متطابقة من الخارج: نقرة، لا شيء يحدث، لا خطأ. أخطأ Claude في التخمين كل مرة، وكل مرة جملة إضافية في البرومبت أغلقت القضية. النمط المشترك:

1. أخبره بالفرق المُكمَّم بين المتوقع والفعلي — لا تكتفِ بـ "هذا غلط"

ليس "عنوان المحفظة به مشكلة"، بل "أطول بحرف واحد من المتوقع".
ليس "الزر لا يعمل"، بل "الاستجابة 302، لكن المتصفح لم يتّبعها".
ليس "التبديل معطوب"، بل "دالة الـ controller تُطلق لكن الـ target هو undefined".

كلما ضاق الفرق، ضاقت مساحة بحث Claude.

2. وجّهه إلى الطبقات غير المرئية — الإطار، المتصفح، الترميز

الأخطاء الصامتة نادراً ما تعيش في كود أعمالك. تعيش في Turbo، في scope الخاص بـ Stimulus، في ترميز الأحرف، في CSP، في CORS، في service workers. الافتراضي لـ Claude هو قراءة كودك. قل له صراحةً أن يذهب لينظر في تلك الطبقات الأخرى.

3. اطلب حالة وسيطة، لا استنتاجات

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


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