تخطَّ إلى المحتوى

الضوابط — سياسة واحدة لكل إجراء يعدّل الحالة

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

محرك واحد، وكل نقاط الاختناق

قسم بعنوان “محرك واحد، وكل نقاط الاختناق”

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

المحرك لا يغني عن بيئة نظام التشغيل المعزولة، والبيئة المعزولة لا تغني عن المحرك. السؤالان مختلفان: الضوابط تقرر هل يُسمح أصلًا لنداء يراه Control Center بأن يقع، والبيئة المعزولة تحصر المدى الذي تبلغه العملية الناتجة. يطبّق cc_server البيئة المعزولة حيث يوفّر المضيف خلفية، لكن Windows لا يوفّر خلفية، وLinux يحتاج bwrap وsocat. على هذين المضيفين تكون الضوابط طبقة الفرض الخارجية، لا سياسة فوق أرضية، وهذا يهم حين تقرر كم تتساهل.

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

الصنف الافتراضي المضمّن ما يغطيه
fileDelete السؤال أولًا حذف ملف (كتابة على هيئة حذف، rm)
gitPush السؤال أولًا الدفع إلى مستودع بعيد
prCreate السؤال أولًا إنشاء طلب سحب
prPublish السؤال أولًا نشر مراجعة، أو دمج طلب سحب
vendorSyncWrite السؤال أولًا الكتابة إلى متتبع خارجي (Linear وJira ومزامنة GitHub)
fileWriteOutsideWorktree السماح كتابة ملف خارج شجرة العمل المعزولة
gitCommit السماح إنشاء إيداع Git
networkEgress السماح الخروج إلى الشبكة (جلب من الويب، أو HTTP اعتباطي)
secretAccess السماح قراءة سر أو بيانات اعتماد
packageInstall السماح تثبيت حزمة (npm أو pip أو pub أو brew)
processSpawn السماح تشغيل عملية (الصدفة، وكل ما يستدعي صدفة)
workspaceMutation السماح تغيير بنية مساحة العمل (المستودعات والمساحات والوكلاء)
enclosureControl السماح قيادة حاوية معزولة (منصة): إقلاع آلة افتراضية، وإرسال إدخال إليها

أُختيرت الأصناف الثمانية التي تسمح افتراضيًا على افتراض أن أرضية البيئة المعزولة تعمل تحتها. تعمل تلك الأرضية حيث يوفّر المضيف خلفية، ولا تعمل على Windows ولا على جهاز Linux ينقصه bwrap وsocat. فإن أردت بوابة على fileWriteOutsideWorktree أو packageInstall هناك، فتلك قاعدة تكتبها أنت.

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

تعيّن القاعدة (scope, ActionClass | command prefix) → allow | prompt | deny. تتداخل النطاقات من الأكثر تحديدًا إلى الأعم:

المساحة > الوكيل > مساحة العمل > الإعداد المسبق للوضع > الافتراضي المضمّن

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

قراران ضمانهما قاطع:

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

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

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

قد يسمّي القيد:

  • المسارات — أنماط glob (repos/** و!**/.env)
  • المراجع — مراجع Git، مع نفي ! (['**', '!main'] تُقرأ «أي فرع إلا main»)
  • المضيفات — وجهات على الشبكة، مع رموز بدل للنطاق الفرعي (*.internal)
  • الأوامر — بادئات أوامر، والمطابقة عند حد كلمة
  • الأسقف — maxCount / maxCents، فيصير «احذف حتى 50 ملفًا» قابلًا للصياغة

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

ثلاث قواعد لها وزن:

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

ما تلتقطه قاعدة الأمر وما يفوتها

قسم بعنوان “ما تلتقطه قاعدة الأمر وما يفوتها”

قاعدة commandPrefix تطابق عند حد كلمة: git push تغطي git push origin main ولا تغطي git pushx. وهي لا تُطبّع الصدفة. git push (مسافتان) وcd sub && git push وGIT_DIR=… git push سلاسل مختلفة، ولا واحدة تطابق قاعدة عن git push.

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

