تُسلّم خطأً برمجياً إلى Claude Code وميزة صغيرة إلى Codex، ثم تنصرف، وعند عودتك تجد ملفاً يبدو خاطئاً. تعديل أحد الوكيلين مفقود. أو كلا التعديلين موجود لكن البناء يفشل. أو الأسوأ: كل شيء يُترجَم بنجاح، ثم ينكسر اختبار في موضع لم يلمسه أيٌّ من الوكيلين. لم ينهَر شيء أثناء غيابك. كلا الوكيلين نفّذا العمل الذي طلبته. لكن الملف لم يستطع احتواء مؤلِّفين في آن واحد، ولم يخبرك أحد أيّهما انتصر.
هذا الصمت هو المشكلة الحقيقية. فالملف ليس أداة دمج. حين يكتب وكيلان برمجيان في auth.ts داخل المجلد نفسه في أوقات متقاطعة، لا يوجد قفل، ولا طابور انتظار، ولا تحذير؛ هناك فقط العملية التي حفظت أخيراً. تستعرض هذه المقالة التصادم خطوة بخطوة، وتسمّي نمط الفشل في كل مرحلة، وتنتهي بقائمة تحقق يمكنك تنفيذها قبل جلستك المتوازية التالية.
كما تشير إلى سير العمل الذي تؤدي إليه هذه الإصلاحات. Sharkly منصّة شاملة لإدارة وأوامر الوكلاء، تجلس فوق الوكلاء البرمجيين الذين تشغّلهم بالفعل. تمنح كل Task شجرة عمل (worktree) معزولة خاصة به، وتُبقي السياق والتقدّم والعوائق والنتائج والمراجعة البشرية ظاهرة من الطلب حتى الإصدار. يمكنك بناء الانضباط نفسه يدوياً باستخدام Git وحده، ومعظم هذه المقالة يوضّح لك كيف. القسم الأخير يتناول متى يتوقف الأداء اليدوي عن العائد.
لا يوجد قفل: آخر كتابة تفوز
لنبدأ بالأسوأ، لأنه الأكثر شيوعاً والأقل ظهوراً. يعمل كلا الوكيلين في مجلد العمل نفسه. يقرأ الوكيل A ملف auth.ts، ويخطّط لتعديل، ويبدأ الكتابة. يقرأ الوكيل B الملف نفسه auth.ts بعد ثانية، ويخطّط لتعديل مختلف على النسخة التي قرأها، ويكتب أيضاً.
لم يقرأ أيٌّ منهما تعديل الآخر، لأن التعديل لم يكن محفوظاً بعد لحظة قراءته. ومن يحفظ ثانياً يطمس الملف على القرص بنسخة لم تحتوِ أبداً على عمل الوكيل الأول. هذا هو مبدأ «آخر كتابة تفوز»، وهو صامت. لا علامة تعارض، ولا خطأ، ولا رمز خروج. يُبلّغ الوكيل A بالنجاح. ويُبلّغ الوكيل B بالنجاح. أحدهما يكذب عليك دون أن يدري.
سباق ملف القفل هو الفشل نفسه في موضع أكثر تكلفة. يشغّل الوكيل A الأمر npm install ويعيد كتابة package-lock.json؛ في أثناء التثبيت، يعيد الوكيل B كتابته من جديد انطلاقاً من حالة بداية مختلفة. النتيجة ملف قفل لا يطابق أيّاً من شجرتي الاعتماديات، وينفجر الخلل على جهاز شخص آخر بعد أيام. مخرجات البناء المشتركة، ومنفذ خادم تطوير واحد، وذاكرة تخزين مؤقت عامة، كلها تفشل بالطريقة ذاتها.
الحل ليس وكيلاً أذكى. الحل أن تمنح كل وكيل غرفة خاصة به.
نسخة عمل واحدة لكل وكيل: الطمس الصامت يتحوّل إلى تعارض مرئي
شجرة عمل Git (worktree) هي نسخة ثانية مُستخرجة من مستودع واحد، على فرع خاص بها، تتشارك مخزن كائنات .git واحداً. يحصل كل وكيل على مجلد حقيقي يحرّر فيه ويبني ويختبر، دون أن يرى العمل الجاري لأي وكيل آخر.
# From the main repo
git worktree add ../repo-agent-a -b agent/auth-refactor
git worktree add ../repo-agent-b -b agent/session-timeout
وجّه الوكيل A إلى ../repo-agent-a والوكيل B إلى ../repo-agent-b. الآن تعديلات A غير المحفوظة على auth.ts غير مرئية لـ B، ولا يستطيع أيٌّ منهما طمس ملف الآخر على القرص، لأنهما يحرّران ملفين مختلفين على فرعين مختلفين. نموذج شجرة العمل مشروح بعمق أكبر في Git worktrees لوكلاء البرمجة بالذكاء الاصطناعي وأشجار عمل وكلاء البرمجة: نسخة مستخرجة واحدة لكل مهمة.
لاحظ ما فعله العزل هنا. لم يمنع الوكيلين من تحرير الملف نفسه. بل حوّل فقدان بيانات صامت إلى تعارض دمج مرئي في اللحظة التي يعود فيها الفرعان ليلتقيَا. وهذا ترقية حقيقية: يتوقف Git ويريك الأسطر المتعارضة بالضبط، فتحلّها وأنت مطّلع على النسختين كاملتين. تعارض تراه هو مشكلة تستطيع إصلاحها. أما الطمس الذي لم تره، فهو الذي وصل إلى الإنتاج.
ثمة حدّ يستحق التصريح به: أشجار العمل تعزل الملفات، لا الآثار الجانبية. قاعدة بيانات مشتركة، أو منفذ واحد، أو مسار مخرجات مُثبّت في الكود، كلها تتصادم رغم أشجار العمل المنفصلة. اعزل هذه أيضاً، أو اجعل الوكلاء الذين يحتاجونها يعملون بالتتابع.
التعارض الذي لا تراه
هذا هو نمط الفشل الذي ينجو من العزل. الوكيل A، وهو يعمل في شجرة عمله الخاصة، يعيد تسمية الدالة getUser() إلى fetchUser() ويحدّث كل موضع استدعاء يملكه. الوكيل B، وهو يعمل في شجرة عمله الخاصة في الوقت نفسه، يكتب ثلاثة مواضع استدعاء جديدة لـ getUser()، لأن هذا كان اسم الدالة حين قرأ B الملف.
يندمج الفرعان بلا أي علامة تعارض. يقارن Git الأسطر، ولم يتقاطع خطياً أي سطر: A غيّر ملفاته، وB أضاف ملفات جديدة. الدمج نظيف. ثم ينكسر البناء بسبب getUser غير المُعرّف، أو يُترجَم بنجاح ويفشل وقت التشغيل حيث لا يصل مدقّق الأنواع. لم يُبلّغ أي أداة، لأنه لم يكن هناك تصادم نصيّاً. اختلف التعديلان حول معنى الملف، لا حول نصه.
لهذا فإن «اعزل الوكلاء فحسب» ليس الجواب الكامل. العزل يؤجّل تصادم الملف نفسه إلى تصادم الواجهة نفسها، والنوع الثاني يتخفّى أفضل من الأول. والعنصر الحاسم هنا هو تحديد نطاق المهام، لا الأدوات. قاعدة مفيدة: إن لم تستطع وصف مهمتي وكيلين دون تسمية الملف نفسه أو الدالة نفسها، فهما مهمة واحدة لا مهمتان. تقسيم العمل وفق حدود الوحدات هو معظم ما يمنع تشغيل Claude Code وCodex معاً على قاعدة شيفرة واحدة من أن يتحوّل إلى فترة كاملة من تصحيح الأخطاء. وحين تتشارك مهمتان واجهة فعلاً، اجعلهما تعملان بالتتابع، أو ادمج وأعِد الأساس (rebase) بين الخطوات حتى يقرأ الوكيل الثاني واقع الوكيل الأول.
حيث تتوقف الحلول اليدوية عن التوسّع، وما تضيفه لوحة المهام
وكيلان وملف واحد مشترك، تستطيع إدارتها يدوياً: شجرتا عمل، وتحديد نطاق دقيق، ودمج تراقبه بنفسك. تصمد الطريقة حتى يصبح العدّ صعباً. مَن على أي فرع. أي شجرة عمل تقابل أي مهمة. أيٌّ من خمس عمليات دمج نظيفة غيّر واجهة مشتركة في صمت. أي الفروع تنتظر مراجعتك، وأيها راجعته بالفعل. لم تصبح أوامر Git أصعب أبداً؛ بل صارت المحاسبة حولها كذلك، وهذه المحاسبة تحديداً هي الموقع الذي تتسلّل منه تصادمات الملف نفسه، لأنك وافقت على دمج لم تقرأه كاملاً.
لوحة المهام تغيّر ما تتبّعه. فبدلاً من جدار من الطرفيات وأشجار العمل المنفصلة، تصبح كل وحدة عمل «مهمة» (Task) لها مالك وفرع وحالة. في Sharkly، تحصل المهمة تلقائياً على شجرة عمل معزولة خاصة بها، فيصبح العزل على مستوى الملف هو الوضع الافتراضي بدلاً من أمر تذكّر تشغيله. ويعود التنفيذ والعوائق والنتائج إلى المهمة، فتراجع التعديل في ضوء نطاقه المعلن بدلاً من استنتاج النية عكسياً من الـ diff. ويبقى تقسيم الأدوار ثابتاً: الوكلاء يبحثون وينفّذون ويختبرون ويقدّمون تقاريرهم؛ والبشر يحدّدون الاتجاه، ويمنحون الصلاحية، ويقبلون النتيجة. وتتوقف الأتمتة عند حدّ الحاجة إلى حكم الفريق.
وهذه النقطة الأخيرة هي ما لا يحلّه العزل وحده أبداً. الأدوات الأفضل تسهّل العزل والدمج، لكن المراجعة لا تتوسّع مع عدد الوكلاء؛ إنها تتوسّع مع انتباهك، والمراجعة البشرية هي عنق الزجاجة الحقيقي في العمل المتوازي للوكلاء. خمسة وكلاء ينتجون خمسة فروع في الوقت الذي تقرأ فيه فرعاً واحداً بتمعّن. لوحة المهام لا تقرأ الكود نيابة عنك. إنها تُبقي الطابور صادقاً، حتى لا يُمرَّر تعديل يبدو معقولاً بعد قراءة سريعة ودمجه لأنك فقدت أثر أي كومة ينتمي إليها. وحساب التعارض نفسه يظهر على مستوى المستودع أيضاً، وهو مشروح في وكلاء برمجة متعددون في المستودع نفسه.
يمكنك بناء هذا بأشجار العمل والانضباط، أو استخدام Sharkly لإدارة سير العمل. وإن كنت تشغّل عدة وكلاء بالفعل، فالقيمة تكمن في وجود مكان واحد تعيش فيه مهامهم ونتائجهم بدلاً من تناثرها بين الطرفيات.
قائمة التحقق التي تنفّذها قبل جلستك المتوازية التالية
نفّذ هذا قبل أن توجّه وكيلين إلى أي شيء:
- امنح كل وكيل شجرة عمل خاصة به. لا تضع وكيلين أبداً في مجلد عمل واحد. الأمر
git worktree add ../repo-<task> -b agent/<task>لكل مهمة. راجع وكلاء متوازيون في أشجار عمل معزولة. - حدّد نطاق المهام بحيث لا تسمّي الملف نفسه أو الدالة نفسها. وإن اضطررت، فهي مهمة واحدة؛ اجعلها تعمل بالتتابع.
- اعزل الآثار الجانبية، لا الملفات فقط. قواعد بيانات ومنافذ ومسارات مخرجات وذاكرات تخزين مؤقت منفصلة، أو اجعل الوكلاء المتشاركين فيها يعملون بالتتابع.
- أعِد الأساس (rebase) قبل الدمج عندما يعمل الفرعان انطلاقاً من الـ commit الأول نفسه، حتى يتراكب عمل الوكيل الثاني فوق الأول.
- شغّل تدقيق الأنواع ومجموعة الاختبارات الكاملة بعد كل دمج، لا بعد الأخير فقط. التعارضات الدلالية تنجح في دمج نظيف وتفشل هنا.
- راجع كل فرع في ضوء نطاق مهمته، واحداً تلو الآخر. كومة من علامات النجاح الخضراء غير المقروءة هي المكان الذي ينتقل منه التعارض غير المرئي إلى الإنتاج.
وكيلان يحرّران ملفاً واحداً ليس حالة نادرة استثنائية؛ إنه ما يبدو عليه العمل المتوازي في اللحظة التي تتقاطع فيها مهامك بمقدار بوصة واحدة. خطّط للتصادم، واجعله مرئياً، وأبقِ قرار الدمج بيد شخص واحد.



