# 37 — خطة `rider_new` و `driver_new` (Cubit + باك إند NestJS) تاريخ: 2026-08-04 · الحالة: مسودّة للموافقة ## 1. القرار المطلوب ومعناه المطلوب: بناء تطبيقين جديدين (`rider_new`, `driver_new`) بـ Bloc/Cubit، يعملان على **`backend-archive/` (NestJS)**، بأخذ الأصول والحزم من تطبيقات سيرو، مع الحفاظ على `build.gradle` و`Info.plist` كي لا تنكسر هوية النشر. هذا **عكس لقرارين مقفلين**: | القرار | التاريخ | المرجع | |---|---|---| | الباك إند المعتمد = سيرو PHP، وNestJS مؤرشف | 2026-07-27 | `backend-archive/README-ARCHIVE.md`, `docs/34` | | التطبيقات = نقل سيرو GetX بدل إعادة بناء Cubit | 2026-07-21 | `docs/30` | سبب الأرشفة وقتها كان مكتوباً صراحة: «NestJS مكتمل تقنياً لكن **بلا تطبيقات تعمل عليه**، وبناء تطبيقات له من الصفر أطول من نقل تطبيقات سيرو». الخطة الحالية تقول: نعم، سنبني التطبيقات. هذا يحلّ الاعتراض — بشرط قبول أن شغل نقل PHP يتوقّف مؤقتاً. ## 2. القاعدة الحاكمة — بناء من الصفر، وسيرو مرجع لا مصدر نسخ قرار المالك 2026-08-04، وهو يعلو على أي اقتراح في هذه الوثيقة: 1. **كل كود دارت يُكتب من الصفر.** لا يُورَّث سطر من `apps/rider-archive-cubit` (77 ملف) ولا `apps/driver-archive-cubit` (53 ملف) — السبب: أداؤها وشكلها مرفوضان، ووراثتها تعني وراثة المشكلة. تبقى للقراءة فقط ثم تُحذف بعد المرحلة 3. 2. **سيرو مرجع بصري وسلوكي، لا مصدر نسخ.** نقرأ منه: كيف بُنيت الشاشة، ما ترتيب الخطوات، أي حالات حافة عولجت ميدانياً. لا ننسخ منه ملفات دارت. 3. **سقف الجودة = شكل تريبز الحالي أو أفضل.** الهدف ليس المعادلة بل التفوّق: أداء (60fps، بلا rebuild زائد) وشكل (نظام تصميم واحد متّسق). ### أ. الشيء الوحيد الذي يُنقل حرفياً: الطبقة الأصلية لأن كل شيء آخر يُكتب من جديد، تبقى الطبقة الأصلية (`android/`, `ios/`, `shorebird.yaml`) هي الوحيدة التي تُنقل كما هي — لأنها هوية النشر ولا يجوز إعادة اختراعها. مصدرها محسوم في §2.ج. ### ب. `pubspec` — قائمة تُبنى بالطلب، لا تُنسخ `siro_rider/pubspec.yaml` فيه ~60 اعتماد، ومنها `get` و`get_storage` كحزمتين **محليّتين معدّلتين** (`./packages/get`) — أي أن نسخه يجرّ GetX كاملاً معه، وكذلك `intaleq_maps` و`secure_string_operations` المربوطة بعقد PHP. لذلك `pubspec` الجديد يُكتب من الصفر، وتُضاف الحزمة **فقط عند الحاجة إليها في مرحلتها** — لا قائمة مسبقة من 60 اعتماداً. سيرو يجيب على سؤال «أي حزمة نجحت ميدانياً لهذه المهمة»، لا «ما القائمة». - ✅ نأخذ منه: الأصول (`assets/`)، والحزم المحليّة غير المرتبطة بـ GetX (`secure_string_operations`, `trip_overlay_plugin`, `bubble-master`)، والحزم الأصلية (webrtc · geolocator · location · permission_handler · firebase · local_auth · image_cropper · live_activities · quick_actions …) لأن هذي هي القيمة الحقيقية المجرّبة ميدانياً. - ❌ لا ننسخ: `get`, `get_storage` — تُستبدل بـ `flutter_bloc` + `hydrated_bloc`/`shared_preferences`. - ⚠️ نراجع: `intaleq_maps` (قرار الخرائط المباشرة لـ map-saas بمفتاح `x-api-key` قائم — يبقى)، و`socket_io_client` (NestJS realtime قد يكون عقده مختلفاً — يُتحقّق من `backend-archive/src/realtime`). ### ج. خطر هوية النشر — يُحسم قبل أي نسخ | التطبيق | applicationId | shorebird app_id | |---|---|---| | `Siro/siro_driver` | `com.siro.siro_driver` | `f8e9c087-…0120c` | | `Tripz/apps/driver` | `com.sefer_driver` | `f8e9c087-…0120c` ← **نفس سيرو** | | `apps/driver-archive-cubit` | `com.sefer_driver` | `68cc9345-…5836d` | | `Siro/siro_rider` | `com.siro.rider` | `44245793-…305dd` | | `Tripz/apps/rider` | `com.mobileapp.store.ride` | `44245793-…305dd` ← **نفس سيرو** | | `apps/rider-archive-cubit` | `com.mobileapp.store.ride` | `496cb3ac-…a13ac` | قرار المالك: **لكل تطبيق تريبز هويته الخاصة**، منفصلة تماماً عن سيرو. لذلك `apps/driver` و`apps/rider` — اللذان يحملان app_id سيرو نفسه — **أثرُ نسخٍ خاطئ عند نقل GetX، وليسا مرجع هوية**. باتش شوربيرد منهما قد يُدفع لتطبيق سيرو المنشور. المرجع الصحيح هو `*-archive-cubit`: | | applicationId | shorebird app_id | |---|---|---| | `driver_new` | `com.sefer_driver` | `68cc9345-…5836d` | | `rider_new` | `com.mobileapp.store.ride` | `496cb3ac-…a13ac` | القاعدة: **لا يُنسخ أي `build.gradle` أو `shorebird.yaml` أو `Info.plist` من سيرو.** الطبقة الأصلية تُنقل من `*-archive-cubit` (هي وحدها كودٌ لم يكتبه النموذج السابق — ملفات إعداد مُولّدة من Flutter ومعدّلة يدوياً)، ويُقرأ من سيرو ما يلزم من أذونات وخدمات خلفية و entitlements فيُكتب يدوياً سطراً سطراً. ## 3. الخطة بالترتيب ### المرحلة 0 — تثبيت الأساس (قبل أي كود) — **نُفّذت 2026-08-04** 1. ✅ عقد NestJS موثّق في **[`docs/38-api-contract.md`](38-api-contract.md)** — 37 controller + `e2e-test.mjs`، ومعظم الأشكال **مُتحقَّقة حيّاً** من السيرفر المنشور. المصدر الوحيد للحقيقة لطبقة الشبكة. 2. ⚠️ **جزئي**: الباك إند حيّ ويعمل (`/health` → 200)، والدخول والمستخدم والمحفظة وأنواع الرحلات والتعرفة والمسار والتقييم المعلّق كلها تحقّقت حيّاً. لكن **`e2e-test.mjs` الكامل لم يُشغَّل**: يحتاج `OTP_DEV_MODE=true` وهو مطفأ على المنشور، والـSSH من الماك مرفوض (`Permission denied (publickey)`). المتبقّي غير المُتحقَّق مذكور في `docs/38` §13. **بلوكر يحتاج المالك.** 3. ✅ المرجع البصري في **[`docs/39-siro-screens.md`](39-siro-screens.md)** — 89 شاشة راكب + 113 شاشة سائق، مصنّفة داخل/خارج النطاق، مع ترتيب الخطوات وحالات الحافة وملاحظات الأداء. **بلا نسخ كود.** **ما انحسم في م0** (كان مخاطرة، صار حقيقة): - `x-app-role` **منفّذ فعلاً**: نفس الرقم أعطى user id مختلفاً للراكب والسائق. لا عمل باك إند إضافي في م3. - `access_token` = **15 دقيقة فقط** → تجديد استباقي إجباري في طبقة الشبكة. - `GET /tenant/config/:slug` يرجّع **خريطة ميزات كاملة** → هي ربط lite/pro/max بالخادم، لا أعلام مكتوبة في الكود (يعدّل فهم م6.25). - حسابا مراجعة المتاجر `0790000001`/`0790000002` بالرمز `1234` يعملان **بلا** `OTP_DEV_MODE` — مدخلنا للاختبار حتى يتوفّر وصول السيرفر. - ثغرات مؤكَّدة على المنشور: `/maps/geocode` معطوب (قائمة دول فارغة)، و`/tariff/quote` يفشل صامتاً بقيم `null` عند إرسال المعاملات الخطأ. ### المرحلة 1 — سقالة `rider_new` و `driver_new` من الصفر — **نُفّذت 2026-08-04، متوقّفة عند البوابة** الحالة: الخطوات 4–8 ✅ · الخطوة 9 (البوابة) ⚠️ جزئية — `flutter analyze` **نظيف في التطبيقين (0 مشاكل)**، لكن البناء الفعلي لم يُجرَّب: SSH للسيرفر مرفوض (بلوكر م0.2 نفسه). قرارات اتُّخذت أثناء التنفيذ ولم تكن في الخطة: - **الأصول مصدرها `*-archive-cubit` لا سيرو.** أصول الأرشيف **مجموعة أشمل** من أصول سيرو (كل صور سيرو + `login_hero`/`name_hero`/`onboarding_1..3`/ `splash_logo`) وخطوطها هي خطوط `docs/26` (IBM Plex Sans Arabic + Inter) بينما سيرو عليه `mohanad`/`josefin`. النسخ من سيرو كان سيكون **تراجعاً**. استُكمل السائق بعشرة ملفات ناقصة من سيرو (شعارات مزوّدي الدفع + صوتان). الأصول ليست كوداً كتبه النموذج السابق، فلا تخالف قاعدة «من الصفر». - **أُصلح تعارض هوية في iOS للسائق**: هدف `RunnerTests` في `driver_new` كان يحمل `com.mobileapp.store.ride` (bundle الراكب) — نُقل إلى `com.sefer.driver.RunnerTests`. - `flutter_secure_storage` + `shared_preferences` أُضيفتا لقائمة م1 الدنيا: التوكن لا يُخزَّن إلا في التخزين الآمن، وهذا أساس لا إضافة لاحقة. 4. `flutter create` نظيف لكل تطبيق، ثم **استبدال `android/` و`ios/` و `shorebird.yaml` بنسخة من `*-archive-cubit`** (الهوية فقط — §2.ج). 5. `lib/` فارغ يُبنى من جديد: `core/` (شبكة · تخزين · أخطاء · توجيه · DI) ثم `features/` — بلا استيراد أي ملف من الأرشيف. 6. نقل الأصول من سيرو: `assets/` + الخطوط + الأيقونات + ملفات الترجمة. 7. `pubspec` بالحد الأدنى: `flutter_bloc` · `dio` · `go_router` · `get_it` · `freezed`. الباقي يُضاف في مرحلته. 8. مواءمة الأذونات في `AndroidManifest` و`Info.plist` مع ما تحتاجه كل حزمة عند إضافتها — بلا لمس المعرّفات. 9. **بوابة**: يبني على السيرفر، يقلع على المحاكي بشاشة splash، و`flutter analyze` نظيف. ### المرحلة 2 — نظام التصميم — **نُفّذت 2026-08-04** مُنجز: `core/design/` (tokens · typography · TripzColors · buildTheme) · `core/ui/` عدّة من 11 مكوّناً · `core/l10n/` ARB عربي/إنجليزي مع `gen_l10n` · `SettingsCubit` (المظهر واللغة محفوظان محلياً). **تصحيح بنيوي**: بنية م1 كانت تخالف `docs/23 §1` المُلزِم (`lib/app/` و `core/config/` و`core/network/`). أُعيدت للشجرة المفروضة حرفياً: `app.dart` · `core/{config,build_config,di,router}.dart` · `core/{design,ui,l10n,api,storage,session}/` · `features//{cubit,data,view}/`. **قراران يستحقّان المراجعة:** 1. **لا `packages/tripz_ui`.** خطتي الأصلية (م2.11) اقترحت حزمة مشتركة، لكن `docs/23 §1` و`docs/26 §10` كلاهما مُلزِم ويفرض `lib/core/design` و `lib/core/ui` **داخل** كل تطبيق. اتُّبعت الوثيقتان، والملفّان متطابقان حرفياً بين التطبيقين (يُزامنان بـ`rsync` وأي تباعد يُكتشف بـ`diff -rq`). 2. **رمز الفشل لا نصّه.** `docs/23 §3` يقول إن الـCubit يبني الرسالة العربية، و`docs/26 §4` يوجب مرور كل نص بالترجمة — والـCubit لا يعرف `BuildContext`. الحل: الـCubit يُصدر `AuthFailure` كرمز، وتترجمه الواجهة في ملف واحد (`auth_failure_text.dart`). الشاشة تبقى بلا منطق، والنص بلغتين. ### (الأصل المعتمد للمرحلة 2) 10. تطبيق `docs/26-flutter-design-system.md` + `docs/23-flutter-conventions.md`: theme · ألوان · تايبوغرافي · مسافات · حركة · RTL/LTR بأربع لغات. 11. حزمة مشتركة `packages/tripz_ui` يستهلكها التطبيقان (تجنّب ازدواج الشاشات). 12. **ميزانية أداء مكتوبة** تُفحص في كل بوابة بعدها: بلا `setState` فوق شجرة كبيرة · `BlocSelector`/`buildWhen` افتراضياً · `const` على كل widget ساكن · قوائم كسولة · بلا عمل ثقيل في `build`. سبب الأداء السيئ سابقاً يُشخّص من `*-archive-cubit` **مرة واحدة** ويُكتب هنا كقائمة ممنوعات. ### المرحلة 3 — التسجيل والدخول — **نُفّذت 2026-08-04، البوابة معلّقة** مُنجز: طبقة الشبكة (Dio + تجديد استباقي بطلقة واحدة + `x-app-role` + `x-device-id` من `device_info_plus`) · `AuthRepository` · `SessionCubit` في `core/session/` خلف واجهة `SessionSource` (كي لا يستورد `core` من `features`) · `LoginCubit` بخطواته الخمس · `ProfileCubit` · ست شاشات: الشروط ← إذن الموقع ← الهاتف ← الرمز ← إكمال الملف ← هيكل الرئيسية. نُقلت من سيرو كسلوك لا ككود: بوابتان **قبل** أي حقل إدخال · إعادة فحص الإذن عند العودة من إعدادات النظام (`didChangeAppLifecycleState`) · تحقّق الهاتف الثلاثي (فارغ/صفر بادئ/قصير). أُضيف ما ينقص سيرو: **عدّاد إعادة الإرسال** — بدونه تُحظر ثلاث ضغطات المستخدمَ خمس دقائق (`docs/38 §2`). `flutter analyze` نظيف في التطبيقين · 48 ملف · 3,140 سطر لكل تطبيق · الفرق بينهما **أربعة ملفات فقط** (`build_config` · `config` · ملفّا ARB). البوابة (دخول حقيقي على الخادم) تنتظر التشغيل. ### (الأصل المعتمد للمرحلة 3) 13. طبقة الشبكة: Dio + interceptors + تجديد التوكن + `x-app-role`. 14. شاشات: onboarding → إدخال الهاتف → OTP → إكمال الملف → استعادة الجلسة. 15. مراعاة قرار **هوية الراكب/السائق المنفصلة** (قيد `tenant,phone_bidx,role` + ترويسة `x-app-role`) — يجب أن يكون موجوداً في NestJS، وإن لم يكن فهو عمل باك إند إضافي يُحسب في هذه المرحلة. 16. **بوابة**: دخول حقيقي من التطبيقين على NestJS المشتغل + لقطات شاشة تُقارن بمرجع سيرو، ولا تُقبل إن كانت أدنى منه. ### المرحلة 4 — الخريطة وطلب الرحلة — **نُفّذت 2026-08-04، البوابة معلّقة** **حُسم تعارض الخرائط المسجّل في `docs/38 §7`** لصالح القرار الأحدث (2026-07-20): كل الخرائط — البلاطات والبحث والعكسي والمسار — **مباشرة إلى `map-saas.intaleqapp.com`** بترويسة `x-api-key`، بلا مرور بباك إند تريبز. نقاط `/maps/*` في الباك إند **لا تُستعمل**، وإحداها (`geocode`) معطوبة على المنشور أصلاً. تُحقّق العقد حيّاً بـcurl قبل الكتابة. المنجز: - `core/api/antlaq_api.dart` — عميل منفصل **بلا** `AuthInterceptor`: توكن تريبز لا شأن لخادم الخرائط به، وإرساله إليه تسريبٌ بلا مقابل. - `core/realtime/realtime_service.dart` — Socket.IO واحد للتطبيق: `trip:update` · `driver:location` · `trip:offer` · `trip:offer_taken`. - `core/location/location_service.dart` — نقطة الموقع الوحيدة، بمرشّح 25م. - `features/trip/data/` — سبعة نماذج + `MapsRepository` + `TripRepository` (متطابق حرفياً بين التوأمين). - **الراكب**: `RideCubit` بستّ مراحل + `RidePage` وثمانية ويدجت (الخريطة · دبّوس المنتصف · المخطّط · الخط الزمني · اختيار النوع · التأكيد · البحث عن سائق · الرحلة الجارية). - **السائق**: `DutyCubit` بأربع مراحل + `DutyPage` وأربعة ويدجت (مفتاح الاتصال · العرض بمؤقّت · الرحلة الجارية · بطاقة الراكب). قرارات تصميمية مقصودة: 1. **`GeoPoint` مستقلّ عن حزمة الخريطة**؛ `intaleq_maps` مستوردة في **ملف واحد فقط** لكل تطبيق (`ride_map.dart`). تبديل محرّك الخريطة لا يلمس منطقاً. 2. **الخريطة خارج الـ`BlocBuilder`** الذي يبني الورقة، ولها `BlocSelector` بما تحتاجه وحدها — نبضة موقع كل 25م لا تعيد بناء الشاشة. هذا تطبيق مباشر لميزانية الأداء (م2.12). 3. **مهلة بحث محليّة 90 ثانية + استطلاع كل 5ث**: الخادم قد لا يُطلق `expired`/`no_drivers` أبداً (ثغرة R1)، وانتظار حدثٍ قد لا يصل = شاشة بحث أبدية. و`offeredDrivers == 0` يُعالَج فوراً بلا انتظار المهلة. 4. **الأجرة لا تُعرض إلا إن كانت صالحة**: `/tariff/quote` يرجّع 200 بقيم `null` صامتة عند المدخلات الخطأ (`docs/38 §7`). 5. موقع السائق يُرسَل بمسارين عمداً: REST يغذّي مطابقة Redis، والسوكت يغذّي خريطة الراكب — سقوط أحدهما لا يُعمي الآخر. `flutter analyze` نظيف · الراكب 70 ملف/5,294 سطر · السائق 67 ملف/4,771 سطر. ### (الأصل المعتمد للمرحلة 4) 17. الخريطة مباشرة لـ map-saas بمفتاح `x-api-key` مقيّد ببصمة التطبيق (قرار `maps-direct-decision`) — لا تمرير عبر الباك إند. 18. الراكب: اختيار المصدر/الوجهة · التسعير المسبق · تأكيد الطلب · انتظار سائق. 19. السائق: أونلاين/أوفلاين · استقبال العروض · قبول/رفض. 20. الواقع اللحظي (WebSocket) حسب `backend-archive/src/realtime`. 21. **بوابة**: رحلة كاملة من الطلب إلى الإنهاء بين جهازين + الخريطة تتحرّك بسلاسة أثناء التتبّع (لا اهتزاز، لا إعادة رسم للشاشة كاملة). ### المرحلة 5 — مسار الرحلة وما بعدها — **نُفّذت 2026-08-04** - **التقييم مفروض لا اختياري**: `SessionCubit` يفحص `GET /trips/rating/pending` بعد إثبات الجلسة، ووجودُ رحلة معلّقة **يحجز التطبيق كله** عند `/rating` حتى تُحسم. دورُ المستخدم في الرحلة يأتي من الخادم لا من نوع التطبيق (نفس الرقم قد يكون له حسابان — `docs/38 §1`). التقييم المزدوج يرجّع 400 وهو **نجاحٌ من منظور المستخدم**: الرحلة مُقيَّمة فعلاً، فلا يُحبس في شاشة بلا مخرج. - **`core/format/money.dart`**: كل المبالغ تصل نصوصاً، والتحويل والعرض يمرّان من نقطة واحدة. عدد المنازل من العملة (`JOD` ثلاث)، والعملة من إعداد المستأجر لا من ثابت في التطبيق. - **المحفظتان لا تُوحَّدان**: الراكب `WalletPage` (رصيد + شحن = **التزام** على المنصّة)، والسائق `EarningsPage` (أرباح + سحب = **إيراد** له). شكلهما متقارب وحسابهما مختلف — دمجهما خطأ محاسبي لا اختصار (`docs/39 §4`). - **السحب بخطوتين**: `request` يرسل رمزاً **بلا خصم**، و`confirm` هو ما يخصم ويحجز. شاشة سيرو كانت خطوة واحدة ولم تُقلَّد. الرمز الخاطئ لا يُرجع المستخدم لخطوة المبلغ — الطلب قائم على الخادم أصلاً. - **الرصيد التشغيلي معروض منفصلاً** عن الأرباح: منه تُخصم العمولة، ونفاده يوقف وصول الطلبات — خلطه بالأرباح يخفي سبب توقّف العمل عن السائق. - **`FareSummary` يعرض رقم كلّ طرف**: `price_for_passenger` للراكب و `price_for_driver` للسائق — حقلان منفصلان والفرق عمولة (`docs/38 §4`). - **الإلغاء بتأكيد صريح مع سبب**. السبب لا يقبله عقد الخادم بعد، لكن السؤال يخفض الإلغاء العرضي ويجهّز الحقل. `flutter analyze` نظيف · الراكب 86 ملف/6,280 سطر · السائق 83 ملف/5,818 سطر. ### (الأصل المعتمد للمرحلة 5) 22. حالات الرحلة · التتبّع الحيّ · الإلغاء · الإنهاء · الأجرة · التقييم. 23. المحفظتان (محفظة الراكب = التزام، محفظة السائق = إيراد — لا تُدمجان). ### المرحلة 6 — الدراور والإضافات — **نُفّذت 2026-08-04** - **طبقتا الميزات مُطبَّقتان معاً** في `AppDrawer`: `Features.wallet && context.tenantHas('wallet')`. الأولى `const` تقرّر ما **يُبنى** في الـbinary (إطفاؤها يحذف الكود فلا يُستخرج — docs/23 §5)، والثانية من `GET /tenant/config/:slug` تقرّر ما **يُعرض** ممّا بُني حسب اشتراك المستأجر (docs/38 §10). هذا هو ربط lite/pro/max عملياً. - `TenantCubit` يعرض **النسخة المخزّنة فوراً** ثم يحدّثها من الشبكة: إقلاعٌ بلا إنترنت يعرض التطبيق بميزاته المعروفة آخر مرة، لا شاشة فارغة. - الشاشات: الدراور · تعديل الحساب · سجلّ الرحلات · الإعدادات (مظهر + لغة) · المساعدة. الرقم في تعديل الحساب **معطّل مع سبب معروض** لا مخفي: هو هوية الحساب على الخادم وتغييره يعني حساباً آخر (docs/38 §1). - الإشعارات: `PushService` معزول — **الملف الوحيد الذي يعرف Firebase**. التسجيل يقع بعد إثبات الجلسة عبر خطّاف في `SessionCubit`، ويشترك في `onTokenRefresh` (بدونه تتوقّف الإشعارات بصمت بعد أسابيع). الإعداد الأصلي كان جاهزاً مسبقاً: `google-services.json` منقول و`build.gradle` يطبّق `com.google.gms.google-services` أصلاً. **تصحيحات بنيوية أُجريت في هذه المرحلة** (كشفها فحص آلي لا قراءة): `docs/23 §1` يمنع أن تستورد ميزةٌ من أخرى، وكان هناك خمس مخالفات: 1. `features/history` أُدمجت في `features/trip` — سجلّ الرحلات جزء من ميزة الرحلة لا ميزة مستقلّة. 2. `AppDrawer` صعد إلى `core/ui/` — هيكل تطبيق لا ميزة. 3. `AuthFailure` صعد إلى `core/api/api_failure.dart`، ومترجمه إلى `core/ui/failure_text.dart` — مشتركان بين المصادقة والملف. 4. `AuthHeader` صار `core/ui/form_header.dart`. 5. `ProfileRepository` مستقلّ عن `AuthRepository`: تعديل الملف ليس مصادقة. `flutter analyze` نظيف · الراكب 98 ملف/7,497 سطر · السائق 95 ملف/6,931 سطر. فحوص آلية نظيفة: صفر استيراد بين ميزتين · `core` يستورد `features` من `di.dart` و`router.dart` وحدهما · Firebase و`intaleq_maps` و`geolocator` و `socket_io` كلٌّ معزول في ملفه. ### (الأصل المعتمد للمرحلة 6) 24. الدراور · الملف الشخصي · الرحلات السابقة · الإشعارات · الدعم · اللغة. 25. الوحدات المدفوعة السبع (`docs/33`) خلف أعلام `const` حسب نموذج lite/pro/max — بناء مولَّد، لا فروع. ## 3.7 إكمال التغطية (2026-08-05) فحصٌ آليّ لنقاط `docs/38` مقابل الكود كشف ثغرات لم تظهر بالقراءة: - **تسجيل السائق لم يكن موجوداً أصلاً** — أخطر ثغرة: سائق جديد يسجّل دخوله ثم يقف. بُنيت `features/onboarding` كاملة: تقديم ← ملف ← مركبة ← وثائق ← انتظار الاعتماد. **الخطوة تُشتقّ من حالة الخادم لا من تقدّم محلّي**، فسائقٌ يعيد تثبيت التطبيق يعود إلى حيث وقف. والاعتماد يقع على الخادم تلقائياً حين تكتمل الوثائق — التطبيق لا يعتمد أحداً. وعند الاعتماد **خروجٌ إجباري**: الدور يتغيّر على الخادم والتوكن القديم يحمل القديم فتفشل نقاط السائق بـ403. - **بوابة توجيه**: مستخدمٌ دوره ليس `driver` يُحجز في `/onboarding`. - **الدردشة** (`Features.chat` كان مفعّلاً بلا ميزة): `features/chat` باستطلاع كل خمس ثوانٍ — قائمة أحداث الخادم لا تتضمّن الرسائل (`docs/38 §9`)، فالاستطلاع **قيد خادم لا اختيار تصميمي**. - **الكوبون**: حقل في ورقة التأكيد خلف طبقتَي الميزات؛ يُقيَّم على الخادم. - **`Features.calls` أُطفئ صراحةً**: الخادم يدعم WebRTC والتطبيق لا. عَلَمٌ مفعّل بلا ميزة كذبٌ على القارئ التالي. - `ApiClient.upload` للرفع متعدّد الأجزاء · `AuthFailure` صار `ApiFailure` في `core/api` لأنه يخدم المصادقة والملف والتسجيل معاً. - **`tool/sync_from_rider.sh`**: المزامنة اليدوية بين التوأمين انكسرت أربع مرات، فصارت سكربتاً واحداً يعرّف ما يملكه كل تطبيق. تُشغَّل بعده `flutter gen-l10n` و`flutter analyze`. `flutter analyze` نظيف · الراكب 101 ملف/7,809 سطر · السائق 110 ملف/8,116 سطر. ## 4. مصير المجلدات القائمة | المجلد | المصير | |---|---| | `apps/rider-archive-cubit`, `driver-archive-cubit` | مصدر الطبقة الأصلية في م1، ومصدر تشخيص الأداء في م2 — ثم **يُحذفان** بعد نجاح بوابة م3 | | `apps/rider`, `apps/driver` (نقل GetX) | مرجع سلوك ميداني فقط → `apps/*-archive-getx` بعد م3. **ليسا مرجع هوية** (§2.ج) | | `Siro/siro_rider`, `siro_driver` | مرجع خارجي دائم — لا يُعدَّل ولا يُنسخ منه دارت | | باك إند PHP (`docs/34`) | يتوقّف العمل عليه — لا يُحذف | ## 5. المخاطر | الخطر | التخفيف | |---|---| | تعارض shorebird app_id بين سيرو وتريبز | الهوية من `*-archive-cubit` حصراً (§2.ج)؛ يُفحص قبل أول باتش | | تكرار الأداء السيئ نفسه | م2.12 ميزانية أداء مكتوبة + بوابة أداء في كل مرحلة | | «من الصفر» تتحول لنسخ صامت من الأرشيف | ممنوع أي `import` من `*-archive-*`؛ يُفحص بـ grep عند كل بوابة | | NestJS «جاهز» لكن غير مختبر منذ 2026-07 | بوابة م0.2 قبل أي شغل تطبيق | | هوية الراكب/السائق المنفصلة غير منفّذة في NestJS | يُتحقّق في م0.1 | | جرّ GetX بالغلط عبر pubspec | ممنوع `get`/`get_storage` في `*_new` | | بناء/اختبار على الماك | كل بناء وهجرة على السيرفر |