تضع عدة مواقع في مستودع واحد، وتضع قواعد الذكاء الاصطناعي (CLAUDE.md أو AGENTS.md) في مجلد كل موقع، فيتّبع الذكاء الاصطناعي قواعد ذلك الموقع—هذا هو الافتراض الطبيعي، لكن لا Claude Code ولا Codex «يقرأ دائمًا» القواعد الموجودة في المجلدات الأدنى. عند التشغيل من جذر المستودع، لا يحمّلها Claude Code حتى يلمس ملفات في ذلك المجلد، ولا يحمّلها Codex تلقائيًا (قد يفتحها الذكاء الاصطناعي من تلقاء نفسه، لكن لا ضمان لذلك). يقارن هذا المقال، بندًا بندًا واستنادًا إلى الوثائق الرسمية للأداتين، متى تُحمَّل هذه الملفات وكيف تتحقق من تحميلها، ثم يبيّن كيف تفرض القواعد بفحوص آلية بدلًا من الوثائق. وقد نشأ مما تعلّمناه أثناء تصميم هذا الترتيب في بيئة تتولى فيها وكلاء الذكاء الاصطناعي تشغيل الخوادم وعدة مشاريع.

متى تُحمَّل القواعد الموجودة في مجلد كل موقع؟ (عند التشغيل من جذر المستودع)

repo/ ├─ CLAUDE.md, AGENTS.md ← قواعد المستودع كله └─ sites/ ├─ blog/CLAUDE.md, AGENTS.md ← قواعد blog ├─ shop/CLAUDE.md, AGENTS.md ← قواعد shop └─ shared/ ← مكوّنات مشتركة

Claude Code

  • عند البدءCLAUDE.md في الجذر
  • عند اللمسيحمّل CLAUDE.md الخاص بـ blog عندما يقرأ ملفًا في sites/blog/ أو يكتبه أو يحرّره
  • ليس بعدقواعد المجلد الذي لم يلمسه بعد (shop) لا تُحمَّل

Codex

  • عند البدءAGENTS.md في الجذر
  • غير مضمونAGENTS.md في sites/blog/ لا يُحمَّل تلقائيًا (لا يبحث Codex أسفل المجلد الذي شُغِّل منه)
  • غير مضمونAGENTS.md في sites/shop/ (كذلك)

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

المصادر: صفحة How Claude remembers your project من Anthropic، وصفحة Custom instructions with AGENTS.md من OpenAI (رُوجعتا كلتاهما في 1 نوفمبر 2026).

1. ما تعلّمناه من وضع القواعد في مجلد كل موقع

في بيئة تتولى فيها وكلاء الذكاء الاصطناعي تشغيل الخوادم وعدة مواقع، درسنا تصميمًا يضع عدة مواقع في مستودع واحد ويضع قواعد كل موقع في مجلده (حُذفت التفاصيل التي قد تكشف الأماكن أو الأسماء، وعُرض المحتوى التقني بصورة عامة). كان الهدف تقسيم العمل: الذكاء الاصطناعي المسؤول عن blog يقرأ قواعد blog وحدها ولا يلمس shop أبدًا.

لكن عند مراجعة الوثائق الرسمية للأداتين، تبيّن أن القواعد التي وضعناها لا يُضمن تحميلها قبل بدء العمل. فالقواعد التي تُحمَّل تتوقف على المجلد الذي تشغّل منه. وفوق ذلك، تصف وثائق Claude Code ملف CLAUDE.md بأنه سياق (context)، لا إعداد مفروض. وحتى إذا حُمِّلت القواعد، فلا شيء يضمن الالتزام بها.

  1. !

    وضع القواعد في المجلدات لا يكفي لتقسيم المسؤوليات

    الخطر
    ذكاء اصطناعي شُغِّل من الجذر يغيّر الملفات دون أن يقرأ قواعد ذلك الموقع، أو قبل أن يقرأها
    العلاج
    شغّل عمل الموقع من مجلد الموقع، وأوقف التغييرات خارج النطاق بفحص قبل الإصدار (القسم 4)
    التحقق
    استخدم الأداتين للتأكد من القواعد التي حُمِّلت (القسم 3)، وغيّر شيئًا خارج النطاق عمدًا لترى هل يتوقف الإصدار

الأسباب العامة لتجاهل الذكاء الاصطناعي القواعد وطرق علاجها مشروحة في «لماذا يتجاهل الذكاء الاصطناعي قواعد ‎.md؟ تحقق من التحميل والتنفيذ». أما هذا المقال فيركّز على ما يحدث حين توزّع القواعد على المجلدات. وهناك تنبيهان آخران من البيئة نفسها، بشأن تسليم عدة مستودعات إلى الذكاء الاصطناعي، في القسم 5.

