سجل التغييرات

GitHub

كل تغيير جدير بالذكر، إصدارًا بعد إصدار. الملف نفسه يأتي مع المستودع ومترجم إلى جميع اللغات الـ19.

[1.231.2] — 2026-09-06

أُصلح — شريط الأعلى في الجوال كان ينكسر إلى سطرين، والخادم يعمل بمشروع أب أقدم، وإعادة توجيه cvstart.ru تعيد 404 لأي مسار يسمّي لغةً أصلًا.

أُصلح

  • عاد شريط الأعلى في الجوال إلى سطر واحد، وصارت الأزرار أيقونات فقط. كانت أزرار الإجراءات تُدفَع قسرًا إلى سطر ثانٍ بعرض كامل، فيظهر على الهاتف [☰ · البحث] وفوق سطرٍ آخر [🔔 🌙 Doctor فتح Scan] — قرصان عريضان في سطر خاص بهما. صارت الآن مربّعات 36 بكسل (🩺 / ⚡) بجوار الجرس ومبدّل السمة، ويتّسع كل شيء في سطر واحد عند 320 بكسل. وهذا مقيس لا مقدَّر بالنظر: عرض المستند يساوي إطار العرض عند 320 و360 و390 و430 بكسل، وفي كامل النطاق 560–760 بكسل حيث كانت الأزرار تركب على حقل البحث. ولم تعد اللغة تؤثّر في تخطيط الجوال إطلاقًا، لأنّ التسمية مخفية والزر مربّع ثابت؛ وفوق 900 بكسل تعود التسميات كما كانت. ويأتي الاسم الميسَّر من aria-label، لأنّ تسميةً بـdisplay:none تسقط من حساب الاسم.
  • كان الخادم يعمل بالمشروع الأب 1.31.0 مع أنّ الإصدار بُني على 1.32.0. كان /api/health على خادم الإنتاج يبلّغ parentVersion: 1.31.0. زُوملت 95 ملفًا من ملفات التشغيل وحُذف ملفان. اكتشفه فحصُ جودةٍ من المتصفّح على السطح الحيّ.
  • كان cvstart.ru يضع /ru/ أمام أي مسار دون النظر إلى ما فيه. فيصير /ru/help هو /ru/ru/help، و/en/help هو /ru/en/help، وكلاهما 404. الأول ما تحصل عليه بنسخ عنوان من الموقع الرئيس؛ والثاني أشدّ، إذ كان يغلق على الزائر الناطق بالإنجليزية طريقه إلى النسخة الإنجليزية كليًّا. أمّا الآن فالمسار الذي يسمّي أصلًا واحدًا من 16 بادئة لغوية يمرّ كما هو، وen تُحذَف (فالإنجليزية في الجذر ولا يوجد مجلّد /en/)، وما عدا ذلك يظلّ يأخذ /ru/. واختبار اللغة مثبَّت الطرفين، فلا يُخلَط /enterprise/ بـen.

تغيّر

  • نُقل 98 موجِّه فحصٍ متجاوَزًا إلى qa/archive/superseded-prompts/. يوثّق qa/README.md منذ v1.137.0 أنّ الذي يبقى في المستوى الأعلى هو موجِّه الإصدار الحالي وحده — ولم تُطبَّق القاعدة على مدى 95 إصدارًا.
  • وغادرت خمسة موجِّهات أخرى المستوى الأعلى — كانت تصف نفسها بأنّها الحالية وليست كذلك. كان REGRESSION-FINAL.md يسمّي نفسه «موجِّه الانحدار الوحيد المُعتمَد، المستقلّ عن الإصدار»، غير أنّ فقراته §§11–15 خمسة سجلّات لدورات مغلقة (v1.55.x → v1.59.7)، وأساس المساعدة الذي يعلنه 28 H2 / 103 H3 عبر 16 لغة مقابل 32 / 122 و17 اليوم: وكيل فحصٍ يتبعه كان سيسقط عند §9 من الخطوة الأولى. وQA-REGRESSION-PROMPT.md يحمل اسمًا بلا إصدار ومحتوًى من v1.226.0. أمّا QA-REGRESSION-PROMPT-v1.76.0-FULL.md فمثبَّت قبل 155 إصدارًا، وREGRESSION-PROMPT-FINAL.md يُغلق دورة v1.58.52 → v1.59.10، وQA-FULL-REGRESSION.md كان يكرّرها جميعًا على أساس v1.131.1. ورافقها PARENT-SYNC-WORKLIST-v1.26.0.md (والمشروع الأب صار عند 1.32.0)، وانتقل UX-AUDIT-2026-07-06.md المؤرَّخ إلى reports/. وبقيت أربعة مُشغِّلات، حُدِّثت أُسسها إلى أرقام v1.231.2 الفعلية. وأُعيدت كتابة qa/README.md ليصف المجلّد الموجود فعلًا.

ملاحظات

  • وُثِّق في app.css فخّان في الأولوية وقعتُ فيهما أثناء الإصلاح: قواعد سطح المكتب مُعلَنة أسفل كتلة max-width: 900px، فتغلب عند تساوي الأولوية — فبقي gap الجوال 24 بكسل بصمت (وهو ما أفاض 320 بكسل)، وبقيت الأيقونة مخفية (فصارت الأزرار مربّعات فارغة).
  • يُسنِد applyI18n() إلى el.textContent، فنُقل مفتاح الترجمة إلى <span> داخلي؛ ولو بقي على الزر لمَحا الأيقونة مع كل تبديل لغة.
  • أمّا أول rsync للمشروع الأب فقد انقطع في منتصف النقل عند ملفٍ لم يعد موجودًا محليًّا، وأبلغ رغم ذلك بالنجاح. لذا تُتحقَّق عمليات نشر الأب الآن بمجاميع التحقّق، لا برمز الخروج.

[1.231.1] — 2026-09-06

أُصلح — مسح العدادات في v1.231.0 أعاد كتابة اسم الحساب في كل الروابط.

إصلاح

  • صار كل رابط Fighter90 هو Fighter92. كان مسح عدّاد المصادر في v1.231.0 يستبدل 90 بـ 92 تحت حراسة حدّ رقمي يتخطّى 290 و901 بحقّ، لكنه يطابق 90 داخل Fighter90: فقبله حرف وبعده ينتهي الرمز ببساطة. 562 رابطًا في جميع ملفات README السبعة عشر، وفي حزم المساعدة السبع عشرة، وفي مرايا المساعدة على الموقع، إضافة إلى 138 في الويكي، كانت تشير إلى حساب غير موجود؛ ولافتة providers.png وكل روابط «التحرير على GitHub» كانت تعيد 404. انتبه إليها مستخدم بسبب الصورة الغائبة.
  • انزاحت خمس إشارات إلى إصدارات سابقة. إذ حوّل مسح 85 ← 87 في الإصدار نفسه v1.85.0 إلى v1.87.0، فاخترع إصدارًا لم يصدر قط.

ملاحظات

  • الحدّ الرقمي وحده لا يكفي: فهو يحمي الرقم من الأرقام المجاورة، لا من كونه جزءًا من معرّف أو سلسلة إصدار. أمّا الأعداد نفسها فصحيحة ولم تتغيّر: 92 مصدرًا، و87 محوّلًا إنجليزيًّا، و3009 اختبارات.
  • لم يتأثّر المستودع الأب: لم تُمسح عدّاداته أصلًا.

[1.231.0] — 2026-09-05

أُضيف — مصدرا مسح من career-ops الأب v1.32.0 (Collage وTelegram الصارم)، ووضع REST في Gem، وعيب كشفه النقل في شيفرة الأب نفسه.

