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

عقد بنية ذاكرة التخزين المؤقت لمسح نظام الملفات

يحدد هذا المستند العقد الحالي لذاكرة التخزين المؤقت المشتركة لمسح نظام الملفات المُنفَّذة بلغة Rust (crates/pi-natives/src/fs_cache.rs) والمُستهلَكة من قِبل واجهات برمجة التطبيقات الأصلية للاكتشاف/البحث المكشوفة لـ packages/coding-agent.

ما هي هذه الذاكرة المؤقتة

Section titled “ما هي هذه الذاكرة المؤقتة”

تخزن الذاكرة المؤقتة قوائم إدخالات المسح الكامل للمجلدات (GlobMatch[]) مفهرسة حسب نطاق المسح وسياسة الاجتياز، ثم تسمح للعمليات عالية المستوى (تصفية glob، والتسجيل الضبابي، واختيار ملفات grep) بالعمل على تلك الإدخالات المخزنة مؤقتاً.

الأهداف الرئيسية:

  • تجنب عمليات المشي المتكررة في نظام الملفات لاستدعاءات الاكتشاف/البحث المتكررة
  • الحفاظ على الاتساق بين glob وfuzzyFind وgrep عندما يتشاركون نفس سياسة المسح
  • السماح باسترداد صريح للبيانات المنتهية الصلاحية للنتائج الفارغة والإبطال الصريح بعد تعديلات الملفات

الملكية والواجهة العامة

Section titled “الملكية والواجهة العامة”
  • تنفيذ الذاكرة المؤقتة والسياسة: crates/pi-natives/src/fs_cache.rs
  • المستهلكون الأصليون:
    • crates/pi-natives/src/glob.rs
    • crates/pi-natives/src/fd.rs (fuzzyFind)
    • crates/pi-natives/src/grep.rs
  • ربط/تصدير JS:
    • packages/natives/src/glob/index.ts (invalidateFsScanCache)
    • packages/natives/src/glob/types.ts
    • packages/natives/src/grep/types.ts
  • مساعدات إبطال التعديلات في coding-agent:
    • packages/coding-agent/src/tools/fs-cache-invalidation.ts

تقسيم مفتاح الذاكرة المؤقتة (عقد ثابت)

Section titled “تقسيم مفتاح الذاكرة المؤقتة (عقد ثابت)”

كل إدخال مفهرس بواسطة:

  • مسار المجلد root المُعياري
  • القيمة المنطقية include_hidden
  • القيمة المنطقية use_gitignore

الآثار المترتبة:

  • عمليات المسح المخفية وغير المخفية لا تتشارك الإدخالات.
  • عمليات المسح التي تحترم gitignore وتلك التي تعطل التجاهل لا تتشارك الإدخالات.
  • يجب على المستهلكين تمرير دلالات مستقرة لسلوك hidden/gitignore؛ تغيير أي من العلمين ينشئ قسم ذاكرة مؤقتة مختلف.

تضمين node_modules ليس في مفتاح الذاكرة المؤقتة. تخزن الذاكرة المؤقتة الإدخالات مع تضمين node_modules؛ ويتم تطبيق التصفية لكل مستهلك بعد الاسترجاع.

يستخدم ملء الذاكرة المؤقتة ماشياً حتمياً (ignore::WalkBuilder) مُكوَّناً بواسطة include_hidden وuse_gitignore:

  • follow_links(false)
  • مرتب حسب مسار الملف
  • يتم تخطي .git دائماً
  • يتم جمع node_modules دائماً في وقت مسح الذاكرة المؤقتة (ويتم تصفيته اختيارياً لاحقاً)
  • يتم التقاط نوع ملف الإدخال + mtime عبر symlink_metadata

يتم حل جذور البحث بواسطة resolve_search_path:

  • يتم حل المسارات النسبية مقابل مجلد العمل الحالي (cwd)
  • يجب أن يكون الهدف مجلداً موجوداً
  • يتم تعيير الجذر عند الإمكان