2. متى تُحمَّل القواعد الخاصة بكل مجلد؟

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

البندClaude CodeCodex
ما يُحمَّل عند البدءكل CLAUDE.md وCLAUDE.local.md في مجلد التشغيل (launch directory) وفي جميع المجلدات التي فوقهمن جذر المشروع (project root، وهو عادةً جذر git) نزولًا إلى مجلد التشغيل، ملف واحد لكل مجلد: AGENTS.override.md، وإلا فـ AGENTS.md
أسفل مجلد التشغيللا يُحمَّل عند البدء؛ يُحمَّل عندما يقرأ Claude ملفًا آخر في ذلك المجلد أو يكتبه أو يحرّرهلا يُحمَّل تلقائيًا (يتوقف البحث عند مجلد التشغيل؛ قد يفتحه الذكاء الاصطناعي من تلقاء نفسه)
هل يُضاف أثناء الجلسة؟نعم (قواعد المجلدات التي يلمسها تُضاف لاحقًا)لا (تُبنى مرة واحدة في بداية كل تشغيل)
حدود الحجمالإرشاد أن يكون الملف أقل من 200 سطر (الملفات الأطول يُلتزم بها بدرجة أقل)؛ وفي ملفات CLAUDE.md بالمجلدات الأدنى، يُقطع ما يتجاوز 40,000 حرف لكل ملف (مع سياق بحجم 200 ألف رمز)، فلا يُمرَّر إلا أولهلا يُضاف شيء بعد أن يتجاوز المجموع 32 KiB (القيمة الافتراضية لـ project_doc_max_bytes)
ملف قواعد الأداة الأخرىيقرأ AGENTS.md عندما لا يوجد CLAUDE.md أو .claude/CLAUDE.md أو CLAUDE.local.md في مجلد التشغيل أو فوقه (الإعداد الافتراضي)لا يقرأ CLAUDE.md (لا تُقرأ إلا الأسماء المضافة إلى project_doc_fallback_filenames)

المصادر: صفحتا How Claude remembers your project وMonorepos and large repos من Anthropic، وصفحة Custom instructions with AGENTS.md من OpenAI (رُوجعت في 1 نوفمبر 2026). يسري حدّ المجلدات الأدنى في Claude Code ابتداءً من v2.1.309. ومع سياق بحجم مليون رمز يصبح 150,000 حرف (و200,000 لـ Opus 4.6 وSonnet 4.6). يقرأ Claude Code ملف AGENTS.md مباشرةً ابتداءً من v2.1.277؛ ويُقرأ AGENTS.md الموجود في مجلد أدنى عندما لا يحتوي ذلك المجلد على CLAUDE.md ويفتح Claude ملفًا فيه بأداة Read. ولقراءة الاثنين معًا، اضبط Project instructions في /config على claude-md-and-agents-md.

مكان التشغيل يغيّر القواعد التي تُحمَّل

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

التشغيل منClaude CodeCodex
جذر المستودعقواعد الجذر فقط؛ وتُضاف قواعد كل موقع عندما يلمس Claude ملفات ذلك الموقعقواعد الجذر فقط؛ وقواعد المواقع لا تُحمَّل تلقائيًا
sites/blog/يقرأ قواعد blog والجذر عند البدء (تحرير الملفات خارج مجلد التشغيل يطلب الموافقة)يقرأ الجذر ثم blog؛ وقواعد shop ليست على المسار، فلا تُحمَّل تلقائيًا

ما يسهل إغفاله هو ما الذي يُعدّ «لمسًا» في عبارة «يُحمَّل عند اللمس». ما تعدّه وثائق Claude Code قراءةً هو قراءة ملف أو كتابته أو تحريره بأدواته، إضافةً إلى الأوامر التي تعرض ملفًا واحدًا مثل cat أو head. أما العمليات التي تغيّر الملفات بطرق أخرى، مثل البحث والاستبدال الجماعي عبر مجلدات عدة مواقع، فليست في تلك القائمة. وقد تتغير ملفات موقع ما قبل أن تُقرأ قواعده.

في Codex، إذا شغّلت من جذر المستودع وطلبت «أصلح blog وshop معًا»، فلن يُحمَّل AGENTS.md الخاص بأي من الموقعين تلقائيًا. تقول الوثائق إن Codex يتوقف عن البحث عند بلوغ مجلد العمل الحالي، لذا لا تُضمَّن قواعد مجلد أدنى بشكل موثوق عند البدء إلا إذا شغّلت من ذلك المجلد أو مما تحته.