أُضيف

  • مصدران جديدان، 90 ← 92 (87 EN + 5 RU)، وALL_ADAPTERS 85 ← 87. كلاهما بلا رمز وصول.
    • Collage (provider: collage) — واجهة Collage HR العامة لمواقع الوظائف. مقطع المسار هو عنوان الموقع الذي اختاره المستأجر بنفسه، لا اختصار مشتق من اسم الشركة، ولذلك يُقرأ من api: أو من عنوان secure.collage.co/jobs/<العنوان> ولا يُخمَّن أبدًا: العنوان المخمَّن يعني مسح لوحة شخص آخر.
    • Telegram (الصارم) (provider: telegram-channel) — يقرأ معاينات t.me/s/<القناة> نفسها التي يقرأها المصدر telegram القائم، لكنه يعقد الصفقة المعاكسة: لا يصير المنشور صفًّا إلا إذا سمّى صاحب عمل وأحال إلى صفحة وظيفة معًا. وتُسقَط المنشورات المجمَّعة، وتلك التي تخفي صاحب العمل، وتلك التي تكتفي بوسيلة تواصل. انخفاض التغطية مقصود — قِيس أعلى المجرى أن 32-77% من المنشورات تمرّ في القنوات التي يديرها متخصصو التوظيف — لكن كل صف يحمل صاحب عمل حقيقيًّا ورابطًا حقيقيًّا. وكلاهما يُشحن، كما في المشروع الأصل.
  • أضيف إلى Gem وضع REST اختياري إلى جانب مسار GraphQL (api: https://api.gem.com/job_board/v0/<board>/job_posts). ولا يُشتق قط من معرّف لوحة، ويبقى مسار GraphQL هو الافتراضي. منقول من الأصل #3783.

إصلاح

  • مرشّح الموقع في telegram-channel لم يكن يرى الحروف السيريلية، فكان يسمّي المدن أصحاب عمل. استخدم LOCATIONISH_RE في المشروع الأصل الحدّ \b الخاص بـ ASCII وحده، وهو لا ينشط قط بجوار الحروف السيريلية؛ فصارت كل البدائل الروسية داخله (москва وспб وофис وудалёнк*) غير قابلة للوصول، وكان Senior Engineer | Москва يعيد «Москва» بوصفها صاحب العمل. وهذا بالضبط إحلال الحقل الخطأ محلّ الحقيقة الذي وُجدت سياسة الإسناد كلها لمنعه، وقد أصاب الجمهور الأساسي لهذا المزوّد: القناة الناطقة بالروسية تكتب الموقع بالسيريلية. ويستخدم نقل web-ui فحوص يونيكود (\p{L} / \p{N})، وهو ما يفعله الأصل أصلًا في الثابت التالي ROLE_WORD_RE. وثُبّت ذلك باختبار يفشل مع صيغة ASCII. لم يُصلَح بعد أعلى المجرى.

ملاحظات

  • عمل الأصل على استرجاع Workday بالأوجه (#3851 و#3874) لم يحتج نقلًا. فـ web-ui لا يرقّم صفحات Workday ولا يقسّمها بالأوجه إطلاقًا — طلب POST واحد عند offset: 0. وهو الاستنتاج نفسه في v1.229.0، وقد أُعيد التحقق منه لا افتراضه.
  • providers/_types.js تغيّر في التوثيق فقط.
  • الاختبارات: 2962 ← 3009 (+47).

[1.230.0] — 2026-09-05

تغيّر — ثلاثة إصلاحات للمزوّدين من المشروع الأصل career-ops v1.32.0، وعيب واحد كشفه النقل نفسه.

أُضيف

  • صار Welcome to the Jungle يقبل تعبير filters من Algolia مطبَّقًا على الخادم (wttj: { filters: "offices.country_code:FR AND contract_type:full_time" }). الكلمة المفتاحية وحدها لا تضيّق لوحة عالمية: فالذي يقرّر أيّ max_hits من النتائج يعود هو ترتيب الملاءمة لدى Algolia لا مرشّحات الماسح — إذ تعمل مرشّحاتنا بعد ذلك على ما وصل فعلًا، فيبقى كل ما تجاوز الحدّ غير مرئي مهما حسُنت مطابقته. والمرشّح يقلّص مجموعة النتائج نفسها بدل إعادة ترتيبها، ولذلك يرتفع الحدّ لكل استعلام من 200 إلى 1000. ومع وجود مرشّح يصبح queries اختياريًّا: فالاستعلام الفارغ يعني «كل ما يجتاز المرشّح». منقول من المشروع الأصل #3757.

إصلاح

  • لم يعد Radancy ينبّه بـ«truncated» حين يقدّم المستأجر ببساطة وظائف أقل مما تعلنه لافتته نفسها. قارن المشروع الأصل قيمة data-total-results بتسعة مستأجرين حقيقيين: تطابق خمسة، لكنّ أربعة — بينهم صغير وضخم معًا — سلّموا وظائف فريدة أقلّ بنسبة 10–56 % ممّا أُعلن، دون صفّ مكرَّر واحد. فاللافتة تبالغ فيما يقدّمه فهرس بحث المستأجر فعليًّا، ومن ثمّ كانت مقارنة العدد المتراكم بها إنذارًا كاذبًا على غالبية اللوحات. والتنبيه الآن لا يظهر إلا حين يوقف المسار حدُّنا نحن max_jobs/max_pages، وهي الحالة الوحيدة التي يستطيع المستدعي التصرّف حيالها؛ أمّا totalResults فيبقى في الرسالة سياقًا ولا يقرّر شيئًا. منقول من #3839.
  • يتجاوز Radancy ذاكرة التخزين المؤقت في كل طلب لجزء JSON. طبقة تخزين مؤقت أمام هذا المسار تعيد لدى بعض المستأجرين استجابات قديمة: تظهر الوظيفة نفسها بعد صفحات بينما لا تُقدَّم أخرى إطلاقًا — وقد أُعيد إنتاج ذلك تسع مرات من تسع في المنبع. لم تغيّر ترويسات Cache-Control/Pragma شيئًا، بينما حلّ المشكلةَ معاملٌ عشوائي مع كل طلب. ويجب أن يكون فريدًا لكل استدعاء، لا مشتقًّا من رقم الصفحة: فالمعامل الحتمي يمنح الذاكرة المؤقتة مفتاحًا ثابتًا، ومعه المطابقة الثابتة الخاطئة نفسها. منقول من #3839.
  • صارت قوائم مكاتب Greenhouse مرتَّبة ترتيبًا حتميًّا. لا يضمن Greenhouse ترتيب offices[] بين استجابة وأخرى، والمجموعة المستخلصة من شجرة /offices تتبع ترتيب اجتيازها. وكلاهما يصل إلى حقل location في الوظيفة، فكانت اللوحة التي تعيد ترتيب مكاتبها تعيد كتابة تلك السلسلة — فتُقرأ وظيفةٌ لم يتغيّر فيها شيء وكأنها جديدة. أصلح المشروع الأصل المجموعة (#3839)؛ وكان في web-ui مثال ثانٍ للعيب نفسه لا وجود له في الأصل — وهو البديل offices[0] المستخدَم حين لا تحمل الوظيفة موقعًا خاصًّا بها — وقد رُتِّبا كلاهما الآن.

ملاحظات

  • إصلاح iCIMS في المشروع الأصل (#3728) لم يحتج نقلًا. فهو يقرأ كل مدخلات jobLocation من JSON-LD في صفحة التفاصيل، لأنّ بعض المستأجرين يصدرون مدخلًا كلّه UNAVAILABLE قبل العنوان الحقيقي. أمّا iCIMS في web-ui فيأخذ الموقع من HTML صفحة القائمة ولا يفتح صفحة تفاصيل أبدًا.
  • تغيير _http.mjs لم يحتج نقلًا. فهو يستخرج isRefusedRedirectError لمستهلك ثانٍ لا يوجد في web-ui؛ وسلوك isRetryableError لم يتغيّر.
  • إزالة التكرار الواعية بالموقع (#3751) غير منسوخة — فهي في مسار scan.mjs بواجهة الأصل السطرية.
  • السجلّ دون تغيير: 90 مصدرًا (85 EN + 5 RU). الاختبارات: 2956 ← 2962 (+6).

[1.229.0] — 2026-09-03

أُضيف — أربعة مصادر مسح من مشروع career-ops الأب v1.31.0: Built In وFeishu Jobs وGarena وMokaHR.

أُضيف

  • أربعة مصادر جديدة، 86 ← 90 (85 EN + 5 RU)، وALL_ADAPTERS 81 ← 85، وكلّها بلا رموز. أما Built In فيغطي أسواق اللوحة التقنية الأمريكية التسعة، ولا يحمل استعلامًا افتراضيًا: فالمُدخل الخالي من queries: وcategories: لا يمسح شيئًا ويقول ذلك صراحةً، لأن مصدرًا مشتركًا لا يجوز أن يحمل كلمات بحث شخصٍ بعينه؛ والسوق خارج القائمة يُرفَض ولا يُستبدل خِلسةً باللوحة الوطنية. وFeishu Jobs لا يقبل سوى صيغتَي مضيفٍ اثنتين، وهما تستعملان مسارين مختلفين لصفحة الوظيفة. وGarena شركةٌ واحدة: فـoffice يصوغ الرابط لا القائمة. أما MokaHR فيعيد استجابةً مشفَّرة (ظرف AES-128-CBC ومفتاحه بجواره) — تعميةٌ لا حماية، لكن بغير فكّ التشفير لا يُقرأ شيء.

إصلاح

  • كان htmlToText قد يُسرّب إلى النص فاتحةَ وسمٍ ناقصة. فـsafe &lt;img src=x onerror=1 كان يخرج هكذا: safe <img src=x onerror=1. وصار كل فكّ ترميزٍ يتبعه تنظيف، وتفقد الفاتحةُ الأخيرة قوسها. مُنقولٌ عن المشروع الأب (#3491).

ملاحظات

  • لم يُنقَل شيء من إصلاح Workday في المشروع الأب. فهو ينقذ المستأجرين الذين يُوقف خادمُهم الترقيم عند الإزاحة 2000 — وweb-ui لا يُرقّم Workday أصلًا (طلب POST واحد عند offset: 0 بمئة صف).
  • لم يحتج إصلاحا verify-cv-facts وmerge-tracker إلى نقل: إذ يُشغّل web-ui هذين البرنامجين من نسخة المشروع الأب.
  • الاختبارات: 2909 ← 2956 (+47).

[1.228.5] — 2026-08-30

إصلاح — كانت النسخة المُبلَّغة تصف الملف على القرص لا الشيفرة المُنفَّذة.

إصلاح

  • كان /api/health يعيد قراءة package.json عند كل طلب، فيبلّغ عمليةٌ قديمة عن نسخة لا تنفّذها. وجد فريق الفحص نسخة محلية تجيب version: 1.228.4 بينما تردّ 404 على /api/ping، وهو مسار أُضيف في 1.228.3: بدأت قبل النشر، وتخدم الشيفرة القديمة، وتبلّغ عن نسخة الملف الجديد. وهكذا بالضبط يبدو ناجحًا نشرٌ نسخ الملفات ولم يُعِد التشغيل قط: السلسلة الوحيدة التي يفحصها المشغّل هي عينها التي لا تقدر أن ترى العطل. وهو الصنف نفسه الذي كان عليه الرابط الرمزي في v1.228.1، حيث توقّف rsync في منتصف النقل وأبلغ رغم ذلك عن نجاح.

    صارت النسخة تُلتقط عند تحميل الوحدة، فتصف الشيفرة المُنفَّذة. والعملية القديمة تبلّغ عن النسخة القديمة — وهي الحقيقة، ظاهرةً من أول طلب. أما parentVersion فيبقى مقروءًا عند كل طلب: فهو يصف نسخة المشروع الأب التي لا تحمّلها هذه العملية والتي قد تتغير بحق تحت خادم يعمل.

[1.228.4] — 2026-08-30

إصلاح — كان مِجَسُّ الحياة غير المُوثَّق يقوم بعمل قرصي عند كل طلب.

إصلاح

  • كان GET /api/ping يقرأ package.json ويحلّله عند كل نداء. وهي نقطة النهاية الوحيدة التي تُبلَغ بلا اعتمادات، فقراءةٌ من القرص وتحليلُ JSON لكل طلب رافعةُ حجبِ خدمةٍ تُسلَّم لأي أحد — وقد وسمها CodeQL بانعدام تحديد المعدّل. صارت النسخة تُقرأ مرة واحدة عند تسجيل المسار؛ فهي لا تتغير أصلًا دون إعادة تشغيل، ومن ثمّ لا يقوم المعالج بأي إدخال أو إخراج. كان تحديد المعدّل ليحدَّ من الضرر؛ أما إزالة العمل فتزيل الرافعة نفسها.
  • صار إخفاءُ الاسم الحقيقي لصاحب الملف خارج loopback مثبَّتًا باختبار. فقد كان /api/health يستبدله بـhidden أصلًا، لكن لأن ذلك الصف يشارك حارس hidden ?? value مع جذر المشروع فحسب. ولم يكن شيء يختبر ذلك، فتعديلٌ يمنح الصف قيمته الخاصة كان ليسرّب اسم شخص حقيقي إلى الشبكة المحلية دون أن ينتبه أحد.

[1.228.3] — 2026-08-30

Fixed — the docs assistant could not answer in English, Telegram company names arrived truncated, and a monitor had no way to see the service was up.

Fixed

  • The docs assistant answered “no matching help sections were found” in English while the same question worked in Russian. Retrieval was fine; the context builder was not. buildAskPrompt did if (ctx.length + sec.body.length > MAX_CONTEXT) break — a break, so one oversized section ended the loop and left the context empty, and the assistant truthfully reported it had nothing. The English §5 had grown to 16 081 bytes against a 14 336-byte budget (partly because v1.228.0 added telegram_channels to it); the Russian §5 was 13 009 and fit. Same ranking in both languages — §5 was first — only the size differed.

    Two changes: a section past 6 KB is now split at its ### boundaries, so telegram_channels is its own retrievable chunk (1 340 bytes) that ranks first in both languages; and an oversized section is skipped or truncated rather than ending the loop. The guide went from 32 chunks to 75.

  • A Telegram company label yielded the sentence after it, not the name. Компания: FinCore Technology (продуктовая разработка, высоконагруженные системы) became FinCore Technology (продуктовая разработка, высоконагруженны — capped at 60 characters, cut mid-word. Three of 141 rows. The name now ends at the first separator that introduces a description — a spaced dash (so Coca-Cola survives), a comma, semicolon, pipe or opening parenthesis — and any remaining cap falls on a word boundary. This source attributes a company as fact; a truncated sentence is as wrong as a guessed name, only less obviously so.

Added

  • GET /api/ping — a liveness probe that leaks nothing. { ok, version } and no more. /api/health reports absolute paths, the profile owner’s real name and which API keys are set: legitimate for an authenticated operator, never for the public internet. A monitor behind a Basic-auth gate previously had no way to see the service was up at all.

[1.228.2] — 2026-08-30

Fixed — a two-word search in /api/scan-results matched the phrase, not the words, and under-reported badly.

Fixed

  • ?q= now matches every term independently instead of the literal phrase. The query was a single includes(q) over title + company + location, so продакт менеджер found only rows carrying those two words in that exact adjacency — 32 of the 162 that carry both. Менеджер продукта and Менеджер по развитию продукта were invisible to it: the same words, reordered or with filler between them.

    This is how a narrow match becomes a wrong answer. The Telegram assistant asked for all Продуктовый менеджер reported 9 — faithfully, because 9 was what the endpoint said. The count it quoted was honest; the endpoint’s was not. Word order and the words between are not something a person typing two words intended to pin.

    Matching is AND across terms, so widening precision costs nothing: a row must still carry every term. менеджер разработчик returns zero rather than the union.

[1.228.1] — 2026-08-29

Fixed — a dangling symlink in the repo that silently truncated deploys.

Fixed

  • .claude/skills/refero-design was a tracked symlink to a path that has never existed here. It pointed at ../../.agents/skills/refero-design, which resolves to .agents/ at the repo root; there is no such directory, and the link arrived in the v1.118.0 parity pack from a machine where the surrounding folders nested differently. Anyone cloning the repo has been getting a broken link ever since.

    It was not inert. rsync aborts on it mid-transfer — a deploy today reported success while the server kept running the previous version, and the mismatch only surfaced because the version was checked afterwards. The parent project’s node test-all.mjs also crashes outright when a web-ui checkout sits inside its tree, because its fixture copy walks the working directory and cannot stat the link. Nothing referenced it: the only refero matches in the repo are the Polish word preferowany.

[1.228.0] — 2026-08-29

أُضيف — قنوات تيليجرام مصدرًا للمسح، ووضع استعلام مُصفَّح في /api/scan-results.

أُضيف

  • صارت قنوات تيليجرام مصدرًا قابلًا للمسح. يسردها بلوك telegram_channels: في portals.yml، وتُقرأ كل قناة من المعاينة العامة https://t.me/s/<القناة>. السجل: 85 ← 86 مصدرًا (81 EN + 5 RU)، وALL_ADAPTERS 80 ← 81. لا ينشر تيليجرام RSS، ولا تستطيع Bot API قراءة قناة لا يديرها البوت — فالطريقان البديهيان مغلقان كلاهما. أما معاينة /s/ فهي HTML عادي مولَّد على الخادم: بلا تسجيل دخول ولا JS ولا كعكات. جرى التحقق على 16 قناة: أعادت 15 منها 20 منشورًا لكل واحدة على طلب GET مجرّد.
  • اكتسب GET /api/scan-results وضع استعلام مُصفَّحًا. إذ يعيد ?q=<نص>&limit=50[&region=ru|en][&set=filtered|fresh][&offset=N] الشكل { total, returned, offset, limit, set, rows }، حيث total هو العدد قبل التصفيح. وبلا معاملات يظل المسار يعيد اللقطة كاملة — إذ يعتمد جدول #/scan على ذلك الشكل.

إصلاح

  • كان مساعد تيليجرام يجيب من جزء يسير من البيانات. كانت مهارته تشير أصلًا إلى /api/scan-results، لكن المسار لم يكن يعرف سوى إعادة اللقطة كاملة — نحو 2 ميغابايت على جهاز حقيقي — وهو ما لا يتسع في سياق وكيل. فعند طلب «اعثر على كل مديري المنتج» أبلغ عن 9 نتائج بينما كانت في اللقطة مئات، وقال إنه «يعمل بفهرس محلي محدود». وكان صادقًا: يرى نقطة النهاية ولا يقوى على هضمها. ومع الوضع المُصفَّح صار السؤال نفسه يكلّف 2 كيلوبايت ويعيد total صادقًا. وأُعيدت كتابة المهارة أيضًا: إذ كانت تطلب تلخيص المنشورات الجديدة، أي تشير إلى fresh بدل filtered.

ملاحظات

  • منشور القناة نثرٌ لا سجلُّ وظيفة. فلا حقول مبنينة فيه: يُؤخذ العنوان من أول سطر ذي مضمون، والشركة والموقع والأجر من وسوم صريحة. أما فصل الوظائف الحقيقية عن الإعلانات والملخّصات فمتروك لـtitle_filter القائم — نفسه المضبوط سلفًا لبقية المصادر. فهذه دلائل لا قائمة.
  • لا تُخمَّن الشركة أبدًا. فبغياب الوسم يُنسب الصف إلى القناة (@rabotaphp)، لأن صاحب عمل خاطئًا يدخل المتتبّع بوصفه حقيقة.
  • التحليل الفارغ خطأ لا «لا وظائف». إذ يردّ t.me بإعادة توجيه على قناة خاصة أو غير موجودة؛ وإعادة صفر صفوف هناك تُقرأ كيوم هادئ وتخفي خطأً مطبعيًا في التهيئة إلى الأبد. وjobGeeks يفعل هذا بالضبط (HTTP 302) ولم يُهيَّأ عمدًا.
  • يستعمل كشفُ العمل عن بُعد حدَّ كلمة بمعيار يونيكود بدل \b الذي لا يعرف سوى ASCII — وهو الفخ نفسه الذي وقع فيه مرشّح العناوين في v1.227.3، وقد التقطه هنا اختبارٌ قبل الإصدار.
  • هُيِّئت 15 قناة وتُحُقِّق منها حيًّا: 299 منشورًا، 15/15 قناة. و30 اختبارًا جديدًا. الاختبارات: 2865 ← 2895.
  • The README test counts jumped by hundreds, and no, this release did not add them. The localized READMEs carried unit-test totals frozen at 1856 / 1945 / 1955 / 549 / 419 / 284 / 2527 depending on the file — each stale since a different release, and several contradicting the badge at the top of the same page. They now all read 2895, verified by running the suite rather than by copying a badge. Playwright (70 / 90 / 12 → 101), e2e smoke (20 → 21), route modules (12 / 32 → 37), smoke flows (12 → 22) and the test-file count (218 → 329) were stale in the same way. The Total row also repeated the unit count, so it under-summed the three suites below it; it now reads the real sum (3040).

[1.227.5] — 2026-08-29

إصلاح — كان المسح الإقليمي يستنزف ذاكرة الخادم، وكانت إحدى مجموعات Playwright تسقط عشوائياً عند تفكيكها هي نفسها.

إصلاح

  • كان المسح الإقليمي يقتل الخادم بنفاد الذاكرة. إذ كان ru-scanner يراكم كل النتائج الخام من كل الاستعلامات ولا يزيل التكرار ولا يرشّح إلا في النهاية. وقائمة الاستعلامات مليئة عمداً بمترادفات متقاربة (Golang وGo разработчик وGolang разработчик وSenior Go …)، فتعود الوظيفة نفسها مرة مع كل استعلام، وكانت المصفوفة تحتفظ بأضعاف العدد الفريد — ومعها الأوصاف التي تشكّل معظم حجم كائن الوظيفة وتستبعدها المرشّحات كلها تقريباً على أي حال. القياس: 742 ميغابايت من الكومة في تشغيلة حقيقية من 21 استعلاماً. ويحدّ الخادم كومة Node عند 490 ميغابايت (إذ يشتقّها V8 من ذاكرة الجهاز البالغة 956 ميغابايت)، فكان المسح يُسقِط الخدمة بـFATAL ERROR: Reached heap limit — أربع مرات في يوم واحد. صارت إزالة التكرار والترشيح تجريان مع كل استعلام، فنزلت التشغيلة نفسها إلى 177 ميغابايت، بانخفاض 76%. ولم تتغيّر الأعداد المعروضة: إذ يحمل المجموعَ الفريد Set من سلاسل الروابط.
  • كانت net::ERR_SOCKET_NOT_CONNECTED تُسقِط التكامل المستمر عشوائياً. تستدعي ثلاث مجموعات Playwright الدالة window.stop() لإيقاف الطلبات الجارية قبل التأكيد على غياب أخطاء الطرفية. ويُبلِّغ Chromium عن هذا الفعل المتعمّد الواحد بثلاثة أسماء بحسب حال المقبس: ERR_ABORTED عند الإلغاء، وERR_EMPTY_RESPONSE أثناء الطيران، وERR_SOCKET_NOT_CONNECTED إذا كان المقبس قد اختفى أصلاً. ولم يكن المرشّح المشترك يعرف سوى الأولين. ظهر ذلك في مسح اللغات عند tr، بينما كانت playwright-forms وplaywright-smoke على بُعد تشغيلة واحدة تعيسة من الأمر نفسه.

ملاحظات

  • لماذا بدأ الفشل الآن تحديداً. كبرت القائمة من 14 إلى 21 استعلاماً عند إضافة عمليات البحث الخاصة بمدير المنتج. وكانت ذاكرة التنفيذ القديم تتناسب مع إجمالي النتائج، فتجاوزت زيادةُ الاستعلامات بنسبة 50% سقفَ الجهاز.
  • تغيير إزالة التكرار يحافظ على السلوك، وهذا مُختبَر لا مُدّعى. يثبّت tests/ru-scanner-dedup-order.test.mjs الاتجاه العسير — إذ يجب على نسخة مكرّرة لاحقة لا تجتاز المرشّحات أن تطرد نسخة سابقة كانت قد اجتازت — ويجري 200 فحص تكافؤ عشوائي مقابل التنفيذ القديم.
  • يظل مرشّح الطرفية ضيّقاً. فالحميد هو عائلة الطلبات الملغاة وحدها؛ أما الاتصال المرفوض وفشل DNS وإعادة التعيين وأي خطأ 5xx وكل استثناء JS غير مُلتقَط فما زالت تُسقِط التأكيد.
  • جرى اختبار فرضيتين ورفضهما بدل شحنهما: مهلة Caddy (فتهيئته نظيفة وflush_interval -1 صحيح لـSSE)، ومهلة requestTimeout الافتراضية في Node البالغة خمس دقائق (إذ أظهر استنساخ مصغّر أنها لا تقطع استجابة بثّية طويلة). أما الدليل الحقيقي فكان Reached heap limit في السجل. الاختبارات: 2860 ← 2865.

[1.227.4] — 2026-08-29

إصلاح — كان content_filter لا يزال يتجاهل word: وstem:؛ إذ لم تُصلح النسخة v1.227.3 سوى مرشّح العناوين.

إصلاح

  • لم يكن content_filter يحترم البادئتين. علّمت v1.227.3 مرشّحَ العناوين بادئتَي word: وstem: من المشروع الأصل، لكنها تركت content_filter على استدعائه الخام lower.includes(k)، فظل المُدخَل المسبوق ببادئة يُطابَق هناك بوصفه النص الحرفي "word:java" — العملية الصامتة نفسها بلا أثر، على بُعد مرشّح واحد. يقرأ content_filter وصف الوظيفة، وكان افتراضه دائماً سلسلة جزئية بسيطة؛ ولهذا فإن نفياً مجرّداً مثل java يرفض كل إعلان لا يفعل سوى ذكر «JavaScript»، والبادئة هي وسيلة استثناء مُدخَل واحد من ذلك. نُقلت دالة compileContentKeyword من المشروع الأصل: تشترك في آلية البادئات نفسها لكنها لا تثبّت الكلمات القصيرة عمداً — فمرشّح العناوين يثبّت الاختصارات من حرفين أو ثلاثة لأن «COO» داخل «Coordinator» خطأ دائماً، بينما التسلسل القصير داخل فقرة نثرية يكون مقصوداً عادةً (aws وgcp وsql وgo).

ملاحظات

  • اكتُشف في المراجعة الآلية لطلب دمج v1.227.3، التي لاحظت أن الشيفرة لا تغطي ما يدّعيه تعليقها نفسه — إذ كان مكتوباً «title_filter / content_filter» بينما المسار الموصول هو مسار العناوين وحده. صار التعليق الآن يفصل صراحةً: كلا المرشّحين يحترم البادئات، وتثبيت الاختصارات في مرشّح العناوين وحده.
  • تحقّقنا كذلك انطلاقاً من تلك المراجعة، ولا تغيير: نقل قاعدة الاختصارات من حرفين أو ثلاثة من \b إلى حدّ اليونيكود متطابق سلوكياً في ASCII وأشدّ فقط حيث ينبغي — إذ يظل COO_lead وcoo1 محفوظين (الشرطة السفلية والرقم محرفا كلمة في الحالتين)، ويظل Coordinator محفوظاً، ويُستبعَد «Директор COO» و«VP «Продукт»» على نحو صحيح. جرى فحص 14 حالة حدّية.
  • تحتفظ مُدخَلات content_filter بلا بادئة بمطابقتها السابقة بايتاً ببايت، ولا يزال الوصف الفارغ أو الغائب يمرّ، ولا تطابق word: أو stem: الفارغة شيئاً بدلاً من كل شيء. 6 حالات جديدة في tests/content-filter.test.mjs. الاختبارات: 2852 ← 2858.

[1.227.3] — 2026-08-29

إصلاح — كانت بادئتا word: وstem: في مرشّح العناوين تُتجاهَل بصمت، فتغدو تلك السطور بلا أثر.

إصلاح

  • لم يكن web-ui ينفّذ بادئتَي word: وstem: الخاصتين بالمشروع الأصل. يطابق title_filter (وcontent_filter) افتراضياً سلسلة جزئية دون تمييز حالة الأحرف، ولهذا فإن نفياً مجرّداً مثل intern يرفض أيضاً «International Product Manager» و«Internal Tools Engineer». وبدلاً من قلب هذا الافتراضي — وهو تغيير يكسر كل تثبيت مُهيَّأ — جعل المشروع الأصل الدقّة اختيارية لكل مُدخَل: فـword:intern يطابق الكلمة كاملةً فقط، وstem:agent يجب أن يبدأ كلمة ويجوز أن تستمر (فيفصل «Agentforce» عن «Reagents»). ولم يكن لدى web-ui أيٌّ منهما، فكان يطابق المُدخَل بوصفه نصاً حرفياً هو "word:intern"، وهو نص لا يرد في أي عنوان وظيفة. فصار السطر عملية صامتة بلا أثر: يرشّح ملف portals.yml نفسه ترشيحاً صحيحاً عبر سطر الأوامر ولا يرشّح هنا إطلاقاً، فتصل كل وظيفة تدريب استُبعدت إلى جدول #/scan رغم ذلك. نُقل من ملف title-keywords.mjs في المشروع الأصل، بما في ذلك حدّ الكلمة المدرك لليونيكود (\p{L}\p{M}\p{N}_) بدلاً من \b الذي لا يعرف سوى ASCII وكان يطابق داخل الكلمة في العناوين ذات العلامات أو السيريلية.

ملاحظات

  • اكتُشف أثناء فحص ملف portals.yml حقيقي أُضيف إليه word:intern للتوّ تحديداً كي يكفّ intern المجرّد عن استبعاد «International Product Manager». كانت البادئة تعمل في سطر الأوامر، بينما كان web-ui يُبقي بصمت على كل وظائف التدريب. صار السلوك متطابقاً الآن.
  • الإعدادات الافتراضية لم تتغيّر. تحتفظ الكلمة المفتاحية بلا بادئة بمطابقتها السابقة: سلسلة جزئية للعبارات ولكل ما يحوي محارف غير أبجدية (.NET وSAP وL&D)، وتثبيت على حدود الكلمة للاختصارات من حرفين أو ثلاثة. أما word: أو stem: الفارغة فتُعدّ خطأً مطبعياً ولا تطابق شيئاً — إذ إن نمطاً فارغاً بوصفه نفياً سيطابق كل عنوان ويُبطل مسحاً كاملاً بسبب نقطتين شاردتين.
  • صارت قاعدة الاختصارات من حرفين أو ثلاثة تستخدم حدّ اليونيكود نفسه: كانت تستعمل \b، فاختلف coo عن word:coo في العناوين غير ASCII — صياغتان لقاعدة واحدة.
  • 6 حالات جديدة في tests/title-filter.test.mjs. الاختبارات: 2846 ← 2852.

[1.227.2] — 2026-08-29

إصلاح — كانت صفحة #/config تنزلق أفقياً على الهواتف الضيقة.

إصلاح

  • MOBILE-1: كانت #/config تفيض أفقياً على العروض ≤ 352 بكسل. كان كل حقل من نوع select يحمل min-width: 300px مضمَّناً. والنمط المضمَّن يتغلّب على ورقة الأنماط، فلم يكن العنصر قادراً على الانكماش مع حاويته: عند عرض 280 أو 320 بكسل كانت الحافة اليمنى للـselect تقع عند 353 بكسل، فيظهر شريط تمرير أفقي وتُدفع عناصر التحكم خارج الحافة اليمنى. تأثّرت جميع قوائم المزوّدين/النماذج الثماني عشرة (cfg-llm-provider وكل cfg-*-model). صار الآن min-width: min(300px, 100%) مع max-width: 100%، فلا يُطبَّق عرض الـ300 بكسل المريح إلا عند توفّر المساحة. الأجهزة عند الحد أو دونه: iPhone SE (الجيل الأول، 320 بكسل)، وأجهزة أندرويد القديمة، والشاشة الخارجية لـGalaxy Fold.

ملاحظات

  • لماذا فات ذلك كل جولات الفحص على الهاتف سابقاً. عند 360 و375 بكسل يوجد من الفسحة ما يكفي تماماً لإخفاء الفيض، ومجموعات Playwright تعمل في نافذة عرضها 1280 بكسل. ولم يقس أي اختبار في الـCI يوماً عرضاً أضيق من 375 بكسل. المجموعة الجديدة tests/playwright-narrow-viewport.mjs تمرّ على المسارات التي تحمل نماذج عند 320 بكسل وتفشل عند أي فيض أفقي، مع تسمية أعرض عنصر في الرسالة؛ وتثبّت حالة ثانية أن أي select في #/config لا يتجاوز حدود العرض. وقد تُحقِّق أولاً من أنها تعيد إنتاج العلة (scrollWidth 353 > clientWidth 320، و18 قائمة حافتها اليمنى 353). Playwright: 99 ← 101.
  • معروف ومتروك عمداً خارج البوابة حتى الآن: 280 بكسل (الشاشة الخارجية لـGalaxy Fold) ما زالت تفيض بنحو 5 بكسل، لكن لسبب مختلف — محتوى طويل داخل كتل <details> في بطاقة الإعدادات، والشريط الجانبي بعرض 256 بكسل أمام عرض 280 بكسل، لا الـmin-width المضمَّن الذي يعالجه هذا الإصدار. وإعادة التدفّق عند عرض لا يُعلنه أي هاتف شائع عملٌ منفصل؛ وقائمة WIDTHS في المجموعة جاهزة له.
  • صُحِّح أيضاً موجّه ضمان الجودة لـv1.227.1 الذي سمّى مسار المتابعة #/modes/followup. فصفحات الأوضاع تُسجَّل بالـslug مجرّداً (Router.register(cfg.slug))، وعليه فالمسار هو #/followup، بينما يُعيد #/modes/followup رمز 404 بشكل صحيح. توثيق فقط.
  • مجموعة اختبارات الوحدة دون تغيير عند 2846 (التغطية الجديدة على مستوى المتصفح).

[1.227.1] — 2026-08-29

إصلاح — تفصيل المصادر في الدليل لم يكن متسقاً مع نفسه، وكان بانر GitHub يُطبع في التطبيق كوسم خام.

إصلاح

  • كان §17 يذكر إجمالاً وتفصيلاً لا يتجمّعان. كانت كل حزم الدليل تقول «سجل server/lib/sources/ يشحن 85 محوّلاً — 78 إنجليزية + 5 روسية». و78 + 5 = 83 لا 85؛ وفي ja/ko بلغ الانحراف 77 (= 82). كل مصدر جديد كان يرفع الإجمالي ويترك الحدّ كما هو، فتباعد الرقمان عبر عدة إصدارات. صار التفصيل الآن 80 EN + 5 RU في اللغات السبع عشرة كلها. وهذا أبعد من الشكل: فملف docs/help/<lang>.md هو المتن الوحيد الذي يستند إليه «Ask the docs»، فمن يسأل عن عدد المصادر المدعومة كان يتلقى رقمين متناقضين من الجملة نفسها.
  • الجملة ذاتها كانت تثبّت مرساة إصدار قديمة. «اعتباراً من v1.213.0 يشحن السجل …» بقيت في 16 لغة بينما تحرّك العدّاد مرتين (83 في v1.219.0، و85 في v1.226.0). وقد أُصلحت هذه المرساة يدوياً من قبل في v1.210.1 ثم انحرفت من جديد. أُزيلت الآن لصالح صياغة بلا إصدار — إذ يُتحقَّق من العدّاد نفسه مقابل السجل الحيّ الذي لا يمكن أن يتقادم.
  • كان بانر المزوّدين يُطبع كوسم مهروب أعلى #/help. سجّلت v1.225.0/v1.225.1 أنه «يُزال داخل التطبيق بواسطة UI.md() (لا دعم للصور)». وهذا يخلط بين أمرين: فـUI.md() يهرّب أولاً — يهرّب كل بايت من المصدر قبل أي تحويل markdown، وهو الحد الصحيح لمنع XSS لكنه نقيض الإزالة. لم يُزل أحد البانر لأن أحداً لم يحاول، فكانت اللغات السبع عشرة كلها تفتح #/help بـ268 حرفاً من <p align="center"><img src="https://… حرفياً. ومَدخل مشترك جديد (server/lib/help-markdown.mjs::stripGithubOnlyBlocks) صار يُسقِط عند القراءة كتل HTML المستقلة التي تحمل صورة — لكلٍّ من GET /api/help/:lang ومتن docs-assistant. وتحتفظ الملفات بالبانر، فتظل واجهة العرض على GitHub كما هي.
  • كان عمود «#» في لوحة الإيقاع فارغاً منذ v1.117.0. كان يقرأ entry.appNum، بينما يُصدِر followup-cadence.mjs الحقل num — ولم يوجد appNum في الحمولة قط. صار الاثنان يُقرآن الآن. وقد ورث سطر «Next up» في v1.227.0 الخطأ نفسه وأُصلح معه.

ملاحظات

  • مرّت الأربعة دون ملاحظة لأن لا شيء كان يحرسها. فبوابات الدليل (canonical-docs-coverage, help-ui, help-ru-config-section) تَعُدّ العناوين — 32 H2 و121 H3 — ولم يقرأ أي اختبار قط رقماً داخل فقرة، ولا نظر فيما يقدّمه مَدخل الدليل فعلياً. وتسدّ هذه الثغرة مجموعتان جديدتان: tests/help-source-counts.test.mjs (الإجمالي المذكور يساوي SOURCES.length؛ والحدود تساوي التقسيم الحيّ EN/RU ومجموعها الإجمالي؛ والفقرة لا تثبّت إصداراً) وtests/help-banner-strip.test.mjs (يبقى البانر على القرص من أجل GitHub، ولا ينجو أبداً من التجريد، وأول سطر مُصيَّر في كل حزمة هو عنوانها، ويظل التجريد ضيّقاً، ويمرّ المستهلكان كلاهما عبره). وقد تُحقِّق من فشل الاثنتين أمام هذه العلل بالذات قبل الالتزام. مجموعة الاختبارات: 2837 ← 2846.
  • لا تغيير في السجل ولا الماسح ولا عقود المسارات؛ وتبقى المصادر 85 (80 EN + 5 RU) وALL_ADAPTERS 80.

[1.227.0] — 2026-08-28

أُضيف — SEEK هونغ كونغ (JobsDB)، ومرشّح «المستحقة فقط» للمتابعات، وجهات العمل السرية صارت تُظهر الوكالة التي قدّمتها.

أُضيف

  • JobsDB هونغ كونغ عبر مصدر Jobstreet/SEEK القائم. تعمل هونغ كونغ تحت علامة JobsDB لكنها تقوم على منصة SEEK نفسها خلف نقطة النهاية v5 نفسها، فلا تحتاج مصدرًا مستقلًا — يكفي hk.jobsdb.com في قائمة المضيفين المسموح بهم وsiteKey: HK-Main. السجل دون تغيير: 85 مصدرًا (80 EN + 5 RU) وALL_ADAPTERS 80 — فهذا سوق جديد على مُهايئ قائم، لا مُهايئ جديد.
  • مرشّح «المستحقة فقط» في لوحة الإيقاع (#/modes/followup). مربع اختيار جديد يحصر اللوحة فيما يمكن التصرف فيه الآن. تُقرأ الاستحقاقية من حقل urgency الذي يحسبه أصلًا followup-cadence.mjs في المشروع الأصل (urgent / overdue)، ولا تُقرأ أبدًا من status في المتتبّع، إذ قيمه applied/responded/interview ولن تطابق شيئًا. وحين لا يوجد مستحق، تذكر اللوحة أقرب متابعة مجدولة بدل إظهار حالة فارغة. يعيش المنطق في الوحدة النقية public/js/lib/followup-view.js، وهي مرآة لـfollowup-view.mjs في المشروع الأصل.
  • تُنسب جهات العمل السرية إلى الوكالة التي قدّمتها. الإعلان الذي يُخفي جهة العمل يُسجَّل ? في المتتبّع، ويوضع المُوظِّف في عمود Via. كان #/tracker يعرض ? مجرّدة، فتعذّر تمييز عدة صفوف سرية، ولم يجد محلّل الشعارات ما يطابقه. صار هذا الصف يُقرأ «سري · عبر <الوكالة>»، ويحلّ شعاره من اسم الوكالة، ويمكن العثور عليه بذلك الاسم في مربع البحث. وهذا عرض فقط — لا يُكتب اسم المُوظِّف أبدًا فوق الحقل المرجعي company. وحدة نقية جديدة public/js/lib/company-presentation.js، مرآة لـcompany-presentation.mjs في المشروع الأصل.

إصلاح

  • كان Jobstreet/SEEK ينادي نقطة نهاية مهجورة. ظل المصدر يشير إلى chalice-search v4 (/api/chalice-search/v4/search) التي أوقفتها SEEK؛ وانتقل المشروع الأصل إلى /api/jobsearch/v5/search ولم يتبعه web-ui قط. صار كل مدخل Jobstreet/SEEK يستعلم من v5. تُقرأ بنية عنصر v5 أصلًا (يُبنى رابط الوظيفة من id، وlocations[0].label، وadvertiser.description، وsalaryLabel)، وأُزيل معامل الإسقاط solrFields الخاص بـv4، أما المدخل الذي ما زال portals.yml يثبّت فيه مسار v4 الميت فيحتفظ باسم مضيف سوقه بينما يُعاد بناء المسار على v5 — دون أي تعديل من المستخدم.

ملاحظات

  • مطابقة career-ops 1.30.0 (رأس المشروع الأصل 8d64f65 ← 6cc46a4؛ ما زال VERSION لديه يقرأ 1.30.0 — إذ يتأخر release-please عن main). غير مَنقول، مع الأسباب: ملء الموقع من JSON-LD في iCIMS — مصدر iCIMS في web-ui لا يملك خطّاف enrichDate() أصلًا (صفحات القوائم فقط)، فلا مسار برمجي لإصلاحه؛ stderr غير قاتل من الـCLI (#1974) — يحسم مُشغّل web-ui الفشل من رمز الخروج وحده ولم تكن لديه تلك الاستدلالية قط؛ تتبّع المسارات في Turbopack — لا حزّام في web-ui؛ إصلاحات cv-sync-check — تُمرَّر للقراءة فقط وبتحمّل الأخطاء؛ تسلسل Playwright في scan.mjs، وإجابات Block H، وحواجز updater/doctor/update-system، وفحص الصياغة العَودي، ولوحة Go، وإضافات الاختبار الذاتي في jd-skill-gap/verify-cv-facts — أسطح خاصة بالـCLI لا يستدعيها web-ui، أو اختبارات ذاتية دون تغيير سلوك. مجموعة الاختبارات: 2818 ← 2837.

[1.226.0] — 2026-08-27

أُضيف — مصدرا وظائف فيتناميان: ITviec و CareerViet.

أُضيف

  • ITviec + CareerViet (فيتنام). لوحتان فيتناميتان مثبَّتتا المضيف تنضمّان إلى الماسح عبر إدخالات provider: itviec / provider: careerviet: ITviec (itviec.com، تقني) وCareerViet (careerviet.vn، تغطية واسعة). كلاهما كاشطا HTML بلا رموز، مع تثبيت المضيف ضد SSRF، وHTTPS فقط، وتحمّل الأخطاء لكل شركة، وحدود صفحات. صار السجل يحوي 85 مصدرًا (80 EN + 5 RU)، وALL_ADAPTERS 80 — ويظهر كلاهما في مرشّح المصدر بـ #/scan.

ملاحظات

  • مطابقة career-ops 1.30.0 (parentVersion 1.29.0 → 1.30.0). غير مَنقول (CLI/مجتمع فقط أو مغطّى): Hired Wall و/calibrate وclean-markers وحزم القوالب وdoctor وpdf/LaTeX وأدوات eval/tracker/scan التي لا يستدعيها web-ui؛ إصلاح _html-to-text وroleFuzzyMatch كانا موجودين؛ بادئة title-filter stem: مؤجَّلة. مجموعة الاختبارات: 2818.

[1.225.1] — 2026-08-27

إصلاح — لافتة شعارات المزوّدين تظهر الآن في README ودليل المساعدة.

إصلاح

  • لافتة v1.225.0 لم تكن تظهر على GitHub: ملف images/providers.svg المستقل كان به font-family غير صالح (علامات اقتباس مزدوجة متداخلة) يرفضه GitHub، و<img> استخدم src نسبيًا (HTML <img> الخام لا يحلّ المسارات النسبية على GitHub)، وكتلة <p> كانت تفتقر لسطر فارغ ختامي فاندمجت مع السطر التالي. صار الآن يشير إلى images/providers.png مُصيَّر عبر رابط مطلق، مع فصل بأسطر فارغة؛ كما صُحِّح الـ SVG إلى XML صالح.

ملاحظات

  • توثيق فقط؛ بلا تغيير في الكود أو الاختبارات. واجهة العرض في cvstart.org وبطاقات التطبيق كانت صحيحة أصلًا (SVG مضمّن، لا هذا الملف). 2784

[1.225.0] — 2026-08-27

أُضيف — لافتة شعارات المزوّدين في README ودليل المساعدة.

أُضيف

  • لافتة شعارات المزوّدين. ملف images/providers.svg واحد قائم بذاته — 18 مزوّد LLM، بـالشعار الحقيقي للعلامة (simple-icons، CC0) للأحد عشر الذين ينشرون أيقونة، وبحرف مختصر بلون العلامة للسبعة الباقين — يتصدّر الآن README (×17) ودليل المساعدة داخل التطبيق. (التطبيق نفسه يرسم بطاقات الشعار الحقيقي؛ واللافتة تُعرض على GitHub ويزيلها في التطبيق المُصيِّر الآمن من XSS.)

ملاحظات

  • توثيق فقط — بلا تغيير في الكود أو الاختبارات؛ الـ SVG مضمّن (بلا مورد بعيد). مصادر الفحص دون تغيير: 83. مجموعة الاختبارات: 2784.

[1.224.0] — 2026-08-26

تغيير — بطاقات مزوّدي LLM تعرض الآن شعارات العلامات الحقيقية.

تغيير

  • شعارات المزوّدين الحقيقية. بطاقات المزوّد — حقول مفتاح API في الإعدادات، صفوف الاستخدام، شريحة لوحة التحكم، ملخّص “نشط” في الإعدادات، نتيجة تقييم ⚡ ولافتة الترحيب — تعرض الآن الشعار الحقيقي للعلامة للمزوّدين الـ 11 الذين ينشرون أيقونة مفتوحة المصدر (Anthropic · Gemini · OpenAI · Qwen · OpenRouter · GitHub · DeepSeek · Kimi · MiniMax · Mistral · Ollama؛ ملفات SVG أحادية المسار من simple-icons، CC0). أما السبعة بلا أيقونة منشورة (Hermes · GLM/Z.ai · Grok · Together · Fireworks · BytePlus Ark · Volcengine Ark) فتُبقي الحرف المختصر بلون العلامة. لا يزال آمنًا مع CSP بحكم البناء: مسار الشعار ثابت ساكن مضمّن — بلا مورد بعيد وبلا innerHTML.

ملاحظات

  • لا تغيير في المسار أو السلوك؛ نفس واجهة ProviderLogo.el(slug). كبر حجم provider-logo.js (مسارات الشعارات المضمّنة). مصادر الفحص دون تغيير: 83. مجموعة الاختبارات: 2784.

[1.223.0] — 2026-08-26

أُصلح — تقييم ⚡ مباشر ناجح كان يُلوَّن كخطأ؛ وأيضًا ثبّت coworker الخاص بـ OpenWorker مباشرةً من رابط GitHub أو .zip.

أُصلح

  • لون شارة نتيجة تقييم ⚡. لم يعد التقييم المباشر الناجح يُلوَّن كفشل. المزوّدون داخل العملية (OpenRouter، DeepSeek، …) لا يُعيدون code خروج عملية فرعية، فكان code === 0 الصارم خطأً وكل نجاح يظهر أحمر (رغم عرض “exit 0”). صار اللون يتبع النتيجة — العملية الفرعية التي تخرج بكود غير صفري وحدها تُلوَّن بالأحمر؛ ونتيجة مباشرة بلا كود هي badge-ok، ولاحقة “· exit N” تظهر فقط عند وجود كود خروج حقيقي.

تغيير

  • ثبّت coworker الخاص بـ OpenWorker من رابط GitHub / .zip. توثيق الـ coworker يصف الآن مثبّت الأمر الواحد (curl … | bash، خامل التكرار — يعيد استخدام career-ops / web-ui الموجود) وطرق التثبيت الثلاث في OpenWorker (رابط GitHub · .zip · استيراد ملف واحد) في docs/integrations/openworker.md والمساعدة داخل التطبيق §32 (×17) وREADME.

ملاحظات

  • أُغلقت ثغرة تغطية قديمة: صار لمصدر Recruitee اختبار شامل لإصلاح v1.214.2 للقوس المقتبس (> داخل سمة وسم يجب ألا يتسرّب إلى الوصف). مصادر الفحص دون تغيير: 83. مجموعة الاختبارات: 2783.

[1.222.0] — 2026-08-26

تغيير — تلميحات حقول المزوّدين الموسّعين (DeepSeek … Volcengine Ark) صارت مترجمة إلى الـ 17 لغة كاملة.

تغيير

  • ترجمة تلميحات حقول المزوّدين. كل config.<slug>Hint للمزوّدين الـ 11 المتوافقين مع OpenAI المضافين في v1.216.0–v1.217.0 (DeepSeek · GLM/Z.ai · Kimi · MiniMax · Mistral · Grok · Together · Fireworks · Ollama · BytePlus Ark · Volcengine Ark) — تلميحات مفتاح API والنموذج وعنوان URL الأساسي ضمن الإعدادات ← مفاتيح API — صار مترجَمًا إلى الـ 17 لغة بدلاً من العودة إلى الإنجليزية. تُحفظ روابط التسجيل ومعرّفات النماذج وعلامة ⚡ التقييم المباشر كما هي.

ملاحظات

  • لا تغيير في السلوك — hintFallback الإنجليزي في field-specs.js لا يزال يحمي المفتاح المفقود، واختبار عقد جديد يؤكد أن كل hintKey في واصفات الحقول يُحَلّ في الـ 17 لغة. مصادر الفحص دون تغيير: 83. مجموعة الاختبارات: 2779.

[1.221.0] — 2026-08-26

الأمان — دفاع متعمّق ضد إعادة ربط DNS في مسار جلب الماسح.

الأمان

  • صارت نواة HTTP للماسح (fetchJson / fetchText في server/lib/http-json.mjs) تُحَلّ مضيف كل مصدر على مسار الشبكة الحقيقي وترفض الاتصال إذا حُلّ إلى عنوان خاص أو محلي أو link-local أو CGNAT أو بيانات وصفية سحابية — مُغلِقةً ناقل إعادة ربط DNS. مضيفات الماسح مثبَّتة أصلًا على نطاقات عامة، فهذا دفاع متعمّق؛ ومسار روابط المستخدم كان لديه تثبيت اتصال أقوى (safe-fetch.mjs). يعمل الحارس فقط على وسيلة fetch الحقيقية — ولا يُحَلّ أبدًا fetchImpl المحقون في الاختبارات.

ملاحظات

  • لا تغيير في السلوك لمسحٍ سليم. مصادر المسح دون تغيير: 83. الاختبارات: 2775.

[1.220.0] — 2026-08-26

أُضيف — امسح عدّة فئات من Get on Board من مُدخل واحد.

أُضيف

  • يمكن الآن لمُدخل getonbrd تعيين categories: [programming, operations-management, machine-learning-ai] (أو category: واحدة) بدل تغذية programming وحدها؛ وتُزال تكرارات الإعلانات حسب الرابط عبر الفئات، بحدٍّ أقصى 12. تبقى المُدخلات الحالية كما هي (الافتراضي لا يزال programming).

ملاحظات

  • من جهة الخادم فقط؛ تثبيت مضيف SSRF (www.getonbrd.com) وredirect:'error' بلا تغيير، ويُرفَض السلَج غير الصالح للفئة قبل أي طلب. مصادر المسح دون تغيير: 83. الاختبارات: 2771.

[1.219.0] — 2026-08-26

أُضيف — Torre ينضمّ إلى الماسح، وصارت كل تسمية مزوّد صحيحة.

أُضيف

  • Torre (provider: torre) — سوق المواهب عبر أمريكا اللاتينية من torre.ai، بحثٌ عام بلا مصادقة يحمل وظائف عن بُعد لا تصل إلى Greenhouse/Lever/Ashby؛ طلبٌ واحد محدود لكل مُدخل. صار السجل يضمّ 83 مصدرًا (78 إنجليزيًا + 5 روسية). كما تظهر شارة المزوّد النشط على لافتة الترحيب.

أُصلح

  • كان مسح a16z Speedrun يتوقّف مبكرًا على لوحةٍ تتبدّل (تُعامَل صفحة قصيرة في منتصف المسح كأنها الأخيرة، فتُقتطع إلى 149 من ~300)؛ وصار الآن يثق بـ total_pages من التغذية. وكانت لافتة الترحيب تستنتج المزوّد من خريطة من 4 مدخلات (وتعرض السلَج الخام للـ14 الباقين) — وصارت تستخدم تسمية+شارة الـ18؛ وملاحظة المزوّدين في الإعدادات تنتقل من 7 إلى 18.

ملاحظات

  • خيار تعدّد الفئات لكل مُدخل في Get on Board غير موصولٍ هنا بعد (يحتاج إعادة هيكلة على مستوى المصدر). الاختبارات: 2768.

[1.218.0] — 2026-08-26

أُضيف — شارات المزوّدين (والأسماء الصحيحة) في كل موضع يظهر فيه مزوّد.

أُضيف

  • شارات المزوّدين على شريحة اللوحة وملخّص «نشط» في الإعدادات وترويسة نتيجة التقييم ⚡.

أُصلح

  • تسميات قديمة: كانت ثلاث شاشات تستنتج الاسم من خريطة من 5 مدخلات (وكانت نتيجة التقييم تسمّي أي مزوّد غير Anthropic بـ«Gemini»)؛ وصارت تستخدم ProviderStatus.label (18). كان عدّاد المفاتيح يعرض «/ 7» → 18. اكتسبت قائمة slugs في تلميح LLM_PROVIDER القيمتين ark / arkcn في اللغات الـ17 جميعها.

ملاحظات

  • من جهة العميل فقط، وآمن لـ CSP. بلا تغيير في الخادم. مصادر المسح: 82. الاختبارات: 2758.

[1.217.1] — 2026-08-26

تعزيز الاختبارات — تغطية نقاط النهاية لكامل قائمة المزوّدين.

أُضيف

  • اختبار run<Provider> مُعَلَّم يتحقّق من نقطة النهاية والنموذج الافتراضي ومصادقة Bearer لـ Kimi وMiniMax وMistral وFireworks.

[1.217.0] — 2026-08-26

أُضيف — Ark ينضمّ إلى قائمة LLM (18 مزوّدًا). صار BytePlus Ark وVolcengine Ark — نماذج Doubao من ByteDance — مزوّدَي تقييم ⚡ حيّ عبر نفس النواة المتوافقة مع OpenAI وعنوان URL أساسي حسب المنطقة.

أُضيف

  • BytePlus Ark (ARK_API_KEY، دولي) وVolcengine Ark (ARK_CN_API_KEY، الصين) — Chat Completions متوافق مع OpenAI عبر runOpenAICompatible(). عيّن ARK_MODEL / ARK_CN_MODEL (اسم نموذج Doubao أو مُعرّف نقطتك ep-…)؛ ويبدّل ARK_BASE_URL / ARK_CN_BASE_URL المنطقة. يدخل كلاهما في ترتيب auto (أخيرًا) بشارته في الإعدادات والاستخدام. تغطّي واجهة الويب الآن 18 مزوّدًا حيًّا.

ملاحظات

  • المسار نفسه: إعدادات موثوقة + فحص مخطّط http(s) كبقية المزوّدين (ولا يمرّ عبر مدقّق SSRF لعناوين الوظائف). مصادر المسح دون تغيير: 82. الاختبارات: 2755.

[1.216.0] — 2026-08-26

أُضيف — تسعة مزوّدي LLM إضافيون، لكلٍّ منهم بلاطة علامته. يمكن الآن تشغيل تقييماتك ⚡ الحيّة عبر DeepSeek وGLM (Z.ai) وKimi (Moonshot) وMiniMax وMistral وGrok (xAI) وTogether وFireworks أو Ollama محلي بالكامل — على بُعد مفتاح واحد، ويظهر لكلٍّ شارة بجوار حقله في الإعدادات.

أُضيف

  • تسعة مزوّدين متوافقين مع OpenAI. DeepSeek وGLM (Z.ai) وKimi (Moonshot) وMiniMax وMistral وGrok (xAI) وTogether AI وFireworks AI وOllama (محلي بالكامل، بلا مفتاح). عيّن مفتاحًا — أو OLLAMA_BASE_URL لـ Ollama — في الإعدادات لينضمّ إلى ترتيب auto بعد Hermes؛ ثبّت أيًّا منها عبر LLM_PROVIDER. كما يستضيف Together نموذج Inkling من Thinking Machines (thinkingmachines/Inkling). تغطّي واجهة الويب الآن 16 مزوّدًا حيًّا.
  • شارات المزوّدين. بلاطة حرفٍ أوّل بلون العلامة وآمنة لـ CSP تُميّز كل مزوّد بجوار حقل مفتاحه في الإعدادات وفي صفّه بصفحة الاستخدام — SVG مضمّن، بلا شعارات خارجية، ولا يغادر شيء جهازك.

ملاحظات

  • عناوين URL الأساسية للمزوّدين إعداداتٌ موثوقة تمرّ عبر runOpenAICompatible() مع فحص مخطّط http(s) (يُسمح بالحلقة المحلية لـ Ollama)، ولا تمرّ أبدًا عبر مدقّق SSRF لعناوين الوظائف. مصادر المسح دون تغيير: 82. الاختبارات: 2752.

[1.215.0] — 2026-08-26

أُضيف — شغِّل بحثك عن العمل بالكامل من OpenWorker. يقود coworker جديد هذا المسار من تطبيق coworker الذكي مفتوح المصدر لأندرو نغ، وصارت المساعدة داخل التطبيق تغطّيه ليتمكّن مساعد «اسأل الوثائق» من إرشادك.

أُضيف

  • coworker الخاص بـ OpenWorker. coworker بلا كود لـ OpenWorker — Fighter90/career-ops-coworker — يُجري البحث عن العمل بالكامل (مسح، تقييم، تفصيل، تتبُّع، متابعة) ويمكنه تشغيل هذه اللوحة، كلّه على سطح مكتبك. إنه شخصية من ملف واحد تُشحَن بـ17 لغة، ومُتحقَّق من قابليتها للتثبيت بمُحمِّل OpenWorker نفسه.
  • مساعدة §32 “Run it from OpenWorker” (كل اللغات الـ17) توثّقه، ليتمكّن مساعد اسأل الوثائق داخل التطبيق وزر المساعدة العائم من الإجابة عنه. ومربوط أيضًا من تذييل صفحة الهبوط، وREADME، والويكي، وdocs/integrations/openworker.md.

ملاحظات

  • وثائق + موقع فقط — دون تغيير في كود التطبيق. مصادر المسح دون تغيير عند 82. مجموعة الاختبارات: 2742.

[1.214.2] — 2026-08-25

أُصلح — لم يعد خط أنابيب HTML→نص المشترك (أوصاف Greenhouse / Recruitee) يُسرّب سمات الوسم إلى الوصف حين تحتوي قيمة سمة على الرمز >.

أُصلح

  • أوصاف HTML التي بها > داخل سمة تُنظَّف بنظافة. كان مُزيل الوسوم خلف أوصاف Greenhouse وRecruitee يستخدم مطابقة ساذجة تتوقف عند أول >، فوسمٌ مثل <a title="salary > 100k"> كان يترك نص السمة اللاحق (100k">) في الوصف. الآن يحترم قيم السمات المُقتبَسة (مفردة أو مزدوجة)، ويزيل الترميز قبل فكّ ترميز الكيانات (كي لا يصير اقتباس مُرمَّز داخل سمة فاصلاً زائفًا)، ولا يزال يحفظ <> الحرفي في النص العادي.

ملاحظات

  • مجموعة الاختبارات: 2742. مصادر المسح دون تغيير عند 82. لا جديد آخر من الأصل هذه الجولة: بقية التغييرات تُمرَّر للقراءة فقط (إصلاحات follow-up / weekly-digest / rejection-latency / salary-gap لا تحتاج تغييرًا في web-ui) أو ليست أسطح web-ui (إضافة خارجية X/Xquik بمفتاح المستخدم، إصلاحات CLI/verify). البنود الاختيارية — أوصاف تفاصيل SmartRecruiters، تخطّي المُجمِّعات في detect-reposts، بادئات العنوان stem: / word:، تحصين DNS-rebinding — تبقى في الانتظار.

[1.214.1] — 2026-08-25

أُصلح — صارت أوصاف Ashby تُقتطَع إلى الطول نفسه مثل Greenhouse وRecruitee، فيتصرّف مُرشِّح المحتوى بالطريقة نفسها على اللوحات الثلاث، ولا تستطيع لوحة واحدة تضخيم ذاكرة المسح المؤقتة.

أُصلح

  • صارت أوصاف Ashby تلتزم بحدّ الطول نفسه كبقية اللوحات. كانت v1.214.0 تُغذّي متن وظيفة Ashby إلى مُرشِّح المحتوى، لكن — خلافًا لـ Greenhouse وRecruitee اللتين تقتطعان عند 4000 حرف — كان متن Ashby يُربَط مباشرة بلا حدّ (بعضه أطول بأربعة أضعاف). فقد تُطابِق الكلمة نفسها في content_filter على Ashby دون Greenhouse بحسب موضعها في النص فقط، وقد تُضيف لوحة واحدة مئات الكيلوبايتات إلى ذاكرة المسح التي تحمّلها #/scan. الآن تُقتطَع Ashby إلى الـ4000 حرف نفسها (نصّها عاديّ سلفًا، فيُقتطَع فقط ولا تُزال منه الوسوم).

ملاحظات

  • مجموعة الاختبارات: 2738. مصادر المسح دون تغيير عند 82.

[1.214.0] — 2026-08-25

أُصلح — صارت أوصاف وظائف Greenhouse وAshby وRecruitee تُغذّي مُرشِّح المحتوى، فتُطابق مُرشِّحات الكلمات / الدولة / التأشيرة تلك اللوحات فعلاً؛ ولم يعد “Associate <لقب رفيع>” (بما فيها الرتب الأكاديمية) يُصنَّف كمستوى مبتدئ.

أُصلح

  • مُرشِّحات المحتوى تعمل الآن على Greenhouse وAshby وRecruitee. صارت هذه اللوحات تحمل نص كل وظيفة كاملاً كوصف لها، فيُطابق content_filter (أو مُرشِّح كلمات الدولة / التأشيرة) متن الوظيفة بدل تمرير اللوحة بلا فحص. تُرسِل Ashby وRecruitee المتن في حمولة القائمة مجانًا؛ وGreenhouse كان يفعل ذلك سلفًا. الثلاثة تُنقَّى عبر خط أنابيب HTML→نص مشترك واحد.
  • “Associate Director” / “Associate Professor” رفيع، لا مبتدئ. كان مُصنِّف الأقدمية (يستخدمه skip_tiers:) يعامل كلمة “associate” كعلامة مستوى مبتدئ حتى حين تسبق لقبًا رفيعًا. صار يقرأ “Associate <اسم رفيع>” — بما فيها الرتب الأكاديمية (Associate Professor، Associate Dean) — كرفيع، مع إبقاء المتغيرات المبتدئة الحقيقية (Associate Attorney، Associate Editor) عند المستوى المبتدئ، ولا يزال يصنّف “Intern, Associate Dean” المتصدِّر كتدريب.

ملاحظات

  • مجموعة الاختبارات: 2736. مصادر المسح دون تغيير عند 82. في قائمة الانتظار لاحقًا: إثراء أوصاف SmartRecruiters الاختياري (طلب تفاصيل لكل وظيفة)، وتخطّي لوحات aggregator: true في detect-reposts، وبادئات العنوان بالكلمة المُثبَّتة stem: / word: — كل منها إعداد اختياري لا تغييرًا للسلوك الافتراضي. ولا يزال تحصين DNS-rebinding في الانتظار لإصداره الأمني الخاص.

[1.213.0] — 2026-08-22

أُضيف — MyCareersFuture، بنك الوظائف الوطني في سنغافورة، كمصدر مسح. أُصلح — صارت وظائف Greenhouse تحمل نصها الكامل لتعمل مُرشِّحات المحتوى، ولم تعد وظائف Ashby عن بُعد تختفي خلف موقع «المدينة فقط».

أُضيف

  • MyCareersFuture (سنغافورة) (mycareersfuture.gov.sg) — مصدر مسح جديد بلا رموز لبنك الوظائف العام الوطني في سنغافورة، الذي تديره Workforce Singapore. اخترها من مُرشِّح المصدر في #/scan، أو أضِف شركة بـ provider: mycareersfuture مع قائمة keywords اختيارية (وإلا تعود إلى الأدوار المستهدَفة في ملفك، مثل Job Bank). تقرأ واجهة البحث العامة، مثبَّتة على المضيف، بلا مفتاح.

أُصلح

  • صار بالإمكان تصفية وظائف Greenhouse بالمحتوى. تُجلب لوحات Greenhouse مع نص الوظيفة الكامل، مفكوكًا إلى نص عادي كوصف للوظيفة — فصار content_filter (أو مُرشِّح كلمات الدولة/التأشيرة) الذي يقرأ الوصف يطابق فعلاً وظائف Greenhouse بدل تمريرها بلا فحص.
  • لم تعد وظائف Ashby عن بُعد تُسقَط بمُرشِّح المدينة. يحفظ Ashby نمط العمل (Remote/Hybrid/Onsite) منفصلاً عن مدينة المكتب، فوظيفة عن بُعد بالكامل كانت تُقرأ مثلاً «San Francisco» — ومُرشِّح موقع يحجب تلك المدينة كان يُخفي وظيفة يمكنك أخذها. تُضاف الآن «Remote» إلى الموقع حين تكون الوظيفة عن بُعد، ويتغلَّب workplaceType على راية isRemote القديمة كي لا تُصنَّف وظيفة هجينة مرتبطة بالمكتب خطأً.

ملاحظات

  • مصادر المسح: 82 (77 إنجليزية + 5 روسية). مجموعة الاختبارات: 2724. تحصين ضد DNS-rebinding (التحقق من العنوان المُحلَّل للمضيف قبل الاتصال) مؤجَّل لإصدار مخصَّص — يحتاج تصميمًا خاصًا بـ web-ui لا نقلاً مباشرًا.

[1.212.1] — 2026-08-21

أُصلح — كانت صفحة cvstart.org تعدّ مصادر الوظائف في الماسح أقل من الواقع (تعرض 80 وتُسقط Job Bank (كندا))؛ صارت تطابق 81 في التطبيق مجددًا، ويفشل بناء الموقع بصوتٍ عالٍ إن اختلف الرقمان.

أُصلح

  • عدّاد «مصادر الوظائف» في الصفحة عاد متزامنًا مع التطبيق. بعد v1.212.0، كان cvstart.org يعرض 80 لوحة وينقصه رقاقة Job Bank (كندا) الجديدة، بينما كان التطبيق وقائمة المسح المنسدلة ودليل المساعدة تسرد جميعها 81. تبني الصفحة قائمتها بتحميل سجلّ الماسح الحيّ، وقد فشل مصدر في التحميل ضمن ذلك البناء بسبب طريقة جلبه لاعتمادية YAML — فأُسقط بصمت. الآن يحمّل Job Bank تلك الاعتمادية بكسل، تمامًا كما يفعل بقية التطبيق وقت المسح، فيظهر دائمًا.
  • بناء الموقع يرفض الآن نشر عدد مصادر غير متطابق. إذا عدّ السجلّ مصادر أقل مما هو موجود على القرص (سمة مصدر فشل تحميله)، يفشل البناء برسالة واضحة بدل نشر الرقم الخاطئ بصمت.

ملاحظات

  • سلوك التطبيق لم يتغيّر — كان لدى الماسح دائمًا كل الـ81 مصدرًا؛ تأثّرت صفحة الهبوط فقط. مصادر المسح: 81 (76 إنجليزية + 5 روسية) — دون تغيير. مجموعة الاختبارات: 2687.

[1.212.0] — 2026-08-21

أُضيف — Job Bank (كندا)، لوحة الوظائف الوطنية الفيدرالية. أُزيل — EchoJobs (صار موجزه محجوبًا بمكافحة الروبوتات). أُصلح — لوحات Consider تُعيد النتائج مجددًا، ولم تعد وظائف Lever متعددة المواقع تُخفي نصف مواقعها.

أُضيف

  • Job Bank (كندا) (jobbank.gc.ca) — مصدر مسح جديد بلا رموز لخدمة التوظيف الوطنية الفيدرالية في كندا، وهي لوحة عالية الحجم لا يغطيها أي مُجمِّع جيدًا. اخترها من مُرشِّح المصدر في #/scan، أو أضِف شركة بـ provider: jobbankca مع قائمة keywords اختيارية (وإلا تعود إلى الأدوار المستهدَفة في ملفك). تقرأ موجز ATOM العام، مثبَّتة على المضيف، بلا مفتاح.

أُزيل

  • EchoJobs — أُحيل. صار موجزه العام خلف حماية مكافحة الروبوتات ولا يُعيد شيئًا، فإبقاؤه كان يهدر خانة مسح فقط.

أُصلح

  • لوحات Consider تُعيد النتائج مجددًا. صار Consider يتطلب مصافحة مجهولة (طلب GET يزرع كوكي جلسة + رمز CSRF) قبل قبول البحث؛ وبدونها كان الطلب يُرفض بصمت وتبدو اللوحة فارغة.
  • وظائف Lever متعددة المواقع لم تعد تُخفي نصف مواقعها. يضع Lever مدينة رئيسية واحدة في location والبقية في allLocations؛ فقراءة الرئيسية وحدها كانت تُظهر وظيفة مفتوحة في برشلونة ومونتيفيديو وكأنها في برشلونة فقط (فتُسقَط خطأً بمُرشِّح المواقع). الآن تُدمَج الاثنتان.

ملاحظات

  • وتيرة أهدأ بين الصفحات (250 مللي ثانية بدل 150) على اللوحات المُصفَّحة، تأدُّبًا مع مواقع الوظائف أحادية المضيف. مصادر المسح: 81 (76 إنجليزية + 5 روسية) — دون تغيير (Job Bank يدخل، EchoJobs يخرج). مجموعة الاختبارات: 2685.

[1.211.0] — 2026-08-19

أُضيف — Yourator، لوحة وظائف تقنية تايوانية. أُصلح — الكيانات المُشكَّلة في العناوين/أسماء الشركات تُفكّ الآن في كل مكان، ولم تعد الشركة التي في اسمها حرف مُشكَّل تُوسَم خطأً.

أُضيف

  • Yourator (yourator.co) — مصدر مسح جديد بلا رموز لسوق الوظائف التقنية والرقمية في تايوان. اخترها من مُرشِّح المصدر في #/scan، أو أضِف شركة بـ provider: yourator. تقرأ واجهة JSON العامة (بلا مفتاح، بلا متصفح)، وتجتاز كل صفحات اللوحة، وتُخرِج رابط صاحب العمل الحقيقي لكل إعلان (نظام التوظيف الخاص به) بعد إزالة معاملات التتبّع.

أُصلح

  • الكيانات المُسمّاة المُشكَّلة تُفكّ الآن في كل مكان. اكتسب مُفكِّك HTML المشترك حروف Latin-1 (&eacute; → é، &ccedil; → ç، …)، فلم تعد لوحة أوروبية تكتب D&eacute;veloppeur أو Fran&ccedil;ais تترك ذلك النص في عنوان أو المتعقِّب أو مستند مُولَّد. (تبقى الأحرف الكبيرة كبيرة — &Eacute; هو É لا é — ويُحلّ بحثٌ مثل &constructor; إلى نفسه.)
  • لم تعد الشركة التي في اسمها حرف مُشكَّل تُوسَم خطأً لكونها على نطاقها الخاص. «Işık» تُطوى الآن إلى «isik» وتطابق isik.com.tr؛ و«Société Générale» تطابق societegenerale.com. كان الفحص القديم يحذف الأحرف المُشكَّلة بدل طيّها إلى أساسها ASCII.

ملاحظات

  • مصادر المسح: 81 (76 إنجليزية + 5 روسية). مجموعة الاختبارات: 2667.

[1.210.1] — 2026-08-19

أُصلح — عناوين وظائف Habr Career وأسماء الشركات التي تحوي «&» أو علامات اقتباس لم تعد تصل مشوَّهة.

أُصلح

  • صار مصدر Habr Career يفكّ ترميز كيانات HTML في العنوان واسم الشركة قبل تمريرهما. تصل البطاقات المُصيَّرة على الخادم مُهرَّبة (“Changellenge >>”، “Demand Forecasting & Inventory Optimization”، “ООО "М-ТЕХ"”)، فكان «&» غير المفكوك يُخفِق بصمت في مُرشِّح العنوان «&» الخاص بك — وهو العَرَض ذاته الذي أغلقه الإصدار السابق على خمس لوحات أخرى — وتصل أسماء الشركات مشوَّهة إلى المتعقِّب والتقارير. اكتمل الآن فكّ ترميز الكيانات عبر المصادر الستة المتأثرة.

ملاحظات

  • مجموعة الاختبارات: 2644.

[1.210.0] — 2026-08-19

أُضيف — Senjob، أول لوحة وظائف أفريقية للماسح (السنغال)؛ ومطابقة عناوين أدق على خمس لوحات إضافية.

أُضيف

  • Senjob (senjob.com) — مصدر مسح جديد بلا رموز للسنغال، وأول لوحة أفريقية للماسح. اخترها من مُرشِّح المصدر في #/scan، أو أضِف شركة بـ provider: senjob. تقرأ القائمة العامة عبر HTTP عادي (بلا مفتاح، بلا متصفح)، وتثبِّت كل طلب على senjob.com، ولأنها تحلّل HTML فإنها تعامل قائمةً توقّفت فجأة عن إعطاء أي نتيجة كلوحة معطوبة (خطأ ظاهر) لا كبلد بلا وظائف.

أُصلح

  • العناوين التي تحوي «&» لم تعد تُسقِط وظائف على خمس لوحات — في beesite وCornerstone (csod) وHacker News «Who is hiring» وPhenom وTKMS تصل العناوين مُرمَّزة بكيانات HTML، فكان «&» المُهرَّب في مسمًّى مثل “R&D Engineer” يُخفِق في كلمتك المفتاحية “r&d” فتختفي الوظيفة بصمت (ولا يعمل رفض “sales & marketing” أيضًا). الآن تُفكَّك شفرة العناوين — ومواقع Phenom — قبل الترشيح.

ملاحظات

  • مصادر المسح: 80 (75 إنجليزية + 5 روسية). مجموعة الاختبارات: 2643.

[1.209.0] — 2026-08-17

أُضيف — تغطّي المساعدة داخل التطبيق الآن كيفية تسجيل نتيجة تقديم، و«اسأل التوثيق» يمكن أن يرشدك إليها.

أُضيف

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

ملاحظات

  • صار كل حزمة مساعدة الآن 31 H2 / 119 H3 (بعد أن كانت 118)؛ ورُفِعت حُرّاس التطابق لتوافقها. توثيق فقط — دون تغيير في الكود أو السلوك. المجموعة: 2625.

[1.208.2] — 2026-08-16

أُصلح — على الهاتف لم يعد زرَّا الإشعارات والسمة يجلسان فوق مربع البحث.

أُصلح

  • منعت v1.208.1 أزرار الشريط العلوي من التداخل مع عنوان الصفحة، لكن على شاشة ضيّقة — وإن لم تكن الأضيق — وخصوصًا في اللغات ذات التسميات الطويلة، ظلّ الشريط كلّه ينحشر في صفّ واحد، فقد يجلس زرّا 🔔 و🌙 فوق مربع البحث. الآن تنزل أزرار الإجراءات (الإشعارات، السمة، التشخيص، فتح Scan) دائمًا إلى صفّها الثاني بعرض كامل على الهاتف، فيبقى مربع البحث مقروءًا بالكامل ولا شيء يتداخل.

ملاحظات

  • على الهاتف تنتقل أزرار إجراءات الشريط إلى صفّ ثانٍ بعرض كامل، ما يزيل الشريط الهشّ «الصفّ شبه الممتلئ» حيث كان التخطيط يوزّع الفراغ السالب المتبقّي كتداخل. حارس Playwright يعيد الآن إنتاج المُطلِق الدقيق — لغة ذات تسميات طويلة عبر نطاق 565–640px — ويؤكّد أنّ عناصر الشريط لا تتشارك البكسلات أبدًا. المجموعة: 2621.

[1.208.1] — 2026-08-16

أُصلح — على الهاتف لم تعد أزرار الشريط العلوي تتداخل مع الصفحة.

أُصلح

  • في v1.208.0 كانت أزرار الشريط العلوي (التشخيص، فتح Scan، الإشعارات، السمة) تلتفّ إلى صفّ ثانٍ على الشاشات الضيّقة، لكن الشريط ظلّ بارتفاع ثابت، فطفح الصفّ الملتفّ وجلس فوق عنوان الصفحة. الآن يتمدّد الشريط ليستوعب صفوفه ويتدفّق المحتوى أسفله.

ملاحظات

  • صار height الثابت للشريط min-height، فيتمدّد مع محتواه بأي عرض (سطح المكتب دون تغيير). حارس Playwright يتحقّق الآن أيضًا من ألّا يطفح الشريط فوق الصفحة. المجموعة: 2621.

[1.208.0] — 2026-08-16

أُصلح — صار التطبيق يتّسع لشاشة الهاتف: لا تمرير جانبي بعد الآن.

أُصلح

  • على الشاشة الضيّقة كان التطبيق كلّه ينزلق جانبيًا — الشريط العلوي والجداول ومقالات المساعدة وألسنة الإعدادات تتجاوز الحافة اليمنى. الآن تتّسع كل صفحة لأي عرض: أزرار الشريط العلوي تنتقل إلى صفّ ثانٍ، والجداول وكتل الشيفرة العريضة تتمرّر داخل صندوقها، والمساعدة تضع الفهرس فوق المقال، وصفوف الأزرار/الألسنة تلتفّ، والمسارات أو الروابط الطويلة تُكسَر بدل أن تمدّ الصفحة.

ملاحظات

  • كان السبب فخّ flex/grid الكلاسيكي min-width: auto إضافةً إلى بضعة عناصر عريضة غير مُغلَّفة؛ عولج بـmin-width: 0 على عناصر الشبكة، وoverflow-wrap على markdown/العناوين، وجدول markdown قابل للتمرير، وتكديس شبكة المساعدة عند نقطة الهاتف. حارس Playwright يتحقّق من صفر فيضان أفقي عند 375 بكسل على المسارات الرئيسية. tests/playwright-smoke.mjs. المجموعة: 2621.

[1.207.2] — 2026-08-16

أُصلح — لم تعد خطط الذكاء الاصطناعي وملفات التوجيه المهني تظهر ككتلة شيفرة خام.

أُصلح

  • بعض النماذج تُغلِّف الإجابة كاملةً داخل سياج شيفرة markdown … . حين يحدث ذلك كانت خطة التطوير وملف التوجيه تظهران ككتلة شيفرة أحادية التباعد بدل مستند بعناوين وقوائم. الآن يُزال السياج المُغلِّف — فقط حين يُحيط بالإجابة كلها ويكون اللغة صراحةً markdown/md، فتبقى إجابة شيفرة حقيقية بـpython/js/``` بلا لغة كما هي.

ملاحظات

  • عولج مرة واحدة في خطوة تنظيف LLM المشتركة (cleanLlmMarkdown)، فتستفيد كل مسارات الذكاء الاصطناعي، وتبقى كتل الشيفرة الداخلية داخل الإجابة المُغلَّفة. tests/llm-output.test.mjs (+3). المجموعة: 2621.

[1.207.1] — 2026-08-16

أُصلح — لم تعد صفحة الهبوط تفيض جانبيًا على الهواتف الصغيرة.

أُصلح

  • على هاتف ضيّق كان القسم الرئيسي — العنوان وسطر المقدمة وطرفية التثبيت — قد يُقتطع عند الحافة اليمنى لأن أمر التثبيت الطويل وأعمدة التخطيط لا تتقلّص لتناسب الشاشة. أصبحت الآن تتّسع لأي عرض؛ ويتمرّر أمر التثبيت داخل صندوق طرفيته.

ملاحظات

  • كما جرى تقوية فحص E2E غير مستقر كان قد يفشل بسبب 404 عابر لمورد: صار يتجاهل ضجيج الشبكة غير الضار (favicon / الاتصال / مورد فاشل) مثل الفحوص المجاورة، مع الاستمرار في التقاط أخطاء السكربت الحقيقية. لم يتغيّر سلوك التطبيق. المجموعة: 2618.

[1.207.0] — 2026-08-15

أُضيف — سجّل نتيجة التقديم مباشرةً من المتتبّع.

أُضيف

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

ملاحظات

  • المسار الجديد POST /api/outcome يُرحّل أداة النتائج: dryRun:true معاينة للقراءة فقط (يطابق الصفّ، يذكر الحالة الناتجة، لا يكتب شيئًا)؛ والاستدعاء الحقيقي يسجّلها. أمان الكتابة: نوع النتيجة محصور في المجموعة المعروفة، وكل حقل نصّي يُرفض إن حوى محارف تحكّم قبل الاستدعاء (وسائط كمصفوفة، spawn — بلا صدفة). tests/outcome-route.test.mjs. المجموعة: 2618.

[1.206.0] — 2026-08-15

التوثيق — يغطّي دليل المساعدة داخل التطبيق الآن الميزات الخمس الأحدث بجميع اللغات الـ17.

أُضيف

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

ملاحظات

  • بنية الدليل تنمو إلى 31 عنوان H2 / 118 عنوان H3، مع ضمان التكافؤ لكل لغة. حُدِّثت الوثائق المرجعية: يوثّق docs/architecture/API.md المسارات الخمسة لهذه الميزات، وعدّادات المسارات/الإصدار في CLAUDE.md وdocs/sdd/CONVENTIONS.md مُحدَّثة (36 وحدة مسار). المجموعة: 2610.

[1.205.0] — 2026-08-15

أُضيف — سجلّ مهارات لتدوين نتائج الاختبارات/التقييمات.

أُضيف

  • يتيح سجلّ المهارات الجديد (التحليلات ← سجلّ المهارات) تدوين تقييم ذاتي — الشركة، المنصة، المهارة، الدرجة %، وملاحظة اختيارية — يُضاف إلى data/assessments.tsv، مع قائمة بالإدخالات السابقة (الأحدث أولًا). بلا توكنات وحتمي؛ وصيغة الملف تديرها أداة المشروع الأب.

ملاحظات

  • مسار جديد GET /api/assessments (يمرِّر قائمة JSON الافتراضية لِـ assessment-log.mjs؛ فشل ليّن {available:false}) وPOST /api/assessments (كتابة صريحة: تُمرَّر الحقول كـ وسائط مصفوفة إلى assessment-log.mjs add). أمان الكتابة: يُرفض أي حقل نصي يحوي محرف تحكّم (يكسر TAB عمودًا، ويحقن سطرٌ جديد صفًّا) → 400 قبل الكتابة؛ الدرجة/العتبة محصورة 0–100، والأطوال مُقيَّدة. tests/assessments-route.test.mjs. المجموعة: 2610.

[1.204.0] — 2026-08-15

أُضيف — لوحة «طبيب الإعداد» في الإعدادات تُبرز سيرة/ملفًا غير مكتمل أو ببيانات أمثلة.

أُضيف

  • الإعدادات ← طبيب الإعداد تُجري الآن فحصًا بلا توكنات لِـ cv.md وconfig/profile.yml وتُدرج المشاكل المانعة (ملفات/حقول ناقصة) والتحذيرات (بيانات أمثلة/عناصر نائبة متبقية، أرقام مثبّتة) — لتلتقط إعدادًا ناقصًا قبل أن يُضعف عمليات المسح والتخصيص. للقراءة فقط؛ يُعاد التشغيل بنقرة.

ملاحظات

  • مسار جديد للقراءة فقط GET /api/cv-sync-check يمرِّر cv-sync-check.mjs من المشروع الأب، الذي يطبع نصًا + رمز خروج (بلا --json)؛ يُحلّل المسار أسطره الثابتة ERROR: / WARN: تحليلًا خفيفًا إلى {ok, errors[], warnings[]} — الترويسة، لا رمز الخروج، تُقرّر النجاح. فشل ليّن {available:false} في التثبيتات المستقلة. tests/cv-sync-check-route.test.mjs. المجموعة: 2602.

[1.203.0] — 2026-08-15

أُضيف — تلميح «إعادة استخدام سيرة سابقة؟» في CV Studio.

أُضيف

  • عند فتح وظيفة محفوظة في CV Studio، يقارنها التطبيق الآن بوظائفك المحفوظة الأخرى (تداخل كلمات حتمي، بلا توكنات) ويخبرك إن كانت الأقرب مشابهة بما يكفي لإعادة استخدام تلك السيرة المُخصَّصة، أو إعادة استخدامها مع تعديلات، أو تخصيص سيرة جديدة — كي لا تُعيد الإنشاء من الصفر لدور استهدفته سابقًا.

ملاحظات

  • مسار جديد للقراءة فقط GET /api/jds/:name/reuse يمرِّر jd-similarity.mjs من المشروع الأب (تداخل جاكار + حارس أقدمية؛ JSON {decision, score, reason}) مرة لكل وظيفة سابقة (حد التوزيع 25، يفوز الأفضل)؛ فشل ليّن {available:false} عند غياب السكربت أو الوظائف السابقة. tests/jd-similarity-reuse-route.test.mjs. المجموعة: 2594.

[1.202.0] — 2026-08-15

أُضيف — اكتشف لوحة ATS لشركة من #/portals وابدأ بتتبّعها.

أُضيف

  • في #/portals، اكتب اسم شركة فيفحص التطبيق Greenhouse وAshby وLever بحثًا عن لوحتها العامة — بلا LLM، بلا متصفح — ويعرض اللوحات الموجودة التي تدرج حاليًا وظيفة واحدة على الأقل. نقرة واحدة تضيف اللوحة المختارة إلى الشركات التي يراقبها الماسح. الفحص للقراءة فقط؛ ولا تُكتب portals.yml إلا عند الضغط على إضافة.

ملاحظات

  • ملف جديد server/lib/discover-ats.mjs (فحص slug بمضيف ثابت ومحارف مُتحقَّق منها عبر safeGet المثبِّت لـ DNS، ≤12 فحصًا لكل طلب) + POST /api/portals/discover (قراءة فقط) وPOST /api/portals/track (كتابة صريحة: withFileLock + دمج نصي + حارس إعادة تحليل + إعادة تسمية ذرّية؛ مضيفات ATS معروفة فقط، عديم التأثير المكرر). يعيد استخدام سجل مُحوّلات الماسح. i18n ×17. tests/discover-ats-resolver.test.mjs + tests/discover-ats-route.test.mjs. المجموعة: 2588.

career-ops-ui v1.267.1 · page 4/11 · mirror of the project changelog for cvstart.org