بنود العمل
بنود مفتوحة يحتاج الفريق للتصرف حيالها — قرار، تصميم يحتاج مراجعة، أو وثيقة يجب إنتاجها — أُثيرت عبر مراجعات هذه السياسة ولم تُحسَم بعد. هذا هو المكان الوحيد الذي تُتتبَّع فيه؛ تُشير السياسة والملحق A إلى هنا بدلاً من تكرار الشرح الكامل في أكثر من مكان. بمجرد حسم بند، انقل نتيجته إلى السياسة/الملحق A مباشرة وضعه كـمُغلَق هنا بدلاً من حذف السطر — تاريخ سبب اتخاذ القرار يستحق الحفظ.
الأنواع: قرار (يحتاج لجنة مراجعة المعمارية أو حسماً حوكمياً) · تصميم (يحتاج تصميماً تقنياً يُراجَع/يُعتمَد) · توثيق (يحتاج إنتاج وثيقة) · تنفيذ (يحتاج كتابة شيفرة، النمط محسوم بالفعل).
الحالة: مفتوح · مُقترَح (إجابة ملموسة صيغت، بانتظار الاعتماد) · مُغلَق.
ACT-001 — آلية وصول إداري للقراءة عبر المستأجرين
النوع: تصميم + قرار · الحالة: مُقترَح · المسؤول: _____________
أُثير بواسطة: الملحق A.11.2 (تقييم سوابق)، تأكَّد كعائق حقيقي عبر قصة الأعمال المتراكمة للسياحة TEN-DASH-01
المشكلة: تفتح withTenant(tenantId, work) بالضبط معاملة واحدة محدَّدة النطاق بـRLS لمستأجر واحد، بتصميم مقصود. لا توجد طريقة لطلب "كل مستأجر" منها، فأي أداة إدارة/تشغيل تحتاج عرضاً عبر المستأجرين — بدءاً بقصة لوحة الإداري الأعلى الملتزَم بها فعلياً في السياحة — لا يمكن بناؤها وفق النمط كما هو محدَّد حالياً.
الاقتراح: دالة منفصلة withPlatformAdminScope(actor, reason, work) (لا معامل على withTenant() أبداً، لتجنّب فخّ null السحرية)، بأربع خصائص: (1) يُتحقَّق من التفويض مقابل دور IAM الخاص بالفاعل قبل تشغيل أي استعلام؛ (2) قراءة فقط افتراضياً — تحتاج الكتابة عبر المستأجرين دالة خاصة بها أكثر تقييداً؛ (3) تُضاف عبارة تجاوز RLS فقط للجداول المحددة التي تحتاجها لوحة إدارية (بيانات المستأجر الوصفية، الخطة/الحالة)، لا أبداً بشكل شامل على بيانات الأعمال؛ (4) reason إلزامي ومُسجَّل عبر logAction() قبل تشغيل work، فيُتتبَّع كل استخدام بسجل تدقيق. الشيفرة الكاملة ومثال TEN-DASH-01 الملموس: الملحق A.11.2.
أسئلة فرعية مفتوحة لا يحسمها هذا الاقتراح بذاته:
- من يُمنَح دور IAM الخاص بـ
platform_admin، وهل منحه بذاته إجراء مُسجَّل وخاضع للاعتماد؟ - دور قائم يُراجَع دورياً، أم محدود بوقت/إعادة مصادقة لكل جلسة (رفع صلاحية آني)؟
ACT-002 — مصفوفة ملكية البيانات
النوع: توثيق · الحالة: مفتوح · المسؤول: _____________ أُثير بواسطة: المراجعة المعمارية الرسمية (سجل التغييرات v1.4)
المشكلة: تنصّ السياسة على المبدأ (كل محرك يملك جداوله الخاصة؛ لا وصول مباشر لجداول محرك أو قطاع آخر) لكن لا توجد وثيقة تُعدِّده جدولاً بجدول. يُجاب حالياً على سؤال "أي محرك يملك هذه البيانات" بالاتفاقية ومراجعة الشيفرة، لا بشيء يمكن لأي أحد التحقق منه — نفس فئة الخطر التي تحرص عليها قاعدة عدم الدورانية في القسم 4 وإلزام getDb()/withTenantScope() في القسم 5 بوسائل أخرى.
الاقتراح: إنتاج مصفوفة (محرك/قطاع × جدول × مالك × صلاحية قراءة/كتابة) كوثيقة معتمَدة. مُسنَدة للجنة مراجعة المعمارية وفق السياسة، القسم 9؛ لا مسودة موجودة بعد للإشارة إليها.
ACT-003 — ضمان ديمومة ناقل الأحداث
النوع: قرار · الحالة: مفتوح · المسؤول: _____________ أُثير بواسطة: المراجعة المعمارية الرسمية (سجل التغييرات v1.4)
المشكلة: تحلّ emitEvent()/onEvent() الخاصتان بـ@rarit-kernel/event-bus (المُستخدَمتان لكسر حلقتَي IAM↔Notifications وEntitlements↔Notifications في الملحق A.7) مشكلة اتجاه الاستيراد لكنهما لا تقولان شيئاً عن ضمانات التسليم. إذا كانت باعثاً داخل العملية، يُسقِط انهيار بين التزام حدث وتشغيل مشتركه ذلك الحدث بصمت — لتسليم OTP هذه مشكلة تجربة استخدام، ولأي شيء يمسّ Payment أو Audit مشكلة صحة.
الاقتراح: التأكد من مالك @rarit-kernel/event-bus إن كانت داخل العملية أم دائمة. إذا كانت داخل العملية، تبنّي صندوق صادر معاملاتي (تُكتب حمولة الحدث إلى جدول outbox ضمن نفس معاملة التغيير المُطلِق؛ ويُرسِل عامل منفصل من هناك) لأحداث OTP/الدفع/الحساسة للتدقيق تحديداً — قد لا تحتاج أحداث أقل حساسية نفس الضمان. الاستدلال الكامل: الملحق A.7.
ACT-004 — هل تُنسَخ وثائق ADR الخاصة بـrarit-saas-kernel إلى هذا الموقع؟
النوع: قرار · الحالة: مُغلَق · المسؤول: _____________ أُثير بواسطة: سؤال مباشر أثناء مراجعة الملحق A
المشكلة: استشهد السياسة، القسم 5 والقسم 6 كلاهما بـdb-access-pattern-decision.md عبر رابط GitHub لفرع main في مستودع rarit-saas-kernel. خطران حقيقيان: لم يُتحقَّق من الرابط عبر بناء هذا الموقع نفسه كما يُتحقَّق من كل رابط داخلي، ورابط فرع main يُشير بصمت لما يصبح عليه الملف لاحقاً. تبيّن أيضاً عملياً أن الرابط نقطة فشل واحدة — أصبح الملف المصدري غير متاح.
القرار: انعكاسه في هذه المجموعة كـقرار معماري: كيف يصل المستودع إلى قاعدة البيانات، الإصدار 1.0، بتاريخ يطابق اعتماد الأصل في 2026-08-26. يُلغي هذا توصية "لا تنسخ، ثبّت الرابط فقط" الأصلية أدناه، أُبقيت لسجلّ ما جرى النظر فيه. الانعكاس لقطة صريحة، لا مزامنة حية — إذا تغيّرت نسخة rarit-saas-kernel، تحتاج هذه الصفحة تحديثاً يدوياً وترقية إصدار، بنفس الانضباط الذي يُطبِّقه signed-records/ بالفعل على كل مستند معتمَد آخر. تُشير أقسام السياسة 5 و6 والملحق A.10 الآن للانعكاس المحلي بدلاً من الرابط الخارجي.
الاقتراح الأصلي (مُلغى، أُبقي للسياق): يُوصى بعدم نسخ ملفات ADR إلى هذا المستودع، وبدلاً من ذلك تثبيت روابط GitHub القائمة على SHA أو وسم، بالاعتماد على إعادة صياغة الملحق A.10 المكتفية ذاتياً لحمل معظم القيمة. أُلغي لأن فقدان مصدر خارجي أسوأ من انعكاس يحتاج مزامنة يدوية — خطر انجراف النسختين حقيقي لكنه أقل تكلفة من انقطاع الرابط لوثيقة بهذه الأهمية.
ACT-005 — تهيئة Claude Code Review على مستودعات النواة والقطاعات الرأسية
النوع: تنفيذ (Implementation) · الحالة: مُغلَق · المسؤول: _____________
النتيجة: حُلّ هذا البند عبر
سياسة مراجعة الكود وطلبات السحب
(الإصدار 1.0، 28 أغسطس 2026)، التي تُقنِّن سلوك المُشغِّل ("Once after PR
creation")، والفصل بين CLAUDE.md وREVIEW.md، ومفردات درجة الخطورة،
والإنفاذ الاستشاري فقط. راجع تلك السياسة للاطلاع على النسخة الحالية
والمعتمدة من هذا الإعداد بدلًا من نص الاقتراح الأصلي في هذا البند.
ACT-006 — منع تباعد (drift) ملف REVIEW.md عبر المستودعات
النوع: تنفيذ (Implementation) · الحالة: مفتوح · المسؤول: _____________ أُثير عبر: سياسة مراجعة الكود وطلبات السحب، القسم 4
المشكلة: يحمل ملف REVIEW.md في كل مستودع من مستودعات النواة
والقطاعات الرأسية جزءًا مشتركًا من قواعد العمارة، يُنشر حاليًا بالنسخ
واللصق اليدوي. أي تغيير في القواعد المشتركة (مثل تصعيد جديد في Annex A)
يتطلب إعادة نسخه يدويًا إلى كل REVIEW.md في كل مستودع؛ ولا توجد آلية
تكتشف أو تُنبِّه إلى التباعد إذا فات أحد المستودعات.
الاقتراح (لم يُصمَّم بعد): من الخيارات المرشَّحة: نص برمجي (script)
صغير يُجمِّع ملف REVIEW.md الخاص بكل مستودع من ملف أساس مشترك بالإضافة
إلى جزء خاص بالمستودع (يُحفظ ضمن مستودع النواة، ويُنشر كجزء من CI أو
كخطوة pre-commit)، أو مراجعة يدوية دورية كإجراء مؤقت. لم يُتخذ قرار بعد.
ACT-007 — نشر قالب PR واتفاقية تسمية الفروع على مستوى المنصة بأكملها
النوع: تنفيذ (Implementation) · الحالة: مفتوح · المسؤول: _____________ أُثير عبر: سياسة مراجعة الكود وطلبات السحب، القسمان 2 و5
المشكلة: .github/PULL_REQUEST_TEMPLATE.md واتفاقية تسمية الفروع
بصيغة <PROJECT>-<number> محدَّدتان لكن لم يُتأكَّد بعد من وجودهما في كل
المستودعات ضمن النطاق — بما فيها التطبيقات المحمولة المستقلة (Dw2ni
وAdl وHajez وLasscar وRizq وBindoo وCashBlash وAxil)، والتي لا توجد لها
أي خطة نشر قائمة أصلًا لأنها تقع خارج نطاق سياسة العمارة نفسها.
الاقتراح: تأكيد/إضافة القالب واتفاقية تسمية الفروع إلى كل مستودع؛
بالنسبة للتطبيقات المحمولة، يعتمد هذا أيضًا على إكمال ملفات REVIEW.md
الخاصة بها (انظر النقاش السابق — لا تزال تحتاج تأكيد اختيار إدارة الحالة،
وطبقة الشبكة، وخيار التخزين المحلي لكل تطبيق قبل أن يتجاوز REVIEW.md
الخاص به مرحلة المسودة).
ACT-008 — شكل جهاز لوحة حالة الطابور: شاشة هاتف أم كشك/شاشة تلفزيون في غرفة الانتظار
النوع: تصميم · الحالة: مفتوح · المسؤول: _____________
أُثير بواسطة: تخطيط فريق موبايل Clinivo للسبرنتين 1–2؛ تأكَّد عبر Jira أن CLIN-305 ("تحديث حالة طابور المريض (موبايل)") شاشة إجراء موجهة للموظفين، لا شاشة عرض عامة، ما يترك APQ-05/CLIN-185 ("لوحة حالة الطابور") دون أي تذكرة خاصة بالموبايل أو الكشك.
المشكلة: "لوحة حالة الطابور (انتظار/قيد التنفيذ/منتهي)" تُقرأ وكأنها شاشة تلفزيون/كشك في غرفة الانتظار، لكن تنفيذها الوحيد (CLIN-185، مُنجَز) وقصص طابور الموبايل الأحدث (CLIN-303، CLIN-305) كلاهما شاشات موجهة للموظفين عبر الهاتف/الويب. لا يعالج شيء حالياً شاشة عرض عامة حقيقية في غرفة الانتظار، وبناؤها كـ"مجرد شاشة موبايل أخرى" يحمل خطر اختيار شكل الجهاز الخاطئ — فهاتف يحمله موظف ليس شاشة تلفزيون في غرفة انتظار.
الاقتراح (لم يُصمَّم بعد): فحص سريع (spike) للتأكد مع أصحاب المصلحة في عمليات العيادة من كون شاشة عرض فعلية في غرفة الانتظار مطلوبة فعلاً للـ MVP، وإن كانت كذلك، فهل هي (أ) هدف بناء "وضع عرض" (display mode) عام غير مُصادَق عليه ضمن rarit-clinivo-mobile، أم (ب) صفحة ويب خفيفة منفصلة يُقصَد فتحها على متصفح تلفزيون، أم (ج) خارج نطاق الـ MVP، مع اعتبار شاشتَي الطابور الحاليتين الموجهتين للموظفين (CLIN-303، CLIN-305) كافيتين وحدهما.
ACT-009 — توسيع Consumer Identity لوصول المريض/ولي الأمر عبر المستأجرين
النوع: تصميم + قرار · الحالة: مفتوح · المسؤول: _____________
أُثير بواسطة: نقاش وصول المريض لسجله الطبي الإلكتروني (EMR) في Clinivo؛ مبني على سابقتين قائمتين — PHY-INT-01 (Clinivo، هوية الطبيب عبر المستأجرين عبر IAM-16–IAM-19 في محرك المصادقة) وPORTAL-04 (EduSuite، مبدّل مساحة عمل ولي الأمر متعدد المستأجرين، المرحلة 2).
المشكلة: آلية الهوية عبر المستأجرين في محرك المصادقة (IAM-16–IAM-19) مثبتة لهويات الموظفين (طبيب يعمل في أكثر من مستأجر)، ومُفترَضة بالفعل بشكل مستقل لهوية مستهلك (ولي أمر مرتبط بأكثر من مستأجر EduSuite في PORTAL-04) — لكن قسم "المستخدمون المستهدفون" في SRS الخاص بمحرك IAM لا يسمّي أبداً فئة شخصية مستهلك/غير موظف. نتيجة لذلك، ميزات Clinivo الموجهة للمرضى (مريض يشاهد سجله الطبي الخاص، أو يمنح/يسحب موافقة المشاركة عبر المستأجرين وفق CON-01–04) ليس لها نموذج هوية تُبنى عليه.
الاقتراح: تسجيل فئة شخصية عامة باسم "Consumer Identity" في محرك المصادقة، بتوسيع IAM-16–IAM-19 بدلاً من بناء آلية خاصة بكل قطاع رأسي؛ يُعرِّف كل قطاع شخصية المستهلك الخاصة به (Patient لـ Clinivo، Parent لـ EduSuite) ضمن role_manifest الخاص به. الاستدلال الكامل وكلا السابقتين: راجع Clinivo_Cross_Tenant_Consumer_Identity_Note.md.
أسئلة فرعية مفتوحة لا يحسمها هذا الاقتراح بذاته:
- هل يجب أن تكون هوية المستهلك الخاصة بالمريض قابلة للقراءة افتراضياً عند نشوء علاقة مستأجر جديدة، أم اختيارية/مشروطة بموافقة منذ البداية — يرتبط هذا مباشرة بـ
CON-01–04؟ - هل لدى SAFAR نفس الحاجة الكامنة (مسافر لديه حجوزات عبر أكثر من وكالة)؟ غير مؤكَّد، مُشار إليه فقط.
- هل يُفترَض بالفعل أن التقاط الموافقة في
CON-01ذاتي الخدمة (المريض مسجَّل الدخول) أم يُسجِّله الموظف نيابةً عن المريض؟ يؤثر هذا على ما إذا كان يُعطِّل أيضاً عمل العقد الخلفي لـCON-01–04، أم يُعطِّل فقط عمل عرض السجل الطبي الجديد.
ACT-010 — نص المرساة (anchor) القديم لـwithTenantScope() في 4 روابط إلى الملحق A.10
النوع: توثيق · الحالة: مفتوح · المسؤول: _____________
أُثير بواسطة: تحذير بناء npx docusaurus build (onBrokenAnchors) ظهر أثناء إصلاح عطل بناء MDX الخاص بـACT-008/009 — غير مرتبط بذلك العطل، تُرك بلا إصلاح هناك عمداً.
المشكلة: تشير 4 روابط عبر الموقع إلى annex-a#a10-tenant-scoped-database-access--the-getdb--withtenantscope-pattern. أُعيدت تسمية ذلك القسم في وقت ما ليعكس الـ API الحقيقي (withTenant() / withPlatformScope()، لا getDb()/withTenantScope())، لكن نص المرساة المُضمَّن في تسميات الرابط الظاهرة بالصفحات المرتبطة لم يُحدَّث ليطابق ذلك — فالصفحات حالياً تقول شيئاً وتشير إلى عنوان يصف الآن شيئاً آخر. ليست مشكلة بناء معطَّل (تحذير onBrokenAnchors في Docusaurus افتراضي فقط)، لكنها تناقض محتوى حقيقي لأي شخص يقرأ تلك الصفحات.
الاقتراح (لم يُحسَم بعد): يحتاج هذا قراراً في المحتوى لا تخميناً — تحديداً، هل يجب أن يقول نص الرابط الباقي withTenant()/withPlatformScope() (مطابقاً للهدف المُعاد تسميته) أم شيئاً آخر، وهل يستحق الأمر أن يُنفَّذ إلى جانب أو بشكل مستقل عن تنظيف تسمية getDb()/withTenantScope() الأوسع المُتتبَّع بالفعل عبر إصلاح ADR الخاص بـdb-access-pattern-decision.md.
ACT-011 — نمذجة الفروع (مستأجرون متعددو المواقع)
النوع: تصميم + قرار · الحالة: مُقترَح · المسؤول: _____________
أُثير بواسطة: تدقيق backlog الخاص بالنواة (kernel)؛ قصة EduSuite Super Admin Console رقم ESA-03 (شاشة إدارة الفروع — واجهة UI فقط ببيانات تجريبية)
المشكلة: قصة ESA-03 تُسلّم شاشة Branch Management بدون أي نموذج بيانات فعلي خلفها — نص القصة نفسه يذكر صراحةً أنه لا يوجد أي engine يمثّل "الفرع" ككيان منفصل عن الـ tenant حالياً. هذه ليست مشكلة خاصة بـ EduSuite فقط: نفس النمط يتكرر في أي مكان تُدير فيه المنظمة أكثر من موقع فعلي تحت اشتراك واحد — مدرسة بعدة حرم جامعي (EduSuite)، مجموعة مستشفيات بعدة منشآت (Clinivo)، وكالة سفر بعدة فروع (SAFAR). إذا تُرك هذا بدون حل، فمن المرجّح أن يبتكر كل قطاع رأسي تمثيله الخاص لمفهوم "الفرع" بشكل عشوائي — وهو بالضبط نوع التشتت الصامت بين القطاعات الرأسية الذي تهدف قواعد الملكية في Annex A لمنعه — ويوازي هذا فجوة الهوية عبر القطاعات الرأسية الموثّقة مسبقاً في ACT-009 الخاص بـ Consumer Identity.
الاقتراح — أربعة بدائل تنفيذ محتملة، لم يُختر أي منها بعد:
-
الفرع كـ tenant تابع (تسلسل هرمي parent-tenant). إضافة حقل اختياري
parent_tenant_idإلى جدولtenantsالحالي. كل فرع يصبح tenant كاملاً، بحيث لا تحتاجwithTenant(tenantId, fn)وآلية RLS لأي آلية جديدة — يُعامَل الفرع بنفس طريقة أي tenant اليوم تماماً. يحتاج الأمر لقاعدة تحدد في Entitlements/Billing ما إذا كان الفرع يرث خطة (plan) المستأجر الأصلي أم يحتاج اشتراكاً خاصاً به (سؤال فرعي مفتوح). المخاطرة: مفهوم "tenant" لن يعني بعد ذلك "عميل واحد يدفع"، وهو افتراض قد تعتمد عليه ضمنياً عدة آليات قائمة (مثل السعر المُقفل لكل tenant فيENT-08بـ Entitlements، وجدولة إعادة المحاولة dunning في Payment). -
الفرع ككيان جديد مشترك ضمن النواة في
platform-core(مثال: جدولbranchesبمفتاح خارجيtenant_id، مملوك لـ platform-core، ومكشوف عبر API بسيط لعمليات CRUD). يحافظ هذا على معنى "tenant" كما هو؛ الفرع يصبح مورداً فرعياً (sub-resource) تابعاً للـ tenant وليس نظيراً له. كل قطاع رأسي يحصل على مفهوم الفروع جاهزاً دون إعادة ابتكاره. يبقى نطاق RLS محدداً بـtenant_idكما هو الحال اليوم، مع فلتر إضافي بـbranch_idيُطبَّق على مستوى التطبيق عند الحاجة. هذا هو الخيار الأكثر قابلية لإعادة الاستخدام والأكثر اتساقاً مع الاتجاه الذي تسلكه Consumer Identity (ACT-009) — أي بنية أساسية مشتركة عابرة للقطاعات الرأسية بدلاً من حل مخصص لكل قطاع على حدة. -
الفرع كوحدة (module) خاصة بـ EduSuite فقط. أسرع طريق لفك قفل
ESA-03وحدها، لكنه يعيد إنتاج مفهوم الفرع بشكل مستقل لاحقاً في Clinivo وSAFAR، دون أي ضمان أن التنفيذات الثلاثة ستتفق في الشكل — وهي بالضبط النتيجة التي يحاول هذا الاقتراح تجنّبها. -
الفرع كمورد فرعي (sub-resource) داخل IAM (تابع لـ tenant ضمن محرك IAM، إلى جانب المستخدمين والأدوار). خيار مناسب طبيعياً إذا كانت الحاجة الأساسية للفروع هي نطاق صلاحيات الموظفين الخاص بها (مثال: مدير فرع لا يجب أن يرى فروعاً أخرى)، وأقل ملاءمة إذا كانت الفروع بالأساس تجميعة لأغراض التقارير والـ entitlements وليست حدود تحكم بالوصول (access-control boundary).
التوصية (غير مُلزِمة): الخيار الثاني (كيان مشترك في platform-core) يتوافق بشكل أفضل مع نمط "البنية الأساسية القابلة لإعادة الاستخدام عبر القطاعات الرأسية" المُعتمَد فعلياً لـ Consumer Identity، ويتجنّب إثقال معنى tenant كما قد يفعل الخيار الأول. القرار النهائي يعود لمجلس مراجعة المعمارية (ARB).
أسئلة فرعية مفتوحة لا يحسمها هذا الاقتراح بذاته:
- هل يحصل الفرع على entitlements/اشتراك خاص به، أم يرث دائماً خطة المستأجر الأصلي (parent tenant)؟
- هل التحكم بالوصول على مستوى الفرع (تقييد مستخدم بفرع واحد) داخل نطاق هذا القرار نفسه، أم متابعة منفصلة لاحقاً؟
- هل يُعطِّل هذا القرار خروج
ESA-03من حالة "واجهة فقط" (UI-shell)، أم يمكن شحن نسخة مبسّطة أولاً بينما يُحسَم النموذج الكامل لاحقاً؟
ACT-012 — IAM: تغيير الدور لا يسري حتى يُجدَّد رمز المستخدم
النوع: تنفيذ · الحالة: مفتوح · المسؤول: _____________
أُثير بواسطة: اختبار محرك البحث الشامل (packages/search/tests/e2e/e2e.test.ts)، قصة البحث SRC-22 (SASK-437)
المشكلة: يُرجع PATCH /api/v1/iam/users/:userId/role الرمز 200، لكن رمز الوصول نفسه يحتفظ بالدور القديم لأن الجلسة تقرأ الدور من الرمز، فتبقى الصلاحية المسحوبة قابلة للاستعمال حتى يُجدَّد الرمز. بالمقابل، سحب صلاحية من بيان الأدوار يسري في الطلب التالي مباشرة.
الاقتراح (غير محسوم): قراءة دور العضوية من قاعدة البيانات في كل طلب (أو تخزين مؤقت قصير يُبطَل عند تغيير الدور). مؤقتاً: توثيق عمر الرمز بوصفه تأخير السحب.
ملاحظة: الاختبار مثبَّت بـit.fails؛ عند الإصلاح يبدأ بالفشل ويجب قلب .fails.
ACT-013 — IAM: انقطاع قاعدة البيانات يُجاب عنه بـ401 ويكشف نص الخطأ الخام
النوع: تنفيذ · الحالة: مفتوح · المسؤول: _____________
أُثير بواسطة: القسم g من اختبار محرك البحث الشامل (docs/E2E_TRANSCRIPT.md)
المشكلة: عند توقف قاعدة البيانات يفشل بحث الجلسة في IAM أولاً ويُرجع 401 unauthorized، فيُقال للمستدعي أن يسجّل الدخول من جديد بدل أن الخدمة غير متاحة، ويحمل الجسم نص الخطأ الخام. النتيجة مغلقة (لا بيانات) لكن الحالة خاطئة والرسالة تسريب معلومات.
الاقتراح (غير محسوم): تحويل أي فشل غير AppError في بحث الجلسة إلى 503 access_control_unavailable دون نسخ الرسالة الأصلية إلى الجسم.
ملاحظة: الاختبار مثبَّت بـit.fails؛ يُقلب عند الإصلاح.
ACT-014 — IAM: POST /invitations/accept يُرجع 500
النوع: تنفيذ · الحالة: مفتوح · المسؤول: _____________
أُثير بواسطة: اختبار محرك البحث الشامل (مسبار في docs/E2E_TRANSCRIPT.md: الدعوة 201، القبول 500)
المشكلة: المسار عام، فيعمل في نطاق المنصة، ثم تكتب acceptInvitation سجل تدقيق لمستأجر الدعوة، وترفضه سياسة أمان الصفوف في audit_log. فيفشل قبول أي دعوة حقيقية بالرمز 500. الاختبارات الحالية ترسل رمزاً وهمياً يفشل قبل ذلك.
الاقتراح (غير محسوم): فتح نطاق مستأجر الدعوة لكتابات المستخدم والتدقيق بعد حلّ الرمز.
ACT-015 — platform-core: مجمّع الاتصالات بلا مستمع لحدث error
النوع: تنفيذ · الحالة: مفتوح · المسؤول: _____________ أُثير بواسطة: القسم g من اختبار محرك البحث الشامل
المشكلة: يُطلق pg.Pool الحدث error عند إسقاط اتصال خامل (إعادة تشغيل أو تبديل قاعدة البيانات، 57P01). لا يضيف initDatabase مستمعاً، فتعدّه Node استثناءً غير ممسوك وتُنهي أي عملية بلا معالج خاص.
الاقتراح (غير محسوم): إضافة pool.on("error", …) في initDatabase يسجّل الاسم والرمز ويترك المجمّع يستبدل الاتصال.
ACT-016 — platform-core: استثناء من hasPermission يفلت من التحكم بالمسارات كخطأ 500 عارٍ
النوع: تنفيذ · الحالة: مفتوح · المسؤول: _____________
أُثير بواسطة: البحث (القسم 2 من packages/search/docs/PROPOSALS.md فيه نص البلاغ الكامل)
المشكلة: ينتظر route-access.enforce.ts الدوال hasPermission وauthorizeSupportAccess وresolveSession دون معالجة. يتحول أي استثناء غير AppError من IAM إلى 500 عام بدل access_control_unavailable (503). يحوّله البحث في نطاقه الخاص، وكل محرك آخر سيحتاج الأمر نفسه.
الاقتراح (غير محسوم): لفّ كل استدعاء للمنفذ: تمرير AppError كما هو وتحويل غيره إلى رفض 503 مع تسجيل الاسم والرمز والمسار ومعرّف الطلب دون الرسالة.
ACT-017 — قرارات دمج محرك البحث (سجل للاعتماد)
النوع: قرار · الحالة: مُقترَح · المسؤول: _____________
أُثير بواسطة: طلبا الدمج #375 و#376 في rarit-saas-kernel
القرارات المتخذة (كلٌّ منها في الشيفرة واختباراتها):
- A. اكتشاف المستأجرين على مستوى المنصة عبر ست دوال SECURITY DEFINER، دور قارئ واحد غير قابل للدخول لكل جدول؛ رُفضت سياسة مبنية على
app.scope. - B. يزوّد
registerSearchContextResolverقيم نطاق الصفوف فقط؛ تُسأل الصلاحيات من IAM لكل مفتاح لكل طلب دون تخزين مؤقت؛ المستأجر والمستخدم من الجلسة. - C. يقبل
rowScopeالصيغة النصية من المواصفات والصيغة الكائنية؛ تُطبَّع النصية إلى{ name: s, field: s }. - D. يرث
SearchErrorمنAppErrorمع الاحتفاظ بـmessageKeyوparams. - E. الفهارس في
locales/(النطاقsearch) مع تصديرsearchCatalogDir. - F. إدراج
searchفيscripts/npm-packages.mjsوسلسلة البناء. - G. لا سبب ولا إجراءات حساسة لإدخالات تدقيق
search.*. - الاستحقاق
search.enabledمنطقي ولا يُحتسب، ويُفحص بـcheckEntitlement. - يسجّل تدقيق الاستعلامات الكيان وطول الاستعلام وعدد المصطلحات وأسماء حقول المرشّحات والمجموع، ولا يسجّل نص الاستعلام أبداً.
بانتظار تأكيد Ossama (غير محسوم): صياغة TSS-REG-019، وتأخّر الفهرسة لكل كيان في SRC-28.