ومع ذلك، فإن التعليمات الافتراضية (base_instructions/default.md) في مستودع Codex تطلب من النموذج، في مضمونها، أن يراجع ملفات AGENTS.md المنطبقة عند العمل أسفل مجلد العمل الحالي أو خارجه. قد يفتح الذكاء الاصطناعي AGENTS.md الخاص بموقع ما من تلقاء نفسه، لكن ذلك تقدير الذكاء الاصطناعي، لا ضمان من آلية التحميل.

ملفات الإعدادات الخاصة بكل مجلد تُورَّث بطريقة مختلفة أيضًا

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

البندClaude CodeCodex
ملف إعدادات المشروع.claude/settings.json.codex/config.toml
ملف أي مجلد يُقرأالملف الموجود في مجلد التشغيل (primary working directory)؛ وخلافًا لـ CLAUDE.md، لا يُورَّث من المجلدات الأعلىمن جذر المشروع نزولًا إلى مجلد التشغيل (الأقرب له الأولوية)
العلاقة بالثقة (trust)قواعد مثل allow تسري بعد أن تثق بالمجلد؛ وdeny وask تسريان فورًالا يقرأ .codex/ إلا في المشاريع الموثوقة

المصادر: صفحتا Claude Code settings وMonorepos and large repos من Anthropic، وصفحة Config basics من OpenAI (رُوجعت في 1 نوفمبر 2026).

3. التحقق من تحميل القواعد

بعد وضع القواعد، تحقّق بعد التشغيل من أنها حُمِّلت فعلًا. تختلف الطريقة باختلاف الأداة، وفي Claude Code تبحث في مكانين مختلفين عن القواعد المحمَّلة عند البدء وتلك المحمَّلة لاحقًا.

ما تتحقق منهClaude CodeCodex
القواعد المحمَّلة عند البدءقائمة Memory files في /contextاسأل «Show which instruction files are active.» (هذا تقرير الذكاء الاصطناعي نفسه؛ وللتأكد، استخدم السجل أدناه)
القواعد المحمَّلة أثناء الجلسةلا تظهر في Memory files؛ وعند تحميلها يظهر في الطرفية سطر Loaded مع مسارهالا يُضاف شيء أثناء الجلسة (كما سبق)
الاحتفاظ بسجلاستخدم خطاف InstructionsLoaded لتسجيل القواعد التي حُمِّلت ومتى ولماذا (لا يعمل مع AGENTS.md المقروء مباشرةً)شغّل codex -c log_dir=./.codex-log لكتابة سجل TUI (./.codex-log/codex-tui.log) ومعرفة القواعد التي حُمِّلت

المصادر: قسم استكشاف الأخطاء في صفحة How Claude remembers your project من Anthropic، وفقرة «Verify your setup» في صفحة Custom instructions with AGENTS.md من OpenAI (رُوجعت في 1 نوفمبر 2026). أضف مجلد السجل إلى .gitignore كي يبقى خارج git.

لاختبار ملف قواعد أُضيف حديثًا في مجلد أدنى في Claude Code، اتّبع نصيحة الوثائق: أنشئ ملف القواعد من الصدفة (shell) بدلًا من أن يكتبه الذكاء الاصطناعي، ثم اطلب من الذكاء الاصطناعي قراءة ملف في ذلك المجلد وانظر هل يظهر سطر Loaded.

4. افرض القواعد بفحوص آلية لا بالوثائق

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

وثائق القواعد
تشرح الأسباب والخطوات CLAUDE.md وAGENTS.md. قد لا تُحمَّل، وحتى إذا حُمِّلت فلا شيء يضمن الالتزام بها. ومع ذلك يحتاج إليها الذكاء الاصطناعي ليفهم سبب توقف شيء ما
حواجز الأداة
توقف الإجراء قبل وقوعه الخطافات وإعدادات الصلاحيات في أداة الذكاء الاصطناعي (نسمّيها في هذا المقال «حواجز الأداة»). تختلف طريقة ضبطها من أداة إلى أخرى، وقد لا تسري بحسب مكان التشغيل وإعدادات الثقة
فحص الإصدار
نقطة التوقف الأخيرة، أيًّا كانت الأداة التي أنجزت العمل إذا تغيّر أي شيء خارج المجلد المسنَد، تتوقف عملية الإصدار نفسها. ولا يعتمد ذلك على أداة الذكاء الاصطناعي

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

حدّد نطاق كل وكيل بإعداد خاص بكل worktree