سياسة الحداثة والإخلاء

Section titled “سياسة الحداثة والإخلاء”

السياسة العامة (قابلة للتجاوز عبر متغيرات البيئة):

  • FS_SCAN_CACHE_TTL_MS (الافتراضي 1000)
  • FS_SCAN_EMPTY_RECHECK_MS (الافتراضي 200)
  • FS_SCAN_CACHE_MAX_ENTRIES (الافتراضي 16)

السلوك:

  • get_or_scan(...)
    • إذا كان TTL يساوي 0: تجاوز الذاكرة المؤقتة بالكامل، مسح جديد دائماً (cache_age_ms = 0)
    • عند إصابة الذاكرة المؤقتة ضمن TTL: إرجاع الإدخالات المخزنة + cache_age_ms غير صفري
    • عند إصابة منتهية الصلاحية: إخلاء المفتاح، إعادة المسح، تخزين إدخال جديد
  • يتم فرض الحد الأقصى للإدخالات عبر إخلاء الأقدم أولاً حسب created_at

إعادة الفحص السريع للنتائج الفارغة (منفصل عن الإصابات العادية)

Section titled “إعادة الفحص السريع للنتائج الفارغة (منفصل عن الإصابات العادية)”

إصابة الذاكرة المؤقتة العادية:

  • إصابة الذاكرة المؤقتة ضمن TTL تُرجع الإدخالات المخزنة ولا تفعل شيئاً آخر.

إعادة الفحص السريع للنتائج الفارغة:

  • هذه سياسة من جانب المُستدعي باستخدام ScanResult.cache_age_ms
  • إذا كانت النتيجة المصفاة/المستعلمة فارغة وعمر المسح المخزن مؤقتاً يبلغ على الأقل empty_recheck_ms()، يقوم المُستدعي بعملية force_rescan(...) واحدة ويعيد المحاولة
  • يهدف إلى تقليل النتائج السلبية المنتهية الصلاحية عند إضافة ملفات مؤخراً لكن الذاكرة المؤقتة لا تزال ضمن TTL

المستهلكون الحاليون:

  • glob: يعيد الفحص عندما تكون المطابقات المصفاة فارغة ويتجاوز عمر المسح الحد الأدنى
  • fuzzyFind (fd.rs): يعيد الفحص فقط عندما يكون الاستعلام غير فارغ والمطابقات المُسجَّلة فارغة
  • grep: يعيد الفحص عندما تكون قائمة الملفات المرشحة المختارة فارغة

الإعدادات الافتراضية للمستهلكين واستخدام الذاكرة المؤقتة

Section titled “الإعدادات الافتراضية للمستهلكين واستخدام الذاكرة المؤقتة”

الذاكرة المؤقتة اختيارية في جميع واجهات برمجة التطبيقات المكشوفة (cache?: boolean، الافتراضي false).

الإعدادات الافتراضية الحالية في واجهات برمجة التطبيقات الأصلية:

  • glob: hidden=false، gitignore=true، cache=false
  • fuzzyFind: hidden=false، gitignore=true، cache=false
  • grep: hidden=true، cache=false، ومسح الذاكرة المؤقتة يستخدم دائماً use_gitignore=true

مُستدعو coding-agent حالياً:

  • اكتشاف مرشحي الإشارات عالي الحجم يُفعّل الذاكرة المؤقتة:
    • packages/coding-agent/src/utils/file-mentions.ts
    • الملف الشخصي: hidden=true، gitignore=true، includeNodeModules=true، cache=true
  • تكامل grep على مستوى الأداة يُعطّل حالياً ذاكرة المسح المؤقتة (cache: false):
    • packages/coding-agent/src/tools/grep.ts

نقطة دخول الإبطال الأصلية:

  • invalidateFsScanCache(path?: string)
    • مع path: إزالة إدخالات الذاكرة المؤقتة التي يكون جذرها بادئة للمسار المستهدف
    • بدون path: مسح جميع إدخالات ذاكرة المسح المؤقتة

