الإشعار قرار
لا شيء في وولفيش يبلّغك تلقائياً. لا مستمعُ أحداثٍ يطلق دفعةً حين تنتهي أتمتة، ولا مراقبٌ ينبّهك حين يحطّ تنزيل. حين يهتزّ هاتفك فذلك لأنّ الوكيل قرّر أنّ عليك أن تعلم — صاغ عنواناً ومتناً، واختار كم كانت اللحظة ملحّة، واستدعى أداة. أمّا الذي تغيّر في الإصدار 1.0.250 فهو الافتراضُ الذي ينطلق منه ذلك القرار: صار الإبلاغ هو القاعدة والصمت هو ما يحتاج إلى سبب — لكنّ كلَّ إرسالٍ يبقى استدعاءً مصوغاً متعمَّداً، لا تكراراً أبداً، ولا ضجيجاً تولّده الآلة. فالإشعار الذي لا يحمل معلومةً حقيقية أسوأ من لا إشعار، والوكيل مُلزَمٌ بذلك. هذه الصفحة هي المنظومة كلّها: الأداة التي يستدعيها النموذج، والحواجز التي يختمها سطح المكتب على كلّ إرسال، وأين تحطّ النقرة، وكيف تبقى شارات غير المقروء صادقة، وما الذي يراه المُرحِّل في المنتصف بالضبط. أمّا الاقتران نفسه — رمز QR، والرمز المكتوب، والنفق المشفَّر — فيسكن صفحة تطبيق الهاتف.أداة notify_phone
حين تكون إشعارات الهاتف مشتغلة، يحمل الوكيل قدرةَphone وفيها أداةٌ واحدة: notify_phone. ومنذ الإصدار 1.0.250 انقلبت التعليمات التي تعمل تحتها على التقنين القديم رأساً على عقب: كلُّ دورٍ ينتهي بإشعارٍ واحدٍ يحمل النتيجة الفعليّة — الرقم، والمحصّلة، وما التالي — ضربةَ ختامٍ بعد أن يُنجَز العمل وتُسلَّم الملفّات إن وُجدت، حتى للدور الذي شاهدتَه ينتهي، لأنّك بعد خمس دقائق في مكانٍ آخر، والهاتف سطحٌ ثانٍ لا مرآةٌ للشاشة التي تصادف أنّك عندها. أمّا العائق، أو الإخفاق الذي يغيّر الخطّة، أو النتيجة الجديرة بفعلٍ الآن فيخرج في منتصف التشغيل، لحظةَ وقوعه؛ والعمل الطويل يعلن نفسه بـstarted؛ والموجز المجدوَل يختم بـcompleted خاصٍّ به. وارتفع معيار الكلمات مع العدد: «اكتملت المهمّة» ليست إشعاراً — أمّا «أُعيد بناء الجدول — 214 صفّاً، وأُصلحت 3 تواريخ مكسورة» فهي كذلك.
وللصمت مواضعُه بعدُ، منصوصٌ عليها بالصراحة نفسها: الكلام العابر والإقرارات من سطرٍ واحد، والجواب الذي كان الإشعار سيكرّره حرفاً بحرف، والسرد في منتصف الدور (فالنثر يقوم بذلك المقام)، وأدوار تيليجرام وواتساب — حيث وصل الردُّ الهاتفَ نفسه رسالةً بإشعارها هي، فيُدَّخر الدفع للعوائق والإخفاقات والنتائج — ومتى طلبتَ السكوت، وطلبُك يعلو على ذلك كلِّه. والإشعار يكمّل ردَّ المحادثة ولا يحلّ محلَّ جزءٍ منه أبداً: فهو لا يُعرَض في المحادثة، ولذلك يجب أن يقوم الردُّ تامّاً بذاته.
النموذج يختار الكلمات واللحظة؛ وكلُّ استدعاءٍ يسمّي أيضاً طوراً، والطور يقرّر كم يبقى الإشعار وجيهاً قبل أن تكفّ المنصّات عن عرضه:
وقيمة
urgency حين تكون high تطلب من المنصّة عرضَ الإشعار عاجلاً؛ وnormal هي الافتراض.
الإطفاء يجعل الأداة ترفض — بإزالتها. فالمفتاح لا يرجو الوكيل أن يمتنع؛ بل يلغي تسجيل قدرة
phone، فتختفي notify_phone من سطح أدوات النموذج كليّاً. والأمر نفسه حين لا يكون هاتفٌ مقترناً: وجود الأداة هو فحص التوافر، فلا يصوغ الوكيل أبداً إشعاراً لا مكان له يذهب إليه.ما يكتبه النموذج وما يختمه سطح المكتب
النموذج مؤتمَنٌ على الكلمات ولا شيء غيرها. وكلُّ ما يقرّر إلى أين يذهب الإشعار وأيُّ إشعارٍ هو يأتي من سطح المكتب:- الهويّة — معرّف الإشعار والهاتف الذي يُوجَّه إليه يختمهما العتاد، ولا يؤخذان من النموذج أبداً. والمعرّف يُسكّ جديداً مع كلّ استدعاء، وهو ما يجعل إعادات المحاولة والتسليمات المكرّرة تنطوي في لافتةٍ واحدة بدل اثنتين.
- القصّ — العنوان يُقصّ إلى سطرٍ واحد من 60 محرفاً والمتن إلى 180، وتُنزع محارف التحكّم. فمخرجات النموذج تُعقَّم ولا يُوثق بها؛ والإطارات المشوّهة تُرفض رفضاً صريحاً ولا تُبتر في صمتٍ أبداً، فما يصل هو دائماً شيءٌ صاغه سطح المكتب حقاً.
- التشغيلة — معرّف التشغيلة يسافر مع الإشعار، مقروءاً من نطاق الدور نفسه لا من ذاكرة النموذج عنه.
- الرابط — الرابط العميق يُرفض ما لم يستعمل مخطَّط التطبيق نفسه
wolffish://ويسمِّ شاشةً يملكها الهاتف حقاً. والرابط الفاسد يخفق عند استدعاء الأداة، ومعه قائمة الوجهات الحقيقية في نصّ الخطأ، بدل أن يسافر إلى الهاتف كلّ تلك المسافة ليُلقيك في صمتٍ على الشاشة الرئيسة.
أين تحط النقرة
يستطيع الإشعار أن يسمّي شاشة، ويستطيع أن يسمّي محادثة هذا التشغيل نفسه دون أن يعرف مُعرِّفها:current يزيل التخمين، وهو الوجهة التي يُؤمَر الوكيل بتفضيلها في كلّ مرّةٍ تقريباً.
والوجهات قائمةٌ ثابتة يتشاركها التطبيقان، وتغطّي كلّ شاشةٍ يملكها الهاتف حقاً:
وعلى الهاتف، تفعل النقرة الصواب من أيّ الحالين: النقرة والتطبيق يعمل تدفع الشاشة دفعاً، فيعيدك الرجوع إلى حيث كنت؛ والنقرة التي أطلقت التطبيق تُقرأ في أوّل تصييرٍ لشاشة الدخول، فتصير وجهتها وجهةَ الإقلاع بدل تنقّلٍ ثانٍ يسابق إعادة التوجيه. والمحادثة التي تُبلَغ هكذا تنتظر سطح المكتب مهلةً قصيرة بدل أن تومض محادثةً جديدةً فارغة، والرابط الذي لا يستطيع هذا البناء تحديده يتركك حيث أنت بالضبط، والنقرة الواحدة تحرّك التطبيق مرّةً واحدة بالضبط — حتى حين يراها المساران كلاهما.
التسليم: النفق قبل الدفع
يسلك الإشعار أخصَّ طريقٍ متاح، ولا يسقط إلى غيره إلا مضطرّاً:- النفق الحيّ. إن كان الهاتف متصلاً، ركب الإشعارُ الوصلةَ المشفَّرة إطارَ تحكّمٍ وصُيِّر داخل التطبيق. والمُرحِّل ينتظر لحظةً حتى يقرّ الهاتف بوصوله.
- دفع المنصّة. إن كان الهاتف خارج الاتصال — أو لم يأتِ الإقرار أبداً — سلّم المُرحِّل الإشعارَ إلى خدمة دفع المنصّة (Expo، أمام APNs على iPhone وFCM على أندرويد)، ويسلّم نظام التشغيل اللافتة. ودفعات أندرويد تخرج بأولويّة FCM العالية، فيعرضها الهاتف النائم الآن لا حين يصحو مصادفةً — والعثرة في جانب المُرحِّل على هذا الطريق تكلّف الإشعارَ، لا النفقَ أبداً الذي يجري هذا الطريقُ بمحاذاته.
- لا مكان. إن لم يسجّل الهاتف للدفع قطّ ولم يكن متصلاً، أُبلغ عن الإرسال ساقطاً — يسمعها سطح المكتب فوراً، ويُخبَر الوكيل بدل أن يُترك يفترض أنّك تعلم.
شارات غير المقروء
كلُّ إشعارٍ يسمّي محادثةً يُغذّي عدّاداً غيرَ مقروءٍ لكلّ محادثة، وتظهر العدّاداتُ في كلّ موضعٍ تظهر فيه المحادثة: على صفوف ورقة المحادثات، وعلى قائمة السجل، ومجموعةً على زرّ القائمة العائم، ورقماً على أيقونة التطبيق — محدوداً بـ99+ في القوائم، وبـ999 تحتها.
والمحادثةُ تُختَم، ولا تُستنتَج (v1.0.301). كان الهاتف يستنتج أيَّ محادثةٍ ينتمي إليها الإشعار بقراءة رابطه — لكنّ الرابطَ هو حيث يذهب النقر، والوكيل يتركه عمداً خارج معظم إشعارات منتصف الدور، فمحادثةٌ أرسلت خمسة إشعارات كان يمكن أن تلبس شارةً تقرأ 2. فثلاثةٌ من الخمسة لم تكن تسمّي محادثةً أصلاً ولم يعدَّها شيء، بينما قائمةُ الإشعارات — التي تعدّ السجلّات غير المقروءة، برابطٍ أو بلا رابط — كانت تعرض الخمسة كلَّها. وكلُّ إشعارٍ الآن يحمل محادثته، مختومةً من سطح المكتب من نطاق الدور نفسه الذي يأتي منه معرّف التشغيلة، ولا تؤخذ من النموذج أبداً. والنقرُ يذهب بالضبط حيث يشير الرابط؛ والتغييرُ في العدّ وحده. وسطحُ مكتبٍ أقدم مقترنٌ بهاتفٍ أحدث لا يرسل محادثةً فيرتدّ الهاتفُ إلى الرابط كما كان تماماً — وعلى المُرحِّل أن يكون في الأمر، لأنه يعيد بناء كلّ إطارٍ حقلاً حقلاً.
والمحو هو القراءة. فتحُ محادثةٍ يُصفّر دلوَها ويُزيل إشعاراتها من علبة النظام؛ وحذفُ محادثة — من الهاتف، أو من سطح المكتب والهاتف يراقب — يأخذ عدّادَها معه؛ وفصلُ الاتّصال يمسح كلّ شيء. ولا وجودَ لواجب «وسم كمقروء»، لأنّ الشارةَ لم تكن دفتراً منفصلاً قطّ — إنها عدٌّ جارٍ لما لم تنظر إليه بعد.
واللطيفُ في الأمر هو أيقونةُ التطبيق حين يكون ميتاً. فتطبيقٌ مغلق لا يستطيع عدَّ شيء، فيحتفظ المُرحِّل بـعددٍ صحيحٍ واحدٍ لكلّ جهاز — يزيد واحداً مع كلّ إشعارٍ يوجّهه، ويُختم على كلّ دفعة منصّةٍ فتكون الأيقونةُ صحيحةً قبل أن يستيقظ التطبيق أصلاً. ولحظةَ يعمل التطبيقُ ثانيةً، يُبلِغ الهاتفُ بمجموعه الحقيقيّ ويستبدل رقمَ المُرحِّل استبدالاً مطلقاً — فعدُّ الهاتف هو الحجّة، وعدُّ المُرحِّل بديلٌ عن غياب أحدٍ في البيت، ولا يمكن لإطارٍ ضائعٍ أو مُعاد الترتيب أن يجعلهما يتباعدان.
وصفحةُ الإشعارات على الهاتف هي سجلُّ ما أُرسل، مستقلّةً عن الشارات: تسرد كلَّ إشعارٍ مع أرشيف، وعدُّها الخاصّ يجيب علبةَ النظام. ومنذ v1.0.64 على الهاتف تعدّ كلَّ إشعارات المحادثة لا بعضها — وهو الالتباسُ نفسه بين الرابط والمحادثة الموصوف أعلاه، منظوراً إليه من الجهة الأخرى.
ما يراه المرحل
يحمل المُرحِّل الأعمى حركةَ المحادثات إطاراتٍ مختومة لا يقرؤها. أمّا الإشعارات فهي الاستثناء المتعمَّد الوحيد، وينبغي أن تعرف شكله بالضبط. فدفعة المنصّة لا بدّ أن تغادر أجهزتك مكشوفةً في نهاية المطاف — Apple وGoogle يصيّران اللافتة، فApple وGoogle يريان نصّها. وذلك يجعل ختمَ ساق المُرحِّل وحدها طمأنينةً كاذبة، ووولفيش لا يتظاهر بغير ذلك: يسافر الإشعار إلى المُرحِّل إطارَ تحكّمٍ مقروءاً، ويُتصرَّف فيه هناك، ولا يُمرَّر أبداً بوصفه حركةَ نفق. وما يعنيه ذلك عمليّاً:- يرى المُرحِّل، في اللحظة: العنوان والمتن اللذين كتبهما النموذج، والرابط العميق — بما فيه معرّف المحادثة حين تقصد النقرة محادثة — والطور، والإلحاح، ومعرّف التشغيلة، والهاتف الموجَّه إليه.
- ولا يخزّن المُرحِّل شيئاً منه. العناوين والمتون لا تُدوَّن أبداً ولا تُسجَّل. وما يبقى هو دفترُ مسكٍ لا غير: رمز دفع الجهاز، ومنصّته وإصدار تطبيقه، وعدد الشارة الصحيح، وسجلّات تسليمٍ قصيرة العمر تشيخ وتذهب وحدها — دقائق لتذاكر التسليم، ويومٌ لمعرّفات طيّ المكرّرات.
- ومحتوى المحادثات لا يركب هذا الطريق أبداً. فنصوصك وملفاتك وإعداداتك تعبر المُرحِّل إطاراتٍ مختومةً لا يفتحها. وتعليمات الوكيل نفسها تقول الشيء ذاته من الجهة الأخرى: الإشعار عنوانُ خبرٍ لا نصُّ محادثة.
التسجيل، ومغادرة بلا أثر
يسجّل الهاتف للدفع تحت معرّف جهازٍ عشوائيّ — يُسكّ مرّةً واحدة، ويُحفظ في مخزن مفاتيح النظام، ولا صلة له عمداً بهويّة النفق التشفيرية، فلا يستطيع التسجيل تسريبَ المفاتيح. والتسجيل يجدّد نفسه مع كلّ اقتران، وكلّ إعادة اتصال، وكلّ عودةٍ إلى المقدّمة، وكلّ تدوير لرمز الدفع؛ ومطالبةُ إذن النظام تظهر أوّلَ مرّةٍ يتصل فيها الهاتف بعد التثبيت. ارفضها فلا شيء ينكسر — الإشعارات ببساطة لا تصل إلا والتطبيق متصل، عبر النفق. والتعارف عاجلٌ في الاتجاهين: يسجّل iPhone في غضون ثوانٍ، برمزٍ أو من دونه — وتسليمُ شاشة القفل يشغّل نفسه لحظةَ يسلّم iOS الرمزَ — بدل أن يتعطّل على سؤالٍ يحقّ للنظام أن يتركه بلا جواب. والمغادرة تعكس ذلك كلّه، بالترتيب الصحيح. قطعُ اتصال الهاتف يمحو كلّ شارة — الدلاء، والصينيّة، وأيقونة التطبيق — ويأمر المُرحِّل بتصفير نسخته، ثم يرسلunregister_push، فيمحو المُرحِّل تسجيلَ الجهاز في مكانه: يُنسى رمز الدفع، وكلُّ إشعارٍ لاحق موجَّهٍ إلى ذلك الهاتف يُجاب عنه ساقطاً لا مُعادَ توجيهه. وعندها فقط يُسقط الهاتف الوصلة ويمسح نفسه. والاقتران من جديد يبدأ من صفحةٍ بيضاء — تسجيلٌ جديد، وشارةٌ عند الصفر.
سطح المكتب نفسه لا يعرض أيَّ إشعارات نظام. فمهمّته أن يقرّر إرسالها؛ والهاتف هو حيث تحطّ. وإن كنت عند سطح المكتب فالمحادثة أمامك أصلاً.
المفتاحان
إعدادان، كلاهما يسكن موضعاً واحداً على كلّ جهاز — والمفتاح الرئيس هو الإعداد نفسه على الجهازين، لا اثنان قد يختلفان:قدرةُ
phone في جوهرها قدرةٌ عادية — إطفاؤها، سواء أطفأتَها أنت أو أطفأها الوكيل نفسه، يزيل notify_phone بالطريقة ذاتها. ومفتاح إشعارات الهاتف هو أداة التحكّم المقصودة؛ أمّا مفتاح القدرة فهو الذراع نفسها من موضعٍ أعمق.