امنح كل موقع worktree خاصًا به (git worktree)، وضع في أعلى الـ worktree ملف إعدادات صغيرًا (.agent-owner) يسرد المجلدات التي يُسمح للوكيل العامل هناك بتغييرها. لو كتبت النطاق في وثيقة قواعد داخل المستودع، لكان متطابقًا في كل worktree ولما أمكن أن يختلف بحسب المكان. أبقِ ملف الإعدادات خارج git (أضفه إلى .git/info/exclude)، واكتبه عند إنشاء الـ worktree.

لاحظ أنك تشغّل الذكاء الاصطناعي لا من أعلى الـ worktree، بل من مجلد الموقع داخله. فأعلى الـ worktree هو نفسه جذر المستودع، والتشغيل من هناك يعطي النتيجة نفسها التي يعطيها «جذر المستودع» في القسم 2.

# من النسخة الرئيسية (/srv/repo)، أنشئ worktree لـ sites/blog في /srv/work-blog
cd /srv/repo
git worktree add /srv/work-blog -b blog-work
printf 'sites/blog/\n' > /srv/work-blog/.agent-owner
echo '.agent-owner' >> .git/info/exclude

# شغّل الذكاء الاصطناعي من sites/blog/ داخل الـ worktree
cd /srv/work-blog/sites/blog
claude        # أو codex في حالة Codex

# قبل الإصدار، شغّل فحص النسخة الرئيسية في أعلى الـ worktree
cd /srv/work-blog
bash /srv/repo/tools/check-owner.sh origin/main

شغّل سكربت الفحص من النسخة الرئيسية (حيث لا يعمل الذكاء الاصطناعي) أو من CI، لا من النسخة الموجودة داخل الـ worktree. فلو شغّلت النسخة الموجودة في الـ worktree، لمرّ مباشرةً ذكاء اصطناعي يعيد كتابة سكربت الفحص نفسه.

أوقف الإصدار عندما يتغير أي شيء خارج النطاق

ضع الفحص التالي (tools/check-owner.sh المذكور أعلاه) في بداية عملية الإصدار. يفترض أن الإصدارات لا تتم إلا من الإيداعات (commits): يتوقف إذا بقيت تغييرات غير مُودَعة، ويتوقف إذا احتوى الفرق المُودَع عن الأساس (origin/main أو ما شابه) على ملف واحد خارج المجلدات المسنَدة. وهو يقرأ .agent-owner، لذا شغّله في أعلى الـ worktree.

#!/usr/bin/env bash
# يُشغَّل في أعلى الـ worktree قبل الإصدار: يتوقف إذا تغيّر أي شيء خارج المجلدات المسنَدة
set -euo pipefail

base="${1:-origin/main}"
owner_file=".agent-owner"   # في أعلى الـ worktree، خارج git

if [ ! -s "$owner_file" ]; then
  echo "لا يوجد ملف النطاق ($owner_file). إيقاف الإصدار" >&2
  exit 1
fi

# توقف إذا بقيت تغييرات غير مُودَعة (الإصدار من الإيداعات فقط)
if [ -n "$(git status --porcelain)" ]; then
  echo "توجد تغييرات غير مُودَعة. إيقاف الإصدار" >&2
  exit 1
fi