الإجابة عن السؤال بـ«تذكّر هذا» تكتب قاعدة سياسة حقيقية: بنطاق (المساحة / الوكيل / مساحة العمل)، ومقيّدة بالوسائط (الموافقة على دفع إلى feature/login تمنح feature/** لا main)، وتلغي نفسها عند TTL صريح. الموافقة لبضع ساعات ليست إعادة كتابة صامتة لسياستك الدائمة. القواعد التي تُكتب تستقر في المخزن نفسه، بمصدر remembered وبأجل، فيمكن إلغاؤها قبل الأجل.

كل قاعدة تحمل واحدًا من هذه:

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

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

ضبط CC_SERVER_MANAGED_POLICY على ملف JSON يعلو القواعد المخزّنة كلها، وبهذا يثبّت المشغّل وضعية لا تقلبها واجهة إدارة. وهذا جواب «هل أستطيع أن أمنع المطوّرين من تعطيل ضوابط الأمان؟».

قرص الاستقلالية ملف تعريف لا نظام موازٍ

قسم بعنوان “قرص الاستقلالية ملف تعريف لا نظام موازٍ”

قرص الاستقلالية في كل مساحة ملف مسمّى فوق مخزن السياسة نفسه، لا آلية ثانية. إعداداته الثلاثة تُخزَّن وتُرسَل proposeOnly وactWithApproval وactFreely (نص الواجهة «اقتراح فقط» و«التصرف بموافقة» و«التصرف بحرية» للعرض فقط، وautonomy.setForSpace يرفض ما عدا هذه الثلاث). تركه بلا قيمة يسلك سلوك actWithApproval.

أثر كل منها في القرار الذي بُتّ فيه:

  • proposeOnly يرفض كل أداة محكومة رفضًا باتًا. يظل الوكيل يستدل ويرد، ولا يفعل، ورسالته تبيّن السبب.
  • actWithApproval (وغير المضبوط) هو بوابة الموافقة المغلقة عند الفشل الموصوفة أعلاه.
  • actFreely يوافق مسبقًا على كل ما لم يُبتّ إلى deny صارم.

هذا الأخير يُقال بلا تلطيف. تحت actFreely قرار prompt لا يُصعَّد إلى معتمِد، بل يُسمح به ولا يُسأل أحد. لا يصمد أمام القرص إلا قاعدة deny صريحة. فـactFreely منح استقلالية عن عمد، لا إعداد راحة، وقاعدة الإغلاق عند الفشل تحمي الحالة التي يهم فيها ذلك (actWithApproval، وهو الافتراضي).

تفويض وكيل لوكيل محروس عند نقطة اختناق delegate_task بأربعة حراس: حد للعمق (افتراضه 3)، وكشف الحلقات، وسقف استقلالية (لا يعمل المفوَّض إليه باستقلالية أعلى من المفوِّض، ولا يُبيَّض الامتياز بتسليم العمل لوكيل أوسع حرية)، وغلاف ميزانية (التفويض يُخصم من بقية ميزانية المفوِّض، ولا يستطيع أن يصدر ميزانية جديدة). يُرفض كل منها رفضًا صريحًا، ويعود سبب الحارس إلى الوكيل بنصّه.

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

مدخلان يستحقان القراءة قبل أن تثق بتهيئة. يصرّح الإطار المضمّن أن أدوات الملفات داخل عمليته ليست في بيئة معزولة، فسطح الأدوات ومحرك السياسة هذا هما حد نظام الملفات الوحيد عنده. يصرّح Claude Code أن استدعاءات أدواته غير قابلة للاعتراض، لأنه يُطلَق مع --dangerously-skip-permissions. يرى Control Center استدعاءات mcp__* وأسطر أوامر Bash عبر خطاف، وأدواته للقراءة والكتابة والتحرير تعمل من غير رصد.

شاشة الإعدادات تعرض هذه المصفوفة، لأن الفشل الذي لا يُغتفر في نظام ضوابط هو ادّعاء تغطية غير موجودة.

الضوابط في الإعدادات ← مساحة العمل ← أذونات الوكلاء (/workspaces/<id>/settings/workspace/permissions). في الشاشة مصفوفة السياسة حيث تُكتب القواعد، وقوالب السياسات، ومسبار «ماذا لو؟»، وسجل تدقيق التفويض، ومصفوفة صدق المحوّل.

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