تفاصيل معالجة المسار:

  • يتم حل مسارات الإبطال النسبية مقابل cwd
  • يحاول الإبطال التعيير
  • إذا كان الهدف غير موجود (مثل الحذف)، يتم تعيير المجلد الأب كبديل وإعادة إرفاق اسم الملف عند الإمكان
  • هذا يحافظ على سلوك الإبطال لعمليات الإنشاء/الحذف/إعادة التسمية حيث قد لا يكون أحد الطرفين موجوداً

مسؤوليات تدفق التعديلات في coding-agent

Section titled “مسؤوليات تدفق التعديلات في coding-agent”

يجب أن يُبطل كود coding-agent بعد تعديلات نظام الملفات الناجحة.

المساعدات المركزية:

  • invalidateFsScanAfterWrite(path)
  • invalidateFsScanAfterDelete(path)
  • invalidateFsScanAfterRename(oldPath, newPath) (يُبطل كلا الطرفين عندما تختلف المسارات)

مواقع استدعاء أدوات التعديل الحالية:

  • packages/coding-agent/src/tools/write.ts
  • packages/coding-agent/src/patch/index.ts (تدفقات hashline/patch/replace)

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

إضافة مستهلك جديد للذاكرة المؤقتة بأمان

Section titled “إضافة مستهلك جديد للذاكرة المؤقتة بأمان”

عند تقديم استخدام الذاكرة المؤقتة في مسار ماسح/بحث جديد:

  1. استخدم مدخلات سياسة مسح مستقرة

    • قرر دلالات hidden/gitignore أولاً
    • مررها بشكل متسق إلى get_or_scan/force_rescan بحيث تكون أقسام الذاكرة المؤقتة مقصودة
  2. تعامل مع بيانات الذاكرة المؤقتة على أنها مُصفاة مسبقاً فقط حسب سياسة الاجتياز

    • طبّق التصفية الخاصة بالأداة (أنماط glob، مرشحات النوع، قواعد node_modules) بعد الاسترجاع
    • لا تفترض أبداً أن الإدخالات المخزنة مؤقتاً تعكس بالفعل مرشحاتك عالية المستوى
  3. نفّذ إعادة الفحص السريع للنتائج الفارغة فقط لمخاطر النتائج السلبية المنتهية الصلاحية

    • استخدم scan.cache_age_ms >= empty_recheck_ms()
    • أعد المحاولة مرة واحدة مع force_rescan(..., store=true, ...)
    • أبقِ هذا المسار منفصلاً عن منطق إصابة الذاكرة المؤقتة العادية
  4. احترم وضع عدم التخزين المؤقت صراحةً

    • عندما يُعطّل المُستدعي الذاكرة المؤقتة، استدعِ force_rescan(..., store=false, ...)
    • لا تملأ الذاكرة المؤقتة المشتركة في مسار طلب بدون تخزين مؤقت
  5. اربط إبطال التعديلات لأي مسار كتابة جديد

    • بعد نجاح الكتابة/التحرير/الحذف/إعادة التسمية، استدعِ مساعد إبطال coding-agent
    • لإعادة التسمية/النقل، أبطل كلاً من المسار القديم والجديد
  6. لا تُضف مقابض TTL لكل استدعاء

    • العقد الحالي هو سياسة عامة فقط (مُكوَّنة عبر البيئة)، بدون تجاوز TTL لكل طلب
  • نطاق الذاكرة المؤقتة محلي للعملية في الذاكرة (DashMap)، ولا يُحفظ عبر إعادة تشغيل العملية.
  • تخزن الذاكرة المؤقتة إدخالات المسح، وليس نتائج الأداة النهائية.
  • يتشارك glob/fuzzyFind/grep إدخالات المسح فقط عندما تتطابق أبعاد المفتاح (root، hidden، gitignore).
  • يُستبعد .git دائماً في وقت جمع المسح بغض النظر عن خيارات المُستدعي.