# --no-renames: يُحسب النقل من مجلد آخر حذفًا وإضافةً معًا
# أضف / في آخر كل بند من النطاق (حتى لا يطابق sites/blog المجلد sites/blog-old)
# اختُبر مع gawk (Git Bash). قد يفشل BSD awk إذا احتوت قيمة -v على أسطر جديدة
outside=$(git diff --name-only --no-renames "$base"...HEAD |
  awk -v list="$(tr -d '\r' < "$owner_file")" '
    BEGIN { n = split(list, own, "\n")
            for (i = 1; i <= n; i++) if (own[i] != "" && substr(own[i], length(own[i])) != "/") own[i] = own[i] "/" }
    { ok = 0; for (i = 1; i <= n; i++) if (own[i] != "" && index($0, own[i]) == 1) ok = 1
      if (!ok) print }')

if [ -n "$outside" ]; then
  echo "تغيّرت ملفات خارج النطاق. إيقاف الإصدار:" >&2
  echo "$outside" >&2
  exit 1
fi
echo "التغييرات داخل النطاق فقط"

هذه نتائج الاختبار الذي أنشأ فيه Claude Code، كاتب هذا المقال، مستودعًا تجريبيًا فيه موقعان ومجلد مشترك، وشغّل الفحص في 1 نوفمبر 2026 مع Git 2.56 (Git Bash على Windows).

ما جرّبناهالنتيجة
غيّر وكيل blog ملفات blog فقطيمرّ
نقلنا ملفًا من shop إلى مجلد blogيتوقف (ويُدرَج ملف shop الأصلي)
غيّرنا ملفًا في المجلد المشتركيتوقف (المجلد المشترك خارج النطاق ما لم يُدرَج)
كتبنا النطاق دون / في آخره وأضفنا sites/blog-old/يتوقف (النسخة التي لا تضيف / كانت تمرّره)
تركنا تغييرات غير مُودَعة في shopيتوقف (النسخة التي لا تفحص إلا الفرق المُودَع تفوّت ذلك)
حذفنا ملف النطاقيتوقف

--no-renames هو ما يمنع عمليات النقل من التسلل. فبدونه، يُدرج git التغيير الذي يعدّه نقلًا باسم الوجهة فقط، فيمرّ دون انتباه تغييرٌ يجلب ملفات من موقع آخر. كما أن الفحص «يتوقف» بدلًا من أن «يمرّ» حين يغيب ملف الإعدادات، حتى لا يمرّ worktree لم يُحدَّد نطاقه قط.

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

أضف حواجز الأداة

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

البندClaude CodeCodex
منع الكتابة إلى الملفاتأضف Edit(path) إلى deny في إعدادات الصلاحيات (يسري على الأدوات المدمجة وعلى الأوامر التي يتعرّف عليها Claude Code، مثل sed وtee وإعادة التوجيه)في خطاف PreToolUse، افحص المكان الذي يكتب إليه apply_patch (ويُطابَق أيضًا باسمَي Edit وWrite)، وأعد deny بوصفه permissionDecision
منع الأوامرأضف Bash(command *) إلى deny، أو أعد رمز الخروج 2 أو JSON بالرفض من خطاف PreToolUseالقاعدة prefix_rule(pattern=[...], decision="forbidden") (تجريبية) وخطافات PreToolUse
منع كل العملياتصندوق العزل (sandbox) (فـ deny لا يوقف سكربتًا يفتح الملفات ويكتب فيها بنفسه)صندوق العزل (sandbox) (sandbox_mode = "workspace-write" يقصر الكتابة على مجلد التشغيل؛ وwritable roots تضيف أماكن قابلة للكتابة، فلا تضفها باستخفاف)
الشروط عند وضعها في المشروعيقرأ .claude/settings.json في مجلد التشغيل؛ وdeny يسري حتى قبل أن تثق بالمجلدلا يقرأ .codex/ الخاص بالمشروع إلا إذا كان المشروع موثوقًا؛ ولا تعمل الخطافات حتى تراجعها وتثق بها

المصادر: صفحتا Configure permissions وHooks guide من Anthropic، وصفحتا Hooks وRules من OpenAI (رُوجعت في 1 نوفمبر 2026).

في Claude Code، تبدو الإعدادات التي تمنع worktree الخاص بـ blog من تغيير ملف النطاق (.agent-owner) وسكربتات الفحص (tools/) وقواعد الجذر وshop والمجلد المشترك على النحو التالي (بافتراض أن الـ worktree في /srv/work-blog). المسارات التي تبدأ بـ // مسارات مطلقة. ولأن أماكن الـ worktree تختلف من جهاز إلى آخر، أضف هذه القواعد إلى إعدادات المستخدم على ذلك الجهاز (~/.claude/settings.json) عند إنشاء الـ worktree، لا إلى الإعدادات المحفوظة في المستودع.

{
  "permissions": {
    "deny": [
      "Edit(//srv/work-blog/.agent-owner)",
      "Edit(//srv/work-blog/tools/**)",
      "Edit(//srv/work-blog/CLAUDE.md)",
      "Edit(//srv/work-blog/AGENTS.md)",
      "Edit(//srv/work-blog/sites/shop/**)",
      "Edit(//srv/work-blog/sites/shared/**)",
      "Edit(//srv/repo/**)"
    ]
  }
}

في Codex، سجّل سكربت الفحص تحت PreToolUse في إعدادات الخطافات (ملفك الشخصي ~/.codex/hooks.json، أو .codex/hooks.json في المستودع، أو ما شابه)، مع apply_patch بوصفه matcher. ولا تعمل الخطافات حتى تراجعها وتثق بها عبر /hooks في Codex (وإذا غيّرتها، يجب أن تثق بها من جديد).

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "^apply_patch$",
        "hooks": [ { "type": "command", "command": "python3 ~/.codex/hooks/owner_guard.py" } ] }
    ]
  }
}

يستخرج سكربت الفحص المسارات المستهدفة من أسطر *** Add File: و*** Update File: و*** Delete File: و*** Move to: في مدخلات apply_patch، ويعيد deny إذا كان أيٌّ منها خارج النطاق أو محميًا. وهذا مثال مبسّط.

import json, posixpath, re, sys

# خطاف PreToolUse في Codex: يوقف apply_patch إذا غيّر ملفات خارج النطاق أو محمية
OWN = ["/srv/work-blog/sites/blog/"]                  # النطاق (مع / في آخره)
PROTECT = ["/srv/work-blog/.agent-owner", "/srv/work-blog/tools/"]

event = json.load(sys.stdin)
command = event.get("tool_input", {}).get("command", "")
if isinstance(command, list):
    command = "\n".join(command)

bad = []
for m in re.finditer(r"^\*\*\* (?:Add File|Update File|Delete File|Move to): (.+)$", command, re.M):
    path = posixpath.normpath(posixpath.join(event.get("cwd", ""), m.group(1).strip()))
    if not any(path.startswith(o) for o in OWN) or any(path.startswith(p) for p in PROTECT):
        bad.append(path)

if bad:
    print(json.dumps({"hookSpecificOutput": {
        "hookEventName": "PreToolUse",
        "permissionDecision": "deny",
        "permissionDecisionReason": "ملفات خارج النطاق: " + ", ".join(bad)}}, ensure_ascii=False))

شغّل Claude Code، كاتب هذا المقال، هذا السكربت على مدخلات أُعدّت يدويًا بالصيغة التي يمرّرها Codex (داخل النطاق، وshop، و.agent-owner، ونقل إلى shared، وحذف تحت tools/)، وتأكد من أن التغيير الذي داخل النطاق وحده هو الذي مرّ. ولم نشغّله داخل Codex.

يمكن كتابة أوامر Bash بطرق كثيرة، فلا يستطيع الخطاف أن يحدد تمامًا أين تكتب. وتصف OpenAI نفسها الخطافات بأنها «a useful guardrail, not a complete enforcement boundary». امنع الأوامر التي تريد إيقافها، مثل rm، بقاعدة تستخدم decision = "forbidden"، ودع فحص الإصدار يلتقط الباقي. توضع القواعد في مجلد rules/ لطبقة إعدادات، مثل ~/.codex/rules/default.rules. ولا يُقرأ .codex/rules/ الخاص بمشروع إلا إذا كان ذلك المشروع موثوقًا.

يجب إعادة كتابة حواجز الأداة لكل أداة، وقد لا تسري بحسب مكان التشغيل وإعدادات الثقة. لذا ابنِ على فحص الإصدار، الذي يجب أن يمرّ به كل تغيير في النهاية، وأضف فوقه حواجز الأداة لالتقاط المشكلات مبكرًا. لكتابة خطافات Claude Code، راجع «ما هي خطافات Claude Code؟ تشغيل أوامر الشِل بشكل حتمي»؛ وللإعدادات للقراءة فقط، راجع «Codex وClaude Code بوضع read-only: كيف تقصرهما على التحقيق والمشورة فقط».

5. انتبه أيضًا إلى: الخلط بين المستودعات ومتغيرات البيئة

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

ميّز المستودعات المتشابهة الأسماء بعنوان remote URL

قرّر ذكاء اصطناعي أن مستودع خدمة أخرى ذات اسم شديد الشبه هو «نسخة قديمة من الخدمة نفسها» فنقله إلى مكان آخر (أُعيد فورًا، ولم يتغير محتواه). كان منطقه «لا تغييرات محلية، وآخر تحديث قديم»، لكن ذلك لا يثبت إلا أن «لا شيء في منتصف تغيير» وأن «لم يحرّره أحد مؤخرًا». فكلما عملت الخدمة باستقرار أكبر، قلّ تحديثها.

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

# Remote URL (احذف أي رمز أو كلمة مرور مضمّنة في العنوان قبل عرضه)
git -C <folder> remote get-url origin | sed -E 's#://[^/@]+@#://#'

# تحقّق أولًا من أنه ليس استنساخًا سطحيًا (إذا كانت النتيجة true فالنتيجة التالية غير موثوقة)
git -C <folder> rev-parse --is-shallow-repository

# الإيداع الجذري (تشترك فيه النسخ المتفرعة من التاريخ نفسه)
git -C <folder> rev-list --max-parents=0 HEAD
  • قارن عناوين remote URL بالمضيف ومسار المستودع: https://github.com/org/app.git وgit@github.com:org/app.git هما المستودع نفسه مكتوبًا بطريقتين؛ وعلى العكس، مع المرايا (mirrors) أو الأسماء المستعارة للمضيف، قد يبدو المستودع نفسه مختلفًا
  • في الاستنساخ السطحي (shallow clone)، الإيداع الجذري ليس أول إيداع حقيقي: في استنساخ جُلب بـ --depth 1، تحصل على أقدم إيداع جُلب (في اختبارنا، HEAD نفسه)
  • لا تعرض الرموز المضمّنة في العنوان: قد يحتوي remote URL على رمز مصادقة، وطباعته كما هو تسرّب سرًّا إلى المخرجات، تمامًا كمتغيرات البيئة أدناه

حتى إذا أظهرت المقارنة أنها «نسخة غير مستخدمة»، لا تحذفها فورًا؛ انقلها إلى مكان آخر أولًا. دوّن أين ذهبت، وأين كانت، والأمر الذي يعيدها، ولا تحذفها إلا بعد التأكد لفترة من أن شيئًا لم يتعطل. في هذه البيئة، أمكن تدارك الخلط فورًا تحديدًا لأن المجلد نُقل ولم يُحذف.

انظر إلى أسماء متغيرات البيئة فقط

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

متغيرات بيئة الحاوية

يعرض القيم (لا تستخدمه)

docker inspect <container>
docker exec <container> printenv
docker exec <container> sh -c 'env | cut -d= -f1'

الأسماء فقط

docker inspect --format '{{range .Config.Env}}{{index (split . "=") 0}}{{println}}{{end}}' <container>
docker exec <container> awk 'BEGIN{for(k in ENVIRON) print k}'

بلا خيارات، يطبع docker inspect الإعدادات كلها، ويحتوي حقل Env فيها على القيم كما هي. وفي الحاويات التي لا تتضمن awk، استخدم docker inspect --format.

إعدادات Docker Compose

يعرض القيم (لا تستخدمه)

docker compose config

دون تعويض المتغيرات

docker compose config --no-interpolate

مع --no-interpolate، يظهر ${DB_URL} كما هو، ولا يُظهر env_file إلا مسار الملف. أما المتغيرات التي كُتبت قيمها مباشرةً في compose.yaml فتظهر.

الصدفة المحلية

يعرض القيم (لا تستخدمه)

env
printenv
Get-ChildItem Env:

الأسماء فقط

compgen -e                  # bash
(Get-ChildItem Env:).Name   # PowerShell

هذه نتائج الاختبار الذي أجراه Claude Code، كاتب هذا المقال، بقيم وهمية في 1 نوفمبر 2026، باستخدام Docker 29.8 وDocker Compose v5.6. في حاوية تحتوي قيمة واحدة فيها أسطر جديدة (الصيغة المستخدمة عند تمرير محتوى ملف مفتاح)، طبع تشغيل env | cut -d= -f1 السطرين الثاني والثالث من القيمة كما هما، مختلطَين بأسماء المتغيرات. ولم يُظهر ENVIRON في awk ولا docker inspect --format القيم. وعرض docker compose config القيم الوهمية المكتوبة في .env وenv_file؛ ومع --no-interpolate لم يظهر أيٌّ منهما، ولم تظهر إلا القيمة المكتوبة مباشرةً في compose.yaml.

للتأكد، اكتب «انظر إلى أسماء متغيرات البيئة فقط» في وثائق القواعد، وامنع أيضًا الأوامر التي تطبع القيم بحواجز الأداة من القسم 4. وإذا طُبعت قيمة رغم ذلك، فاعدّها «مكشوفة» وقرّر هل تغيّرها (تدويرها). وللاطلاع على إعداد في Codex يمنع تمرير متغيرات البيئة نفسها إلى أوامر الذكاء الاصطناعي، راجع القسم 6 من «كيف تمنع Codex من قراءة الملفات السرية: الصلاحيات والموافقات والاتصال بالشبكة».

6. قائمة التحقق قبل تسليم العمل

  • تقرّر أن عمل الموقع يبدأ بتشغيل الذكاء الاصطناعي من مجلد الموقع داخل الـ worktree
  • بعد التشغيل، تأكدت من تضمين قواعد الموقع: Memory files في /context في Claude Code، وقائمة ملفات التعليمات النشطة في Codex
  • للعمل الذي يُشغَّل من الجذر، جهّزت طريقة لمعرفة هل حُمِّلت قواعد المجلدات الأدنى أثناء الجلسة (سطر Loaded في Claude Code، وخطاف InstructionsLoaded)
  • أضفت فحصًا يوقف الإصدار إذا تغيّر أي شيء خارج المجلدات المسنَدة، وشاهدته يتوقف عند تغيير متعمَّد خارج النطاق
  • كتبت نطاق كل worktree مع شرطة مائلة في آخره، وجعلت الفحص يتوقف عند غياب ملف النطاق
  • منعت بحواجز الأداة الكتابة خارج النطاق وإلى ملف النطاق
  • كتبت في القواعد إجراءً لمقارنة remote URL والإيداع الجذري مع بيئة الإنتاج قبل نقل مجلد أو حذفه، وللنقل بدلًا من الحذف
  • كتبت في القواعد الأوامر التي لا تعرض إلا أسماء متغيرات البيئة، ومنعت بحواجز الأداة الأوامر التي تطبع القيم
نص نموذجي لوثائق القواعد (جاهز للاستخدام)
## قواعد العمل مع عدة مشاريع
- نطاق هذا الـ worktree هو المجلدات المدرجة في .agent-owner فقط. لا تغيّر الملفات خارجه
  (التغييرات هناك توقف فحص الإصدار)
- قبل نقل مجلد أو حذفه، قارن git remote get-url origin و
  git rev-list --max-parents=0 HEAD مع بيئة الإنتاج (انتبه للاستنساخ السطحي).
  انقل بدلًا من الحذف، ودوّن طريقة الاستعادة
- انظر إلى أسماء متغيرات البيئة فقط. لا تستخدم env أو printenv أو docker inspect (دون --format) أو
  docker compose config (دون --no-interpolate)

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

الخلاصة

  • عند التشغيل من الجذر، لا يحمّل Claude Code القواعد الموضوعة في مجلد كل موقع حتى يلمس ملفات في ذلك المجلد، ولا يحمّلها Codex تلقائيًا (قد يفتحها الذكاء الاصطناعي من تلقاء نفسه، لكن لا ضمان لذلك)
  • في الأداتين كلتيهما، التشغيل من مجلد الموقع يحمّل قواعد ذلك الموقع منذ البداية (ومع worktree، شغّل من مجلد الموقع داخله)
  • تحقّق مما حُمِّل عند البدء عبر /context في Claude Code أو قائمة ملفات التعليمات النشطة في Codex، ومما حمّله Claude Code أثناء الجلسة عبر سطر Loaded أو خطاف InstructionsLoaded
  • حوّل ما يجب فرضه إلى فحص آلي يوقف الإصدار إذا تغيّر أي شيء خارج المجلدات المسنَدة، وأضف فوقه حواجز الأداة لالتقاط المشكلات مبكرًا
  • وكذلك، ميّز المستودعات المتشابهة الأسماء بـ remote URL والإيداع الجذري، وانظر إلى أسماء متغيرات البيئة فقط

الأسئلة الشائعة

س. هل يُحمَّل CLAUDE.md الموجود في مجلد فرعي؟

ج. نعم، لكن ليس عند التشغيل. يحمّل Claude Code ملف CLAUDE.md الخاص بمجلد ما عندما يقرأ Claude ملفًا داخل ذلك المجلد أو يكتبه أو يحرّره. وحينها لا يظهر في Memory files ضمن /context؛ بل يظهر في الطرفية سطر Loaded مع مساره. وإذا شغّلت من ذلك المجلد، فإنه يُحمَّل عند البدء ويظهر في Memory files.

س. كيف أجعل Codex يقرأ AGENTS.md الموجود في مجلد فرعي؟

ج. شغّل Codex من ذلك المجلد (في واجهة سطر الأوامر، codex --cd <folder>). عند البدء، يقرأ Codex ملفات AGENTS.md من جذر المشروع نزولًا إلى مجلد التشغيل، ولا يبحث أسفله. وإذا شغّلت من الجذر، فإن AGENTS.md الخاص بمجلد أدنى يُقرأ إذا فتحه الذكاء الاصطناعي من تلقاء نفسه، لكن الوثائق الرسمية لا تقدّم إعدادًا يحمّله بشكل موثوق.

س. في المستودع الأحادي (monorepo)، هل أجمع وثائق القواعد في ملف واحد في الجذر؟

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

س. كيف أجعل Claude Code يقرأ CLAUDE.md وAGENTS.md معًا؟

ج. افتراضيًا، إذا وُجد CLAUDE.md أو .claude/CLAUDE.md أو CLAUDE.local.md في مجلد التشغيل أو فوقه، فلا يُقرأ AGENTS.md. فإما أن تستورده بسطر واحد @AGENTS.md في CLAUDE.md، وإما أن تضبط Project instructions في /config على claude-md-and-agents-md.

المصادر