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

GitHub

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

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

جميع التغييرات المهمة في career-ops-ui. التنسيق وفق Keep a Changelog، والإصدارات وفق SemVer.

الترجمات: 🇬🇧 English · 🇪🇸 Español · 🇧🇷 Português · 🇰🇷 한국어 · 🇯🇵 日本語 · 🇷🇺 Русский · 🇨🇳 简体中文 · 🇹🇼 繁體中文 · 🇫🇷 Français · 🇵🇱 Polski · 🇺🇦 Українська · 🇩🇰 Dansk · 🇩🇪 Deutsch · 🇮🇹 Italiano · 🇹🇷 Türkçe · 🇮🇳 हिन्दी

ملاحظة بشأن الترجمة (v1.70.0) — أُضيفت العربية كإحدى ثلاث لغات واجهة جديدة. يترجم هذا الملف أحدث الإدخالات؛ السجل الكامل في 🇬🇧 CHANGELOG الإنجليزي وهو المرجع المعتمد.


[1.236.0] — 2026-09-18

تكافؤ مع المشروع الأب — career-ops عند الفرع main عند 6a9c84c (VERSION يقرأ 1.33.0، بعد 128 التزامًا). مصدران جديدان، وثلاثة إصلاحاتٍ مُنسوخة — كان اثنان منها يُضيّعان الإعلانات هنا بصمتٍ.

أُضيف

مصدرا مسحٍ جديدان، 92 → 94 (89 EN + 5 RU). فمصدر Python.org Jobs (pythonorg) يقرأ التغذية العامة للوحة؛ ونسبة الوظيفة إلى صاحب عملٍ إلزاميّةٌ، فالصفّ الذي لا يسمّي عنوانُه صاحب عملٍ يُسقَط بدل أن يُملأ باسم القيد في البوّابة. أمّا Generalist World (generalist-world) فيكشط بطاقات اللوحة ويفشل جهرًا أمام أيّ ترميزٍ لا يستطيع تحليله — فبطاقةٌ تغيَّر شكلها ترمي استثناءً بدل أن تُقرأ لوحةً فارغة، وقراءتها فارغةً هي عينُها نمط الفشل الذي يجعل مصدرًا مكشوطًا غير موثوقٍ به. ويظهر كلاهما في مرشِّح المصدر بـ#/scan، وكلاهما مثبَّتٌ بالمضيف (تعبيرٌ نمطيٌّ مُرسًى، وHTTPS فقط)، ويقود كلًّا منهما من الطرف إلى الطرف حزمة اختباراتٍ بنقلٍ وهميّ (43 و36 حالة).

أُصلح

كانت وظيفةٌ في Workday مفتوحةً في عدّة مدنٍ تُسقَط بمرشِّح الموقع. فنقطة نهاية القائمة في Workday تجيب عن الإعلان مُتعدِّد المواقع بـعددٍ حيث يحمل كلّ إعلانٍ آخر مكانًا: "53 Locations". وهذا ليس موقعًا، وbuildLocationFilter يطابق بسلسلةٍ فرعيّةٍ غير حسّاسةٍ لحالة الأحرف — فوظيفةٌ مفتوحةٌ في أوستن و52 مدينةً أخرى لم تطابق أيّ قيدٍ في allow: [austin] وأُسقِطت، بينما نجحت الوظيفة نفسها حين أُدرِجت منفردةً. وقياسًا أعلى المجرى عبر ثلاثة مستأجرين، كانت النسبة 53 of 291 إعلانًا عناصر نائبة، أي إنّ هذه هي الحالة العاديّة لا الحافّة. والأماكن الحقيقيّة للإعلان لا توجد إلّا في مستند التفصيل لدى CXS، فصار كلّ عنصرٍ نائبٍ يكلِّف الآن طلب GET إضافيًّا واحدًا عليه، بحدٍّ أقصى 200 لكلّ إدخال — وبعد هذا الحدّ يُترَك العنصر النائب ظاهرًا بدل أن يُزيَّف، لأنّ إعلانًا لم يُحلّ يجب ألّا يُقرأ أبدًا بوصفه مُحلولًا. والتفصيل الفاشل أو الخالي من الموقع يُبقي الإعلان تمامًا كما أعادته القائمة: فـ"53 Locations" ظاهرةً صادقةٌ بشأن ما تعذَّر حلُّه، بينما موقعٌ فارغٌ كان سيمرّ صامتًا عبر كلّ مرشِّح. ويحمل طلب GET نفسه أيضًا startDate، فيُؤرَّخ الإعلان المُثرَى بدقّةٍ بدل نثر نقطة نهاية القائمة النسبيّ ("Posted 30+ Days Ago"undefined)؛ وكلّ ما ليس بصيغة YYYY-MM-DD الصِّرفة يُترَك على حاله بدل أن يُمرَّر عبر Date.parse، الذي يترك سلوكه الاحتياطيّ غير القياسيّ لتقدير التنفيذ. صار مستوًى مُصرَّحٌ به لا يُدمِج طلبَي توظيفٍ في إعادة نشرٍ واحدة. فـ"Insurance Specialist II" تصريحُ صاحب العمل نفسِه بأنّ هذا الطلب ليس شقيق الآخر — نطاق أجرٍ ومجالٍ ورقم طلبٍ مختلفة تحت عنوانٍ أساسيٍّ واحد. ولم يكن المُجزِّئ يراه: فـw.length > 3 يُسقِط كلّ رقمٍ رومانيٍّ حتى VIII وكلّ رقمٍ مفردٍ، فتجزَّأ "Insurance Specialist" و"Insurance Specialist II" بالشكل نفسه، وسجَّلا نسبة Jaccard مثاليّة، وعلَّم كاشف إعادة النشر الثاني بوصفه إدراجًا مكرَّرًا للأوّل. ولم يكن لدى role-matcher.mjs الخاصّ بـweb-ui أيّ معالجةٍ للمستوى البتّة، فحطّ هذا هنا آليّة المشروع الأب كلَّها: تُستخرَج المستويات مع طيّ الرومانيّ والعربيّ إلى رقمٍ واحد (فـ"Nurse II" و"Nurse 2" تصريحٌ واحدٌ — وذلك الزوج بعينه كان يختلف خطأً من قبل)، ويجب أن يتّفق مستويان مُصرَّح بهما، ويُعامَل مستوًى في جانبٍ واحدٍ وحده بوصفه إعادة صياغةٍ رخوةً إلّا إذا حمل كلّ عنوانٍ أيضًا كلمةً يفتقدها الآخر، وذلك خلافان لا زخرفة. كانت لوحة Avature المثبَّتة على مرشِّحٍ تتجوَّل مع ذلك في اللوحة العالميّة كلِّها. فواجهة بحث Avature تولِّد بارامتراتِ أوجهٍ غامضةً (?42386=[812132] مرشِّح بلدٍ)، والمُشغِّل الذي يثبِّت لوحةً مضيَّقةً في portals.yml يتوقَّع أن يتجوَّل المسح في تلك اللوحة. وكانت resolveSearch تعيد بناء العنوان بصيغة ${origin}${path} وتُسقِط سلسلة الاستعلام كليّةً — فكلّ طلبٍ، بما فيه الأوّل، كان يتجوَّل في لوحة المستأجر بأكملها: صفحاتٌ أكثر، وطلباتٌ أكثر، وإعلاناتٌ رشَّحها المُشغِّل عمدًا. وصارت الأوجه ترافق الآن كلّ طلبٍ مُرقَّمِ الصفحات، مع تطبيق jobOffset الخاصّ بنا أخيرًا كي لا تختطف أوجهٌ ضالّةٌ مشتركة الاسم ترقيمَ الصفحات أبدًا.

أمن

devalue 5.8.1 → 5.9.2 (تبعيّةً متعدّيةً عبر astro)، وأُغلق به التنبيه الوحيد المفتوح من Dependabot — حجبُ خدمةٍ عند مُدخَلٍ مشوَّه. تبعيّةٌ خاصّةٌ بالموقع وحده؛ ولم تتغيَّر شيفرة وقت التشغيل.

ملاحظات

غير مَنقولٍ، مع السبب في كلٍّ. أعطى المشروع الأب lever مهلة 30 ثانية وmanfred مهلة 25 ثانية مقابل افتراضه المشترك البالغ 10 ثوانٍ — وهذا لا يُنقَل: فناقل مسح web-ui يسمح أصلًا بـ60 ثانية (DEFAULT_SCAN_TIMEOUT_MS)، فكلا اللوحتين تملكان هنا هامشًا أوسع ممّا يمنحه الإصلاح هناك. وازدواج بادئة careers- لدى iCIMS لا يمكن أن يحدث هنا أيضًا: فـweb-ui يأخذ أصل المستأجر مباشرةً من api/careers_url ولا يبني مضيفًا من اسم مستأجرٍ أبدًا؛ وتجوال المضيف الاحتياطيّ الجديد هناك ميزةٌ تناسب إعداد المشروع الأب الأكثر تراخيًا، لا إصلاح عيبٍ. واكتسبت deutschebahn وmanfred إعادة محاولةٍ عند الإجهاض العابر، وهو ما يجعله سقف الـ60 ثانية في web-ui أقلّ إلحاحًا بكثيرٍ — فدُوِّن ولم يُنقَل، كي يكون لمهلة تجوالٍ طويلٍ مستقبليّةٍ أوّل ما يُجرَّب معروفًا سلفًا. وكلّ ما عدا ذلك في دلتا الالتزامات الـ128 هو CLI أو مُتحقِّقٌ أو لوحة تحكّمٍ أو web/ (واجهة المشروع الأب نفسها): liveness-browser، وcheck-jd-archive، وverify-ats/verify-portals/verify-cv-facts، وupdate-system، وset-status، وaudit-portals، وdiscover-ats، وscaffolder، وtest-all، وبيانات الإضافات. أُعيد التحقّق من تباعُدات الفرع بعد السحب: إصلاح \p{L} السيريليّ في providers/telegram-channel.mjs، وملفّ الفرع الخاصّ providers/telegram.mjs، وhermes في web/src/lib/clis.ts. واندمج المنبع career-ops-hq/main كاملًا (صفر التزاماتٍ ناقصة). 3066 → 3164 اختبارًا؛ وتذكر المساعدة الآن 94 محوّلًا (89 EN + 5 RU) في كلّ الحزم السبع عشرة، وتبقى بنية العناوين ثابتةً عند 32 H2 / 122 H3.

[1.235.0] — 2026-09-16

تكافؤ مع المشروع الأب — career-ops عند الفرع main عند 68e6b94 (VERSION يقرأ 1.33.0، بعد 42 التزامًا). إصلاحان مُنسوخان؛ كان أحدهما يُضيّع وظائف MokaHR هنا بصمتٍ.

أُصلح

كانت الوظيفة الثانية من لوحة MokaHR نفسها تختفي بدل أن تُضاف. فـurl-key.mjs يختزل عنوان الإعلان إلى مفتاح مقارنة، وكان يُسقِط الجزء الذي يلي الرمز # دون شرط — وهذا معقولٌ، لأنّ هذا الجزء عرضيٌّ بحتٌ في معظم الحالات (#apply، #section-2). وMokaHR هو الاستثناء في سِجلّنا الخاصّ: فروابط إعلاناته على الشكل …/social-recruitment/{tenant}/{id}#/job/{n}، حيث تشترك كلّ وظائف المستأجر الواحد في المسار نفسه، ولا يوجد مُعرِّف الإعلان إلّا في ذلك الجزء الذي يلي #. فكانت كلّها تُختزَل إلى مفتاحٍ واحد. قِيسَ الأمر قبل الإصلاح: الصق وظيفتَي MokaHR مختلفتين في خطّ الأنابيب وتنتهي بـسطرٍ واحد — بلا خطأ، بلا تحذير، بلا ما يلفت النظر؛ ومرشِّح النَّضارة الخاصّ بالماسح (en-scanner.mjs) يقرأ المفتاح نفسه، فبمجرَّد أن تدخل أيّ وظيفةٍ من ذلك المستأجر إلى تاريخ الفحص، تُطرَح كلّ وظيفةٍ أخرى منه باعتبارها «مَشاهَدةً من قبل». وصارت normalizeUrl تُرقّي مسار #/job/{id} أو #/jobs/{id} المُتعرَّف عليه إلى مفتاح استعلامٍ غرضُه المقارنة فقط، قبل إسقاط ذلك الجزء — ويحتفظ MokaHR باسمه الراسخ mokahr_job_id كي تبقى المفاتيح المكتوبة سابقًا قابلةً للمقارنة، بينما يحصل أيّ مضيفٍ آخر على مُعرِّفٍ داخليٍّ هو _career_ops_fragment_job_id. والمُعرِّف المُرقَّى يُلحَق لا يُعيَّن: فالرابط الذي يحمل هذه البارامتر أصلًا يحمل مُعرِّف إعلانٍ آخر، والكتابة فوقه كانت ستدمج بالضبط الشيئين اللذين وُجد المفتاح لِيُبقيهما مُتفرِّقين. وكلّ جزءٍ غير مُتعرَّفٍ عليه لا يزال يَنهار إلى شيءٍ واحد، وهذا هو النصف الذي كان صحيحًا أصلًا. صار accept-encoding مثبَّتًا الآن في كلّ طلبٍ يصدر من أيّ مصدر. فإن تُرك غير مُعيَّن، تتفاوض Node على أيّ ترميزٍ يعرضه إصدار undici المُضمَّن فيها؛ ومن Node ≥ 23 يشمل ذلك zstd، واستجابة amazon.jobs المضغوطة بـzstd تعود مبتورةً عند البايت 1024 تمامًا مع حالة 200 — فيُقرأ الفشل على أنّه Unterminated string in JSON at position 1024، وكأنّه API مشوَّه، لا عطبٌ في طبقة النقل. وكلّ طلبٍ يرسل الآن gzip, deflate, br. وعلى بيئات التشغيل لدينا هذا إجراءٌ وقائيّ لا إصلاحيّ: فـNode 18/20/22 — مصفوفة CI والخادم (22.22.1) — ترسل gzip, deflate، وقد تحقَّقنا من ذلك مباشرةً لا افتراضًا، فلم يُبتَر شيءٌ هنا؛ وما يتغيَّر اليوم هو أنّ br صار مطروحًا، وهو ما لا تطلبه هذه الإصدارات افتراضيًّا لكنّها تفكّ ترميزه بشكلٍ صحيح. ويُطبَّق هذا الافتراضي بصرف النظر عن حالة الأحرف، وهذا هو الجزء الجدير بالمراجعة: فأسماء الترويسات لا تُفرِّق بين الأحرف الكبيرة والصغيرة، وتدمج fetch أيّ مُدخَلين بالاسم نفسه في قيمةٍ واحدةٍ مفصولةٍ بفواصل، فلولا ذلك لكان المستدعي الذي يمرِّر Accept-Encoding: identity يُرسِل بدلًا منه gzip, deflate, br, identity.

ملاحظات

تبقى الترويسات كائنًا عاديًّا عند حدود fetchImpl، حيث يُسلِّم المشروع الأب إلى fetch كائن Headers. وهذا تباعُدٌ مقصودٌ لا سهوًا: فمساعدات fetch في web-ui تأخذ fetchImpl قابلةً للحقن، وخمسٌ وأربعون حزمة اختباراتٍ خاصّةً بالمصادر تقرأ opts.headers['User-Agent'] ممّا تلتقطه — وكائن Headers لا يملك وصولًا بالأقواس المربَّعة، فالنقلُ الأمينُ هنا هو نقل السلوك (الكتابة فوق القيمة بصرف النظر عن حالة الأحرف، وفوز المستدعي) لا نقل التنفيذ ذاته. وكائن المستدعي يُنسَخ ولا يُعدَّل أبدًا، لأنّ عدّة مصادر تتشارك ثابت ترويسةٍ واحدًا. أمّا إصلاح المشروع الأب المرافق — تطبيق وكيل مستخدمٍ افتراضيّ بصرف النظر عن حالة الأحرف — فهو غير مَنقول: إذ لا يحقن web-ui أيّ وكيل مستخدمٍ افتراضيّ أصلًا، وكلّ مصدرٍ يضبط الوكيل الذي تشترطه بوّابته، فلا يوجد افتراضٌ يمكن أن يتصادم مع User-Agent الخاصّ بالمستدعي. تُثبِّت حزمتا اختباراتٍ جديدتان وثلاث حالاتٍ جديدةٍ هذين الإصلاحين: tests/http-accept-encoding.test.mjs (القيمة المُثبَّتة بالضبط، وكلّ واحدةٍ من مساعدات fetch الأربع، وتجاوزٌ من المستدعي بثلاث صياغاتٍ لحالة الأحرف — يُثبِت أنّ مفتاح accept-encoding واحدًا هو مَن يبقى، وهو ما كانت علّة الدمج تُنتِج منه اثنين)، وtests/parsers-pipeline-hash-route.test.mjs الذي يفحص المَعبَر الظاهر للمستخدم لا المفتاح نفسه: يجب أن تصل وظيفتا MokaHR كلتاهما إلى خطّ الأنابيب، وإعادة لصق الوظيفة نفسها بأربع صياغاتٍ يجب أن تنهار إلى سطرٍ واحدٍ كما كانت، وجزءٌ زخرفيٌّ يجب ألّا يتحوَّل إلى عنصر تمييزٍ في المفتاح. وتأكَّد فشل الجميع أوّلًا. التثبيت نفسه يقع في طبقة النقل، لا في تلك المساعدات وحدها: 25 من أصل 92 مصدرًا تستدعي fetchImpl المحقون مباشرةً ولا تمرّ بها إطلاقًا، لذا يطبّقه makeTimeoutFetch — الغلاف الذي يحقنه الماسحان فعليًا — وهو نقطة العبور الوحيدة التي تشترك فيها كل المصادر. (المشروع الأب لا يحتاج مكافئًا: providers/_http.mjs هو طبقة نقله الوحيدة.) صار هناك حارس بنيوي يُسقط التكامل المستمر إذا استدعى مصدر جديد fetch مباشرةً بدل استخدام طبقة النقل المسلَّمة إليه، وحالة سلوكية تُشغّل greenhouse — أحد الـ 25 — عبر الغلاف لتثبت أن الترويسة تصل فعلًا. وغير منقولٍ، مع السبب: liveness-browser.mjs (حارس إعادة استخدام Playwright ذي الواجهة — لا يُشغِّل web-ui متصفِّحًا ذا واجهةٍ)، وcheck-jd-archive.mjs (لا يوجد تناوبٌ له في web-ui)، وtest-all.mjs (أداة تشغيل اختبارات المشروع الأب؛ وتباعُد الفرع الخاصّ بـvpFixtureEnv باقٍ كما هو)، وweb/ (واجهة المشروع الأب نفسها، غير منسوخة)، وبيانات الإضافات وملفّ SIGNATURES.md (خاصّان بالمستودع وحده). أمّا batch/batch-runner.sh (حيث يُهرَّب علامتا العطف قبل تبديل sed) وjd-skill-gap.mjs (الذي يحلّ cv.md من جذر البيانات) فهما مُناوَبان للقراءة فقط — يستدعي web-ui صَدَفةً نسخة المشروع الأب منهما، فيصلان كلاهما دون أيّ تغييرٍ في الشيفرة هنا، وملف jd-skill-gap يجعل تناوب /api/jds/:id/skill-gap أصحّ: فـcv.md يُحَلُّ الآن من جذر البيانات بدلًا من أيّ مجلَّدٍ نشأت فيه العملية. أُعيد التحقّق من تباعُدات الفرع بعد السحب: إصلاح \p{L} السيريليّ في providers/telegram-channel.mjs، وملفّ الفرع الخاصّ providers/telegram.mjs، وhermes في web/src/lib/clis.ts. عدد المصادر ثابتٌ عند 92 (87 إنكليزيّة + 5 روسيّة)، فالمساعدة لم تُمَسّ. 3042 → 3066 اختبارًا.

[1.234.0] — 2026-09-15

تكافؤ مع المشروع الأب — career-ops عند الفرع main عند 56cce8f (لا يزال VERSION يقرأ 1.32.0، بعد 58 التزامًا تجاوزت 1.32.0). إصلاحٌ واحد مُنسوخ عبر 15 مصدرًا، مع حارسٍ يمنع عودته.

أُصلح

مُعرِّف وظيفةٍ واحدٌ مشوَّه لم يعد يُفرغ الصفحة كلَّها. فـencodeURIComponent يرمي URIError عند محرف UTF-16 تعويضي منفرد، وحمولة JSON قد تحمل واحدًا — وJSON.parse('"\\uD800"') يُبقيه كما هو. وكانت خمسة عشر مصدرًا تبني عنوان كلّ إعلانٍ من مُعرِّفٍ أو slug يتحكّم فيه المضيف داخل حلقة التحليل، فيُجهِض إعلانٌ سيّءٌ واحدٌ الحلقةَ ويَخسر كلَّ وظيفةٍ في تلك الصفحة؛ ولم يُسجَّل شيءٌ كفشل مصدرٍ لأنّ الاستثناء كان يتصاعد قبل أن يصل إلى ما يُبلِّغ به الالتقاط الخاصّ بكلّ شركة. أصلحه المشروع الأب في providers/_safe-url.mjs (بحيث تُعيد safeEncodeURIComponent القيمة null عند URIError وتُعيد رمي ما عداه)، ومرَّر مُعرِّف كلّ حلقةٍ عبرها، فتُسقِط الإعلانَ الواحد فقط عند null. وذلك المساعد صار الآن server/lib/sources/_safe-url.mjs، ويُطبَّق التغيير نفسه على المصادر الثلاثة عشر المنسوخة — alibaba وarbeitsagentur وbamboohr وfeishu-jobs وgarena وjibeapply وmanfred وmeituan وmokahr وphenom وthehub وtkms وvdab — وعلى مصدرين خاصَّين بـweb-ui فقط كشفهما الحارس الجديد من أوّل تشغيلٍ له، هما jobstreet وtrudvsem، وكان لهما شكل الحلقة نفسه والرَّمي الكامن نفسه. والنطاق مقصودٌ: فالمعرِّفات الآتية من API وحدها تمرّ عبر المساعد. أمّا القيم المُستمدَّة من الإعداد فتُبقي ترميزها الصارم وتفشل بصوتٍ عالٍ — فـoffice في garena (وهو مقطعٌ من portals.yml) يبقى على urlSegment الذي يرمي، بينما يحصل id الآتي من API على idUrlSegment الذي يُعيد null؛ وcorpName الخاص بـcsod، وslug الخاص بـ4dayweek المتحقَّق منه بـSLUG_RE، مُدرَجان في قائمة السماح بالاسم مع ذكر السبب. العنوان الاحتياطيّ في rheinmetall كان يمكن أن يرمي عند href مشوَّه. فحين لا تحمل البطاقة عنوانًا رئيسًا يُعاد بناء العنوان من slug الرابط عبر decodeURIComponent، وهو يرمي عند تتابع نسبةٍ مئويةٍ فاسدٍ في href المُستخرَج بالكشط — الشكل نفسه: صفٌّ سيّءٌ واحدٌ يقتل الصفحة كلَّها، لكن من جهة فكّ الترميز هذه المرّة. وصار الآن يفكّ الترميز بحذرٍ، ويرجع إلى الـslug الخام عند الفشل. سِجلّ المصادر صار يتخطّى الملفات ذات البادئة _. كان الاكتشاف التلقائيّ يستورد كلّ ملفّ .mjs في server/lib/sources/ عدا نفسه، ويُحذِّر من أيّ ملفٍّ بلا export const meta — فكان المساعد يُنتِج تحذيرًا وقت الإقلاع. وقد اعتُمد عرف المشروع الأب: فالشرطة السفلية البادئة تعني ليس مصدرًا؛ فمثل هذه الملفات لا تُستورَد ولا يُحذَّر منها. أمّا الملف غير المُعلَن وبلا meta فلا يزال يُحذَّر منه، وصار اختبار الاكتشاف يتحقّق من الشِّقَّين معًا (وتأكَّد فشله أمام المرشِّح القديم). كما يعدّ سكربت بناء الموقع sync-assets.mjs ملفات المحوّلات على القرص بالقاعدة نفسها، فلم يعد المساعد يُطلق حارسه ضد الإسقاط الصامت.

ملاحظات

وtests/sources-url-encoding-surrogate.test.mjs ينسخ حزمة اختبارات المشروع الأب في ثلاثة أجزاء: عقد المساعد (المحرف التعويضي المُزاوَج — رمزٌ تعبيريٌّ عاديّ — لا يزال يُرمَّز؛ والرَّمي الصادر عن toString الخاصّة بالقيمة نفسها هو عيبٌ في المستدعي فيتصاعد كما هو)، وسلوك كلّ مصدرٍ من الخمسة عشر (مُعرِّفٌ سيّءٌ واحد + مُعرِّفٌ سليم واحد → لا رَمي، يُبقى السليم ويُسقَط السيّء، كلٌّ عبر المحلِّل النقيّ المُصدَّر من المصدر، أو عبر جالِبه مع نقل وهميّ في حالة thehub)، وحارسٌ على مستوى المصدر: فكلّ ملفٍّ مُحوَّل يجب أن يستورد المساعد ويستدعيه، ولا يجوز لملفٍّ أن يشير إليه دون استيراده، ولا يجوز لملفٍّ لم تُراجَع بنيتُه أن يبني عنوان وظيفةٍ بـencodeURIComponent عاريةٍ — فمصدرٌ جديدٌ بالشكل القديم يُفشِل CI مع رقم السطر المخالف. وتأكَّد فشل حالات الخمسة عشر مصدرًا كلِّها قبل النقل. وكلّ ما عدا ذلك في دلتا المشروع الأب غير مَنقول، مع السبب: batch/batch-runner.sh --cli رايةٌ خاصّة بالـCLI وحده (web-ui لا يستدعي batch-runner عبر الصَّدفة)؛ وinvite-match.mjs صار يحلّ set-status.mjs من جذر الشيفرة — وweb-ui لا يُمرِّره؛ وcompany-funded.mjs اكتسب تغيير safe-url نفسه داخليًّا، وشكل مُخرَجه --json — الذي يقرؤه /api/company-funded — دون تغيير؛ وtest-all.mjs نقل ميزانية مهلةٍ فقط (وتباعُد الفرع الخاصّ بـvpFixtureEnv باقٍ كما هو)؛ وweb/ هو واجهة المشروع الأب نفسها وغير منسوخ؛ وAGENTS.md/SIGNATURES.md خاصّان بالمستودع وحده. أُعيد التحقّق من تباعُدات الفرع بعد السحب: إصلاح \p{L} السيريليّ في providers/telegram-channel.mjs، وملفّ الفرع الخاصّ providers/telegram.mjs، وhermes في web/src/lib/clis.ts. عدد المصادر ثابتٌ عند 92 (87 إنكليزيّة + 5 روسيّة)، فالمساعدة لم تُمَسّ. 3022 → 3042 اختبارًا.

[1.233.2] — 2026-09-10

أُصلح — تنبيهان من CodeQL بدرجةٍ عالية (js/remote-property-injection)، أُغلقا بجعل شرطٍ قائمٍ بنيويًّا لا مُدَّعًى.

أُصلح

فـPOST /api/config ينقل القيم المحفوظة إلى process.env كي تسري بلا إعادة تشغيل، وكانت تلك الحلقة تدور على Object.entries(safe) — وهو كائنٌ مبنيٌّ من جسم الطلب — وفي جوفها حارسٌ صريحٌ ضدّ مفاتيح النموذج الأوّليّ. والشيفرة كانت آمنة، إذ يُملأ safe قبل عشرين سطرًا من KNOWN_KEYS وحدها. لكنّ الحلقة وحدها تُقرأ هكذا: أسنِد إلى خاصّيةٍ اسمُها من كائنٍ بُني من جسم الطلب، ولا يستطيع المحلّل تتبّع القيد عبر حلقتين. صارت الآن تدور على المصفوفة الثابتة مباشرةً، فاسم الخاصّية حرفيٌّ على مستوى الوحدة بالبرهان. والسلوك نفسه لم يتبدّل.

ملاحظات

كان الخيار الآخر ردَّهما بوصفهما إنذارَين كاذبَين، وللمستودع عرفٌ في ذلك. غير أنّ الإصلاح أَولى: فالتوكيد في تعليقٍ أقلّ قيمةً من شكلٍ يتحقّق منه المحلّل والقارئ التالي بلا تتبّع. أمّا الاختبار الذي كان يُثبّت الحارس القديم فكان بحثًا في نصّ المصدر. استُبدل باثنين أقوى: بنيويٍّ (تأكّد سقوطُه أمام الشكل القديم)، وسلوكيٍّ يرسل __proto__ بوصفه JSON خامًا — إذ إنّ { __proto__: … } في المصدر يضبط النموذج الأوّليّ ولا يُنشئ خاصّية. 3021 ← 3022 اختبارًا.

[1.233.1] — 2026-09-10

أُصلح — لم يكن الدليل يصف العنصر الذي أضافته v1.233.0، وكان القسم نفسه لا يزال يحمل إرشاداتٍ بطل صدقها منذ مئتَي إصدار.

أُصلح

DOC-1 — صار كلّ قائمةٍ منسدلة يبدأ بـ«استخدام القيمة الافتراضية (…)»، وهو يعبّر عن حالٍ لم يكن للواجهة سبيلٌ إلى إظهاره: أنّ المفتاح غير مضبوط. والفرق بينه وبين اختيار القيمة نفسها من القائمة خفيٌّ وذو أثر. أُضيفت ثلاث نقاط إلى قائمة ### Behavior القائمة في السبعة عشر بندًا كلّها. DOC-2 — كان صفٌّ في الجدول لا يسمّي مفتاحًا البتّة: ففي خانته (server uses default UA) تصف HH_USER_AGENT الذي أزالته v1.19.0، ثمّ تدعو إلى التسجيل في dev.hh.ru — وهو عملٌ لا طائل تحته منذ v1.65.0. أُزيلت الإشارات الثلاث المتقادمة كلّها؛ أمّا صفّ استكشاف الأخطاء فأُعيدت كتابته لا حذفه، إذ نصفه صحيحٌ بعد: فمنذ تمّوز 2026 يردّ hh.ru بـHTTP 451 على العناوين غير الروسيّة. وصار جدول المفاتيح يقول ما هو. فهو يعدّ ستّةً من خمسةٍ وأربعين، وعنوانه يَعِد بسجلّ. جملةٌ واحدة ×17 تُعلن أنّه انتقاء وتُحيل إلى #/config.

ملاحظات

وtests/help-recognized-keys.test.mjs هو البوّابة التي كانت غائبة: يقابل العمود الأوّل من جداول المفاتيح بـKNOWN_KEYS الحيّ. 3018 ← 3021 اختبارًا؛ وتبقى المساعدة 32 H2 / 122 H3.

[1.233.0] — 2026-09-10

أُصلح — ثلاثة مواضع في مسار الحفظ بـ#/config؛ وCONFIG-3 من صنع إصلاح v1.232.1 نفسه.

أُصلح

CONFIG-3 — اختيارٌ مقصودٌ صادف أن يساوي القيمة المبذورة كان يُطرح بوصفه «لم يُمَسّ». فـinitial يخلط أصلين: البذر من .env (والمساواة تعني أنّ الحقل لم يُحرَّر، فتخطّيه صواب)، والبذر من defaultValue (والمساواة ملتبسة: إمّا لم يُفتح، وإمّا فُتح ورُضي به). ومع غياب HOST يعرض الحقل 127.0.0.1؛ فإدخال هذه القيمة عينها ثمّ الحفظ يرسل {} ويبقى المفتاح غائبًا. أُصلح بتسجيل seededFromFile إلى جانب initial. وكان حذف مفتاحٍ يُبلَّغ بـ«· 0» لأنّ الخادم لا يردّ إلّا بما كُتب. صار يردّ بـremoved أيضًا، ويعدّ التنبيهُ الاثنين. وكانت القائمة تُضبط ولا تُفرَّغ: فلا واحدةٌ من الثمانية عشر تعرض خيارًا فارغًا. صارت كلٌّ تبدأ بـ«استخدام القيمة الافتراضية (…)». ومفتاح i18n واحد جديد ×17.

ملاحظات

خطّ الأساس للاختبارات كما هو: 3018؛ وفي المتصفّح 111 ← 116.

[1.232.1] — 2026-09-10

أُصلح — كان الحفظ في #/config يكتب حقولًا لم يمسّها المستخدم.

أُصلح

CONFIG-2. تبذر v1.57.1 في الحقل غير المضبوط قيمته الافتراضية ليعرض ما يستعمله الخادم فعلًا — وهذا صواب وقد بقي — بينما كان save() يسأل dirty للأسرار وحدها ويرسل سائر الحقول دائمًا. فصارت القيمة الموضوعة للعرض تسافر كأنّها اختيار المستخدم: تغييرُ قائمةٍ واحدة يرسل ثمانيًا وعشرين مفتاحًا ويُثبّت ثمانية عشر لم يفتحها أحد. ولا يتبدّل شيء يومَها، فالمكتوب هو الافتراضيّات نفسها؛ إنّما يتبدّل وضع كلّ إعداد: من «غير مضبوط، اتبع افتراضيّ المشروع» إلى «مثبَّت في .env». وهذه القوائم تتحرّك بين الإصدارات، فمتى غيّرنا افتراضيًّا لن يبلغ التغييرُ صامتًا كلَّ من ضغط الحفظ مرّة.

ملاحظات

وموضع النظر هو أساس المقارنة. فالمقارنة بـcfg.values المحمَّلة — وهو الإصلاح البديهيّ، وأوّل ما جُرّب — لا تكفي: إذ يعرض الـselect غير المضبوط قيمته الافتراضية بينما المحفوظ ''، فيختلفان بحقّ، وتُكتب القوائم السبع عشرة كما كانت؛ فانخفض جسم الطلب من سبعةٍ وعشرين مفتاحًا إلى تسعة عشر لا إلى واحد. أمّا الآن فتُقارَن القيمة بما بُذر به كلّ حقل، وهو التعبير الوحيد عن «لم يُمَسّ» الذي يفصل قيمةً وُضعت للعرض عن مستخدمٍ اختار القيمة نفسها قصدًا. وأُصلح معه: عددُ المفاتيح المكتوبة كان داخل الوسيط الاحتياطيّ لـt()، وconfig.saved موجودٌ في المعاجم السبعة عشر جميعًا، فلم يُعرض العدد قطّ. 3018 اختبارًا كما هي، و105 ← 111 في المتصفّح.

[1.232.0] — 2026-09-09

أُصلح — عيبان كشفهما فحصٌ في المتصفّح، وما جاء أيٌّ منهما من v1.231.5.

أُصلح

PROFILE-1 — كان #/profile يُمرّر الصفحة كلَّها أفقيًّا. فقيمة الحقل كانت <div> مغفلًا لا يحمل إلّا أنماط خطٍّ مضمّنة، فبقي overflow-wrap المحسوب normal. ورابط LinkedIn لا يمنح CSS موضع قطعٍ مشروعًا، فصار عرض min-content هو السلسلة بأكملها؛ وعناصر الشبكة تحمل min-width: auto وتأبى الانكماش دونه — فبعجت البطاقةُ مسارها، ثمّ الصفَّ فالعمودَ الرئيس فالمستند (23 بكسل من التمرير الحقيقيّ عند 1280). أُصلح بصنف مشترك .card-value عليه overflow-wrap: anywhere، ومعه .card-row > * { min-width: 0 } سندًا. و**anywhere لا break-word: فلا يُحسب في min-content إلّا anywhere. CONFIG-1 — كان POST /api/config يقبل أيّ قيمةٍ لحقل select. فـ{"LLM_PROVIDER":"not-a-provider"} يردّ 200 ويُكتب في .env؛ ثمّ لا يفشل شيء، إذ يرتدّ المحلِّل صامتًا إلى بديلٍ فيبدو كلّ شيءٍ سليمًا بينما اختيار المستخدم لا أثر له. وLLM_PROVIDERS، الحاملُ لتلك القيم التسع عشرة بعينها، كان مُصدَّرًا من env-config.mjs طوال الوقت**؛ وإنّما لم يكن المدقّق يرجع إليه.

ملاحظات

ولا يُتحقَّق إلّا من LLM_PROVIDER، وهو تضييقٌ مقصودٌ لما ورد في التقرير. فالمجال الحقيقيّ لقوائم النماذج يخصّ المزوّد ويتغيّر بين إصداراتنا، وفرضُ قائمتنا المنتقاة يمنع اختيار نموذجٍ يقدّمه المزوّد فعلًا. والنموذج الخطأ يفشل جهرًا عند الاستدعاء؛ وحده اسم المزوّد يفشل صمتًا. والقيمة المحفوظة سلفًا يُتجاوَز عنها، إذ أثبت فحصٌ في المتصفّح أنّ الصيغة الصارمة غير صالحة. فالنموذج يُعيد إرسال كلّ حقلٍ غير سرّيّ مع كلّ حفظ، فرفضُ قيمةٍ قديمة يُجمّد الصفحة بأسرها. أمّا الآن فتُرفض التغييرات وحدها. وأُغلق معه: OLLAMA_API_KEY كان يُقبل عبر الواجهة ولا حقل له في #/config. صار له واصفٌ الآن (سرّيّ، ومفتاحا i18n ×17)، ويضمن اختبارٌ بقاء مفاتيح الخادم الخمسة والأربعين مساويةً لواصفات الواجهة. 3013 ← 3018 اختبارًا.

[1.231.5] — 2026-09-09

أمن — أُغلقت سبعة تنبيهات، أحدها تنفيذ شيفرة عن بُعد بالغ الخطورة. ولم يتغيّر أيّ ملفٍّ من ملفّات المصدر.

أمن

مدموجٌ من Dependabot: multer 2.2.0 ← 2.3.0 (عالٍ — حجب خدمةٍ بتسريب واصفات الملفّات عند الإجهاض)، وsvgo 4.0.2 ← 4.1.0 في الموقع (عالٍ ومتوسّط — كان removeScripts يُمرّر روابط قابلة للتنفيذ عبر معالجة فضاءات الأسماء والمحتوى). ثمّ خمسةٌ أُخَر كشفها إعادة الفحص التي أطلقها ذانك الدمجان. وكلّ مدًى متأثّرٍ كان أصلًا ضمن ما يسمح به مُحدِّد ^ القائم، فلزم تحديثٌ لا تعديلُ بيان: astro 7.1.0 ← 7.3.2بالغ، تنفيذ شيفرة عن بُعد عبر تحسين صور AVIF (أُصلح في 7.2.8)، ومعه تجاوزُ تخويلٍ لغياب فحص حدّ مقطع المسار (7.2.4)؛ و**sharp 0.35.3 ← 0.35.4** — عالٍ، ثغرتا libheif؛ و**js-yaml 4.3.1 ← 4.3.2** في كِلا البيانين — عالٍ.

ملاحظات

«التبعيّات وحدها» ليست هنا مرادفًا لـ«بلا مخاطرة». فـjs-yaml يُحلّل كلّ إعدادٍ يقرؤه التطبيق، وmulter يستقبل كلّ رفع، وقد تجاوز Astro إصدارًا ثانويًّا. المتحقَّق منه: npm run test:ci 3013/3013، ومجموعة المتصفّح 4/4، وإعادة بناءٍ كاملة للموقع أعطت 86 صفحة نفسها و17 مرآةً كاملة وfacts.json صحيحًا. ويُبلّغ npm audit عن صفر ثغرات في البيانين.

[1.231.4] — 2026-09-07

أُصلح — أصلحت v1.231.3 ما بعد النقر على Doctor لا لحظتَه. بلاغٌ ثانٍ، من الجوال ومن سطح المكتب.

أُصلح

كان withSpinner لا يزال يُعيد كتابة الزرّ والطلب في الجوّ. جعلته v1.231.3 يلتقط عُقَد الأبناء ويُعيدها في finally، وقد كانت تعود فعلًا — غير أنّ إشارة الانشغال ظلّت تُنفّذ replaceChildren بالنصّ '⏳ ' + original. فطوال مدّة تشغيل doctor.mjs كان مربّع 36 بكسل يحمل النصّ ⏳ 🩺Doctor ويصير قرصًا عريضًا يكسر السطر؛ وعلى سطح المكتب يظهر ⏳🩺Doctor متلاصقًا. فإعادة الأبناء أصلحت ما بعدُ لا اللحظة. والزرّ الذي له أبناءٌ عناصر لم يعد يُعاد كتابته البتّة: إشارته صنف .is-loading، وهو يستبدل الأيقونة بساعةٍ رمليّة في CSS، فلا يمكن للهندسة أن تتغيّر: 36 بكسل في السكون، و36 في الجوّ، و36 بعدُ.

ملاحظات

الدرس في التوكيد لا في الشيفرة. كانت بوّابة v1.231.3 تنتظر زوال aria-busy ثمّ تقيس — فلم يكن بوسعها بنيويًّا أن ترى إلّا الحالة التي أصلحها finally سلفًا. أمّا الفحص الجديد فيُمسك الوعد مفتوحًا بنفسه ويأخذ عيّنته في منتصف الطيران. 3012 ← 3013 اختبارًا.

[1.231.3] — 2026-09-07

أُصلح — النقر على Doctor كان يُفسد شريط الأعلى في الجوال. بلاغٌ من هاتفٍ يوم صدور v1.231.2 نفسه.

أُصلح

كان UI.withSpinner يسحق أبناء الزرّ الذي يزيّنه هو ذاته. فإشارة الانشغال كانت تمرّ عبر textContent: احفظ button.textContent، واكتب '⏳ ' + original، ثم أعد كتابته. والإسناد إلى textContent يستبدل كلّ الأبناء بعقدة نصّية واحدة — وكانت v1.231.2 قد قسمت للتوّ كلّ إجراء في شريط الأعلى إلى .btn-ico و.btn-label، مع إخفاء التسمية تحت max-width: 900px. فأتلفت أوّل نقرة على Doctor كلا الـ<span> إلى الأبد: لم يبقَ للقواعد الجوّالة ما تُمسك به، فعادت التسمية نصًّا عاريًا، ونما مربّع 36 بكسل قرصًا يقرأ 🩺Doctor يركب فوق مبدّل السمة. ولم يكن يُشفى من تلقاء نفسه — وحده إعادة تحميل الصفحة يُعيد الترميز. أمّا الإصلاح فيلتقط عُقَد الأبناء ويُعيدها بـreplaceChildren؛ والأزرار النصّية الخالصة، وهي غالبية مواضع الاستدعاء الأربعة والعشرين، تسلك تمامًا كما كانت.

  • FIND-3 — كان حقل البحث قد انهار إلى 8 بكسل على هاتفٍ عرضه 320 بكسل. فجعل الشريط في سطرٍ واحد أبقى الـsearchbar العنصرَ المرن الوحيد، وقد قال سجلّ التغييرات الماضي ذلك صراحةً: «له أن ينكمش إلى لا شيء». وقد انكمش إلى لا شيء فعلًا: 8 بكسل من الحقل عند 320، و28 عند 340 — حرفٌ واحد من نائبٍ نصّي طوله واحدٌ وعشرون. يُنقر ويُكتب فيه، لكنّه لم يعد يُقرأ حقلًا. ولا يُصلحه أيّ min-width: فعند 320 بكسل يحمل الشريط 32 بكسل حشوًا، وقائمةً بأربعين، وإجراءاتٍ بمئةٍ واثنين وستّين، فلا يبقى 120 بكسل يُعطى. لذا صار الحقل دون 420 بكسل — وهو العرض الذي يتجاوز عنده المئة من تلقاء نفسه — يختبئ خلف عدسةٍ مكبّرة وينفتح على عرض الشريط كلّه عند اللمس: 182 عند 320، و222 عند 360، و252 عند 390، بلا فيضٍ عند أيّ عرض. ويحتفظ الزرّ باسمٍ ميسَّرٍ واحد، ويُبلّغ حالته بـaria-expanded؛ وتبديل 🔍/✕ من CSS خالص — فلا JS يكتب في هذا الزرّ، وتلك الكتابة بعينها هي ما أعطب Doctor في هذا الإصدار نفسه. وفوق 420 بكسل لا يتغيّر شيء. اكتشفه فحصُ انحدارٍ من المتصفّح على البناء الحيّ.

ملاحظات

هذا هو صنف الخلل نفسه الذي وُثِّق في v1.231.2 بوصفه فخّ applyI18n() — شيفرةٌ تُسنِد textContent إلى عنصرٍ صار له أبناءٌ عناصر — بلغناه هذه المرّة من مُستدعٍ آخر. والدرس يتعمّم: ما إن تُقسّم محتوى عنصرٍ إلى <span> أبناء حتى يصير كلّ كاتبٍ قائمٍ لـtextContent على ذلك العنصر عيبًا كامنًا، وهؤلاء الكُتّاب ليسوا دائمًا في الملفّ الذي تُحرّره. أُحكم القفل مرّتين: node:vm فوق withSpinner الحقيقيّ، ونقرةٌ في متصفّحٍ حقيقيّ عند 320 بكسل. وقد تأكّد سقوط كليهما أمام الشيفرة القديمة. 3009 ← 3012 اختبارًا (حالة المتصفّح اختياريّة وليست ضمن test:ci).

[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 على resumecraft.ru يبلّغ 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_filtercontent_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 (رأس المشروع الأصل 8d64f656cc46a4؛ ما زال 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 بلا كود لـ OpenWorkerFighter90/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.

[1.201.0] — 2026-08-15

تم الإصلاح — المتتبِّع ذو ترويسات الأعمدة المُترجَمة أو البديلة لم يعد يظهر فارغًا.

تم الإصلاح

  • إذا كان data/applications.md يستخدم ترويسات غير إنجليزية أو بديلة — الإسبانية empresa / puesto / estado / fecha / enlace، أو position / stage / link — كان المتتبِّع يقرؤها بمفاتيح خاطئة ويعرض أعمدة الشركة / الدور / الحالة / التاريخ / الرابط فارغة. الآن تُطوى هذه الترويسات إلى أسماء الحقول القانونية فيُعرض المتتبِّع بشكل صحيح. المتتبِّع الإنجليزي يُعالَج تمامًا كما كان.

ملاحظات

  • خريطة HEADER_ALIASES جديدة وطيّ تطبيع في parseApplications (server/lib/parsers.mjs)؛ الترويسات غير المعروفة أو القانونية أصلًا تمرّ دون تغيير. tests/tracker-header-aliases.test.mjs. المجموعة: 2563.

[1.200.0] — 2026-08-15

أُضيف — فحص «هل ما زالت متاحة؟» بنقرة واحدة لوظائف منصات ATS في متتبِّعك.

أُضيف

  • في #/tracker، أي طلب يكون رابطه إعلانًا على Greenhouse / Lever / Ashby / Workday / SmartRecruiters يعرض الآن زر «هل ما زالت متاحة؟». نقرة واحدة تسأل JSON العام للمنصة نفسها — بدون توكنات، بدون متصفح — وتعرض متاحة / منتهية / غير معروفة، لترصد الإعلانات الميتة دون فتح كل واحد. متحفِّظ بالتصميم: فقط 404/410 القاطع يُقرأ منتهية؛ وأي التباس يبقى غير معروفة (دون منتهية خاطئة).

ملاحظات

  • ملفان جديدان server/lib/liveness-core.mjs + liveness-api.mjs ومسار للقراءة فقط GET /api/liveness?url= (بلا كتابة، بلا LLM). آمن ضد SSRF: يمرّ الرابط عبر isValidJobUrl، ثم يُوصل إلى API المنصة فقط عبر safeGet (المثبِّت لِـ DNS) بمضيف ثابت ومقاطع مسار مُتحقَّق منها. tests/liveness-core.test.mjs + tests/liveness-route.test.mjs. المجموعة: 2557.

[1.199.0] — 2026-08-15

تم الإصلاح — الجداول العريضة تُمرَّر الآن أفقيًا بدل أن تُقتطع.

تم الإصلاح

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

ملاحظات

  • تم تحويل .table-wrap في public/css/components.css من overflow: hidden إلى overflow-x: auto (مثل حاوية .reports-scroll الحالية)؛ مع الحفاظ على الحد المدوَّر. tests/table-wrap-scroll.test.mjs. المجموعة: 2540.

[1.198.0] — 2026-08-15

أُضيف — تستخدم إعادات محاولة المسح الآن تراجعًا أُسّيًا وتشويشًا (jitter) وتحترم Retry-After القادم من مُحدِّد المعدل.

أُضيف

  • عندما تُحدِّد لوحة وظائف المعدل مؤقتًا أو تُخطئ (HTTP 429 / 5xx) أثناء المسح، تنتظر إعادة المحاولة الآن بـتراجع أُسّي + تشويش بدلًا من تأخير قصير ثابت — فلا تُطرَق اللوحة المزدحمة بالإيقاع نفسه، ولا تتصادم المحاولات المتزامنة معًا من جديد. ويُحترَم Retry-After القادم من اللوحة (لكن مع تقييد، حتى لا يوقف Retry-After: 86400 عدائي المسح كله). أما الأخطاء الدائمة (404، عمليات إعادة التوجيه المرفوضة) فتفشل فورًا كما كانت — دون تغيير.

ملاحظات

  • دالتان جديدتان parseRetryAfterMs() والدالة النقية computeRetryDelayMs() في server/lib/http-json.mjs؛ يلتقط fetchJson الآن .retryAfter عند استجابة غير ناجحة، ويقبل fetchJsonWithRetry قيمة maxDelayMs اختيارية (الافتراضي 8000). tests/http-json.test.mjs (+9). المجموعة: 2536.

[1.197.0] — 2026-08-14

أُضيف — تابِع لوحة وظائف صندوق Getro عبر careers_url وحدها؛ ويُحَلّ مُعرّف المجموعة تلقائيًا.

أُضيف

  • لم تعد لوحة Getro المُتابَعة (b2venture، Earlybird، Point Nine، …) بحاجة إلى getro_collection رقمي يُبحث عنه يدويًا. أعطِها careers_url الخاص باللوحة، فيُحَلّ المُعرّف تلقائيًا من تلك الصفحة عند أول فحص — طلب GET واحد آمن ضد SSRF يقرأ network.id الرقمي مباشرةً من بيانات الصفحة المُضمّنة. ولا يزال getro_collection الصريح له الأولوية ويتخطّى الجلب بالكامل.

ملاحظات

  • دوال جديدة httpsCareersUrl() وextractCollectionId() وresolveCollectionId() غير المتزامنة في server/lib/sources/getro.mjs؛ تُجلَب صفحة اللوحة عبر safeGet (DNS مُثبَّت، حجم مُقيَّد)، ويظل المُعرّف المُحَلّ مُثبَّتًا على المضيف api.getro.com بواسطة assertGetroUrl. صار المُحوِّل الآن يطابق مُدخل provider: getro يحمل careers_url بروتوكول https حتى بدون مُعرّف. tests/sources-getro.test.mjs (+8). المجموعة: 2527.

[1.196.0] — 2026-08-14

تم الإصلاح (الأمان) — محوّل Workday يتحقق من نقطة api عبر اسم المضيف لا عبر سلسلة فرعية.

تم الإصلاح

  • قيمة api: الخاصة بـ Workday في portals.yml تُقبل الآن فقط عندما يكون اسم مضيفها هو myworkdayjobs.com (أو نطاق فرعي .myworkdayjobs.com). كان الفحص القديم مطابقة سلسلة فرعية، لذا أي رابط يحتوي على السلسلة فحسب — مثل https://example.com/?x=myworkdayjobs.com — كان يمر ويُستخدم كنقطة نهاية. نقاط Workday الحقيقية غير متأثرة. (أبلغ عنه CodeQL، #443.)

ملاحظات

  • isWorkdayApi() الجديد يحلّل الرابط ويتحقق من المضيف (server/lib/portals/adapters/workday.mjs). tests/workday-adapter-endpoint.test.mjs (+1). المجموعة: 2522.

[1.195.0] — 2026-08-14

الأداء (الماسح) — يبقى كشف إعادة النشر سريعًا على سجلات المسح الكبيرة.

الأداء

  • لم يعد كشف الإعلانات المكرّرة يتدهور إلى O(N²) على scan-history.tsv كبير. كان تجميع العناوين لكل شركة حلقة متداخلة تدفع roleFuzzyMatch كاملًا لكل زوج؛ أصبح الآن فهرسًا معكوسًا — تجميع الصفوف حسب العنوان المطابق في مرور واحد، ثم مطابقة ضبابية فقط بين الحاويات المختلفة التي تتشارك رمزًا مميّزًا (غير أساسي). المخرجات متطابقة — نفس مجموعات إعادة النشر — أُثبت باختبار تفاضلي ضد الخوارزمية القديمة على أكثر من 200 سجلّ عشوائي.

ملاحظات

  • groupRowsByTitle في server/lib/detect-reposts.mjs (مُصدَّر للاختبار التفاضلي). tests/detect-reposts-grouping.test.mjs (+2). المجموعة: 2521.

[1.194.0] — 2026-08-14

تم الإصلاح (الماسح) — صفحات وظائف Workday ذات المسار المكوّن من مقطع واحد تُمسح الآن بشكل صحيح.

تم الإصلاح

  • محوّل Workday يحلّل الآن روابط الوظائف التي مسارها مقطع واحد — مثل https://parsons.wd5.myworkdayjobs.com/Search و.../KBR_Careers و.../Careers. سابقًا كان الموقع يعود إلى External، فيصيب المحوّل نقطة CXS الخاطئة وقد يبدو الفحص سليمًا دون إرجاع شيء. الآن يأخذ أول مقطع غير فارغ من المسار كموقع (متجاهلًا بادئة لغة مثل en-US)؛ والحالة الموثّقة /en-US/External دون تغيير. (أُبلغ عنه في #255.)

ملاحظات

  • تحليل بنيوي للمسار في server/lib/portals/adapters/workday.mjs. tests/workday-adapter-endpoint.test.mjs (+7). المجموعة: 2519.

[1.193.0] — 2026-08-14

أُضيف (إحصاءات) — تبويب “صمت بعد المقابلة” يُبرز المقابلات التي تستحق تذكيرًا.

أُضيف

  • تبويب صمت بعد المقابلة في #/stats: مقابلات صمتت بعد نافذة مجاملة (30 يومًا افتراضيًا)، يجمع مقابلاتك النشطة والمتتبع — مع مدة صمت كلٍّ منها وتاريخ آخر مقابلة والسبب. قائمة لطيفة للتذكير/الإغلاق؛ اقتراحات فقط، وليست ادعاء رفض أبدًا. بدون رموز.

ملاحظات

  • مسار جديد GET /api/stats/rejection-latency (fail-soft {available:false}). tests/stats-rejection-latency-route.test.mjs (+2). +10 مفتاح i18n ×17؛ help-hint في #/stats من 7→8 تبويبات. المجموعة: 2510.

[1.192.0] — 2026-08-14

أُضيف (cv-studio) — بوابة “تحقّق من حقائق سيرتك” تلتقط أرقامًا لم تمتلكها قط.

أُضيف

  • بطاقة تحقّق من حقائق سيرتك في #/cv-studio: الصق سيرة أو خطابًا معدّلًا وتحقّق من كل مقياس وحقيقة مقابل سيرتك وملفك وصفحتيك الحقيقية. تحصل على حكم pass / warn / block إضافةً إلى المقاييس المختلقة والحقائق غير المدعومة والعبارات المحظورة / التحذيرية بدقة. بدون LLM؛ لا يُكتب شيء.

ملاحظات

  • مسار جديد POST /api/cv-studio/verify-facts: يكتب النص في ملف مؤقت للاستعمال مرة واحدة ويشغّل verify-cv-facts.mjs، ويثق بحكم JSON رغم خروج السكربت بالرمز 1 عند block. tests/cv-studio-verify-facts-route.test.mjs (+4). +15 مفتاح i18n ×17. المجموعة: 2508.

[1.191.0] — 2026-08-14

أُضيف (إحصاءات) — تبويب “ماذا تتعلم بعد ذلك” يرتّب المهارات الأولى بالتعلم.

أُضيف

  • تبويب ماذا تتعلم بعد ذلك في #/stats: تجميع على مستوى المتتبع بالكامل — المهارات الناقصة التي أغرقت غالبًا تطابقًا منخفضًا، موزونة (بـ 5−درجة التطابق في كل تقرير) ومصنّفة Critical / High / Medium — إضافةً إلى ما تغطيه سيرتك/ملفك بالفعل. للقراءة فقط، اقتراحات فقط، بدون رموز.

ملاحظات

  • مسار جديد GET /api/stats/upskill (حقل { error } عند قلة البيانات؛ fail-soft {available:false}). tests/stats-upskill-route.test.mjs (+3). +15 مفتاح i18n ×17. المجموعة: 2504.

[1.190.0] — 2026-08-14

أُضيف (المتتبع) — لوحة “سجل الشركة” تخبرك بالشركات التي ترد عليك فعلاً.

أُضيف

  • بطاقة سجل الشركة في #/tracker: اختر شركة واحصل على أدلة للقراءة فقط — مدى تجاوبها معك (صامتة تجاهك / مختلط / استجابت سابقًا) وما إذا كانت الوظيفة نفسها تُعاد نشرها — بدمج متتبعك والمتابعات وسجل المسح. بدون رموز؛ لا يُستدعى الماسح.

ملاحظات

  • مسار جديد GET /api/stats/company-history[?company=] (fail-soft {available:false}). tests/stats-company-history-route.test.mjs (+3). +18 مفتاح i18n ×17. المجموعة: 2501.

[1.189.0] — 2026-08-14

تم الإصلاح (الماسح) — مستويات الأقدمية المكتوبة بالأرقام الرومانية أصبحت تُحتسب أيضًا في العناوين غير اللاتينية.

تم الإصلاح

  • مصنّف المستوى وراء skip_tiers أصبح يقرأ لاحقة مستوى بالأرقام الرومانية (I / II / III / IV / V) بعد كلمة الوظيفة في أي نظام كتابة — “Инженер III”، “エンジニア I”، “Ingénieur IV” — وليس فقط بعد الكلمات اللاتينية. سابقًا كان رقم المستوى بعد كلمة غير لاتينية يُتجاهل وتقع الوظيفة في mid، فيفوت skip_tiers: [senior] أو [entry] تلك الإعلانات.

ملاحظات

  • lookbehind مستقل عن نظام الكتابة في server/lib/classify-tier.mjs؛ وحُذف مطابق Sr. مكرّر ميت. tests/classify-tier.test.mjs (+1). المجموعة: 2498.

[1.188.0] — 2026-08-14

تم الإصلاح (واجهة) — لم تعد أزرار الإجراء الرئيسية ملتصقة بعنوان الصفحة الفرعي.

تم الإصلاح

  • صف الإجراء / التحكم الرئيسي في الملخص الأسبوعي للمقابلات والشركات الممولة والبوابات والخطة المهنية والتوجيه المهني أصبح له الآن هامش علوي مناسب، بحيث يتنفس الزر أسفل العنوان الفرعي بدلاً من الالتصاق به.

ملاحظات

  • حارس الانحدار tests/lead-row-top-margin.test.mjs (+5). المجموعة: 2497.

[1.187.0] — 2026-08-14

تصحيح (الماسح) — إعداد skip_tiers يعمل من جديد: الوظائف التي طلبت تخطّيها حسب المستوى تُستبعد.

تصحيح

  • أصبحت قائمة skip_tiers: في portals.yml (مثل skip_tiers: [intern, entry]) مُحترَمة في المسح. يُصنَّف عنوان كل وظيفة إلى مستوى (intern / entry / mid / senior) ويُستبعَد إذا كان مستواه في قائمتك. سابقًا كان المسح يطبّق مرشّحات العنوان / الموقع / المحتوى / الثقة لكن بلا مرشّح مستوى، فكان skip_tiers يُتجاهَل بصمت. العناوين بلا كلمة مستوى تقع في mid (فـ skip_tiers: [mid] يستبعد أيضًا معظم الوظائف العادية)، ويقرأ المصنِّف كلمة المستوى الأقصى إلى اليسار.

ملاحظات

  • وحدة نقية جديدة server/lib/classify-tier.mjs (classifyTier + buildTierFilter)، موصولة بسلاسل مرشّحات ماسحَي EN و RU. tests/classify-tier.test.mjs (+7). المجموعة: 2492.

[1.186.0] — 2026-08-14

إضافة (استوديو السيرة) — لوحة “فجوة المهارات”: أيّ المهارات المطلوبة في وظيفة يذكرها سيرتك أو يُلمِّح إليها أو تفتقدها.

إضافة

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

ملاحظات

  • GET /api/jds/:name/skill-gap جديد (يُطهَّر اسم الوظيفة ويُتأكَّد من وجوده تحت jds/ قبل أن يصير وسيطًا؛ تراجع لطيف إلى {available:false} بدون السكربت). +13 مفتاح i18n ×17. الاختبارات: tests/jds-skill-gap-route.test.mjs (+4، بما فيها رفض اجتياز المسار). المجموعة: 2485.

[1.185.0] — 2026-08-14

إضافة (إحصاءات) — علامة تبويب “المسار والسرعة”: كيف يقارَن مسارك بالسوق ومدى سرعة تنقّلك بين المراحل.

إضافة

  • تعرض علامة المسار والسرعة الجديدة في الإحصاءات معدّلي الرد والمقابلة بجوار نطاقات مرجعية للسوق (مع تحفّظات العيّنة الصغيرة وتحيّز الاختيار)، وقائمة انتظار للطلبات الجارية التي تجاوزت نافذة الرد الأول المعتادة، ووسيط الأيام لكل مرحلة (تقدّم → رد → مقابلة → عرض) — تُراقَب الصفوف البطيئة من اليمين حتى لا تُحيّز الوسطاء. للقراءة فقط وبلا رموز؛ يقرأ متتبّعك أنت فقط.

ملاحظات

  • GET /api/stats/funnel جديد (تراجع لطيف إلى {available:false} بدون السكربت). +18 مفتاح i18n ×17. الاختبارات: tests/stats-funnel-route.test.mjs (+2). المجموعة: 2481.

[1.184.0] — 2026-08-14

تصحيح (واجهة) — بلاطات الإجراءات السريعة في لوحة القيادة تصطف الآن في شبكة متساوية.

تصحيح

  • في لوحة القيادة (مركز التحكم)، كانت مجموعة من 3 بلاطات تظهر أعرض من مجموعة من 4، فتترك الأقسام بحافة يمنى غير مستوية. تستخدم كل مجموعة الآن أعمدة متساوية العرض (4 على الشاشة العريضة، تنخفض إلى 3 / 2 / 1 كلما ضاقت النافذة)، فتصبح كل البلاطات بالحجم نفسه وتصطف حوافها اليمنى.

ملاحظات

  • CSS فقط (.qa-grid: repeat(N, minmax(0,1fr)) ثابت بدل auto-fill). محميّ بـ tests/dashboard-grid-align.test.mjs (+2). المجموعة: 2479.

[1.183.0] — 2026-08-14

إضافة (الماسح) — كشف تكرارات أذكى: الوظيفة نفسها المعاد نشرها برابط تتبّع لم تعُد تظهر مرتين.

إضافة

  • يتعرّف الماسح الآن على الوظيفة عبر مفتاح URL أساسي، فالوظيفة نفسها المعاد نشرها بمعامل تتبّع (?utm_…، gclid، …)، أو عبر http مقابل https، أو بشرطة مائلة نهائية / #جزء تُعامَل كوظيفة واحدة — بلا صف مكرَّر في نتائج المسح أو المسار، وبلا تقييم مُهدَر لوظيفة رأيتها من قبل. الوظائف المختلفة فعلاً (مُعرّف وظيفي مُبقى مثل gh_jid) تُحسب منفصلة كما هي.

ملاحظات

  • ملف جديد server/lib/url-key.mjs، موصول بإزالة التكرار في كلا الماسحين وبكاتب المسار. يُطبِّع بأقل قدر عمدًا — لا يدمج أبدًا وظيفتين مختلفتين. الاختبارات: tests/url-key.test.mjs (+5)، tests/parsers.test.mjs (+1). المجموعة: 2477 (+6).

[1.182.0] — 2026-08-14

تصحيح (الماسح) — نطاقات الراتب تظهر الآن بالشكل نفسه في كل اللغات.

تصحيح

  • أرقام الراتب في صفوف المسح والمتابعة تستخدم الرمزين المحايدين و (مثل ≥ 120000 EUR، ≤ 90000) بدلاً من الكلمتين الإنجليزيتين “from” / “up to” اللتين كانتا تتسربان دون ترجمة إلى الواجهات غير الإنجليزية. ينطبق على كل لوحة تُبلِّغ عن نطاق من جهة واحدة (Getro وRemotli وManfred وAgentic Jobs وJustJoin وJobicy)؛ النطاقات ذات الجهتين (100000–150000 USD) كانت محايدة أصلاً.

ملاحظات

  • عرض فقط — يحلّل مرشّح الراتب في العميل الأرقام بصرف النظر عن البادئة، فالتصفية دون تغيير. المجموعة: 2471.

[1.181.0] — 2026-08-14

إضافة (الماسح) — لوحات وظائف Getro تعرض الآن الراتب وجميع المواقع والوظائف عن بُعد.

إضافة

  • أصبح ماسح Getro (لوحات شبكات المواهب للصناديق) يعرض الآن راتبًا لكل وظيفة (نطاق سنوي + عملة)، ويسرد جميع المواقع بدلاً من الأول فقط، ويضع علامة على الوظائف عن بُعد. باتت وظيفة Getro في المسح والمتابعة تحمل نفس تفاصيل الراتب والموقع مثل بقية اللوحات.

ملاحظات

  • الماسح فقط؛ بلا اعتماد جديد، وبلا تغيير في المسارات / CSP / SSRF. الاختبارات: tests/sources-getro.test.mjs (+5). المجموعة: 2470 (+5).

[1.180.0] — 2026-08-14

تصحيح (متوسط، التقارير) — قائمة #/reports أصبحت جدولاً، واستُعيدت درجة حقيقية كان عنصر نائب في Machine Summary يخفيها.

تصحيح

  • قائمة #/reports جدول (التقرير · التاريخ · المصداقية · الدرجة)، لا شبكة من 4 بطاقات. كانت شريحة “لم يتم اكتشاف درجة” الطويلة تضغط عمود العنوان إلى ما يقارب الصفر، ثم يكسر overflow-wrap: anywhere في عنوان البطاقة اسم التقرير حرفاً حرفاً. الآن لكل حقل عموده الخاص، وتلتفّ خلية الاسم عند الكلمات، ويتمرر الجدول أفقياً على الشاشة الضيقة (حاوية .reports-scroll جديدة). مفتاح i18n جديد rep.colReport ×17.
  • لم تعُد الدرجة الحقيقية في المتن (**Итоговый балл:** 1.8 / 5) مخفيّة بعنصر نائب في Machine Summary (score: —). عندما كانت كتلة ## Machine Summary تحمل درجة غير رقمية أو خارج المدى، كانت تشغل خانة الدرجة المُحلَّلة وتمنع بديل صيغة-القيمة بالخط العريض، فيُظهر التقرير “لم يتم اكتشاف درجة” رغم وجود X / 5 حقيقي في المتن. أصبح parseReportHeader يستعيد صيغة-القيمة من المتن كلما لم يبقَ رقم صالح (الخطوة 4.5).

ملاحظات

  • العميل + المحلّل فقط؛ لا تغيير في المسارات / CSP / SSRF / الكتابة إلى الأصل. الاختبارات: tests/reports-table.test.mjs (+5)، tests/report-header-locale.test.mjs (+2). المجموعة: 2465 (+7).

[1.179.0] — 2026-08-13

تغيير (LOW، الماسح) — دمج 20 مفكِّك كِيانات HTML مكرَّرًا في الوحدة المشتركة (متابعة التكافؤ، يُغلق قائمة العمل).

تغيير

  • كان لدى 20 مصدر مسح كلٌّ منها decodeEntities/decodeXmlEntities خاص به (+ مساعد fromCodePoint) — نسخ انحرفت (ثلاث كان يمكن أن تُطلق RangeError، أُصلحت في v1.172.0؛ وأخرى تسمح بـ NUL/C0 أو تحلّل &#1a2; خطأً). صارت جميعها تمرّ عبر server/lib/html-entities.mjs الوحيد (مفكِّك آمن وفق مجموعة Char في XML 1.0)، مع إزالة ~237 سطرًا من التكرار. اكتسبت المصادر الثمانية من نوع RSS فكّ ترميز &nbsp; (كانت تعالج 5 كِيانات فقط)؛ ويُحفَظ فكّ الترميز المزدوج المقصود في cryptocurrencyjobs عبر اسم مستعار. يحتفظ hh بمفكِّكه (يعالج &mdash;/&ndash;، خارج الستة المشتركة). اختبار حارس جديد يفشل إذا أعاد أي مصدر إنشاء مفكِّك محلي.

ملاحظات

  • إعادة هيكلة تحافظ على السلوك؛ لا تغيير في المسارات / CSP / SSRF / الكتابة إلى الأصل. الاختبارات: tests/decoder-consolidation.test.mjs (+2). المجموعة: 2458 (+2).

[1.178.0] — 2026-08-13

أُصلح (LOW، تكافؤ مع الأصل) — تحديث ثابتَين قديمَين ليطابقا المشروع الأصل (PARENT-SYNC GAP #4 + #5).

أُصلح

  • User-Agent للمتصفح (GAP #4) — رُفِع BROWSER_LIKE_USER_AGENT (تُرسله workable/workday/oraclecloud/a16z/eightfold لتجاوز بوابات WAF/الروبوتات) من Chrome 131 إلى 151 ليطابق user-agent.mjs في الأصل؛ فالإصدار القديم أكثر عرضة للحجب. محمي باختبار Chrome major ≥ 151.
  • FALLBACK لحالات المتتبِّع (GAP #5) — اكتسب FALLBACK الاحتياطي في states.mjs (يُستخدم فقط عندما يتعذّر قراءة templates/states.yml الحيّ — نسخة جديدة / جذر CI معزول) أسماء الحالات التركية من الأصل (#2615): değerlendirildi، başvuruldu، yanıt verildi، mülakat، teklif، reddedildi، iptal edildi، uygun değil، kabul edildi/işe alındı. في الإنتاج كان الملف الحيّ يوفّرها بالفعل.

ملاحظات

  • ثابتان فقط؛ لا تغيير في المسارات / CSP / SSRF / الكتابة إلى الأصل. الاختبارات: tests/http-json.test.mjs (+1) + tests/states.test.mjs (+1). المجموعة: 2456 (+2).

[1.177.0] — 2026-08-13

أُصلح (MEDIUM، الماسح) — كان csod (Cornerstone) يُرجِع 0 وظيفة لدى المستأجرين الذين يحمون واجهة البحث بكوكيز الجلسة (parent #2769، PARENT-SYNC GAP #1).

أُصلح

  • بعض مستأجري Cornerstone يضعون كوكيز جلسة على صفحة البداية لموقع الوظائف ويردّون 401 CSOD Unauthorized على واجهة البحث ما لم تَعُد تلك الكوكيز مع رمز الحامل المجهول. تقرأ sources/csod.mjs الآن صفحة البداية عبر مساعد جديد fetchResponse، وتبني ترويسة Cookie من قيم Set-Cookie (cookieHeaderFrom — الاسم=القيمة فقط، دلالة الجرة) وتُعيدها في POST البحث. نفس الأصل فقط (المضيف مثبَّت + redirect:'error')، فلا يمكن أن تصل كوكيز الجلسة إلى طرف ثالث؛ ومستأجر لا يضع كوكيز يتصرف كما كان.

ملاحظات

  • جديد server/lib/http-json.mjs::fetchResponse (إضافي؛ المصادر القائمة غير متأثرة). لا تغيير في المسارات / CSP / SSRF / الكتابة إلى الأصل. الاختبارات: tests/sources-parity-v1118a.test.mjs (+1). المجموعة: 2454 (+1).

[1.176.0] — 2026-08-13

أُصلح (MEDIUM، التقارير) — درجة تحت تسمية عريضة لا تُدرِجها جداول RU ظلّت تُظهر “Score not detected” (FIND-5).

أُصلح

  • كتب تقريران بالروسية الدرجة بالصيغة **Итоговый балл:** 1.8 / 5 / **Скор:** 1.8 / 5 — وهي تسميات عريضة لا يُعدّدها REPORT_LABELS.ru (يعرف “Оценка”/“Балл” فقط)، فبقيت الدرجة دون تحليل. وبدلًا من توسيع قائمة المرادفات، يعود parseReportHeader الآن إلى صيغة القيمة: كسر على مقياس /5 تحت أي تسمية عريضة. إنها مستقلة عن اللغة، ومحصَّنة ضد العناوين (لا **، ولا قيمة /5)، وترفض تاريخًا مثل 5/5/2026 (استباق سلبي على المقام).

ملاحظات

  • محلِّل الخادم فقط؛ لا تغيير في المسارات / CSP / SSRF / الكتابة إلى الأصل. الاختبارات: tests/report-header-locale.test.mjs (+2). المجموعة: 2453 (+2).

[1.175.0] — 2026-08-13

أُصلح (LOW، تحصين) — حارس انحدار لوصف SEO الخاص بـ FIND-3 + تنظيف مشروعية آمن ضد القيم الفارغة (متابعة لمراجعة الذكاء الاصطناعي).

أُصلح

  • حارس تكافؤ وصف SEO — إصلاح v1.174.0 الذي استبدل “~55” المُثبَّت في meta.desc لكل لغة بعنصر نائب {adapters} مشتق من السجل لم يكن له اختبار، فكان يمكن أن ينحدر بصمت عند تحرير اللغة التالي. الاختبار الجديد المعزول عن CI tests/site-meta-desc-parity.test.mjs يفشل إذا فقد أي من الـ17 site/src/i18n/*.json العنصر النائب أو أعاد تثبيت عدد، أو إذا توقّف Landing.astro عن إدراجه في أوصاف meta الثلاثة.
  • تنظيف مشروعية آمن ضد الفراغ — تُرجِع stripEmphasis القيمة '' لمدخل فارغ بدل السلسلة “undefined” (الحقول مُهيَّأة كسلاسل، فهذا دفاع في العمق).

ملاحظات

  • اختبار + حارس من سطر واحد في المحلِّل؛ لا تغيير في المسارات / CSP / SSRF / الكتابة إلى الأصل. الاختبارات: tests/site-meta-desc-parity.test.mjs (+3). المجموعة: 2451 (+3).

[1.174.0] — 2026-08-13

أُصلح (HIGH، التقارير) — كانت التقارير المُترجَمة تُظهر “Score not detected”؛ ووصف SEO قديم.

أُصلح

  • تحليل الدرجة (FIND-1) — تقرير غير إنجليزي يحتوي عنوانه H1 على كلمة تسمية الدرجة (# Оценка вакансии: <العنوان>) لم يَعُد يخلط ذلك العنوان مع الدرجة. صار parseReportHeader يرتكز على التسمية العريضة المُترجَمة (**Оценка:** 1.5 / 5)، ويتخطى أسطر العناوين، ويشترط أن تكون التسمية ملاصقة لنقطتيها — لذا تعرض تقارير RU التي كانت تُظهر “Score not detected” درجتها الحقيقية.
  • شارة المشروعية (FIND-2) — يُزال تشديد Markdown من القيمة، فتُظهر الشارة “High Confidence” بدل “** High Confidence”.
  • تجاوز الدرجة — سطر درجة مذيَّل بنص حالة (“1.8, Status: Evaluated, …”) يُختصر إلى الدرجة فقط؛ ويكتسب .score-pill حدًّا بلا التفاف/تجاوز، ويمكن لعمود العنوان أن ينكمش، فلا تتجاوز الشارة الملونة حافة البطاقة.
  • وصف SEO (FIND-3) — أوصاف meta / OG / Twitter في cvstart.org (بجميع اللغات الـ17) كانت تُثبِّت “Scan ~55 job boards” بينما يَعُدّ المتن السجل الحقيقي (“~75”). صار الوصف يُدرج العدد المُشتق من السجل، فلا ينحرف مجددًا.

ملاحظات

  • محلِّل الخادم + عرض/CSS العميل + i18n الموقع؛ لا تغيير في المسارات / CSP / SSRF / الكتابة إلى الأصل. الاختبارات: tests/report-header-locale.test.mjs (+4). المجموعة: 2448 (+4).

[1.173.0] — 2026-08-13

أُضيف (LOW، الإعداد) — انضمام Hermes إلى قائمة أدوات CLI للذكاء الاصطناعي المكتشَفة (تكافؤ مع career-ops).

أُضيف

  • تبويب #/config ← «أدوات CLI للذكاء الاصطناعي» يكتشف الآن Hermes (Nous Research)، وهو بيئة تشغيل الوكيل التي دعمها المشروع الأصل حديثًا (الملف التنفيذي hermes). تنمو القائمة المسموحة الثابتة في server/lib/routes/cli-detect.mjs من 10 إلى 11 أداة؛ ويظل الاكتشاف مسحًا لـ PATH للقراءة فقط (لا يُنفَّذ أي ملف تنفيذي أبدًا).

ملاحظات

  • لا تغيير في i18n / المسارات / CSP / SSRF / الكتابة إلى الأصل؛ القائمة ثابتة وليست إدخالًا. المجموعة: 2444 (حُدِّث كناري cli-detect من 10 إلى 11).

[1.172.0] — 2026-08-13

أُصلح (MEDIUM، الماسح) — كِيان HTML مشوَّه كان قد يُعطِّل مصدر مسح (تكافؤ مع career-ops #2150).

أُصلح

  • كانت مصادر oraclecloud وgem وdassault تفكّ ترميز كِيانات HTML الرقمية بفحص Number.isFinite ضعيف قبل String.fromCodePoint — أي مرجع أعلى من 0x10FFFF (مثل &#99999999; من تغذية مشوَّهة أو خبيثة) كان يُطلق RangeError غير مُلتقَط ويُجهض تحليل ذلك المصدر بأكمله. وحدة مشتركة server/lib/html-entities.mjs (تعكس _html-entities.mjs في المشروع الأصل) تُقيّد الآن المراجع الرقمية بمجموعة Char في XML 1.0 §2.2 حتى لا يُطلق String.fromCodePoint أبدًا، وتُطابق الست عشري والعشري بشكل منفصل حتى لا يُحلَّل &#1a2; خطأً بعد الآن. المصادر الثلاثة تستوردها.

ملاحظات

  • لا تغيير للتغذيات الصحيحة؛ لا تغيير في JS / i18n / المسارات / CSP / SSRF / الكتابة إلى الأصل. يُتابَع دمج نسخ فك الترميز الـ~20 المتبقية في المصادر في qa/PARENT-SYNC-WORKLIST-v1.26.0.md.
  • الاختبارات: tests/html-entities.test.mjs (+7). المجموعة: 2444 (+7).

[1.171.0] — 2026-08-13

تغيير (منخفض، نظام التصميم) — رموز مقياس الخط + طبقات z-index (D-4، الخطوة الأولى). كانت الأحجام والتراص قيمًا حرفية لكل مكوّن.

تغيير

  • طبقات z-index — أُدخلت رموز --z-* (--z-topbar--z-skiplink) ورُحّل كل قيمة z-index حرفية إليها. القيم محفوظة والتراص متطابق؛ ويمنع اختبار كناري جديد الأرقام السحرية الجديدة.
  • مقياس الخط — سُلّم --font-size-* (xs 112xl 28، الأساس = Inter 15px)؛ ورُحّلت الأحجام الأساسية (دون تغيير بصري). القيم خارج السلّم تُرحّل تدريجيًا (docs/UX-ROADMAP.md).

ملاحظات

  • رمز CSS فقط؛ دون تغيير في السلوك/JS/i18n/المسار/CSP/SSRF/الكتابة. دون تغيير بكسل. tests/design-tokens-scale.test.mjs (+3). المجموعة: 2437 (+3).

[1.170.0] — 2026-08-13

أُضيف (منخفض) — تلميحات ETA صادقة على عمليات التوليد الطويلة بالذكاء الاصطناعي (P4-ETA). كانت العمليات الثقيلة (خطة المسار ~40 ث، التوجيه / السوق / التواصل ~30 ث، two-pager ~20 ث) تعرض “جارٍ التوليد…” فقط دون تقدير للمدة.

أُضيف

  • يظهر الآن بجانب كل زر توليد طويل تلميح خافت ⏱ ~N ثانية (مثل ETA في #/auto). نمط .eta-hint مشترك + مفتاحان عامّان (common.eta ~{n}s، common.etaTitle).

ملاحظات

  • العميل فقط؛ دون تغيير في المسار/CSP/SSRF/الكتابة. +2 مفتاح i18n ×17 (اللقطة 1219 → 1221). tests/generation-eta-hint.test.mjs (+2). المجموعة: 2434 (+2).

[1.169.0] — 2026-08-13

أُضيف (منخفض) — معاينة PDF مضمّنة (D-5). كان GET /api/output/pdfs/:name يفرض Content-Disposition: attachment، حتى إن زر “فتح” في #/cv كان يُنزّل الملف بدل عرضه.

أُضيف

  • ?inline=1 يقدّم نفس الملف المُعقّم مع Content-Disposition: inline، ليعرضه المتصفح في تبويب جديد كـ 👁 معاينة؛ والافتراضي يبقى تنزيلًا. دون مسار جديد؛ ونفس حراسات الاسم.
  • أصبح أول زر في قائمة PDF بـ #/cv هو 👁 معاينة بجانب ⬇ تنزيل. أُعيدت صياغة cv.openPdf من “فتح” إلى “معاينة” ×17.

ملاحظات

  • دون تغيير CSP/SSRF — نفس sanitizePathName. أُعيدت صياغة مفتاح i18n قائم ×17 (اللقطة 1219). tests/output-pdfs.test.mjs (+3). المجموعة: 2432 (+3).

[1.168.0] — 2026-08-13

تم الإصلاح (منخفض، a11y) — أصبحت صفوف مربّعات الاختيار تفي بالحد الأدنى 24×24 من WCAG 2.5.8 (D-2). كانت تسميات مربّع الاختيار/الراديو في #/scan و#/config و#/evaluate و#/cv-studio ضمن شريط ~22 بكسل.

تم الإصلاح

  • قاعدة محدّدة label:has(> input[type="checkbox"/"radio"]) { min-height: 24px } تضمن شريطًا ≥24 بكسل. min-height فقط — التسميات بالفعل flex فلا شيء يتزحزح؛ و.apply-checklist (32 بكسل) كان متوافقًا مسبقًا.

ملاحظات

  • CSS فقط؛ دون تغيير في السلوك/JS/i18n/المسار/CSP/SSRF/الكتابة. tests/checkbox-target-size.test.mjs (+1). المجموعة: 2429 (+1).

[1.167.0] — 2026-08-13

تم الإصلاح (منخفض، نظام التصميم) — أصبحت الأسطح المرتفعة تنفصل عن الخطوط الفاصلة (D-3). كانت الرموز --panel-2 / --surface-elev1 تُحلّ إلى --slate — نفس قيمة الخطوط --line / --border — دون فصل بصري.

تم الإصلاح

  • رمز مخصّص مدرك للسمة --elev (#eef1f6 فاتح / #1e232e داكن، مختلف عن --slate في السمتين) يدعم الآن الأسطح المرتفعة؛ وتبقى الخطوط على --slate. أما بقية النتائج (D-2، D-4، D-5، P4-ETA) فمسجّلة في docs/UX-ROADMAP.md.

ملاحظات

  • رمز CSS فقط؛ دون تغيير في السلوك/JS/i18n/المسار/CSP/SSRF/الكتابة. tests/elevation-token.test.mjs (+2). المجموعة: 2428 (+2).

[1.166.0] — 2026-08-13

تم الإصلاح (منخفض) — أصبحت مصطلحات المعيار تطابق الوثائق المرجعية. تصف career-ops.org/docs “خمسة أبعاد إضافةً إلى تقييم عام شامل”، لكن واجهة الويب وcvstart.org والويكي كانت تقول “معيار من ستة أبعاد” (5 + 1 = 6، لكن الصياغة كانت مختلفة).

تم الإصلاح

  • اعتُمدت صياغة الوثائق — “خمسة أبعاد إضافةً إلى تقييم عام شامل” — بشكل متّسق في README ×17، وموقع cvstart.org ×17، ودليل المساعدة ×17، وdocs/career-ops-canonical.md، والويكي (Home ×17 + Features).

ملاحظات

  • الوثائق/التسويق فقط؛ دون تغيير في الكود/مفتاح i18n/المسار/CSP/SSRF/الكتابة. tests/rubric-terminology.test.mjs (+2). المجموعة: 2426 (+2).

[1.165.0] — 2026-08-13

تم الإصلاح (منخفض) — أصبح مصطلح “Two-pager” متّسقًا داخل كل لغة. في العربية كان الشريط الجانبي يعرض “Two-pager” اللاتينية بينما كان <h1> مُترجَمًا بالكامل — وهي السلسلة اللاتينية الوحيدة في تنقّل RTL معكوس بخلاف ذلك.

تم الإصلاح

  • قرار مُطبَّق: لكل لغة، يتّفق nav.twoPager وtwoPager.title على المصطلح (كلاهما لاتيني أو كلاهما مُترجَم). العربية وحدها كانت منقسمة؛ وأصبح تسمية التنقّل الآن مُترجَمة (“الصفحتان”). يفشل اختبار كناري جديد إذا فصلت أي لغة بينهما مجددًا.

ملاحظات

  • نص فقط؛ دون تغيير في المسار/CSP/SSRF/الكتابة. تغيّرت قيمة i18n واحدة (ar)؛ دون مفاتيح جديدة (اللقطة 1219). tests/two-pager-term-consistency.test.mjs (+2). المجموعة: 2424 (+2).

[1.164.0] — 2026-08-13

تم الإصلاح (منخفض) — لم يعُد نص البحث النائب يفيض في أي لغة. كان “Find a company, role or URL…” يُقتطع (nowrap) عند تقلّص شريط البحث، ولم يكن نصف “…or URL” يظهر أبدًا.

تم الإصلاح

  • أصبح top.search (×17) القصير “ابحث أو ألصق رابطًا” (≤24 حرفًا في كل لغة)، يتّسع حتى في شريط ضيّق ويحافظ على الإشارة إلى الرابط. النص النائب الاحتياطي في index.html مطابق؛ ويحتفظ aria-label بالتفصيل الكامل.

ملاحظات

  • نص فقط؛ دون تغيير في المسار/CSP/SSRF/الكتابة. أُعيدت صياغة مفتاح i18n واحد قائم ×17 (دون جديد؛ اللقطة 1219). tests/search-placeholder-fit.test.mjs (+2). المجموعة: 2422 (+2).

[1.163.0] — 2026-08-13

تم الإصلاح (منخفض) — أصبح مساعد “اسأل الوثائق” داخل التطبيق يغطّي تصدير تقرير إلى PDF. كان يجيب بأن الدليل لا يغطّي ذلك، رغم وجود زر 📄 Generate PDF يعمل في #/reports/:slug.

تم الإصلاح

  • أُضيف عنوان H3 “تصدير تقرير إلى PDF” تحت §10 التقارير في جميع حزم المساعدة الـ17 (مكان الزر، وأن الملف يُكتب في output/*.pdf، ويتطلّب Playwright، وراجِع قبل الإرسال). أصبح بحث المساعد يُظهر قسم التقارير.

ملاحظات

  • الوثائق/المساعدة فقط؛ دون تغيير في الكود/المسار/CSP/SSRF/الكتابة. بوابة المساعدة 112 → 113 H3 (31 H2 دون تغيير). tests/help-reports-pdf-section.test.mjs (+2). المجموعة: 2420 (+2).

[1.162.0] — 2026-08-13

تم الإصلاح (متوسط) — أصبح “?” المساعدة هدفًا بحجم ≥24×24 (WCAG 2.5.8). كان .help-hint بحجم 18×18 بكسل مع padding:0، دون الحد الأدنى، في كل عنوان.

تم الإصلاح

  • صار صندوق .help-hint الآن 24×24 (الهدف القابل للقياس) بينما تبقى الحلقة المرئية 18 بكسل — تُرسم عبر ::before متمركز، فلا يتغيّر المحرف ولا خط أساس <h1>. تتبع حالات التمرير/النشط/التركيز الحلقة؛ الهامش 6→3 بكسل للحفاظ على الفجوة.

ملاحظات

  • CSS فقط؛ دون تغيير JS/i18n/المسار/CSP/SSRF/الكتابة. tests/help-hint-target-size.test.mjs (+2). المجموعة: 2418 (+2).

[1.161.0] — 2026-08-13

تم الإصلاح (متوسط) — يعرض #/reports شارة “لم يُكتشف التقييم” بدل الفراغ. بعد محلّل v1.159.0 المتعدد اللغات، كان التقرير بلا تقييم قابل للتحليل يعرض منطقة فارغة لا تُميَّز عن الفشل.

تم الإصلاح

  • تتفرّع خلية التقييم الآن: يوجد تقييم → حبة اللون؛ لا يوجد → شارة .score-muted خافتة (“لم يُكتشف التقييم”، ×17) مع تلميح “افتح التقرير…”. تبقى البطاقة role="link" قابلة للتشغيل بلوحة المفاتيح ويظهر التاريخ.
  • تعيد استخدام الرمز المحايد القائم؛ دون لون جديد.

ملاحظات

  • العميل فقط؛ دون تغيير في المسار/CSP/SSRF/الكتابة. +2 مفتاح i18n ×17 (اللقطة 1217 → 1219). المجموعة: 2416 (+3).

[1.160.0] — 2026-08-13

تم الإصلاح (عالي) — لم يعُد نص المزوّدين يناقض وعد المزوّدين السبعة. كان #/config يقول إن التقييم المباشر “يستخدم مفتاح Anthropic أو Gemini” وإن مفتاح OpenAI “لا تستخدمه واجهة الويب”؛ وكانت لوحة المعلومات تقول “Anthropic-first scoring” — وكلّه خاطئ منذ سلسلة المزوّدين السبعة (v1.157.0).

تم الإصلاح

  • config.providerModelNote (×17): يذكر الآن أن ⚡ التقييم المباشر يعمل دون واجهة بأيٍّ من مفاتيحك السبعة (Anthropic · Gemini · OpenAI · Qwen · OpenRouter · GitHub Models · Hermes)، مرتّبة تلقائيًا مع الرجوع الاحتياطي. حُذفت جملة OpenAI الخاطئة.
  • dash.quick.evaluateSub (×17): محايد تجاه المزوّد (“تقييم الملاءمة 0–5”). Keys: N / 5N / 7.

ملاحظات

  • نص فقط؛ دون تغيير في المسار/CSP/SSRF/الكتابة. دون مفاتيح i18n جديدة (اللقطة 1217). المجموعة: 2413 (+3).

[1.159.0] — 2026-08-13

تم الإصلاح (عالي) — لم تعُد بيانات التقرير الوصفية مرتبطة باللغة. التقارير المُولَّدة بلغة غير الإنجليزية كانت تعرض شريط بيانات فارغًا في #/reports، لأن parseReportHeader كان يتعرّف على الوسوم الإنجليزية العريضة فقط.

تم الإصلاح

  • يحلّل parseReportHeader الآن كتلة YAML الثابتة ## Machine Summary (score: / legitimacy: / date: — نفس المصدر الذي يقرؤه auto-pipeline بالفعل): الوسوم الإنجليزية → كتلة Machine Summary → الوسوم المحلّية (REPORT_LABELS، 17 لغة). تبقى التقارير الإنجليزية متطابقة بايتًا ببايت.
  • تحليل رقمي متسامح (1.5/5، 1,5/5، 1.5 из 5، 4.5 out of 5)؛ ويرجع التاريخ إلى mtime الملف عند غيابه.

ملاحظات

  • قراءة/تحليل فقط؛ دون تغيير في المسار أو CSP أو SSRF أو الكتابة في الأب. دون مفاتيح i18n جديدة. المجموعة: 2410 (+8).

[1.158.0] — 2026-08-12

تم الإصلاح — خطآن تجميليان في العرض (علامة «?» تتسرّب إلى عنوان التبويب، وعدد مزوّدين خاطئ في صفحة الهبوط). العرض فقط؛ دون تغيير في السلوك أو الأمان أو تدفّق البيانات.

تم الإصلاح

  • لم تعُد «?» الخاصة بـ HelpHint تتسرّب إلى document.title. كان الراوتر يشتق عنوان التبويب من h1.textContent الخام فيظهر «Vacancy search?» بدلاً من «Vacancy search». الآن يستنسخ router.js::focusNewView العنوان ويزيل .help-hint ثم يقرأ النص؛ وتبقى «?» المرئية دون مساس.
  • كانت cvstart.org تُظهر «17 AI providers» بدلاً من «7». كان المساعد sub() في Features.astro يستبدل كل {n} بعدد اللغات (17) قبل الاستبدال لكل بطاقة؛ والآن يُحلّ {n} لكل بطاقة (المزوّدون → 7، اللغات → 17).

ملاحظات

  • دون تغيير في الخادم أو المسار أو CSP أو SSRF أو مفاتيح i18n. المجموعة: 2402 اختبار (+1).

[1.157.0] — 2026-08-12

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

تم الإصلاح

  • السبب الجذري: تثبيت LLM_PROVIDER دون مفتاح (مثل LLM_PROVIDER=claude من init) كان يصل إلى طريق مسدود؛ والآن يتراجع إلى ترتيب auto بين المزوّدين المُهيّئين (في selectActiveProvider وكِلا سلسلتَي الإرسال).
  • منطق الواجهة (#/deep وواجهات mode-page) يستخدم الآن window.ProviderStatus (/api/status/providers، السبعة) بدل فحص Anthropic/Gemini المتقادم؛ نصوص مُعاد صياغتها (deep/eval × 17) + شارة «تقييمات مباشرة» في لوحة المعلومات + config.llmProviderHint.

ملاحظات

  • لا تغييرات أمنية. المجموعة: 2401 اختبارًا (+5).

[1.156.0] — 2026-08-12

إعادة هيكلة — تقسيم scan.js تحت حدّ الحجم (P-16) + إصلاح CodeQL. كان scan.js بحجم 906 أسطر؛ استُخرج مصنعان يحافظان على السلوك → 648. يُكمل زوج تقسيم الواجهات P-15/P-16.

تم التغيير

  • ملفان جديدان scan/runner.js (محرّك تنفيذ الفحص) وscan/filters.js (آلة حالة المرشّحات) عبر حقائب ctx/refs؛ يربطهما scan.js.

تم الإصلاح

  • CodeQL js/useless-assignment-to-local (#428) في config/tab-controller.js: let n = i;let n;.

ملاحظات

  • إعادة هيكلة صرفة دون تغيير سلوك؛ أُعيد توجيه 4 اختبارات تقرأ المصدر. كلتا الواجهتين الكبيرتين أصبحتا تحت 800 (اكتمل P-15/P-16). المجموعة: 2396 اختبارًا.

[1.155.0] — 2026-08-12

إعادة هيكلة — تقسيم config.js تحت حدّ الحجم (P-15). كان config.js بحجم 1030 سطرًا (فوق حدّ 800)؛ استُخرج وحدتان تحافظان على السلوك ليصبح 783.

تم التغيير

  • ملفان جديدان config/field-specs.js (بيانات الحقول + قوائم النماذج) وconfig/tab-controller.js (مصنع شريط التبويبات)؛ يشير إليهما config.js ومنطق العرض دون تغيير.

ملاحظات

  • إعادة هيكلة صرفة دون تغيير سلوك؛ أُعيد توجيه 6 اختبارات تقرأ المصدر. يبقى scan.js (906) كما هو (مقسَّم جزئيًا بالفعل؛ نواته مترابطة أكثر من أن يفيدها تقسيم آلي نظيف). المجموعة: 2396 اختبارًا.

[1.154.0] — 2026-08-12

دليل جديد — «تشغيل المنظومة كاملةً في السحابة». لا يملك career-ops سردًا خاصًا للسحابة/الخادم، فأضفنا واحدًا: وصفة خطوة بخطوة لوضع خط الأنابيب الأصل career-ops وهذا العارض career-ops-ui ومحرّك الذكاء الاصطناعي (اشتراك Claude عبر Claude Code، أو Hermes محلي، أو مفاتيح API) على خادم صغير دائم التشغيل. يصل بوصفه المساعدة §31 بـ17 لغة، وقسمًا في README، وصفحة ويكي.

أُضيف

  • المساعدة §31 «تشغيل المنظومة كاملةً في السحابة» (× 17) — الأجزاء الثلاثة، التجهيز + التثبيت، اختيار المحرّك، والعرض الآمن (بروكسي عكسي HTTPS + مصادقة + ثوابت CSP/SSRF/XSS/لا-أسرار). تنمو حزمة المساعدة إلى 31 H2 / 112 H3.
  • README — قسم «تشغيل المنظومة كاملةً في السحابة» (× 17) + صفحة Cloud-Deployment في الويكي.

ملاحظات

  • توثيق فقط — لا مسار ولا خادم ولا تغيير عميل؛ ولا مفتاح i18n جديد. تنتقل اختبارات المساعدة الأربعة إلى عقد 31 H2 / 112 H3. المجموعة: 2396 اختبارًا (دون تغيير).

[1.153.0] — 2026-08-12

نقل ماسح Jobvite إلى موجز XML العام (مزامنة مع الأصل). أوقف المشروع الأصل واجهة JSON الخاصة بـ Jobvite (تعيد صفر وظيفة)؛ وكان source في web-ui يستخدم النقطة الميتة نفسها، فكانت أي شركة Jobvite متتبَّعة تُمسح فارغة بصمت. نقل إصلاح الأصل (#2623): يقرأ الآن موجز XML العام لكل مستأجر بمفتاح companyEId.

تم الإصلاح

  • كان source يطلب واجهة JSON الموقوفة ويعيد صفر وظيفة؛ وهو الآن يطلب https://app.jobvite.com/CompanyJobs/Xml.aspx?c={companyEId} ويحلّل XML <result><job>… (CDATA + كيانات، detail-url قبل apply-url).

تم التغيير

  • حلّ companyEId: (1) company_eid: في البوابة، (2) معامل c= لـ api: صريح، (3) اكتشاف من صفحة اللوحة. يُرفق fetchText (http-json.mjs) الحقلين .location/.retryAfter بخطأ non-ok (قراءة فقط، متوافق رجعيًا).

ملاحظات

  • الأمان — مضيفان (jobs.jobvite.com، app.jobvite.com) مثبّتان عبر assertJobviteUrl: https فقط، قائمة سماح صارمة، دون اتباع أي تحويل. companyEId مجرد قيمة ?c=؛ وعدد المصادر دون تغيير.
  • المجموعة: 2396 اختبارًا (+4).

[1.152.0] — 2026-08-12

مزوّد Hermes — اكتمل التوصيل + تحديث الوثائق. كشفت مراجعة كود لتكامل Hermes في v1.151.0 عن ثغرتين حقيقيتين وأربعة بنود اكتمال؛ أُصلحت جميعها هنا، ورُفعت قائمة مزوّدي LLM في التطبيق كله إلى السبعة الكاملة على كل أسطح الوثائق واللغات الـ17.

تم الإصلاح

  • تعذّر فرض Hermes من #/config — كانت قائمة LLM_PROVIDER تُدرج ستة مزوّدين فقط، فيمكن ضبط HERMES_API_KEY دون فرض Hermes من الواجهة. أصبح hermes الخيار الثامن، ويمنع اختبار تكافؤ جديد انحراف القائمة مجددًا عن LLM_PROVIDERS.
  • كانت المفاتيح المحلية القصيرة تُرفض بصمت — عُيِّر حدّ الـ20 حرفًا في isUsableKey لمفاتيح السحابة؛ يستخدم hasHermesKey الآن حدًا مخففًا قدره 8 (مثال وثائق Hermes نفسه 19 حرفًا).

تم التغيير

  • نُظِّمت قائمة المزوّدين إلى السبعة الكاملة في README (× 17) والمساعدة داخل التطبيق (× 17) وقاموس config.llmProviderHint (× 17) وdocs/sdd؛ ويكمل hermesChatUrl مضيفًا بلا مسار؛ ويذكر نص التراجع اليدوي Hermes.

ملاحظات

  • الأمان دون تغيير — لا مسار جديد ولا تغيير SSRF/CSP؛ ويكتسب health/doctor صف HERMES_API_KEY.
  • المجموعة: 2392 اختبارًا (+2).

[1.151.0] — 2026-08-12

أصبح Hermes الآن مزوّد LLM موصولًا (Phase 5) — أكّد فحص نطاق Phase 5 أن Hermes من Nous Research يضم خادم API Server متوافقًا مع OpenAI (hermes gatewayPOST /v1/chat/completions)، لذا يُشغّل career-ops-ui الآن تقييمات مباشرة عبر Hermes محلي تمامًا مثل OpenAI/Qwen. اضبط HERMES_API_KEY في إعدادات التطبيق لينضم إلى ترتيب auto (الأخير). يُغلق آخر بند مفتوح في خارطة الطريق — Phase 5, Shape A.

أُضيف

  • مزوّد LLM Hermes (Shape A)runHermes فوق العميل المشترك runOpenAICompatible (server/lib/openai.mjs)، في كلتا السلسلتين (llm-dispatch.mjs + routes/llm.mjs)، في ذيل ترتيب auto + التثبيت LLM_PROVIDER=hermes، و/api/status/providers، وllm-pricing.mjs. يصل إلى base URL محلي قابل للضبط (افتراضيًا http://127.0.0.1:8642/v1) بمصادقة Bearer — وهو نقطة نهاية مزوّد مُهيّأة (مثل OpenRouter/Qwen)، وليست عنوان وظيفة يقدّمه المستخدم، لذا لا يمرّ عبر حارس SSRF.
  • حقول #/configHERMES_API_KEY (سرّي) + HERMES_BASE_URL + HERMES_MODEL (افتراضيًا hermes-agent)، مع 6 مفاتيح i18n جديدة × 17 لغة (لقطة 1208 → 1214).

تغيّر

  • حُلّ فحص النطاق: ينتقل docs/integrations/HERMES.md والمساعدة المدمجة §30 (× 17) وعرض README (× 14) ومهارة hermes-bridge وخارطة الطريق من «مخطَّط له / غير موصول بعد» إلى موصول (Shape A). لم يلزم Shape B (relay مخصّص لبيئة تشغيل الوكيل).

ملاحظات

  • الأمان: جلب المزوّد هو نقطة نهاية مُهيّأة، من الفئة نفسها لبقية المزوّدين المتوافقين مع OpenAI — لا سطح SSRF جديد، ولا تغيير في CSP/المنقّي. HERMES_API_KEY هو SECRET_KEY (لا يُعرَض أبدًا).
  • اختبارات (معزولة في CI، نقل وهمي): tests/hermes-provider.test.mjs (+5)؛ اختبار الكناري «لا فرع لـ Hermes» من v1.146.0 مقلوب ليؤكّد أنه موصول؛ حُدّثت اختبارات سطح المزوّدين إلى ترتيب 7 مزوّدين.
  • المجموعة: 2390 اختبارًا (+5).

[1.150.0] — 2026-08-12

حالات فارغة متسقة (صقل Phase 4) — أصبح كل لوح “لا شيء بعد” يُعرَض الآن عبر نمط .empty المشترك الوحيد، بدلاً من أن تعيد بعض العروض إعلان المظهر داخليًا برقم سحري 40px. إصلاح صغير للاتساق البصري؛ الحالات الفارغة في #/activity و#/cv-studio و#/stats و#/usage أصبحت الآن مطابقة لجميع الأخرى (حشوة 48px مرمَّزة + حدّ متقطّع).

تغيّر

  • #/activity و#/cv-studio و#/stats و#/usage أزالت style: { padding: '40px', textAlign: 'center', color: 'var(--foggy)' } المضمَّن على الألواح الفارغة — الخصائص الثلاث تقدّمها فعلاً الفئة المشتركة .empty (--space-7 = 48px، توسيط، لون خافت، حدّ متقطّع). وهكذا تُعرَض هذه الأربعة مطابقةً لنحو 25 لوحًا .empty آخر.
  • التجاوزات المشروعة لكل عرض (#/dashboard width:100%، #/pipeline border:none) لم تُمَس — أُزيلت فقط الإعلانات المكرّرة تمامًا.

ملاحظات

  • تنظيف استخدام CSS في العميل فقط — دون تغيير في المسار أو الخادم أو مفتاح i18n أو قواعد CSS (فئة .empty نفسها دون تغيير)؛ لقطة القاموس 1208. تم التحقق في المتصفّح (لوح #/usage الفارغ يحسب 48px حشوة + حدّ متقطّع، 0 أخطاء وحدة تحكّم).
  • الاختبار الكناري الجديد tests/empty-state-consistency.test.mjs يبقي .empty مصدر الحقيقة الوحيد. تظل Phase 5 (مزوّد Hermes) معطَّلة.
  • المجموعة: 2385 اختبارًا (+2: tests/empty-state-consistency.test.mjs).

[1.149.0] — 2026-08-12

نقل البوابات إلى الإعدادات (Phase 4) — أصبح #/portals الآن في مجموعة التنقّل Setup بجوار إعدادات التطبيق، بدلاً من Sourcing. منذ v1.144.0 وهو واجهة إعدادات (تفعيل/تعطيل الشركات المتتبَّعة + فحص حالة ATS)، وليس إجراء sourcing — لذا هذا مكانه الصحيح. تغيير في التنقّل فقط؛ الصفحة ومسارها دون تغيير.

تغيّر

  • عنصر التنقّل #/portals → مجموعة Setup (في public/index.html)، موضوع مباشرة بعد إعدادات التطبيق. أُزيل من مجموعة Sourcing (التي تحتفظ بـ Scan / Pipeline / Auto-pipeline / الشركات المموَّلة). المسار #/portals والعرض والتسمية nav.portals دون تغيير — تحرّك موضع الشريط الجانبي فقط.

ملاحظات

  • ترميز تنقّل فقط — دون تغيير في المسار أو العرض أو مفتاح i18n أو الخادم. تم التحقق في المتصفّح (0 أخطاء وحدة تحكّم)؛ محميّ بـ tests/portals-nav-placement.test.mjs.
  • المجموعة: 2383 اختبارًا (+2: tests/portals-nav-placement.test.mjs).

[1.148.0] — 2026-08-12

مرشّحات فحص أوضح (Phase 4) — لوحة المرشّحات أصبحت الآن شبكة مرتّبة — انتقلت لوحة مرشّحات #/scan من flex-wrap غير منتظم من صناديق صلبة متفاوتة العرض إلى شبكة متجاوبة، وأصبحت إجراءات «تطبيق / إعادة تعيين» الآن في صفّها الخاص المنفصل المحاذى إلى اليمين. المرشّحات نفسها والسلوك نفسه — فقط أسهل قراءة. تحسين تصميمي (دون parent-sync).

تغيّر

  • لوحة مرشّحات #/scan → شبكة متجاوبة — أصبح .scan-filters الآن display: grid بأعمدة repeat(auto-fill, minmax(180px, 1fr)) وتباعد منتظم، بحيث تصطفّ المرشّحات الـ11 الموسومة في أعمدة مرتّبة عند أي عرض بدلاً من الالتفاف في صفّ غير منتظم.
  • إجراءات «تطبيق / إعادة تعيين» تمتدّ عبر الشبكة كاملةً في صفّها الخاص، مفصولةً بخطّ رفيع ومحاذاة إلى اليمين. أُزيلت حيلة التسمية المخفية القديمة + الغلاف flex الداخلي في scan.js.

ملاحظات

  • CSS فقط + تنظيف بسيط للـ DOM — كل معرّف مرشّح (#scan-filter-*، #scan-apply) وربط SR.render() دون تغيير، لذا يبقى مسار Playwright سليمًا. لا مفاتيح i18n جديدة.
  • تم التحقق في المتصفّح (0 أخطاء وحدة تحكّم)؛ محميّ بـ tests/scan-filters-grid.test.mjs.
  • المجموعة: 2381 اختبارًا (+3: tests/scan-filters-grid.test.mjs).

[1.147.0] — 2026-08-12

Hermes & Telegram — قسم المساعدة داخل التطبيق + واجهة على cvstart.org (Phase 5b، الجزء 2) — الجزء الثاني والأخير من عمل توثيق Hermes: أصبح الشرح الآن داخل دليل المساعدة الخاص بالتطبيق بجميع اللغات الـ17، ويجيب مساعد التوثيق المدمج عن أسئلة Hermes استنادًا إليه. لا يزال الأمر يقتصر على التوثيق — يبقى مسار موفّر LLM الخاص بـ Hermes مخططًا له / غير موصول بعد (Phase 5).

أُضيف

  • مساعدة داخل التطبيق §30 «Hermes & Telegram» × 17 لغة — قسم دليل جديد (ما هو Hermes + شكلا التكامل؛ التشغيل على خادم سحابي؛ Telegram عبر Hermes + قاعدة «ما الذي يجب ألا يُكشَف»)، يمكن الوصول إليه من #/help. يلتقطه grounding في docs-assistant / DocsFab تلقائيًا لأن كليهما يقرأ docs/help/<lang>.md.
  • cvstart.org — رابط إلى دليل Hermes يشير إلى المستند على GitHub.

تغيّر

  • رُفِع حد حزمة المساعدة إلى 29 → 30 H2 / 105 → 108 H3 (canonical-docs-coverage، help-ui، help-ru-config-section)؛ يضيف §30 ثلاثة عناوين H3.

ملاحظات

  • لا شيء يستدعي Hermes حتى الآن. يتحقق الاختبار الكناري الجديد tests/help-hermes-section.test.mjs من أن كل لغة تحوي §30 مع مراسيها المستقلة عن اللغة (docs/integrations/HERMES.md، hermes-bridge، #/help، 127.0.0.1، Telegram). يظل المزوّد معطَّلًا بانتظار عقد واجهة برمجة التطبيقات في Phase 5.
  • هذا يُغلق مُخرَج التوثيق + skill لـ Phase 5b؛ ويظل تكامل المزوّد (Phase 5) بندًا منفصلًا ومعطَّلًا.
  • المجموعة: 2378 اختبارًا (+2: tests/help-hermes-section.test.mjs).

[1.146.0] — 2026-08-12

وكيل Hermes + Telegram — دليل التكامل + skill (Phase 5b، الجزء 1) — يمكنك تشغيل career-ops-ui على خادم سحابي وربط أحداثه (فحص منتهٍ، تقرير جديد، متابعة عاجلة) بـ Telegram عبر وكيل Hermes من Nous Research. يقدّم هذا الإصدار وثائق التصميم + النشر و skill بعنوان hermes-bridge؛ ويبقى مسار موفّر LLM الخاص بـ Hermes مخطَّطًا له / غير موصول بعد (معطَّل بسبب spike عقد API لـ Phase 5). الوثائق تسبق الكود بشكل مقصود.

أُضيف

  • docs/integrations/HERMES.md — الشرح المعمّق: شكلا التكامل (نقطة نهاية متوافقة مع OpenAI مقابل بيئة تشغيل وكيل)، النشر على خادم سحابي (reverse proxy + HTTPS + systemd، وعقد القراءة فقط مع الـ parent على جهاز بلا واجهة)، Telegram عبر Hermes، وقائمة نموذج تهديدات لما «يجب ألا يُعرَض» (لا سيرة ذاتية / راتب / محتوى تقارير / مفاتيح إلى القناة).
  • عرض ## Hermes agent + Telegram في README — إشارة مختصرة مع رابط في README الإنجليزي، وتنعكس في نسخ README المترجمة بالكامل لكل لغة.
  • skill بعنوان hermes-bridge (.claude/skills/hermes-bridge/) تُفعّل الدليل عمليًا — فحوصات الشروط المسبقة وحدود النطاق (Node ≥ 18، وجود المفاتيح، إمكانية الوصول إلى نقطة النهاية عبر المسار الآمن من SSRF)، لا تكتب الأسرار على القرص/السجلات أبدًا، وترفض اختراع نقطة نهاية لـ Hermes أو الزعم بأن الموفّر متصل.
  • قسم Integrations في docs/architecture/OVERVIEW.md يربط بالدليل.

ملاحظات

  • لا شيء يستدعي Hermes حتى الآن. اختبار كناري (tests/hermes-docs.test.mjs) يتحقق من علامات الصدق «مخطَّط له / غير موصول بعد» ومن أن llm-dispatch.mjs لا يحتوي على أي فرع لـ Hermes/Nous — لذا فإن توصيل الموفّر لاحقًا يجب أن يحدّث الوثائق + خارطة الطريق في نفس التغيير.
  • مؤجَّل إلى v1.147.0 (Phase 5b، الجزء 2): قسم المساعدة داخل التطبيق «Hermes & Telegram» H2 × 17 لغة، وواجهة تسويق cvstart.org.
  • المجموعة: 2376 اختبارًا (+4: tests/hermes-docs.test.mjs).

[1.145.0] — 2026-08-12

إحصاءات ثاقبة (تابع): رسم بياني قابل لإعادة البناء — أصبحت علامة التبويب «اتجاه الأدوار المستهدفة» في #/stats تحوي أداة إنشاء رسم بياني: اختر مقياسًا × بُعدًا فيُعاد رسمه مباشرة. طلب UX من المستخدم (بدون parent-sync).

أُضيف

  • رسم بياني قابل لإعادة البناء بحسب المقياس × البُعد — اختر مقياسًا (الوظائف / الراتب الوسيط / متوسط الراتب) وبُعدًا (حسب البلد / حسب الدور) فيُعاد رسم المخطط الشريطي فورًا. تحترم مقاييس الرواتب العملة ومبدّل سنويًا ⇄ شهريًا؛ أما الوظائف فهي عدّ بسيط.
  • 8 مفاتيح i18n جديدة × 17 لغة؛ اللقطة 1200 → 1208.

ملاحظات

  • تم التحقق في المتصفح (0 أخطاء وحدة تحكم). المجموعة: 2372 اختبارًا (+2).

[1.144.0] — 2026-08-12

الإعدادات والمرشحات (المرحلة 4، الجزء 1): تفعيل/تعطيل البوابات المتتبَّعة — يمكنك الآن تشغيل أو إيقاف شركة مراقَبة من #/portals، والماسح يحترم ذلك. طلب UX من المستخدم (بدون parent-sync).

أُضيف

  • زر تفعيل/تعطيل لكل شركة في #/portals — نقرة واحدة تُطفئ بوابة (الماسح EN يتخطى بالفعل الشركات ذات enabled: false، فتخرج البوابة المعطَّلة من كل عمليات الفحص المقبلة) أو تعيد تشغيلها، مع إشعار تفاؤلي.
  • POST /api/portals/toggle — كتابة صريحة من المستخدم تبدّل جراحيًا ومع التحقق من التحليل علامة enabled لشركة في portals.yml (مع الحفاظ على التعليقات والترتيب وبقية الحقول). 5 مفاتيح i18n جديدة × 17 لغة؛ اللقطة 1195 → 1200.

ملاحظات

  • تغيير الماسح كان صفرًاen-scanner.mjs يفلتر بالفعل enabled !== false. المجموعة: 2370 اختبارًا (+3).

[1.143.0] — 2026-08-12

سهولة الفهم (تابع): تلميحات ? على شاشات العمل الأساسية — يغطّي ? المساعدة الآن الصفحات التسع الرئيسية للإجراءات، بكل اللغات. تحسين UX مبلَّغ من المستخدم (بدون parent-sync).

أُضيف

  • تلميح مساعدة ? على 9 عناوين شاشات إضافية#/scan, #/evaluate, #/cv-studio, #/tracker, #/config, #/deep, #/batch, #/auto, #/apply تحصل على ? مضمّن (عبر HelpHint.title) يفتح نافذة منبثقة مترجمة «ماذا يفعل / كيف تستخدمه / ماذا تتوقع» — نفس المكوّن الآمن لـ CSP من v1.139.0.
  • 9 مفاتيح i18n جديدة × 17 لغة (help.hint.scan/…/apply)؛ اللقطة 1186 → 1195.

ملاحظات

  • تم التحقق في المتصفح (0 أخطاء وحدة تحكم). المجموعة: 2365 اختبارًا (+1).

[1.142.0] — 2026-08-12

تصحيح: لا مزيد من النمط المهني “Unknown” — أصبحت #/orientation تُصنّف دائمًا من بين ثمانية متجهات مهنية مُسمّاة بدلاً من الإجابة أحيانًا بـ“Unknown” والتوصية بـ«مضاعفة التركيز» عليه. تصحيح مبلَّغ من المستخدم (بدون parent-sync).

أُصلح

  • #/orientation — يمنع موجّه الذكاء الاصطناعي الآن أي نمط خارج المجموعة. يجب على النموذج ترتيب أفضل ثلاثة من بين الثمانية المُسمّاة تحديدًا، ولا يجوز أبدًا الإجابة بـ“Unknown”/“N/A”/“بيانات غير كافية” أو اختراع تسمية. وإن كانت السيرة ضعيفة، يسمّي الأقرب ثلاثة بثقة أقل ويوضّح الأدلة الناقصة بدل الامتناع.

ملاحظات

  • تغيير موجّه الخادم فقط (buildOrientationPrompt)؛ دون تغيير i18n/المخطط. المجموعة: 2364 اختبارًا (+1).

[1.141.0] — 2026-08-12

إحصاءات ثاقبة (تابع): إثراء الشركات المموَّلة — أصبحت #/funded أكثر بصرية: شعارات الشركات، ورسم بياني حسب مبلغ التمويل، وبطاقات تحوي الجولة / المبلغ / درجة الاكتشاف / الإجراء المقترح. تحسين UX مبلَّغ من المستخدم (بدون parent-sync).

تغيّر

  • #/funded — جدول مسطّح ← شبكة بطاقات. كل شركة مموَّلة حديثًا صارت بطاقة تحوي شعارًا (مشتقًا من الاسم عبر CompanyLogo، مع صورة حرفية كبديل)، ورقاقتَي الجولة + المبلغ، ودرجة الاكتشاف والإجراء المقترح من المشروع الأصلي، مع رابط الخبر وتاريخه.
  • تصوير مبلغ التمويل — رسم بياني شريطي أفقي لأكبر الشركات حسب المبلغ المُعلن؛ تُحلَّل المبالغ النصية الحرة (“$120M”/“€1.5B”) إلى مقدار عبر parseAmount جديد. 3 مفاتيح i18n جديدة × 17 لغة.

ملاحظات

  • لا يزال للقراءة فقط عبر GET /api/company-funded؛ الوصف ونطاق الراتب غير موجودَين في مصدر التمويل. المجموعة: 2363 اختبارًا (+2).

[1.140.0] — 2026-08-12

إحصاءات ثاقبة: أرقام رواتب أغنى — أصبح تفصيل الرواتب في «مساري» على #/stats يعرض الآن المتوسط (وليس الوسيط فقط)، ومبدّل سنويًا ⇄ شهريًا، وجدول الأدنى · المتوسط · الوسيط · الأعلى لكل بلد. أول جزء من المرحلة 3. تحسين UX مبلَّغ من المستخدم (بدون parent-sync).

أُضيف

  • متوسط الراتب — أصبح RoleStats.salaryStats يُرجع avgUsd إلى جانب minUsd/medianUsd/maxUsd. الوسيط يقاوم القيم الشاذة، والمتوسط يكشف الانحراف، فيُقرآن معًا كتوزيع.
  • مبدّل سنويًا ⇄ شهريًا وجدول الأدنى · المتوسط · الوسيط · الأعلى لكل بلد في قسم الرواتب، مرتبطان بمحدِّدَي العملة والفترة. 8 مفاتيح i18n جديدة × 17 لغة.

ملاحظات

  • الأرقام لا تزال مستمدة فقط من الإعلانات ذات الراتب القابل للقراءة ومُطبَّعة إلى الدولار (استرشادية). المجموعة: 2361 اختبارًا (+1).

[1.139.0] — 2026-08-12

سهولة الفهم: تلميحات ? — زر ? قابل لإعادة الاستخدام وآمن لـ CSP يشرح عند النقر «ماذا يفعل / كيف يعمل / ماذا تتوقع» بلغتك. تحسين UX مبلَّغ من المستخدم (بدون parent-sync).

أُضيف

  • نافذة تلميح المساعدة ? (window.HelpHint) — يفتح زر ? الدائري بجوار العنوان نافذة منبثقة خفيفة، تتوافق مع السمة ومنعكسة في RTL، وتعرض شرحًا مترجمًا عبر UI.md()؛ متاحة للوصول (role="tooltip"، aria-expanded، الإغلاق بـ Escape/النقر خارجها، استعادة التركيز) وآمنة لـ CSP.
  • إضافة ? إلى علامات التبويب الخمس في #/stats وإلى 8 عناوين لشاشات الذكاء الاصطناعي/التحليلات (career-plan، التوجيه، two-pager، التواصل، المقابلة التجريبية، الذاكرة، funded، الملخص الأسبوعي) — 14 مفتاح i18n جديدًا × 17 لغة.

ملاحظات

  • كانت كل الشاشات تحمل بالفعل عنوانًا فرعيًا من سطر واحد؛ ويضيف ? الشرح الأعمق عند الطلب ويجعل الحالات الفارغة مفهومة ذاتيًا. المجموعة: 2360 اختبارًا (+4).

[1.138.0] — 2026-08-12

التوليد بلغة واجهتك — كل توليد بالذكاء الاصطناعي يردّ الآن باللغة التي اخترتها في الواجهة، إضافةً إلى تعزيزات اختبار ناتجة عن المراجعة. تحسين UX مبلَّغ من المستخدم (بدون parent-sync).

تغيّر

  • توليدات الذكاء الاصطناعي تحترم الآن لغة الواجهة. عند ضبط الواجهة على الروسية أو الإسبانية أو اليابانية… يعود النص المولَّد بتلك اللغة بدلاً من الإنجليزية دائماً. مُوجِّه لغة الإخراج ممرَّر عبر كل نقاط التوليد — خطة المسار المهني، التوجيه، تقرير السوق، المقابلة التجريبية، خطة التواصل، «اسأل التوثيق»، اقتراح ملاحظة الذاكرة، ومسودة two-pager. تبقى الشيفرة والمعرّفات بالإنجليزية (مثل مفاتيح YAML في two-pager)؛ ويُترجَم فقط النص والعناوين والنقاط.

أُصلح

  • حارس أدوار ألوان CSS (tests/css-role-tokens.test.mjs) — كناري ثابت يتحقّق أن رموز الأسماء المستعارة للوضع الداكن من v1.137.0 لا تعكس دورها أبداً: رموز النص (--fg/--danger/--ok/…) لا تُستخدم أبداً كـ background، ورموز الأسطح (--card/--panel/--line/…) لا تُستخدم أبداً كـ color للنص، عبر كل CSS وأنماط SPA المضمّنة.
  • الفحص الذاتي لمحمّل XSS في UI.md() — الاختبار الذي يحمّل md() من api.js صار يفحص md('<script>…') مباشرة بعد الاستخراج ويرمي خطأً إذا غاب الهروب، فيفشل أي تقطيع خاطئ مستقبلي بصوتٍ عالٍ بدل ترك مجموعة الأمان خضراء على دالة مبتورة.
  • حارس التمرير في #/career-plan — يُنفَّذ scrollIntoView بعد التوليد فقط عندما تكون المعاينة ما زالت متصلة بالمستند.

ملاحظات

  • حُدِّث docs/UX-ROADMAP.md: تلميحات المساعدة ? + أوصاف الصفحات + الحالات الفارغة صارت v1.139.0؛ ومزوّد Nous Research / Hermes — مع دليل نشر على خادم سحابي + Telegram ومهارة Hermes — يُتابَع كـ المرحلة 5 / 5b.
  • المجموعة: 2356 اختباراً (+5).

[1.137.0] — 2026-08-11

إصلاحات قابلية القراءة والعرض — تباين الوضع الليلي، وتسميات الرسوم البيانية، وخطة المسار المهني. مراجعة لتجربة المستخدم (UX) بناءً على بلاغ من مستخدم (بلا تكافؤ مع المشروع الأصل).

أُصلح

  • تعارض الألوان في الوضع الليلي (نص أبيض على أبيض / أسود على أسود) في شاشات عديدة — خمس عشرة خاصية CSS مخصَّصة كانت تستخدمها عدة صفحات (--fg، --panel، --panel-2، --ok، --danger، --card، …) لم تكن مُعرَّفة إطلاقاً، فكانت تعود إلى قيم فاتحة/داكنة ثابتة يدوياً: لا بأس بذلك في الوضع النهاري، لكنها غير قابلة للقراءة في الوضع الليلي (رقائق النظرة العامة في #/pipeline، والتبويب النشط في #/stats، وقسم “Active / Keys” + “✓ set” في #/config، وأقسام #/two-pager، وفقاعة السؤال في #/mock-interview، ونص الخطأ). أصبحت هذه الخاصيات الآن مُستعارة إلى الرموز الحقيقية المُدرِكة للسمة، فتتبع السمة تلقائياً — صفر إخفاقات تباين وفق WCAG-AA عبر جميع الصفحات الـ29، بحسب أداة تدقيق آلية؛ وانتقل التبويب النشط في #/config إلى تصميم شارة مُظلَّلة قابلة للقراءة. حارس انحدار (tests/dark-theme-tokens.test.mjs) يُبقيها مُستعارة.
  • كانت تسميات الرسوم البيانية في #/stats تُقطَع في منتصف الكلمة (“Senior Backend Engineer” → “…Enginee”) — أصبحت الآن تُختصَر بعلامة حذف (…) مع الاحتفاظ بالتسمية الكاملة كتلميح يظهر عند التمرير.
  • كانت #/career-plan تعرض الخطة المُولَّدة كنص Markdown خام — أصبحت تُعرَض تلقائياً الآن كنص مُنسَّق سهل القراءة (يبقى Markdown القابل للتحرير في مربع النص؛ ويُبدِّل زر «معاينة» العرض بينهما).

ملاحظات

  • لا تُعَدّ #/career-plan، و#/two-pager، و#/stats، وموجز المقابلات الأسبوعي معطَّلة — فهي تعرض حالات فارغة إلى أن تُولَّد خطة أو تتوفر بيانات. من المخطَّط توفير توجيه أوضح داخل الصفحة وتلميحات مساعدة بعلامة ؟ في الإصدار القادم (docs/UX-ROADMAP.md).

[1.136.0] — 2026-08-11

تكافؤ مع المشروع الأصل career-ops v1.26.x (الفرع الرئيسي بعد v1.26.0) — مصدر جديد واحد بلا حاجة إلى مصادقة، إضافة إلى موجة من عمليات نقل الجودة والمتانة إلى نظائر web-ui المُبرمَجة. أصبح السجل الآن 79 مصدراً = 74 إنجليزية + 5 روسية (ALL_ADAPTERS 74).

أُضيف

  • eightfold (Eightfold AI، #2684) — لوحات استقطاب المواهب عبر واجهة https://<tenant>.eightfold.ai/api/apply/v2/jobs بلا حاجة إلى مصادقة، مُثبَّتة المضيف على *.eightfold.ai (يُرفض عمداً اسم CNAME المُعلَّم بالعلامة التجارية careers.<company>.com)؛ بترقيم صفحات محدود بسقف أمان، وإطلاق خطأ عند اللوحة الميتة، وإزالة تكرار الروابط. مصدر + مُحوِّل + مجموعة اختبار معزولة عن CI؛ يظهر في مرشِّح المصدر بصفحة #/scan وعلى الصفحة الرئيسية.

أُصلح

  • مفاتيح إزالة التكرار ومطابقة الأدوار المُدرِكة لـUnicode (#2569 / #2587 / #2667) — دالة normalizeTextKey مشتركة جديدة (NFKC، تحتفظ بالحروف والعلامات والأرقام من أي نظام كتابة) تحل محل المفاتيح المقصورة على ASCII: أصبحت detect-reposts تُجمِّع الآن متغيرات الشركة المختلفة في العرض/الترقيم (“Acme, Inc.” ≡ “Acme Inc”)، ولا تدمج أبداً أصحاب عمل متمايزين غير لاتينيين، بينما تطوي role-matcher العناوين ذات العرض الكامل وتحتفظ برموز الأدوار غير اللاتينية بدلاً من محوها.
  • لم تعد fetchJsonWithRetry تُعيد المحاولة عند رفض إعادة توجيه (#2657) — عندما يُقابِل حارس redirect:'error' استجابة من فئة 3xx فإن ذلك حتمي، فأصبح غير قابل لإعادة المحاولة الآن، ويفشل بسرعة بدلاً من استهلاك ميزانية إعادة المحاولة.
  • مجموعات AND في title_filter.positive (#2552) — الفاصل + المُحدَّد بمسافات داخل إدخال إيجابي يتطلب الآن ظهور كل حد في العنوان، بأي ترتيب.
  • أصبحت oraclecloud تقبل نطاقات المستأجرين المُرقَّمة oraclecloud1.com … oraclecloud99.com (#2683) — عائلة محدودة (بلا صفر بادئ، وبحد أقصى رقمين)، وليست نطاقاً عاماً (wildcard) أبداً.
  • تصليب workable (#2675) — إعادة محاولة، وترويسات (headers) شبيهة بالمتصفح، وتسلسل الطلبات في مواجهة المضيف المحمي بـCloudflare.
  • أصبحت personio تعود إلى استخلاص HTML عندما تكون تغذية XML معطَّلة، بدلاً من أن تُعيد شيئاً فارغاً.
  • أُعيدت مزامنة أسماء states البديلة (FALLBACK) مع المشروع الأصل (#2615).

ملاحظات

  • لم يُنقَل (غير مُعكَس في web-ui، أو خاص بواجهة سطر الأوامر فقط): reply-matcher (#2672)، وjd-similarity (#2661)، وjd-skill-gap (#2686)، ومسار البيئة الخاص بالمسح (#2568) / تحليل --flag=value (#2589)، وتغييرات خطاب التقديم / قالب السيرة الذاتية / doctor / ollama / إنشاء PDF. أما تحذيرات الأمان عالية الخطورة الخاصة بـjs-yaml/nanoid للويب فقد جرى تصحيحها بالفعل في web-ui الإصدار v1.135.0.

[1.135.0] — 2026-08-11

تكافؤ مع المشروع الأصل career-ops v1.26.0 — خمسة مصادر مسح جديدة بلا حاجة إلى مصادقة، إضافة إلى إصلاحات تصحيح لأربع لوحات تحملها career-ops-ui بالفعل. أصبح السجل الآن 78 مصدراً = 73 إنجليزية + 5 روسية (ALL_ADAPTERS 73).

أُضيف

  • خمسة مصادر مسح جديدة (كل مصدر منها بملف مصدر + مُحوِّل + مجموعة اختبار معزولة عن CI؛ تظهر في مرشِّح المصدر بصفحة #/scan وعلى الصفحة الرئيسية لـcvstart.org):
    • join (JOIN) — لوحة JOIN الخاصة بشركة ما، تُقرأ من __NEXT_DATA__ الخاص بـNext.js المُضمَّن في join.com/companies/<slug> (مُثبَّت المضيف، ومحدود عدد الصفحات).
    • getro (Getro) — لوحات محافظ صناديق رأس المال الجريء ذات “شبكة المواهب”، عبر واجهة api.getro.com العامة بطلبات POST، بترقيم صفحات تنازلي من الأحدث؛ تُنسب كل وظيفة إلى الشركة المحفظة نفسها لا إلى الصندوق.
    • consider (Consider) — لوحات محافظ صناديق رأس المال الجريء على getconsider.com عبر طلب POST من المصدر نفسه؛ المضيف المُحدَّد عبر الإعداد مُثبَّت بواسطة حارس بنيوي ضد هجمات SSRF (مضيف HTTPS عام فقط).
    • joinup (JOINUP) — اللوحة السويسرية joinup.ch، بقراءة أحدث صفحة مُصيَّرة من جهة الخادم (SSR)؛ تتوقف بأمان عند عطل في أداة الاستخلاص.
    • remotli (Remotli) — remotli.ch، أدوار عمل عن بُعد لدى شركات سويسرية (رواتب بالفرنك السويسري)؛ يُصدِر رابط التقديم الخاص بنظام ATS التابع لصاحب العمل نفسه، بحيث تُزال التكرارات بين الإعلانات المتقاطعة.

أُصلح

  • لم تعد a16z Speedrun تُلغي اللوحة بأكملها عند عطل عابر — أصبحت طلبات جلب الصفحات تمر الآن عبر fetchJsonWithRetry مشتركة (إعادة محاولات محدودة عند أخطاء 429/5xx/انتهاء المهلة العابرة فقط، ولا تُعاد المحاولة أبداً عند خطأ 4xx دائم)، وأُعيد ضبط ميزانية الصفحة لتناسب صفحة الـ50 وظيفة.
  • انتقلت arbeitsagentur إلى واجهة Jobsuche الإصدار v6 (/pc/v6/jobs) — أصبحت نقطة النهاية القديمة v4 تُعيد خطأ 404؛ أُعيدت تسمية شكل الاستجابة، وأصبح تصفية العمل عن بُعد تُضيَّق الآن من جهة الخادم.
  • انتقلت thehub إلى واجهة الإصدار v2 jobsandfeatured — لا تحمل الصفوف تاريخ نشر، وهي مُستثناة من مرشِّح الأقدمية.
  • أصبحت hackernews تعثر على سلسلة “Who is hiring?” الشهرية بموثوقية — عبر تصفية بحث Algolia إلى وسم حساب author_whoishiring بدلاً من استعلام نص حر.

ملاحظات

  • لم يُنقَل (واجهة career-ops-ui آمنة بالفعل، أو مُمرَّرة للقراءة فقط، أو خاصة بواجهة سطر الأوامر فقط): مفاتيح إزالة تكرار الأدوار / مطابقة الشركات الخاصة بالـUnicode (تجميع إعادة النشر الخاص بـweb-ui يُحدِّد الشركة بالفعل بحرف صغير عادي، بحيث لا تتداخل الشركات غير اللاتينية المتمايزة أبداً)؛ إشارة تأخّر الرفض الخاصة بالمتابعة وتحسينات الشركات الممولة (تُمرَّر للقراءة فقط، مع فشل آمن)؛ مسارات المسح القابلة للتجاوز عبر متغيرات البيئة وتحليل --flag=value (تُشغِّل web-ui الماسحات داخل العملية نفسها)؛ إعادة هيكلة توحيد وكيل المستخدم (User-Agent) (تُطبِّقها web-ui مركزياً بالفعل)؛ والعناصر الخاصة بواجهة سطر الأوامر فقط (قائمة المحتوى غير الموثوق، oferta/offer-prep، doctor، تغييرات قالب خطاب التقديم/السيرة الذاتية).

[1.134.1] — 2026-08-05

تصليب التحقق من الصحة — إصلاحات كشف عنها تدقيق شامل للمشروع.

أُصلح

  • لم يعد successfactors يُهدر الوظائف المُجمَّعة عند فشل في منتصف المسح (انحدار في تحويل «إطلاق خطأ اللوحة الميتة» بإصدار v1.134.0) — لم تكن حلقة الترقيم (pagination) الخاصة به تحتوي على try/catch، فكان الفشل في الصفحة 2 وما بعدها (بعد نجاح الصفحة الأولى) يُطلِق خطأً ويُهدر كل ما جُمِع مسبقاً؛ وإذا كان ذلك الفشل من نوع 404 (بقيمة startrow خارج النطاق)، كان en-scanner يحجر مستأجراً (tenant) حيّاً بوصفه ميتاً لأيام. أصبح الآن يُطابق سلوك phenom/radancy: لا يزال الفشل في الصفحة صفر يُطلِق خطأً (لوحة ميتة)، لكن الفشل في صفحة لاحقة يُبقي على النتائج الجزئية.
  • أصبحت رقاقات (chips) تصفية #/scan قابلة للتشغيل عبر لوحة المفاتيح (WCAG 2.1.1) — كانت رقاقات الأوجه (وأيضاً رقاقة «مسح») عناصر span بمعالج نقر فقط، دون tabindex أو دور (role)، فلم يكن بمقدور مستخدمي لوحة المفاتيح وقارئات الشاشة الوصول إليها أو تبديلها. تحمل الآن role="button" وtabindex="0" وaria-pressed، وتُفعَّل بمفتاحي Enter/Space.
  • أصبحت ثلاث سلاسل نصية إنجليزية مُثبَّتة يدوياً (hardcoded) مُترجَمة الآن — كانت تلميحة شارة الثقة (trust-badge tooltip) في #/scan، وعنوان عمود إعادة التموضع (relocation) في #/scan، وعنوان عمود النتيجة في #/dashboard سلاسل حرفية عارية لا تراها بوابة تكافؤ الترجمة (i18n parity gate) — لأنها لم تكن مفاتيح (keys) قط — فبقيت بالإنجليزية في كل لغة غير الإنجليزية. أصبحت الآن scan.trustTip + scan.col.reloc (مفتاحان جديدان) + إعادة استخدام لـtrack.col.score، مع حارس (guard) ثابت المصدر.

[1.134.0] — 2026-08-05

تكافؤ مع المشروع الأصل career-ops v1.25.0.

أُضيف

  • مصدر مسح جديد: getManfred (manfred) — تغذية على مستوى اللوحة بأكملها لوظائف تقنية في إسبانيا والاتحاد الأوروبي بأجور منشورة، من www.getmanfred.com/api/v2/public/offers (بلا مصادقة، مثبَّتة المضيف + HTTPS فقط، كتالوج كامل باستدعاء واحد). مصدر + مُحوِّل + مجموعة اختبار معزولة عن CI (tests/sources-manfred.test.mjs)؛ أصبح السجل الآن 73 مصدراً = 68 إنجليزية + 5 روسية (ALL_ADAPTERS 68). يظهر في مرشِّح المصدر بصفحة #/scan وعلى الصفحة الرئيسية لـcvstart.org.

أُصلح

  • كانت تغذية a16z Speedrun تُقتَطع بصمت إلى 50 وظيفة (#2404) — تُقيِّد التغذية الصفحة الواحدة بـ50 وظيفة، بينما كان المصدر يطلب PER_PAGE = 100، فتوقّف الترقيم بعد الصفحة الأولى. جرى تصحيحها إلى 50.
  • أصبحت اللوحات الميتة تُطلِق خطأً بدلاً من أن تُقرأ وكأنها “حيّة لكن فارغة” (#2379) — cryptocurrencyjobs، وphenom، وradancy، وsuccessfactors: أصبح فشل الجلب الذي لا يُحسَم فيه أي طلب الآن يُطلِق خطأً (بحيث تُسجِّل صحة #/portals والمسح فشلاً حقيقياً)، بدلاً من ابتلاعه كقائمة فارغة؛ الفشل في منتصف المسح بعد نجاح واحد على الأقل يُبقي على النتائج الجزئية.
  • أصبح workable يستخدم الآن واجهة الودجت العامة (#5ab8425) — جرى التحوّل إلى apply.workable.com/api/v1/widget/accounts/<slug>، التي تُعيد قائمة النشر الكاملة لحساب كبير في طلب واحد، بحيث لم تعد الحسابات الكبيرة تُقتَطع.

ملاحظات

  • لم يُنقَل (خاص بواجهة سطر الأوامر فقط أو غير مُعكَس في web-ui): إعادة كتابة أداء تجميع العناوين الخاصة بـdetect-reposts #2389؛ إصلاحات مفتاح الشركة الخاصة بـUnicode (إزالة تكرار المتتبِّع الخاص بـweb-ui آمنة بالفعل للأحرف غير اللاتينية)؛ scan --since؛ cv-facts؛ مسار تدقيق قالب السيرة الذاتية / PDF؛ doctor؛ توجيه المحتوى غير الموثوق للأوضاع.

[1.133.1] — 2026-08-02

أُصلح

  • #/funded (الشركات الممولة) يعرض النتائج الآن — خطآن كانا يجعلان الجدول يظهر دائماً “لا توجد شركات ممولة” حتى عندما يُعيد سكربت المشروع الأصل company-funded.mjs قائمة كاملة. (1) كان العرض يقرأ النتائج تحت res.candidates، بينما يُصدرها المشروع الأصل تحت companies (كل عنصر بالشكل {company, amount, round, funding: { sources: [{ source, url, observed_date }] } })؛ يقرأ العميل الآن المفتاح الصحيح ويُطابق شكل الأدلة الفعلي. (2) كان جدول النتائج يُمرِّر خلاياه إلى UI.el('tr', {}, …) كوسائط متغيرة (varargs)، لكن UI.el(tag, attrs, children) يتوقع أن يكون children عقدة واحدة أو مصفوفة، لذا لم يكن يُعرَض سوى العمود الأول (الشركة) فقط — أصبحت الخلايا الآن تُمرَّر كمصفوفة. جرى التحقّق في متصفح فعلي: تُعرَض 11 شركة عبر التغذيات الأربع بأعمدة الشركة / إشارة التمويل / المصدر / التاريخ، مع روابط أدلة عاملة، ودون أي أخطاء في وحدة التحكم (console). أصبح المرور الفارغ الآن يُظهر أيضاً تشخيصات كل مصدر على حدة، بحيث يمكن تمييز يوم إخباري هادئ عن تغذية محظورة.
  • ضمانات انحدار جديدة في tests/parity-routes-v1133.test.mjs: أصبح سكربت المشروع الأصل الوهمي (fake) يُصدر الآن شكل الإخراج الحقيقي companies (كانت التركيبة (fixture) الأصلية تعكس شكل candidates الخاطئ — وهذا بالضبط سبب انطلاق الخطأ للإنتاج)، إضافة إلى كناري (canaries) ثابتة المصدر تتحقق من أن funded.js يقرأ res.companies (وليس res.candidates أبداً) ويبني صفوف الجدول بخلايا كمصفوفة (+1 ← 2144).

[1.133.0] — 2026-08-01

أُضيف

  • اكتشاف الشركات المموَّلة (#/funded، تكافؤ مع المشروع الأصل #2117) — عرض جديد للقراءة فقط يُمرِّر سكربت company-funded.mjs الخاص بالمشروع الأصل career-ops عبر GET /api/company-funded: قائمة أولية للمراجعة تضم الشركات التي حصلت مؤخراً على تمويل، مُكتشَفة من تغذيات تمويل عامة مُثبَّتة المضيف (TechCrunch، وPR Newswire، وThe Guardian، وHacker News). يُشغِّل التمرير السكربت بالخيارين --json --dry-run (إخراج JSON إلى stdout، دون كتابة ملفات)، ولا يُمرِّر مدخلات المستخدم أبداً إلى --sources، ويحمل تحديد معدّل (rate limiting)، وهو مُشغَّل من قِبل المستخدم فقط (زر اكتشاف، لا يُشغَّل عند التحميل أبداً). وحدة مسار جديدة server/lib/routes/funded.mjs + public/js/views/funded.js، ضمن قسم الاستقطاب.
  • موجز المقابلات الأسبوعي (#/interview-digest، تكافؤ مع المشروع الأصل #2129/#2130) — عرض جديد للقراءة فقط يُمرِّر سكربت weekly-digest.mjs الخاص بالمشروع الأصل (بدون LLM) عبر GET /api/interview/weekly-digest: تجميع آلي لملاحظات جلسات المقابلات — الشركات والجولات التي أُجريت معها مقابلات هذا الأسبوع، والكفاءات المتكررة، والثغرات المفتوحة المُقدَّرة بأفضل جهد. يُمرَّر النطاق الاختياري ?from=&to= فقط عندما يكون كلا الطرفين بصيغة YYYY-MM-DD صالحة؛ النطاق الفارغ يُنتج موجزاً صالحاً بقيمة available:true. أُضيف إلى server/lib/routes/interview.mjs + public/js/views/interview-digest.js، ضمن قسم التحليلات.
  • يتّبع كلا المُمرِّرين عقد “الفشل الآمن” available:false المُعتمَد عندما يكون سكربت المشروع الأصل غير موجود (بيئة CI، التثبيتات المستقلة). 26 مفتاح ترجمة (i18n) جديداً ×17 لغة؛ مجموعة اختبار جديدة معزولة عن CI tests/parity-routes-v1133.test.mjs (+5 ← 2143).

ملاحظات

  • تجاوز المشروع الأصل career-ops الإصدار v1.24.0 بإضافة صفحة متتبّع المتابعة (Follow-up Tracker) الخاصة بتطبيق web/ المبني على Next.js (#1422) وتصيير PDF من جهة الخادم (#2182) — لم يُنقَل ذلك: تمتلك career-ops-ui بالفعل مسار تمرير خاصاً بالمتابعة ومُشغِّلات PDF خاصة بها، وتصليب followup-cadence.mjs الكامن يصل مجاناً عبر مسار التمرير القائم على shell out. تغييرات set-status.mjs / tracker-utils.mjs داخلية بحتة لواجهة سطر الأوامر (CLI) ولا تُعكَس هنا.

[1.132.0] — 2026-07-31

غُيّر

  • استُخلصت منظومة عرض نتائج #/scan إلى public/js/lib/scan-results.js (تسديد ديْن عقد حجم الملف — كان public/js/views/scan.js قد بلغ نحو 1254 سطرًا). انتقلت المنظومة — renderResults، وbuildChipRow، وgetRows، وبانيات الصفوف/الأوجه، ورسّامات خيارات الأقدمية/البلد، ومرآة سجلّ FALLBACK_SOURCES — إلى مصنع window.ScanResults.create(ctx) يُغلق على كائن سياق يوفّره العرض (عناصر التصفية، ومجموعات الأوجه النشطة، ومقسِّم الصفحات، وبيانات الصفحة الثنائية، ودالة جلب lastResults). لا تغيير في السلوك — نُقلت الدوال حرفيًا وأُعيد ربط متغيرات الإغلاق ميكانيكيًا إلى ctx.*؛ أصبح scan.js الآن نحو 906 سطرًا. (لا يزال فوق الحد المستهدف البالغ 800 سطر — من المخطط إجراء عملية استخلاص ثانية؛ عقد حجم الملف لعرضَي JS المتبقيين الشاذّين، scan.js وconfig.js، مُتتبَّع في docs/sdd/CONVENTIONS.md.)
  • تقرأ الاختبارات الثابتة للمصدر الآن الملفين عبر tests/helpers/scan-src.mjs::loadScanSrc() (على غرار loadAppCss())؛ يقرأ tests/scan-fallback-sources.test.mjs الآن مرآة السجلّ من scan-results.js.
  • بوابة انحدار جديدة داخل المتصفح — يُهيّئ tests/playwright-scan-filters.mjs ملف data/last-scan.json مُعَدًّا مسبقًا ويُشغّل كل مرشّحات #/scan (المصدر، الأقدمية، العمل عن بُعد، العمر، تضمين/استبعاد النص)، مُتحقِّقًا من أعداد الصفوف بدقّة، بحيث يُتحقَّق من الاستخلاص مقابل سلوك المتصفح الفعلي (يُشغَّل عبر npm run test:e2e:browser). أُضيفت معرِّفات ثابتة لعناصر التصفية (#scan-filter-*، #scan-apply) لأجله.

صيانة

  • أصبحت لافتة “أحدث إصدار” في README مُختصرة إلى ملخّص من سطر واحد + رابط إلى سجل التغييرات الكامل (يُتقاعَد الجدار السردي الطويل متعدد الإصدارات — يعيش التاريخ في CHANGELOG*.md). طُبِّق ذلك عبر جميع اللغات الـ17.

[1.131.2] — 2026-07-31

غُيّر

  • تقسيم app.css إلى ثلاث صفحات أنماط مرتّبة (ديْن عقد حجم الملف — كان الملف الواحد قد بلغ نحو 1990 سطرًا، متجاوزًا كثيرًا الحد الصارم البالغ 800 سطر). أصبح الآن app.css (نحو 672 سطرًا — إمكانية الوصول، رموز التصميم/السمة، الشريط الجانبي، المحتوى الرئيسي، الأزرار، غلاف المحتوى)، و**components.css** (نحو 595 سطرًا — البطاقات، الشبكات، مقسِّم الصفحات، الشارات، الجداول، النماذج، السجل/وحدة التحكم، markdown، مبدّل اللغة، مرشّح الرقائق، شريط الاتصال)، و**overlays.css** (نحو 737 سطرًا — التنبيه المنبثق (toast)، درج الإشعارات، النافذة المنبثقة (modal)، المتفرقات/الاستجابة، انعكاس [dir="rtl"]، docs-fab، usage-hud)، كل منها ضمن الحد الصارم.
    • القطع متواصل ومرتّب، لذا يكون التتالي (cascade) مطابقًا حرفيًا بايتًا ببايت للملف قبل التقسيم؛ يحمّل index.html الملفات الثلاثة كعناصر <link> مرتّبة. لا تغيير في السلوك أو الترميز أو الترجمة (i18n).
    • أصبحت الاختبارات التي تتحقق من CSS تقرأ الآن السلسلة المُدمَجة عبر مُساعد مشترك tests/helpers/css.mjs::loadAppCss(). يُثبّت ملف اختبار جديد tests/css-modularization.test.mjs هذا التقسيم (وجود الملفات · كل منها ≤ 800 سطر · ترتيب عناصر <link> في index.html) ← المجموعة 2138. جرى التحقق داخل المتصفح: تُحلَّل صفحات الأنماط الثلاث جميعها وتُطبَّق قواعدها.

[1.131.1] — 2026-07-31

أُصلح

  • اتساق تثبيت المضيف للمحوّلات (adapters) في مصدرَي v1.130.0 (متابعات مراجعة الذكاء الاصطناعي، دفاع في العمق؛ لا تغيير في السلوك للمدخلات الصالحة):
    • محوّل a16z-speedrun-talent يعيد الآن التحقّق من تجاوز api: / a16z-speedrun-talent: عند buildEndpoint (HTTPS + مضيف مطابق تمامًا speedrun-talent-network.com) ويعود إلى التغذية القياسية عند فشل التحقّق — تكافؤًا مع محوّل cryptocurrencyjobs، بحيث لا تصل قيمة من مضيف مختلف إلى منفذ الجلب أبدًا (كان يعتمد سابقًا فقط على حارس assertSpeedrunUrl عند وقت الجلب). أصبح فحص تطابق المضيف الدقيق الآن تعبيرًا نمطيًا واحدًا مُصدَّرًا SPEEDRUN_TALENT_HOST_RE يتشاركه الحارس والمحوّل.
    • مُحلِّل cryptocurrencyjobs — يستخدم cleanUrl الآن حارس المضيف المطابق تمامًا نفسه المستخدَم في assertCryptocurrencyJobsUrl وتجاوز المحوّل (كان يستخدم endsWith، الذي يقبل النطاقات الفرعية). لم يعد المُحلِّل أبدًا أكثر تساهلًا من حارس SSRF: يُستبعَد رابط عنصر من sub.cryptocurrencyjobs.co.
    • ‏+2 اختبارَين → المجموعة 2135.

[1.131.0] — 2026-07-31

أُضيف

  • لوحة تبويبات المراحل بأسلوب CRM في #/tracker (منقولة من عرض /pipeline الخاص بتطبيق الويب للمشروع الأصل). يُستبدل شريط رقائق القمع + القائمة المنسدلة للحالة في المتتبِّع بـشريط تبويبات مراحل: تبويب الكل بالإضافة إلى تبويب لكل حالة قياسية — Evaluated · Applied · Responded · Interview · Offer · Rejected · Discarded · SKIP · Hired — يعرض كل منها عداداً حياً لكامل السجل التاريخي، مع تضمين المراحل ذات العدّ الصفري بحيث يظل القمع بالكامل مرئياً دائماً (المظهر النمطي لـCRM). التبويب النشط يقود عملية التصفية؛ والنقر عليه مجدداً يمسح التصفية ويعود إلى الكل. تحافظ الصفوف على تدرّج لون النتيجة والشرعية وإمكانيات PDF والتقرير، وتُظهر خلية الشركة الآن شعار العلامة التجارية عند تفعيل الشعارات (معطّلة افتراضياً ← صفر طلبات إضافية).
    • مسار جديد للقراءة فقط GET /api/tracker/stages يُعيد القمع القياسي (التسميات مرتّبة) + خريطة طيّ للأسماء البديلة، مصدرها server/lib/states.mjs (templates/states.yml، مع احتياطي مُضمَّن) — بحيث لا يُبرمج العميل أبداً القائمة البيضاء للحالات. يبقى رد GET /api/tracker القديم بلا معامِلات دون تغيير ({ rows } فقط).
    • مكتبة عميل جديدة نقية ومُختبرة وحدويًا public/js/lib/tracker-stages.js تُصنّف الصفوف وفق مراحل الخادم، متسامحة مع تنسيق markdown العريض الشارد والأسماء البديلة المترجَمة (مثال: aplicadoApplied). التبويبات قابلة للوصول (role tablist/tab، وaria-selected، ومساحة لمس ≥44 بكسل، والعدادات ضمن الاسم المتاح للوصول لكل تبويب). لا مفاتيح i18n جديدة. المجموعة 2133.

[1.130.0] — 2026-07-31

أُضيف

  • مصدرا مسح جديدان منقولان من المشروع الأصلي career-ops v1.24.0 (داخل العملية، دون تبعيات جديدة؛ يظهر كلاهما في قائمة Source المنسدلة بـ#/scan وعلى الصفحة الرئيسية لـcvstart.org):
    • a16z Speedrun (a16z-speedrun-talent، ‏#2231) — تغذية JSON على مستوى اللوحة بأكملها لشبكة مواهب a16z Speedrun. مضيف مُثبَّت على speedrun-talent-network.com، HTTPS فقط، ترقيم صفحات يبدأ من 0 مع حدّ أقصى لعدد الصفحات، وتمرير q/إعداد لكل شركة، وفشل بأسلوب آمن.
    • Cryptocurrency Jobs (cryptocurrencyjobs) — لوحة وظائف الويب3 cryptocurrencyjobs.co، تُستوعب عبر تغذية RSS 2.0 العامة الخاصة بها (بلا مصادقة). فكّ ترميز كيانات XML على مرحلتين، قوائم عن بُعد فقط، واستخراج جهة التوظيف من ذيل العنوان "… at <Company>".
    • أصبح إجمالي السجل الآن 72 مصدرًا = 67 إنجليزية + 5 روسية (ALL_ADAPTERS = 67 محوّلاً إنجليزيًا).

أُصلح

  • echojobs — تبقى الوظائف الهجينة قابلة للتمييز عن العمل عن بُعد (تكافؤ مع المشروع الأصلي، ‏#2258). أصبحت علامة hybrid غير الحسّاسة لحالة الأحرف تُنتج الآن "<City> · Hybrid" (أو Hybrid مجردة عند غياب المدينة) وworkplaceType: 'Hybrid'، بدلاً من الانطواء ضمن Remote.
  • radancy — دعم ترميز TalentBrew القديم ونقل جزء نتائج JSON (تكافؤ مع المشروع الأصلي a3e6df9)، محكوم بـopts.fetchJson قابل للحقن.

ملاحظات

  • لم يُنقل — ميزات خاصة بـCLI المشروع الأصلي فقط. يبقى السطح الكبير لـCLI/الأوضاع في career-ops v1.24.0 خارج web-ui، الذي هو عارض + كتابة رقيقة عبره، لا مضيف أوضاع: جداول الامتثال/الاختصاص القضائي، دفتر هاتف جهات الاتصال + vCard، تفريغ محاضر المقابلة النصية/اكتشاف منصّة المكالمات، تعيين حالة السجل، تسجيل النتيجة، الفرز الثنائي المرحلة، تشابه إعلانات الوظائف، مخطّط عنصر السيرة الذاتية للطلب المُصدَّر، اكتشاف Playwright-MCP لـdoctor، وملف portals/fix-slugs.mjs. تغييرات تنسيق المسح التي تعيش في scan.mjs الخاص بالمشروع الأصلي — ماسح Interamt.de بواسطة Playwright، مسح iCIMS الشامل العكسي لأنظمة ATS، مرشّح العمل عن بُعد حسب أهلية الدولة، ضبط وتيرة بحث DNS، إزالة تكرار rltr في StepStone، وعمود الشركة الموحَّد في سجل المسح — لا تنطبق: يُشغّل web-ui ماسحات EN/RU داخل العملية ولا يستدعي scan.mjs.
  • مُغطّى مسبقًا. إصلاح طيّ التشكيل في role-matcher (‏#2209) نُقل في v1.127.0، لذا فهو بلا أثر هنا.

[1.129.1] — 2026-07-29

أُصلح

  • متابعات مراجعة الذكاء الاصطناعي على منافذ web من v1.128/v1.129 (كلها استشارية، أُصلحت من الأصل): أسبقية المستوى في job-facets.js (المعدِّل الصريح يتغلّب الآن على كلمة الإدارة — Senior Engineering Managersenior، كان lead)؛ لم يعد fallback في states.mjs مثبَّتًا (تُحفظ القراءة الناجحة، ويُعاد fallback دون تخزين — الأب غير المتاح مؤقتًا عند الإقلاع يُقرأ ثانيةً في الاستدعاء التالي) + console.warn عند ملف موجود لكنه تالف؛ score-tone.js — الصف بلا درجة محايد (muted) لا أحمر؛ يتخطّى domainFromName() الـslugs غير ASCII قبل /api/logo؛ +حارس عزل في tests/states.test.mjs. +4 اختبارات → 2073.

[1.129.0] — 2026-07-29

أُضيف

  • وجه المستوى + عمود المدة في #/scan — مكتبة job-facets.js التي شُحنت في v1.128.0 صارت الآن موصولة بواجهة المسح (كانت منطقًا فقط). قائمة المستوى الجديدة تصنّف عنوان كل وظيفة إلى lead/staff/senior/متوسط/junior/متدرب (JobFacets.seniorityFromTitle) وتُملأ تلقائيًا مما هو موجود فعليًا في النتائج (كوجه الدولة)؛ العناوين بلا كلمة مستوى تمرّ دائمًا. تُحفظ في عمليات البحث المحفوظة وإعادة التعيين والتطبيق. يكتسب الجدول عمود المستوى (شارة) وعمود المدة بلا رموز (اليوم / ، من JobFacets.daysSince). 12 مفتاح i18n ×17، +3 اختبارات → 2069.

[1.128.0] — 2026-07-29

أُضيف

  • أربعة حلول منقولة من تطبيق الويب الخاص بالمشروع الأب (../web/، Next.js)، أُعيدت كتابتها بجافاسكريبت خام/ESM دون تبعيات جديدة: (1) يقرأ server/lib/states.mjs ملف templates/states.yml مباشرةً كمصدر وحيد لمفردات حالات المتتبِّع (مع احتياطي للـCI) — ويلغي إعادة المزامنة اليدوية للقائمة البيضاء كل إصدار؛ يطوي POST الأسماء البديلة (الإسبانية/القديمة) إلى التسمية القياسية، ويجمّع قمع GET حسب الحالة القياسية؛ (2) شعارات الشركات في صفوف مستضافة على ATS عبر domainFromName() (~90 علامة→نطاق)؛ (3) score-tone.js — درجة لون رباعية المستويات (≥4.2/3.8/3.0 + احتياطي حرفي)؛ (4) job-facets.js — أوجه seniority/source/days. +21 اختبارًا.

ملاحظات

  • غير منقول (مفهوم فقط): طبقة الإجراءات الوكيلة للأب (actions/registry.ts + api/assistant/route.ts) — مخطط لحين تحوّل docs-fab إلى مساعد فاعل. لا مصادر جديدة (السجل 70)، لا تغييرات i18n/help.

[1.127.0] — 2026-07-29

أُضيف

  • ثلاثة مصادر مسح جديدة (تكافؤ مع career-ops v1.23.0) — يضم السجل الآن 70 محوّلاً (65 EN + 5 RU): Flowxtra (مُجمِّع شامل بلا مصادقة)، VDAB (واجهة كلمات مفتاحية لخدمة التوظيف الفلمنكية)، وiCIMS (بوابات careers-<tenant>.icims.com، منفصلة عن jibeapply). كما يعود Cursor إلى قائمة الـCLI (parent #2115): يكتشف cli-detect الآن cursor (10 أدوات)، واستُعيدت القائمة في help/README/config ×17.

أُصلح

  • انتقل agenticjobs من كشط HTML إلى واجهة REST (#2167)؛ يستعيد Greenhouse المدينة من /offices عندما يكون location.name مجرد نمط عمل (#2104)؛ تكافؤ role-matcher (#1933/#2164/#2009: بادئة MTS، أساس product، طيّ التشكيل، خلاف ما دون الأساس).

ملاحظات

  • غير منقول. معظم v1.23.0 سطح CLI/لوحة تحكم لا يستخدمه web-ui (batch-tailor، discover-ats، أوضاع NL/PT، سمات PDF، لوحة Go، updater/doctor)؛ سكربتات الترحيل دون تغيير. VERSION الأب → 1.23.0.

[1.126.1] — 2026-07-25

أُصلح

  • نقطتا انحراف في قائمة CLI فوّتتهما إعادة مزامنة v1.126.0 — (1) مقدّمة تبويب API keys في #/config (config.providerModelNote، i18n ×17) كانت تسرد 7 واجهات فقط — تُدرَج الآن Antigravity وGrok Build بعد OpenCode؛ (2) صف ثانٍ في جدول المقارنة داخل دليل المساعدة (×17) وملف help للموقع المبني في CI كانا لا يزالان يعرضان Inside Claude Code / Codex / Cursor / Gemini CLI — المجموعة القديمة مع Cursor — والآن القائمة الكاملة. كلاهما استخدم فواصل شرطة مائلة/نقطة وسطى لم تغطّها أنماط مسح v1.126.0. أُعيد توليد لقطة i18n؛ وتبقى المجموعة 1969.

[1.126.0] — 2026-07-25

أُضيف

  • علامة تبويب أدوات AI CLI تكتشف الآن جميع واجهات career-ops الثماني من الدرجة الأولى — تمّت مزامنة قائمة #/config مع docs/SUPPORTED_CLIS.md للمشروع الأب: يكتسب server/lib/routes/cli-detect.mjs واجهتَي Grok Build CLI (grok) وKimi CLI (kimi)، وتفحص Antigravity الآن ثنائيّها القياسي agy أولاً. يُبلّغ مسح PATH للقراءة فقط الآن عن 9 أدوات؛ وما زال لا يُشغّل أي ثنائي يجده.

غُيّر

  • إعادة مزامنة التوثيق مع career-ops.org/docs — طُوبِقت كل واجهة توثيق مع صفحات المشروع الأب الحيّة (قُرئت الـ31 كلها). قائمة مساعدي الذكاء الاصطناعي القياسية (help ×17 + README ×17) تسرد الآن الواجهات الثماني من الدرجة الأولى — Claude Code، Codex، OpenCode، Antigravity CLI، Grok Build CLI، Qwen Code، Kimi، GitHub Copilot CLI — إضافةً إلى Gemini CLI (غلاف قديم). تحافظ حزم المساعدة على بنية 29 H2 / 105 H3.

[1.125.4] — 2026-07-23

غُيّر

  • تبعيات site (dependabot #151–#153) — sharp 0.34.5→0.35.3 وsvgo 4.0.1→4.0.2 وfast-uri (dev) 3.1.3→3.1.4 في site/؛ بناء Astro أخضر، دون تأثير على SPA/الخادم.

ملاحظات

  • مسح تكافؤ مع المشروع الأب (career-ops 37d17ec..254764a، بعد v1.22.0) — لا شيء يُنقل: حارس الصف الخاطئ في set-status (#2108) خاص بـ CLI فقط (في web-ui تُختار صفوف المتتبّع صراحةً في الواجهة ولا يستدعي أي مسار set-status.mjs)، وRisk Summary للأوضاع المترجمة (#2109) يمسّ ملفات modes/<lang>/ التي لا يقرؤها web-ui أبدًا (يقرأ فقط modes/*.md في المستوى الأعلى)، والتحقق من الـ manifest في update-system (#2111) خاص بالمحدِّث فقط، والباقي وثائق المشروع الأب (README تركي، SIGNATURES ×4، SCRIPTS.md، تشكيل es). يبقى VERSION الأب 1.22.0parentVersion دون تغيير.

[1.125.3] — 2026-07-23

أُصلح

  • كانت مطالبات LLM بالدنماركية والهندية تُجيب بالإنجليزية (أبلغ عنها مستخدم) — لم يُوسَّع LOCALE_NAMES ولا كتل SCAFFOLD_STRINGS الخمس في server/lib/prompts.mjs لتشمل da وhi، فكان resolveLocale() يعود إلى en، وتفقد كل مطالبة ذكاء اصطناعي — البحث المعمّق (المباشر واليدوي)، الأوضاع، التقييم، المقابلة، التواصل، CV Studio — توجيه # Output language في هاتين اللغتين. أصبحت كلتاهما الآن مدعومتين بالكامل: توجيه اللغة + سقالة مترجمة. بوابة الانحدار في tests/locale-scaffold.test.mjs تمسح الآن قائمة اللغات القياسية السبع عشرة بدلاً من 12 مثبّتة يدويًا، وبوابة بنيوية جديدة تُفشل أي مفتاح سقالة يعود إلى الإنجليزية — لغة مستقبلية تُغفل prompts.mjs لن تُنشر بعد الآن (+12 اختبارًا، المجموعة الآن 1969).

[1.125.2] — 2026-07-22

أُصلح

  • البحث المعمّق عبر Gemini: خطأ HTTP 502 (MALFORMED_FUNCTION_CALL) (#145، مساهمة من @Alien10140) — كان موجّه /api/deep المباشر يطلب من النموذج «Use WebFetch / WebSearch» وحفظ الموجز في ملف، لكن مزوّدي واجهات API بلا أدوات ليس لديهم قناة أدوات؛ فكان Gemini يردّ باستدعاء دالة بدل النص، فيظهر ذلك كخطأ 502 فارغ. أصبح لدى buildDeepPrompt وbundleProjectContext علم headless: التشغيلات المباشرة (Anthropic/Gemini/سلسلة الاحتياط) تتلقى موجّهًا بلا أدوات يكتب الموجز من السياق المضمّن، بينما يحتفظ موجّه النسخ واللصق الخاص بـ Claude Code بتعليمات الأدوات. ‎+1 اختبار في tests/critical-fixes.test.mjs.

غُيّر

  • رفع نماذج Gemini الافتراضية بعد إيقاف gemini-2.0-flash (#144، مساهمة من @Alien10140) — القائمة المنسدلة في الإعدادات، واحتياطي الخادم في gemini.mjs (الذي كان يخالف التلميح بصمت)، وسلسلة احتياط OpenRouter، وconfig.geminiModelHint ×17، ودليل المساعدة ×17 كلها تشير الآن إلى gemini-3.6-flash. البوابة الجديدة ضد الانحراف tests/gemini-default-model.test.mjs (+5 اختبارات) تثبّت كل الأسطح على القيمة الحرفية نفسها — وأصبحت الحزمة الآن 1957 اختبارًا.

[1.125.1] — 2026-07-21

أُصلح

  • SuccessFactors: يحتفظ مستأجرو RMK متعددو العلامات التجارية بمسار علامتهم (تكافؤ مع المشروع الأصلي، ‏#2099، بعد v1.22.0) — الشركات القابضة التي تُشغّل عدة علامات تجارية مستحوذ عليها عبر مثيل RMK واحد مشترك تميّزها بجزء من مسار الرابط (careers.nemetschek.com/Bluebeam/ مقابل …/Vectorworks/)؛ كان المحوّل يختزل عنوان URL المُهيَّأ إلى أصله، فيمسح بصمتٍ إعلانات العلامة التجارية الرئيسية فقط. أصبحت نقطة النهاية الآن تحافظ على بادئة العلامة التجارية، ولا تحذف سوى جزء لاحق مثل /search/ أو /tile-search-results/، بحيث لا يتكرر أي شيء على نفسه أبداً؛ ويبقى المستأجرون ذوو النطاق الواحد دون تغيير بايت واحد. دالة مساعدة جديدة مُصدَّرة باسم resolveTenantBase + كتلة اختبار واحدة منقولة في tests/sources-successfactors.test.mjs.

[1.125.0] — 2026-07-21

أُضيف

  • cvstart.org: قسم “مصادر الوظائف” على الصفحة الرئيسية — قسم جديد بين لقطات الشاشة وقائمة المقارنة يسرد جميع مصادر المسح الـ67 كشرائح قابلة للنقر (62 لوحة/نظام ATS إنجليزي + 5 لوحات روسية تحت عنوان فرعي خاص بها)، ويرتبط كل منها بالموقع العام الخاص بالمصدر. تُزامَن القائمة مع سجل المحوّلات الحي عند البناء (sync-assets.mjsfacts.sources)، بحيث لا يمكن أن تنحرف أبداً عن التطبيق؛ وخريطة روابط منسَّقة في Sources.astro محمية باختبار tests/site-sources.test.mjs الجديد. اكتسب شريط التنقّل العلوي مرساة مصادر جديدة؛ و4 مفاتيح i18n جديدة للموقع ×17. كما أُصلحت قائمة inLanguage في JSON-LD الخاص بالصفحة الرئيسية، التي كانت لا تزال تفتقد hi.

[1.124.0] — 2026-07-21

أُضيف

  • خمسة مصادر مسح (تكافؤ مع المشروع الأصلي career-ops v1.22.0، ‏#1808/#1572/#2024/#2055) — Welcome to the Jungle (واجهة JSON على مستوى اللوحة بأكملها)، وAgentic Engineering Jobs (لوحة وظائف الهندسة العميلة/الذكاء الاصطناعي)، وJobvite (نظام ATS لكل مستأجر بلا مصادقة)، وGem (نظام ATS لكل مستأجر)، وAlibaba Group (واجهة JSON لصفحة التوظيف، على غرار نمط Meituan/Tencent). كل واحد منها زوج (مصدر ومحوّل) مُثبَّت على مضيف ومعزول عن CI؛ يوفّر السجل الآن 67 محوّلاً (62 إنجليزية + 5 روسية)؛ حُدِّث احتياطي قائمة Source المنسدلة في #/scan وفحص الانحراف الخاص بها؛ وخمس مجموعات اختبارات جديدة tests/sources-{wttj,agenticjobs,jobvite,gem,alibaba}.test.mjs.

أُصلح

  • Arbeitsagentur: العمل عن بُعد على مستوى البلد بأكمله فقط حين تكون قيمة homeofficetyp مساوية لـ VOLLSTAENDIG (تكافؤ مع المشروع الأصلي، ‏#1981) — استعلام homeoffice=nv_true يُعيد أيضاً وظائف هجينة، لذا يتحقّق تمرير العمل عن بُعد الآن من كل نتيجة عبر نقطة نهاية تفاصيل الوظيفة على دفعات صغيرة، ويتعامل مع الفشل بأسلوب آمن: أي خطأ في البحث يُبقي على المدينة الحقيقية للوظيفة بحيث تظل مرشِّحات الموقع سارية.
  • SmartRecruiters: كانت روابط الوظائف العامة تُبنى دون /postings/ (تكافؤ مع المشروع الأصلي، ‏#2047) — أصبحت الروابط الآن تصل إلى صفحة الوظيفة العامة بدلاً من خطأ 404 لدى المستأجرين الذين يُسقط موقعهم العام هذا الجزء من الرابط.

ملاحظات

  • شحن المشروع الأصلي v1.22.0 أيضاً تغييرات من جهة CLI لا تستدعيها الواجهة عبر الصدفة أو تغطّيها مسبقاً: قالب السيرة الذاتية بالصينية المبسّطة (zh-CN) وطباعة PDF، ووضع /expand، وتعديلات ذاكرة التخزين المؤقت لمِحثّات المزوّدين (Gemini/OpenAI/Ollama)، وتفصيل الرموز لكل خطوة (تملك الواجهة مقياس استخدامها الخاص)، وتسلسل قفل الكتابة الخاص بالمتتبّع (تُمرّر الواجهة عمليات الكتابة عبر withFileLock منذ v1.21)، وأعلام المسح visa_filter وتاريخ النشر المطلق (تملك الواجهة مرشِّح عمرها الخاص «منشورة خلال»)، وبذر إزالة تكرار المصادر التي سبق رصدها (يحتفظ ماسح الواجهة بإزالة التكرار الخاصة بسجل المسح).

[1.123.0] — 2026-07-17

أُضيف

  • مصدر مسح Oracle Recruiting Cloud (تكافؤ مع المشروع الأصلي career-ops v1.21.0، ‏#1929) — واجهة recruitingCEJobRequisitions REST بلا مصادقة لمواقع التوظيف Oracle Fusion/ORC (JPMorgan Chase وOracle وBNY Mellon وAmerican Express وHoneywell وغيرها): مضيف مُثبَّت على *.fa[.<region>][.ocs].oraclecloud.com، ورقم الموقع يُستخرج من careers_url الخاص بكل شركة متابَعة، وترقيم صفحات بالإزاحة مع حدّ أقصى صارم لعدد الصفحات، وترويسات شبيهة بالمتصفح واعية بـ WAF. يوفّر السجل الآن 62 محوّلاً (57 إنجليزية + 5 روسية)؛ حُدِّث احتياطي قائمة Source المنسدلة في #/scan وفحص الانحراف الخاص بها؛ ومجموعة اختبارات جديدة معزولة عن CI باسم tests/sources-oraclecloud.test.mjs.

أُصلح

  • كاشف إعادة النشر: العناوين الأساسية تبقى متمايزة عن أخواتها ذات اللاحقة المتخصصة (تكافؤ مع المشروع الأصلي، ‏#1922) — لم يعد العنوان «Senior Analytics Engineer» يُجمَّع مع «Senior Analytics Engineer, People Analytics»: حين تكون رموز أحد العنوانين مجموعة جزئية صارمة من رموز الآخر وتكون الكلمة الإضافية تخصصاً حقيقياً لا كلمة أساسية، يُعامَل العنوانان بوصفهما وظيفتين قابلتين للنشر منفصلتين. صارت تعليقات إعادة النشر مثل «(Repost)» و«relisted» تُعامَل الآن كضوضاء وصفية عبر كلمات توقف. +2 تأكيدين في tests/detect-reposts.test.mjs.

ملاحظات

  • شحن المشروع الأصل v1.21.0 أيضاً تغييرات من جهة CLI لا تستدعيها الواجهة عبر الصدفة أو تغطّيها مسبقاً: تحذير إعادة التقديم لشركة سبق التقديم لها (تملك الواجهة فترة تهدئة لإعادة التقديم منذ v1.84.0)، وأعلام --format/--report لخطاب التقديم، وأوضاع prompt البريد الإلكتروني للمقابلة (الإشارات الحمراء / معلومات اللجنة / التغيّب عن المقابلة)، وحفظ إشارات الثقة وصحة البوابات في المسح (تُشغّل الواجهة ماسحها الخاص داخل العملية مع trust-validator وصفحة صحة البوابات)، وامتدادات الإحصاءات وفجوة الراتب (تُنقَل بالقراءة فقط وبأسلوب fail-soft).

[1.122.0] — 2026-07-16

أُضيف

  • الهندية (हिन्दी) — اللغة السابعة عشرة — قاموس واجهة كامل (نحو 1,110 مفتاحاً)، ودليل المساعدة الكامل داخل التطبيق (تكافؤ 29 قسماً رئيسياً / 105 قسماً فرعياً)، وREADME.hi.md، وCHANGELOG.hi.md جديد (يبدأ من v1.122.0، على غرار سابقة الألمانية والإيطالية والتركية)، وصفحة الهبوط cvstart.org مع صفحات المنهجية/الرخصة/سجل التغييرات/المساعدة، ومبدّل اللغة (🇮🇳)، والكشف التلقائي عن لغة المتصفح، ولقطة شاشة موطّنة للوحة التحكم. أصبح كل بوابة تكافؤ ×16 تعمل الآن ×17: تكافؤ قاموس i18n واللقطة المرجعية، وبوابتا الأقسام الرئيسية/الفرعية لدليل المساعدة، وتكافؤ سجل التغييرات، وفحص check-i18n في الموقع، ومسح Playwright للغات.

[1.121.0] — 2026-07-16

أُضيف

  • cvstart.org: صفحات المنهجية والرخصة وسجل التغييرات — اكتسبت صفحة الهبوط ثلاثة أقسام جديدة بكل اللغات الست عشرة، إلى جانب كتلة المقارنة القائمة: /methodology/ (معيار التقييم السداسي الأبعاد من 0.0 إلى 5.0، وعتبة التقديم عند 4.0، وقواعد ما لا يُفعل أبداً — ملخّص مُوطَّن من career-ops.org/methodology)، و**/license/** (نص MIT القانوني الكامل مع الإشارة إلى NOTICE.md)، و**/changelog/** (هذا الملف نفسه، مُعروضاً لكل لغة من ملفات CHANGELOG الست عشرة المترجمة في المستودع). إدخال جديد المنهجية في الترويسة وروابط الموارد في التذييل؛ أصبح sync-assets.mjs الآن يزامن CHANGELOG ×16 وLICENSE إلى الموقع وقت البناء، بحيث لا يمكن للصفحات أن تنحرف عن المستودع أبداً.
  • روابط المنهجية عبر التوثيق — أصبح README (بكل اللغات الست عشرة)، وقائمة §1 المرجعية في دليل المساعدة داخل التطبيق (بكل اللغات الست عشرة)، والويكي، تربط جميعها إلى career-ops.org/methodology (إلى جانب الأسئلة الشائعة والمسرد) بجوار أدلة career-ops.org/docs القائمة.

تغيّر

  • تحديث شارة الإصدار وشعار README (1850 اختبارًا، الإصدار v1.121.0) — كانت الشارة لا تزال تعلن عن v1.119.5.

[1.120.0] — 2026-07-16

أُضيف

  • بيان CareerOps (تكافؤ مع الأصل v1.20.0) — أطلق المشروع الأصل بيان CareerOps (MANIFESTO.md · career-ops.org/manifesto) ويُبرزه الآن من README الخاص به، ومن أداة التحديث، ومن لوحة تحكم Go. تحذو الواجهة حذوه: رابط جديد في تذييل الشريط الجانبي يفتح صفحة البيان (مفتاح i18n جديد footer.manifesto في كل اللغات الست عشرة)، ودليل المساعدة داخل التطبيق اكتسب القسم 29 «بيان CareerOps» بكل اللغات الست عشرة، وREADME يشرح ماهية البيان وكيفية توقيعه، وتذييل صفحة الهبوط cvstart.org يربط إليه أيضاً.

ملاحظات

  • أصلح المشروع الأصل v1.20.0 أيضاً كبت المهارة المعروفة في وضع upskill المستهدف، وأسكت dotenv كي يبقى مخرج scan --json القياسي قابلاً للتحليل، وأصلح قالب السيرة الذاتية بصيغة HTML كي يبقى عنوان الدور مع نقاطه — وهي أسطح من جهة CLI لا تستدعيها الواجهة عبر الصدفة؛ لم يتطلّب الأمر أي تغيير في كود الواجهة.

[1.119.5] — 2026-07-13

أُصلح

  • زر اللغة في صفحة الهبوط لم يعد ينكسر — مع أعلام v1.119.2 كان نص المبدّل في الترويسة (مثل «🇷🇺 Русский») قد ينكسر حتى ثلاثة أسطر على عروض سطح المكتب الضيقة؛ نص المبدّل وكل خيارات القائمة المنسدلة أصبحت الآن whitespace-nowrap — العلم + الاسم الأصلي دائماً في سطر واحد. كما انتقلت قائمة اللغات في التذييل من شبكة صلبة بعمودين إلى صفٍّ ملتفٍّ من عناصر بسطر واحد — «🇧🇷 Português (Brasil)» لم يعد ينكسر في منتصف الاسم أيضاً.

[1.119.4] — 2026-07-13

تغيّر

  • LICENSE يسمّي المؤلف — سطر حقوق النشر أصبح: Sergei Emelianov (Fighter90) https://sergey-cv.com and career-ops-ui contributors (نص MIT القانوني كما هو). ملف NOTICE.md جديد يفصّل الترخيص: من يملك حقوق النشر، وما الذي يغطيه منح MIT بالضبط (الكود، الوثائق، الترجمات، صفحة الهبوط، الويكي)، وما لا يغطيه (بياناتك أثناء التشغيل، المشروع الأب، محتوى لوحات الوظائف، العلامات التجارية)، وجدول مكونات الطرف الثالث (express/js-yaml — MIT؛ Astro/Tailwind — MIT؛ خطا Figtree وJetBrains Mono — SIL OFL 1.1؛ sharp — Apache-2.0) وسطر إسناد اختياري.

[1.119.3] — 2026-07-13

أُضيف

  • SECURITY.md — سياسة الأمان التي كان يشير إليها CONTRIBUTING أصبحت موجودة الآن: الإصدارات المدعومة، مسار الإبلاغ الخاص (تم تفعيل الإبلاغ الخاص عن الثغرات في GitHub على المستودع — تبويب Security → «Report a vulnerability»)، نموذج التهديد لتطبيق مستخدم واحد مرتبط بـ localhost (ضمن النطاق: XSS عبر إعلانات وظائف عدائية / SSRF / اجتياز المسارات / تسرب الأسرار / إضعاف CSP؛ خارج النطاق: حجب الخدمة عن localhost الخاص بك ومشكلات المشروع الأب)، وخط الأساس للتحصين للمراجعين.

[1.119.2] — 2026-07-13

أُضيف

  • CONTRIBUTING.md — دليل المساهم الذي طالما أشارت إليه صفحة الهبوط وREADME أصبح موجوداً الآن: الإعداد، خريطة المشروع، القواعد الصارمة للأمان/عدم البناء، مستويات الاختبار، شرح «السجلّين» لإضافة مصدر فحص، عقد i18n ×16، اصطلاحات الالتزامات/طلبات السحب، وعملية الإصدار.
  • أعلام اللغات على صفحة الهبوط — يعرض الآن مبدّل لغات cvstart.org وشبكة اللغات في التذييل ولافتة «اقرأ بلغتك» علم كل لغة بجانب اسمها الأصلي (نفس مجموعة المؤشرات الإقليمية في <select> لغة التطبيق؛ يتدهور إلى حروف المنطقة حيث تغيب محارف الأعلام).
  • إصلاحات تذييل صفحة الهبوط — رابط Discussions الميت (الميزة غير مفعّلة في المستودع) يشير الآن إلى ويكي المشروع، ويذكر التذييل المؤلف: Sergei Emelianov (sergey-cv.com · LinkedIn).

[1.119.1] — 2026-07-13

أُصلح

  • مرشِّح المصادر في #/scan لحق بالسجل — القائمة الثابتة FALLBACK_SOURCES خلف قائمة Source المنسدلة (تُستخدم فقط عندما يتعذّر الوصول إلى GET /api/scan/sources) كانت متخلفة بصمت منذ v1.87.0: كان ينقص الاحتياطي دون اتصال 20 مزوّداً (Amazon وAvature وSAP SuccessFactors وGet on Board وDassault Systèmes وbeesite وHigherEdJobs وJibeApply (iCIMS) وsoftgarden وCornerstone وPhenom وRadancy وDeutsche Bahn وEchoJobs وTKMS وHeckler & Koch وRheinmetall وLaraJobs والجديدان Meituan / Tencent). زُومنت مع كل الـ61 وتحميها الآن فحص انحراف يُفشل CI كلما اختلفت قائمة العميل عن سجل الخادم (القيم والتسميات معاً). +1 اختبار (1845).

[1.119.0] — 2026-07-13

تكافؤ مع مشروع career-ops الأب v1.19.0 + تحديث صفحة الهبوط cvstart.org.

أُضيف

  • مزوّدا فحص جديدان — Meituan (zhaopin.meituan.com) وTencent (careers.tencent.com): واجهتا JSON عامتان بلا مصادقة للوحات التقنية الصينية، تُكتشفان من المضيف أو تُختاران عبر provider: صريح، مع بحث من جهة الخادم لكل كلمة مفتاحية وتقسيم صفحات وإزالة تكرار حسب URL — الآن 61 محوّلاً (56 إنجليزية + 5 روسية). +20 اختباراً (1844).
  • كتلة المساهمين على صفحة الهبوط — يعرض cvstart.org صور كل من ساهم بكود (واجهة GitHub /contributors وقت البناء، مع ترشيح البوتات)، مترجمة إلى اللغات الست عشرة كلها، مع رابط إلى مخطط المساهمين الكامل.
  • عدّاد نجوم GitHub مباشر على صفحة الهبوط — تُحدَّث شارة الترويسة الآن من جهة العميل عبر واجهة GitHub عند كل زيارة (لقطة وقت البناء تبقى احتياطاً)، وإعادة بناء أسبوعية مجدولة لـ Pages تُبقي اللقطة وقائمة المساهمين حديثتين؛ نداءات الواجهة في CI موثّقة برمز token.

أُصلح

  • طلبات Workday CXS تحمل ترويسات شبيهة بالمتصفح (الأب #1813) — المستأجرون خلف Cloudflare (شوهد مباشرةً: geico) يردّون بـ 500 على الطلبات الخالية من UA/accept-language/origin/referer الاعتيادية؛ يستنتج الجالب الآن الـ origin واسم الموقع من عنوان CXS نفسه. ونالت طلبات Glints نفس UA المتصفح + origin/referer، وكلاهما من الثابت المشترك BROWSER_LIKE_USER_AGENT في http-json.mjs.

[1.118.4] — 2026-07-10

أُصلح

  • عمليات فحص hh.ru كانت تعيد 0 نتيجة من عنوان IP روسي (روابط النطاق الفرعي الإقليمي) — من عنوان IP سكني روسي، يعيد hh.ru توجيه البحث عبر 302 إلى نطاق فرعي إقليمي (sochi.hh.ru، spb.hh.ru، …) ويعيد روابط الوظائف على ذلك النطاق الفرعي. كان المحلل يبحث عن رابط العنوان على المضيف الثابت https://hh.ru/vacancy/ ولا يطابق أيًّا من الروابط الإقليمية، فكان الفحص العامل تمامًا يسجّل 0 بصمت. أصبح الآن يقبل أي مضيف *.hh.ru (إعلانات adsrv.hh.ru/click?… تظل مستبعدة — لا تملك مسار /vacancy/<id>) ويوحّد كل رابط نتيجة إلى https://hh.ru/vacancy/<id>. تم التحقق مباشرةً: يجري الآن تحليل 17 وظيفة حقيقية من صفحة sochi.hh.ru كانت تعطي 0 سابقًا. ‏+1 اختبار (1824).

[1.118.3] — 2026-07-10

أُصلح

  • كان hh.ru يعيد 0 نتيجة بصمت (صفحة فحص VPN البينية) — يعيد hh.ru الآن توجيه الشبكات التي يصنّفها كـ VPN/بروكسي (عناوين IP لمراكز البيانات) عبر 302 إلى صفحة بينية /vpncheeck (“VPN мешает работе сайта”) تستجيب بـ HTTP 200 دون أي بطاقة وظيفة، فكان الفحص يبلغ عن 0 دون أي خطأ. يكتشف الماسح الآن إعادة التوجيه عبر عنوان URL النهائي للاستجابة، ويعطّل hh.ru لبقية التشغيل، ويطبع تلميحًا صادقًا: يجب أن يخرج الترافيك فعلًا عبر IP سكني — قد يبقى VPN/بروكسي على مستوى النظام نشطًا حتى مع إيقاف مفتاح المتصفح. ‏+1 اختبار (1823).

[1.118.2] — 2026-07-10

صيانة

  • متابعة صفحة الهبوط (#118) — جرت مطابقة site/README.md مع Astro 7 (ترقية الأمان في #116)، وأزيل استيراد غير مستخدم، وأضيفت 4 حراسات قابلة للتنفيذ لسكربتات بناء صفحة الهبوط: بوابة تكافؤ i18n تفشل فعليًا مع قاموس معطوب، وsync-assets لا يكتب أبدًا خارج site/ — المجموعة 1822. حُلّ تنبيهان من CodeQL (أحدهما أُصلح في المصدر والآخر رُفض بوصفه سلوك بناء مقصودًا).

[1.118.1] — 2026-07-10

أُصلح

  • مسح hh.ru من خارج روسيا — يعيد hh.ru الآن HTTP 451 (حجب قانوني إقليمي) لعناوين IP غير الروسية على صفحات البحث العامة. يعامل الماسح 451 مثل 403: بعد أول حجب يُعطَّل hh.ru لبقية التشغيل مع سطر صادق في السجل يشير إلى عنوان IP روسي / مخرج VPN، فلا تُهدر الاستعلامات المتبقية ولا بقية المصادر الروسية. حُدِّث دليل المساعدة §7 بجميع اللغات الـ16. +1 اختبار (1818).

[1.118.0] — 2026-07-09

حزمة التكافؤ مع مشروع career-ops الأب v1.18.0.

أُضيف

  • 9 مزوّدي فحص جدد — Cornerstone OnDemand (csod), Phenom (phenom), Radancy (radancy), Deutsche Bahn (deutschebahn), EchoJobs (echojobs), TKMS (tkms), Heckler & Koch (hecklerkoch), Rheinmetall (rheinmetall), LaraJobs (larajobs) — الآن 54 محوّلًا. كما يتعرّف محوّل Lever على لوحات مستأجر الاتحاد الأوروبي (jobs.eu.lever.co).
  • حالة Hired في المتتبِّع (تكافؤ مع states.yml الأب): العروض المقبولة تحصل على حالة قياسية خاصة وشارة احتفالية ولافتة «حصلت على الوظيفة» في #/tracker؛ ويحسبها القمع والتحويلات كأنها اجتازت كل المراحل.
  • تبويب الإجمالي في #/stats — تمرير للقراءة فقط لسكربت الأب stats.mjs (ملخص المتتبِّع الإجمالي، معدلات القمع التراكمية، إجماليات الماسح، تغطية البوابات) بالإضافة إلى ملاحظات التعويضات من salary-gap.mjs (المرغوب مقابل المعلن مقابل الفعلي، لكل طلب). مساران جديدان GET /api/stats/lifetime وGET /api/stats/salary-gap — استدعاءات صدفة بلا تكلفة توكنات، وتدهور آمن {available:false} من دون المشروع الأب.
  • 28 مفتاح i18n جديدًا في اللغات الـ16 كلها؛ وتحديث دليل المساعدة §14/§26 بكل اللغات.

الاختبارات

  • +38 اختبار وحدة (ثلاث مجموعات تكافؤ للمزوّدين + مسارات التمرير/الحالة) — الإجمالي 1817.

[1.117.2] — 2026-07-06

إصلاح المتتبِّع الفارغ لاستدعاءات التكافؤ الخارجية. تخرج سكربتات الأصل بالرمز 1 مع JSON منظم {error} عندما لا يحتوي المتتبِّع على طلبات بعد؛ وكانت لوحة المتابعة وتبويب أنماط الرفض يعرضانها كـ«script-error». يمرر المساران الآن ذلك كحالة فارغة سليمة (available:true, empty:true) فتعرض الواجهة رسالتها الصادقة «لا شيء بعد». تم التحقق مباشرة مع أصل حقيقي.

جديد: لا شيء.

[1.117.1] — 2026-07-06

تقوية v1.117.0 (فرز CodeQL). تحمل الآن نقاط النهاية الثلاث ذات الاستدعاء الخارجي (GET /api/followup، POST /api/followup/seed، GET /api/stats/patterns) محدِّد المعدل المشترك حسب IP (تنشئ عملية فرعية لكل طلب؛ no-op على loopback). استخراج نص الرابط في «أضِف إلى السيرة» يزيل الوسوم حتى نقطة ثابتة ثم يحذف كل </> متبقٍ — تعقيم كامل مثبت لنص مطالبة LLM. لا تغيير في سلوك المدخلات الصحيحة.

جديد: لا شيء.

[1.117.0] — 2026-07-06

حزمة التكافؤ مع الأصل — ست قدرات من career-ops الأصل إلى الواجهة. (1) لوحة إيقاع المتابعة في #/followup: استعجال لكل طلب (🔴/🟠/🟡/🔵) من followup-cadence.mjs، مع زر زرع تواريخ المتابعة (followup-seed.mjs --backfill). (2) أنماط الرفض: تبويب إحصاءات رابع يشغّل analyze-patterns.mjs (قراءة فقط) — توزيع النتائج والتوصيات ومعدل التقدّم لكل مزوّد ATS. (3) أضِف إلى السيرة: بطاقة في CV Studio تحوّل رابطًا أو نصًا ملصوقًا إلى نقاط ATS مبنية فقط على ذلك المصدر (اقتراحات فقط، دون كتابة؛ جلب الروابط محمي من SSRF). (4) 4 مزوّدي فحص جدد — beesite وHigherEdJobs (RSS) وJibeApply (iCIMS) وsoftgarden — يضم السجل الآن 50 محوّلاً (45 EN + 5 RU)، كلها في قائمة Scan. (5) خطوة الفحص المسبق للمُقصيات في قائمة Apply. (6) مشغّل reconcile (/api/run/reconcile). مسارات الاستدعاء الخارجي تتدهور بصدق دون سكربتات الأصل.

  • وحدة مسارات جديدة server/lib/routes/followup.mjs (الـ31) + مسارات جديدة + 8 ملفات source/adapter. اختبارات: 6 + 7 جديدة؛ المجموعة 1737 → 1750. 41 مفتاح i18n جديدًا ×16. المساعدة §13/§17/§24/§26 موسّعة ×16.

جديد: GET /api/followup، POST /api/followup/seed، GET /api/stats/patterns، POST /api/cv-studio/add-entry، POST /api/run/reconcile.

[1.116.0] — 2026-07-06

إعادة تصميم مقياس الاستخدام + أول اختبار شامل للأدوات. جرى إصلاح مقياس استخدام الذكاء الاصطناعي (v1.114.0) وتثبيته بشكل صحيح: أصبح الآن مثبَّتًا أسفل الشريط الجانبي الأيسر (بعرض الشريط كاملًا، بنفس السطح) ويحجز في الأسفل مساحة تساوي ارتفاعه بحيث لا تُغطّى القائمة أبدًا — يظل التنقّل وتذييل الإصدار يمرّان بحرّية فوقه. يُحدَّث مباشرةً (كل 15 ثانية، وعند تركيز التبويب وتغيير المسار)، ويعرض كل صف نافذة الآن <الرموز> · <التكلفة التقديرية> الحقيقية (تُقاس الأشرطة مقابل نافذة 30 يومًا) بدل “نسبة” ثابتة عند 100%. كذلك: حاجز typeof دائم في مستورد السيرة يُغلق من المصدر الإيجابية الكاذبة المتكررة لخلط الأنواع في CodeQL، واختبار شامل جديد بـPlaywright يشغّل كلتا الأداتين الدائمتين في متصفح حقيقي.

  • public/js/lib/usage-hud.js + app.css، server/lib/cv-import.mjs. اختبارات: tests/playwright-widgets.mjs (2 E2E) + tests/usage-hud.test.mjs (10). المساعدة §6 موسّعة ×16.

جديد: لا شيء.

[1.115.0] — 2026-07-06

صقل التصميم (محافظ، مع الإبقاء على هوية المرجاني). تمريرة تحسين خفيفة على نظام التصميم المشترك — دون إعادة هيكلة ودون تغيير اللوحة اللونية. تُرفع الآن بطاقات المقاييس في لوحة التحكم وتكتسب حدًّا مرجانيًا عند التمرير (مثل بلاطات الإجراءات السريعة)؛ وتُرفع بطاقات المحتوى قليلًا؛ وتكتسب أزرار primary / dark / danger ظلًّا في السكون ورفعًا لطيفًا عند التمرير لإحساس بالعمق؛ وتُحاذى الأرقام الكبيرة عبر tabular-nums؛ وتحصل عناصر التحكم التفاعلية على هالة مرجانية ناعمة خلف حلقة لوحة المفاتيح بسمك 2 بكسل. تحترم كل الحركة prefers-reduced-motion، والهالة مقصورة على عناصر التحكم — وليست *:focus-visible عامة.

  • CSS فقط (public/css/app.css)؛ دون تغييرات في العلامات أو i18n أو المسارات أو CSP. اختبارات: tests/design-polish-v1115.test.mjs (5). جرى التحقق مباشرةً عبر Playwright.

جديد: لا شيء.

[1.114.0] — 2026-07-06

مقياس استخدام وتكلفة الذكاء الاصطناعي في الشريط الجانبي (أسفل اليسار). يوجد الآن قسم الاستخدام مضغوط في أسفل الشريط الجانبي (بطاقة ثابتة أسفل اليسار إن لم يوجد شريط جانبي؛ أسفل اليمين في RTL) في كل صفحة. يعرض استخدامك لرموز LLM عبر نوافذ 24 ساعة / 7 أيام / 30 يومًا — كلٌّ بصيغة <الرموز> · <النسبة%> بشريط أخضر (النسبة من كامل المدة) — بالإضافة إلى تذييل بالتكلفة التقديرية لـ24 ساعة. البيانات هي تجميع GET /api/usage للقراءة فقط من data/llm-usage.jsonl (محليًا فقط)، نفس مصدر صفحة #/usage؛ التكلفة تقديرية وعمليات الوضع اليدوي مجانية ولا تُحتسب. قابل للطي — يبدّل العنوان وتُحفظ الحالة.

  • عنصر واجهة جديد public/js/lib/usage-hud.js يُحمَّل من index.html، يُركَّب في الشريط الجانبي فوق تذييل الإصدار (بديل الزاوية الثابتة). آمن لـ CSP؛ متوافق مع السمة ومعكوس لـ RTL. دون مسار خادم جديد. اختبارات: tests/usage-hud.test.mjs (8). 3 مفاتيح i18n جديدة ×16.

جديد: لا شيء.

[1.113.0] — 2026-07-06

مساعد “اسأل المساعدة” عائم في كل صفحة. يطفو الآن زر دردشة على شكل روبوت بتدرّج لوني في الزاوية السفلية اليمنى (السفلية اليسرى في RTL) من كل صفحة. انقره لفتح دردشة مضغوطة تجيب عن أسئلة الاستخدام اعتمادًا فقط على دليل المساعدة داخل التطبيق بلغتك — نفس نقطة النهاية لصفحة #/docs-assistant (POST /api/docs-assistant/ask)، لذا لا يقرأ أبدًا سيرتك أو ملفك أو المتتبِّع. مباشر بمفتاح LLM؛ دون مفتاح → مطالبة جاهزة للتشغيل. يعرض الرأس صورة روبوت + حالة اتصال؛ تُدخل الشارات أسئلة شائعة؛ Esc أو النقر خارجًا يغلقه؛ ويختفي في صفحة #/docs-assistant.

  • عنصر واجهة جديد public/js/lib/docs-fab.js يُركَّب عالميًا من index.html؛ آمن لسياسة CSP؛ أنماط متوافقة مع السمة ومعكوسة لـ RTL في app.css. دون مسار خادم جديد. اختبارات: tests/docs-fab.test.mjs (8). 6 مفاتيح i18n جديدة ×16. المساعدة §1 موسّعة في مكانها.

جديد: لا شيء.

[1.112.0] — 2026-07-06

دمج التوثيق وضمان الجودة. دون تغيير مرئي في الشيفرة. حُدِّث مستند اصطلاحات SDD (docs/sdd/CONVENTIONS.md) إلى 30 وحدة مسار الحالية (كانت 24) وخط أساس الاختبارات الحالي؛ وجرى دمج مطالبة ضمان الجودة النهائية لكامل المشروع (qa/QA-REGRESSION-PROMPT.md) — تحديث آلية الإصدار (v1.111، parentVersion 1.17.0، نشر مُشغَّل بحدث الإصدار)، وتصحيح جدول إضافات §14 (إعادة وسم استبعاد Scan إلى v1.109.0) وتوسيعه بإغلاق CodeQL في v1.111 — بحيث يصلح وحده كمطالبة انحدار وحيدة لكل الوظائف. يضيف اختبار تغطية واحدًا لفرع الرفع المفرط الحجم.

جديد: لا شيء.

[1.111.0] — 2026-07-06

الأمان — إغلاق متأخّرات CodeQL. ثلاثة تحصينات دفاع متعمّق تُغلق ما تبقّى من نتائج التحليل الساكن من مصدرها بدل تجاهلها (dismiss). يقوم stripDangerousMarkdown الآن بتهريب الرمز < لأي فتح وسم خطير مبتور (حمولة تنتهي بـ<script/<iframe/…)، بحيث لا يحتوي ناتجه بشكل مثبت على أي وسم خطير حي. يقرأ استيراد السيرة حجم المخزن المرفوع عبر تحويل صريح Number() — حاجز ضد خلط الأنواع. أصبحت أسطر أدوار الأوضاع الآن سلاسل قالبية مُدمَجة عبر String.replace بدل دوال مخزّنة، مما يزيل استدعاء الإرسال الديناميكي بالكامل. لا تغيير سلوكي مرئي للمستخدم.

  • server/lib/security.mjs، server/lib/cv-import.mjs، server/lib/prompts.mjs. اختبارات: tests/security-hardening-v1111.test.mjs (7) + تحديث اختبار الحارس v1108. دون تغييرات i18n/مساعدة/مسارات.

جديد: لا شيء.

[1.110.0] — 2026-07-06

تحديث الوثائق وضمان الجودة (كل اللغات). دون تغيير في الكود. حُدِّث موجّه QA لكامل المشروع إلى v1.109.0 مع §14 جديد (v1.98→v1.109)، واكتسبت موجّهات تدقيق UX وتصدير التصميم الدائمة مجموعة الصفحات الحالية. كل فقرة مساعدة أُضيفت في v1.100–v1.109 مترجمة الآن إلى جميع اللغات الست عشرة.

جديد: لا شيء.

[1.109.0] — 2026-07-06

مرشّح الاستبعاد في Scan + نظرة عامة على المسار (مطابقة تخطيط الويب). في #/scan، يعامل مربع البحث الآن الفواصل كـأو (“الأدوار المطلوبة”)، ويُخفي حقل استبعاد جديد أي صف تحتوي شركته/دوره/موقعه على أي كلمة مفصولة بفواصل (مثل senior, staff)؛ ويُحفَظ كلاهما في عمليات بحثك المحفوظة. في #/pipeline، يعرض شريط نظرة عامة مضغوط مسارك بنظرة واحدة — N في الوارد، N متتبَّع، وأعداد Applied / Responded / Interview / Offer من المتتبِّع، وكل شارة تربط بـ#/tracker.

  • من جهة العميل فقط (لا مسار/كتابات جديدة). public/js/views/scan.js + public/js/views/pipeline.js. اختبارات: tests/scan-pipeline-ui-v1109.test.mjs (2). 4 مفاتيح i18n جديدة ×16. المساعدة §7 + §8 موسّعة في مكانها.

جديد: لا شيء.

[1.108.0] — 2026-07-06

تعزيز الأمان (فرز CodeQL، الجولة 2). أُصلحت ثلاث نتائج إضافية منخفضة الخطورة: يحلّ باني الموجّهات سطر دور اللغة عبر مفتاح خاص + typeof === function كي لا تتمكّن لغة معدَّلة من الإرسال إلى أسلوب في النموذج الأولي (unvalidated-dynamic-method-call)؛ ويُحدَّد slug اسم ملف PDF بـ200 حرف قبل التعبير النمطي كي لا يتراجع دخل من الشرطات فقط (ReDoS كثير الحدود)؛ ويُجبِر استيراد المستند filename من نوع مصفوفة (ترويسة مكرّرة) على سلسلة (type-confusion). لا تغيير في السلوك للمدخلات الصحيحة.

  • server/lib/prompts.mjs، server/lib/routes/runners.mjs، server/lib/cv-import.mjs + tests/security-hardening-v1108.test.mjs (3). خلال v1.106–v1.108 انخفض تراكم التحليل الساكن من 167 إلى نحو 14، مع إصلاح كل نتيجة ذات صلة أمنية حقيقية ورفض الباقي (إيجابيات كاذبة محميّة/مُعقَّمة + lint بمستوى ملاحظة) مع التبرير.

جديد: لا شيء.

[1.107.0] — 2026-07-06

تعزيز أداة التعقيم (دفاع متعمّق ضد XSS في حالة السكون). stripDangerousMarkdown — التي تُبطل HTML الخطير في markdown السيرة/الوظيفة المخزَّن ليبقى أي مستهلك يتجاوز عميل الهروب-عند-العرض آمناً — تُشغّل الآن إزالة الوسوم حتى نقطة ثابتة (كرّر حتى الاستقرار) بحيث تُلتقط الإزالة التي تعيد تشكيل الحمولة (مثل <scr<script></script>ipt>)، وتطابق وسوم إغلاق script/style وغيرها مع فضلات لاحقة (</script foo>)، وتزيل فاتحاً تنفيذياً غير مغلق (<script …>). سلوك markdown الصحيح دون تغيير — تزيل المزيد فقط.

  • server/lib/security.mjs: حلقة نقطة ثابتة (محدودة بـ8 تمريرات) + أنماط إغلاق [^>]*> + إزالة الفاتح غير المغلق. +3 حالات انحدار في tests/cv-xss-bypasses.test.mjs. يبقى حدّ XSS المرجعي هو الهروب عند الإخراج (UI.md)؛ وهذا يعزز ضمان السكون ويغلق نتائج CodeQL المقابلة.

جديد: لا شيء.

[1.106.0] — 2026-07-06

تعزيز الأمان (فرز CodeQL). أُصلحت ثلاث نتائج حقيقية (وإن كانت منخفضة الخطورة) بعد مراجعة تراكم التحليل الساكن: مسار خطأ عرض المسار الآن يهرّب رسالة الخطأ قبل وصولها إلى DOM (قد يعكس خطأ الخادم مدخلات المستخدم، لذا يُعامَل كغير موثوق — حدّ XSS)، وكتابات خصائص الملف/الإعدادات ترفض مفاتيح __proto__ / constructor / prototype (حماية من تلوث النموذج الأولي احتياطاً — المفاتيح تأتي من مواصفات حقول ثابتة لا من مدخلات خام). معظم التنبيهات المتبقية إيجابيات كاذبة على قراءات/كتابات الماسح المشروعة في data/* وعلى مسارات تحمل بالفعل المحدِّد الخاص؛ رُفضت مع التبرير.

  • public/js/router.js يهرّب err.message عبر UI.escapeHtml قبل innerHTML؛ وserver/lib/routes/content.mjs وserver/lib/routes/config.mjs يحميان مفاتيح النموذج الأولي. لا تغيير في السلوك للمدخلات الصحيحة. اختبارات: tests/security-hardening-v1106.test.mjs (3). لا مفاتيح i18n جديدة.

جديد: لا شيء.

[1.105.0] — 2026-07-06

صفحة استخدام الذكاء الاصطناعي وتكلفته. صفحة جديدة استخدام الذكاء الاصطناعي (الشريط الجانبي، بجوار الصحة) تعرض عدد الرموز التي أنفقتها على توليدات الذكاء الاصطناعي الحيّة — التقييمات والتقارير والمحادثات — مفصّلة حسب المزوّد خلال آخر 24 ساعة و7 أيام و30 يوماً وكل الوقت، مع تكلفة تقديرية بالدولار. كل نداء حيّ يضيف سجلاً صغيراً {provider, in, out} إلى data/llm-usage.jsonl (لا يُرسَل شيء إلى أي مكان)؛ التشغيلات بلا مفتاح (الوضع اليدوي) لا تكلّف شيئاً ولا تُسجَّل.

  • وحدة مسار جديدة (الثلاثون) server/lib/routes/usage.mjsGET /api/usage (تجميعات للقراءة فقط) + server/lib/llm-usage.mjs (recordUsage يوحّد أشكال الاستخدام لدى Anthropic/OpenAI/Gemini ويضيف بأفضل جهد؛ وreadUsage/aggregate يجمّعان حسب نافذة 24س/7أ/30ي/الكل × المزوّد) + server/lib/llm-pricing.mjs (جدول أسعار قابل للتعديل لكل مزوّد $/1M رمز — الرموز دقيقة، والدولارات أسعار قائمة تقريبية يمكنك تصحيحها؛ ولا تُحتسب فاتورةً). التسجيل موصول بنقاط الإرسال (runActiveProvider + routes/llm.mjs).
  • عرض جديد public/js/views/usage.js (#/usage، تبويبات النوافذ). اختبارات: tests/usage-routes.test.mjs. 17 مفتاح i18n جديد ×16 (usage.* + nav.usage). المساعدة §6 موسّعة في مكانها.

جديد: server/lib/routes/usage.mjs؛ server/lib/llm-usage.mjs؛ server/lib/llm-pricing.mjs؛ public/js/views/usage.js.

[1.104.0] — 2026-07-06

شعارات الشركات في جدول الفحص (مع حفظ الخصوصية). مفتاح جديد في المظهر ضمن إعدادات التطبيقإظهار شعارات الشركات في جدول الفحص (معطّل افتراضياً) — يرسم شعار كل شركة بجوار اسمها في #/scan. الشعار هو أيقونة الشركة المجلوبة من نطاقها الخاص ويُمرَّر عبر وسيط على الخادم (GET /api/logo)، بحيث لا تعرف أي خدمة شعارات خارجية أيّ أصحاب عمل تطالعهم. الوظائف على بوابة توظيف مشتركة (Greenhouse وLever وAshby …) تُظهر شارة حرفية ملوّنة بدل أيقونة البوابة، وأي شعار يفشل تحميله يعود إلى الشارة نفسها.

  • وحدة مسار جديدة (التاسعة والعشرون) server/lib/routes/logos.mjsGET /api/logo?domain=. تتحقق من النطاق (دون مخطط/مسار/loopback)، وتجلب /favicon.ico عبر safeGet الآمن ضد SSRF (وضع binary جديد يعيد البايتات الخام + content-type؛ وتثبيت DNS والتحقق من إعادة التوجيه وحد الحجم دون تغيير)، وتُجري استشعار توقيع الصورة كي لا تقدّم أبداً صفحة خطأ HTML كصورة، وتخزّن الإصابات والإخفاقات في LRU بالذاكرة، ولا تكتب شيئاً على القرص.
  • مكتبة عميل جديدة public/js/lib/company-logo.js (window.CompanyLogo): معطّلة افتراضياً عبر علم في localStorage؛ وتتخطى مضيفات ATS المشتركة لصالح صورة رمزية حرفية حتمية؛ واحتياطي img.onerror آمن لـCSP. اختبارات: tests/logo-routes.test.mjs. 5 مفاتيح i18n جديدة ×16 (appear.*). المساعدة §2 موسّعة في مكانها.

جديد: server/lib/routes/logos.mjs؛ public/js/lib/company-logo.js.

[1.103.0] — 2026-07-06

الإعدادات: «أدوات CLI للذكاء الاصطناعي» — أيّها مثبَّت. يعتمد career-ops على Claude Code لكنه يعمل مع أي CLI وكيلي يتبع معيار skills المفتوح. يعرض تبويب جديد أدوات CLI للذكاء الاصطناعي في إعدادات التطبيق (#/config) أيّاً من — Claude Code وCodex وGemini CLI وOpenCode وGitHub Copilot CLI وQwen وAntigravity — مثبَّت على الجهاز الذي يشغّل الخادم، ومساراتها. إنه فحص للـPATH للقراءة فقط: يتحقق فقط من وجود كل ملف تنفيذي ولا يشغّله إطلاقاً (دون --version، دون تنفيذ)، ولا يكتب شيئاً ولا يمسّ بيانات المستخدم.

  • وحدة مسار جديدة (الثامنة والعشرون) server/lib/routes/cli-detect.mjsGET /api/cli-detect. يحلّ الكشف مسار الملف التنفيذي من قائمة سماح ثابتة من 7 عناصر عبر process.env.PATH (وسائط .cmd/.exe/.bat في ويندوز؛ بت التنفيذ في POSIX)؛ ولا يمكن أبداً تشغيل ملف خبيث على PATH عبر هذا المسار.
  • تبويب جديد «أدوات CLI للذكاء الاصطناعي» في public/js/views/config.js (تحميل مؤجَّل، رابط عميق عبر #/config?tab=cli). اختبارات: tests/cli-detect-routes.test.mjs. 8 مفاتيح i18n جديدة ×16 (cli.* + config.tabCli). المساعدة §2 موسّعة في مكانها.

جديد: server/lib/routes/cli-detect.mjs.

[1.102.0] — 2026-07-05

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

  • وحدة مسار جديدة (السابعة والعشرون) server/lib/routes/docs-assistant.mjsPOST /api/docs-assistant/ask. استرجاع بلا تبعيات: يُقسَّم دليل لغتك إلى أقسام ## ويُقيَّم بتداخل الكلمات المفتاحية مع سؤالك؛ تُدمَج الأقسام الأعلى، وعلى النموذج الإجابة منها أو القول إن الدليل لا يغطيه (دون اختلاق ميزات/مسارات). سلسلة مزوّدين مشتركة، احتياطي يدوي، محدودة المعدل، دون كتابة، ولا تقرأ بيانات المستخدم.
  • عرض جديد public/js/views/docs-assistant.js. اختبارات: tests/docs-assistant-routes.test.mjs. 14 مفتاح i18n جديد ×16 (docs.* + nav.docsAssistant). المساعدة §1 موسّعة في مكانها.

جديد: server/lib/routes/docs-assistant.mjs؛ public/js/views/docs-assistant.js.

[1.101.0] — 2026-07-05

CV Studio: كيّف سيرتك الذاتية + اكتب خطاب تقديم لوظيفة محددة، عبر قائمة تحقق بمستوى مسؤول التوظيف. بطاقة جديدة التكييف مع وظيفة في #/cv-studio: الصق وصف وظيفة (واختيارياً دوراً/عنواناً مستهدفاً)، فينتج CV Studio سيرة ذاتية مكيّفة مع ذلك الإعلان بالإضافة إلى خطاب تقديم مطابق، ثم يمرّرهما عبر بوابة قائمة تحقق قبل تسليمهما — الأخطاء error تحجب (تُصحَّح قبل أن ترى النتيجة)، والتحذيرات warn تنصح. الآلية مستخلصة من ممارسة التوجيه المهني إلى قواعد عامة — مسؤول التوظيف يقرأ في ثوانٍ، لذا تذهب الخبرة ذات الصلة إلى الأعلى، ويطابق العنوان دور الوظيفة، وتحمل النتائج أرقاماً محددة، ويبقى الخطاب تشويقاً قصيراً بجسر واحد «المتطلب ↔ حقيقتك المطابقة». يستند فقط إلى سيرتك الذاتية وملفك ووثيقتك المكوّنة من صفحتين، ولا يختلق أبداً — دون شركات أو أدوار أو تاريخ مضمّن.

  • نقطة نهاية جديدة POST /api/cv-studio/tailor (توسّع وحدة cv-studio الحالية — دون وحدة سابعة وعشرين): buildTailorPrompt + بوابة عامة TAILOR_INSTRUCTIONS، مبنية على bundleProjectContext، وسلسلة مزوّدين مشتركة، وموجّه يدوي دون مفتاح، ومحدودة المعدل، دون كتابة. تُصدَّر النتيجة إلى Markdown / PDF / DOCX عبر شريط report-export.js المشترك.
  • الاختبارات: +3 في tests/cv-studio-routes.test.mjs. 10 مفاتيح i18n جديدة ×16 (cvs.tailor*). مرجع عام docs/prompts/resume-cover.md. المساعدة §24 موسّعة في مكانها.

جديد: docs/prompts/resume-cover.md.

[1.100.0] — 2026-07-05

الوثيقة المكوّنة من صفحتين: تعبئة تلقائية بالذكاء الاصطناعي من سيرتك الذاتية + معاينة + تصدير إلى PDF/DOCX/Markdown. تلتقط الوثيقة (#/two-pager) ما تريده فعلاً من دورك التالي، لكن سابقاً كان عليك كتابة كل حقل يدوياً أو نسخ موجّه إلى أداة أخرى. الآن يعمل ✨ مساعد التعبئة بالذكاء الاصطناعي مباشرةً مع مزوّدك المُهيّأ — يقرأ فقط سيرتك الذاتية + الملف الشخصي (عبر bundleProjectContext، دون اختلاق أي شيء)، ويصوغ جميع الحقول (من أنا / ما أحب / الأساسيات / ما أكره / العوائق الحاسمة / غير القابل للتفاوض / البيئة المستهدفة) ويملأ النموذج لتراجعه وتعدّله وتحفظه. بدون مفتاح API يعود إلى نافذة نسخ الموجّه كما كان. زر جديد 👁 معاينة وتصدير يعرض الوثيقة كمستند منسّق مع شريط تنزيل .md / حفظ بصيغة PDF / حفظ بصيغة DOCX / نسخ.

  • تصدير .docx بلا تبعيات. ملف جديد server/lib/docx.mjs يُنتج .docx بصيغة Office Open XML صغيراً لكن صالحاً (أرشيف ZIP بضغط DEFLATE للأجزاء الأربعة من OOXML، مع CRC-32 لكل إدخال) — دون تبعية تشغيل جديدة (تبقى التبعيات express + js-yaml). مسار جديد POST /api/export/docx (server/lib/routes/export.mjs، وحدة المسار السادسة والعشرون؛ عديم الحالة، محدود بـ200 كيلوبايت، دون كتابة / دون LLM / دون جلب URL). مدمج في public/js/lib/report-export.js المشترك، لذا يحصل تقرير السوق وخطة المسار المهني والتوجيه المهني أيضاً على تصدير DOCX.
  • تستخدم التعبئة التلقائية المباشرة سلسلة المزوّدين المشتركة (runActiveProvider / providerAvailable)؛ ويُحلَّل YAML العائد ويُعاد ضبطه إلى شكل الوثيقة المحدود (parseYamlFields + normalizeTwoPager) — تُسقَط المفاتيح غير المعروفة وتُحدَّد المصفوفات/السلاسل. الوضع اليدوي محفوظ.
  • الاختبارات: tests/export-routes.test.mjs. 4 مفاتيح i18n جديدة ×16 (export.saveDocx، twoPager.preview، twoPager.aiFilling، twoPager.aiFilled).

جديد: server/lib/docx.mjs؛ server/lib/routes/export.mjs.

[1.99.0] — 2026-07-05

صفحة صحة البوابات (#/portals). يراقب الماسح مجموعة من الشركات في portals.yml؛ وقد ينكسر رمز ATS بصمت فيختفي ذلك الموظِّف من كل عمليات المسح المستقبلية. تعرض صفحة Portals الجديدة كل شركة مُراقَبة، وعند Check portal health تفحص كل careers_url عبر safeGet المثبّت على DNS (آمن ضد SSRF) وتُعلّم الميتة (رمز 404 = مُستبعَدة بصمت) — للقراءة فقط. كما تُقوّي مُبلّغ الأخطاء في v1.98.0 بعد المراجعة: يلتقط مخزن الأخطاء الحلقي الآن إخفاقات الشبكة في fetch، ويُخفي المُنقّي مفاتيح المزوّدين غير المُعنونة.

جديد: server/lib/routes/portals.mjs; public/js/views/portals.js.

[1.98.0] — 2026-07-05

مُبلّغ الأخطاء المدمج (تكافؤ مع واجهة web-v0.2.0 للمشروع الأصلي). يجمع زر 🐞 Report a bug في درج الإشعارات لقطة تشخيصية بحدٍّ أدنى للخصوصية — الإصدارات، شاشتك، المتصفح، ملخّص فحوصات /api/health، وآخر 20 خطأً من مخزن حلقي جديد من جهة العميل — بالإضافة إلى بصمة حتمية لإزالة التكرار (co-web-<base36>)، ويتيح لك مراجعة نص Markdown الدقيق ثم يفتح مشكلة GitHub مُعبّأة مسبقًا. لا يُرسَل شيء تلقائيًا؛ ولا يحمل أبدًا سيرتك الذاتية أو ملفك أو إجاباتك أو روابط الوظائف أو المفاتيح. مكتبتان جديدتان logbuf.js + bug-report.js؛ 11 مفتاح i18n ×16؛ tests/bug-report.test.mjs.

جديد: public/js/lib/logbuf.js; public/js/lib/bug-report.js.

[1.97.1] — 2026-07-05

تصليب مدفوع بالمراجعة وتكافؤ في التوثيق (متابعة لـ v1.97.0). كشفت مراجعةٌ لسجلّات مراجعة الذكاء الاصطناعي عن إصلاحات فعلية:

  • fit-score.js (شارة الملاءمة في المسح). لم تعُد salaryFloor() ترفع معدّلاً دون سنوي إلى حدٍّ أدنى سنوي زائف — إذ تُرجِع «at least 500 EUR/day» و«$80/hr» و«6000 monthly» الآن null بدل عائق صفقة بقيمة 500k/80k. صار تطابق البلد بالكلمة الكاملة (\b…\b)، فلم تعُد «Germany» تطابق الصفة «German» (ولا «Nigeria» داخل «Nigerian») لتُطلق انتهاك «مطلوب في مكان آخر» زائفاً. ‏+3 اختبارات في tests/fit-score.test.mjs.
  • تكافؤ التوثيق. صار كل README مُترجَم يُعلن 16 locales باتساق — فقد ظلّ عدّ/قائمة صفّ Help (‏×13) ونصّ قسم الترجمة + ملاحظة «أضِف المفتاح إلى كل ملفات N» (‏×8) على أعداد ما قبل v1.85 (‏8/9). صُحِّح عدّ المحوّلات في المساعدة داخل التطبيق §17 إلى 46 محوّلاً — 41 إنجليزية + 5 روسية عبر الحُزم الـ16 كلّها.

لا تغيير سلوكي يتجاوز إرشاد شارة الملاءمة؛ لا إضافة مسارات أو مفاتيح أو i18n.

[1.97.0] — 2026-07-05

مصدر ماسح Dassault Systèmes + كنسة جودة على ثلاث جبهات.

  • مصدر مسح جديد — Dassault Systèmes (تكافؤ مع المشروع الأصلي career-ops، ‏#1498). يعكس server/lib/sources/dassault.mjs + server/lib/portals/adapters/dassault.mjs مزوّد Exalead عديم التوكنات لـ«بحث البطاقات» من المشروع الأصلي (التغذية العامة خلف 3ds.com/careers/jobs). إنه نقطة نهاية عالمية واحدة، لذا يُختار عبر المزوّد (provider: dassault) أو يُكتشف تلقائياً من مضيف 3ds.com، ومثبّت مضيف SSRF على www.3ds.com مع redirect:'error'. تُحلَّل XML دون DOM (خرائط <Meta> لكل <Hit>)، وتُستخرج المدينة/البلد من سلسلة الفئة المُترجمة، ولا تُحفَظ الإعلانات إلا حين يكون رابطها العام على *.3ds.com. يوفّر السجل الآن 46 محوّلاً (41 EN + 5 RU)؛ رُفع عدّ ALL_ADAPTERS، وتأكيدات المعرّف المُرتّب ومجموعة EN لـ /api/scan/sources من 40 → 41. المجموعة tests/sources-dassault.test.mjs (10 حالات).
  • نقل إصلاحات المتانة من المشروع الأصلي. يتحمّل مُحلّل Avature الآن نوعين من ترميز المستأجرين الحيّ (article--result بلاحقة فهرس الموضع + مرساة عنوان JobDetail دون فئة، ‏#1541)؛ ويحمي Get on Board من قيمة published_at تساوي 0/سالبة (لا مزيد من تواريخ 1970 الزائفة)؛ ويحدّ SuccessFactors الصفحة الأخيرة كي لا تتجاوز MAX_JOBS (‏#1528).
  • إصلاحات تدقيق الخادم. لم يعُد safe-fetch يتعلّق على استجابة تتجاوز الحدّ — إذ يحسم مسار حدّ الحجم الوعد مباشرةً بدل انتظار حدث 'end' الذي لا يُطلقه مجرى مُتلَف أبداً (إصلاح جلبات /api/pipeline/preview والأنبوب التلقائي للصفحات الكبيرة). صار تسجيل نشاط SSE stream.* قابلاً للوصول من جديد (نُقل فحص /api/stream/ فوق حارس «تخطّي GET» الشامل).
  • إصلاحات تدقيق SPA. يحمي مبدّل تبويبات #/stats من تسابق العرض غير المتزامن — إذ لم يعُد بإمكان نتيجة تبويب بطيء أن تطمس تبويباً أحدث انتقل إليه المستخدم بالفعل. صار تأكيدا حذف المقابلة التجريبية والتواصل المهني يمرّران عنواناً + متناً صحيحين (لا مزيد من حوار بمتن فارغ).
  • إصلاحات الترجمة. تصحيح قيم قاموس غير مترجمة — الأوكرانية config.modes* (Adaptive Framing / Exit Narrative / Location Policy)، والروسية eval.jdLbl («Job Description»)، والإيطالية dash.quick.contactoSub («referral» ← «segnalazione») — إضافةً إلى ترجمة العبارة النمطية الإنجليزية 16 locales في سجلات تغيّر ru/uk/ja/ko/zh-CN/zh-TW.

جديد: server/lib/sources/dassault.mjs؛ server/lib/portals/adapters/dassault.mjs.

[1.96.0] — 2026-07-04

التوجيه المهني (Epic 27). صفحة #/orientation جديدة تجيب عن سؤال «أي الاتجاهات تناسبني فعلاً؟» — وهي القراءة التي قد تحصل عليها من اختبار مهني، لكنها مُستنتَجة من سيرتك الذاتية وملفك الشخصي بدلاً من استبيان. انقر إنشاء الملف، فيُعيد النموذج أفضل المسارات المهنية المطابقة لك (أيّ من الأنماط الثمانية — الوظيفي، والإداري، والمُتواصِل، والأخصائي، والمُحلِّل، والمُبتكِر، والمدير، وريادي الأعمال — يناسبك، مع أدلّة)، وميلاً نحو نوع مهني، وأدواراً موصى بها، ونقاط قوة مهنية مرتبطة بسيرتك الذاتية، ونزعات أسلوب العمل، وتوصيات للتطوير. إنه تأمّل بالذكاء الاصطناعي لكيفية قراءة سيرتك الذاتية — وليس اختباراً نفسياً: لا يختلق الإنجازات ولا يبلّغ عن درجات رقمية كأنها مُقاسة. صدّره إلى Markdown أو PDF؛ لا يُكتب شيء على القرص.

  • مسار جديد server/lib/routes/orientation.mjs (وحدة المسار الرابعة والعشرون) — يبني POST /api/orientation/generate مُوجِّه الملف من السيرة الذاتية+الملف الشخصي+الورقة المكوّنة من صفحتين+الذاكرة عبر تعاقب المزوّدات المشترك، مع بديل يدوي للنسخ واللصق ودون كتابة أي ملفات.
  • يعيد استخدام report-export.js من أجل Markdown/PDF/النسخ، ضمن مجموعة التنقّل Growth.
  • الاختبارات: tests/orientation-routes.test.mjs (صياغة تأمّلية / لا درجات مُختلَقة، وضع يدوي مبذور بالسيرة الذاتية/الملف الشخصي). 7 مفاتيح i18n جديدة ×16 لغة، والمساعدة §28 ×16.

جديد: #/orientation؛ server/lib/routes/orientation.mjs.

[1.95.0] — 2026-07-04

خطة المسار المهني (Epic 26). صفحة #/career-plan جديدة تحوّل سيرتك الذاتية وملفك الشخصي إلى خطة تطوّر ملموسة ومخصّصة. اختر أفقاً (6/12/24 شهراً) وتركيزاً اختيارياً، ويقوم النموذج — بقراءة سيرتك الذاتية وملفك الشخصي وصفحتك المزدوجة (two-pager) ومذكّرتك — بكتابة لقطة لنقطة الانطلاق، وتحليل SWOT لنقاط القوة/النمو، وأهداف بصيغة SMART / OKR / WOOP، ومسارات بديلة، وخطة للمهارات الصلبة/الناعمة، وخريطة طريق شهراً بشهر، وطرق تتبّع التقدّم، والمزالق، وخطوات الدعم. يخطّط انطلاقاً مما تُظهره موادّك فعلاً، ولا يختلق أبداً وقائع عن تاريخك. حرّره داخلياً، واحفظه في طبقة المستخدم (config/career-plan.md)، وصدّره إلى Markdown أو PDF.

  • مسار جديد server/lib/routes/career-plan.mjs (وحدة المسارات الثالثة والعشرون) — GET/PUT /api/career-plan (يكتب config/career-plan.md) + POST /api/career-plan/generate (سلسلة المزودين المشتركة، تراجع يدوي، دون اختلاق). PATHS.careerPlan.
  • يعيد استخدام المساعد المشترك report-export.js (v1.94.0) لـ Markdown/PDF/النسخ، ومجموعة تنقّل النمو جديدة.
  • الاختبارات: tests/career-plan-routes.test.mjs (التحديد، رحلة ذهاب-وإياب GET/PUT، موجّه مُغذّى بالسيرة الذاتية/الملف الشخصي وواعٍ بالأفق). 20 مفتاح i18n جديد ×16 locales، المساعدة §27 ×16.

جديد: #/career-plan؛ server/lib/routes/career-plan.mjs؛ PATHS.careerPlan.

[1.94.0] — 2026-07-04

الإحصاءات، مُعاد تصميمها (Epic 25). صفحة #/stats أصبحت الآن قسم الإحصاءات بثلاث علامات تبويب، برسوم بيانية حقيقية وبيانات أكثر بكثير. علامة تبويب جديدة تقرير السوق تطلب من النموذج تحليلاً للرواتب وسوق العمل لأدوارك المستهدفة في منطقة وعملة تختارها — ملخّص تنفيذي، الرواتب حسب المستوى مع مئينات P10/P25/P75/P90، أبرز أصحاب العمل، جدول للمهارات المطلوبة، تواتر المزايا، توزيع المكتب/الهجين/العمل عن بُعد، اتجاهات على مدى 12–24 شهراً، وإرشادات للتفاوض. كل رقم موسوم بأنه تقدير توجيهي من معرفة النموذج، ولا يُقدَّم أبداً كبيانات مُستخرَجة. علامة تبويب جديدة خط الأنابيب الخاص بي ترسم بيانات متتبّعك: توزيع الدرجات، قمع الحالات، أبرز الشركات والأدوار، الطلبات عبر الزمن، ومعدلات التحويل. عرض الدور المستهدف الأصلي (الشواغر/الرواتب حسب البلد + اتجاه اللقطات المحفوظة) ينتقل إلى علامة تبويب ثالثة، الآن مع محدِّد عملة ونظرة عامة على الإعلانات حسب الدور.

  • صدّر أي تقرير إلى Markdown أو PDF، أو انسخه — عبر المساعد المشترك report-export.js (تنزيل Markdown كـ blob؛ PDF عبر مشغّل PDF المضمّن الحالي).
  • مسار جديد server/lib/routes/market.mjs (وحدة المسارات الثانية والعشرون) — يبني POST /api/stats/market موجّه تحليل سوق من سيرتك الذاتية/ملفك الشخصي (فيعرف أدوارك المستهدفة)، والمنطقة، والعملة، ويمرّره عبر سلسلة المزودين المشتركة، ويرجع إلى موجّه نسخ-ولصق دون مفتاح. لا كتابة أي ملفات.
  • الاختبارات: tests/market-routes.test.mjs (تحديد المنطقة/العملة، موجّه موسوم بالأمانة، وضع يدوي مُغذّى بالسيرة الذاتية/الملف الشخصي). 36 مفتاح i18n جديد ×16 locales، المساعدة §26 ×16.

جديد: #/stats مُعاد تصميمها إلى علامات تبويب؛ server/lib/routes/market.mjs؛ public/js/lib/report-export.js.

[1.93.0] — 2026-07-04

طبقة الذاكرة (Epic 24). صفحة #/memory جديدة تحتفظ بملاحظة قصيرة قابلة للتحرير «تذكّر هذا عنّي» يبقيها المساعد في ذهنه في كل مهمة:

  • ملاحظة واحدة، في كل مكان — لأنها مُضمَّنة داخل bundleProjectContext، تصل الملاحظة تلقائياً إلى كل طلب ذكاء اصطناعي (التقييم، المقابلة التجريبية، التواصل المهني، CV Studio) عبر كل المزودين. اكتبها مرة واحدة؛ فتوجّه كل شيء.
  • توجيه لا حقائق — تلتقط تفضيلاتك وطريقتك المفضّلة في العمل (النبرة، التنسيق، العوائق الحاسمة، الإيقاع)، ولا تلتقط أبداً ادعاءات واقعية جديدة عن خبرتك — فتلك تبقى فقط في سيرتك الذاتية وملفك الشخصي و two-pager. تُحفَظ في طبقة المستخدم في config/memory.md، ولا تُستبدَل أبداً بالتحديثات.
  • اقتراح من بياناتك — يستخرج POST /api/memory/suggest أنماطاً سلوكية من متتبّع طلباتك ويصيغ نقاطاً لتراجعها وتحرّرها. يقرأ متتبّعك؛ لا يختلق حقائق أبداً، ولا يجري أي نداء مباشر.

جديد: server/lib/routes/memory.mjs (وحدة المسارات الحادية والعشرون — GET/PUT /api/memory + POST /api/memory/suggestpublic/js/views/memory.js، PATHS.memory، وكتلة config/memory.md مُضافة إلى bundleProjectContext. 11 مفتاح i18n جديد عبر جميع الـ 16 locales. الاختبارات: tests/memory-routes.test.mjs.

[1.92.0] — 2026-07-04

CV Studio (Epic 21). صفحة #/cv-studio جديدة تمنح سيرتك الذاتية ثلاث أدوات أمينة، معظمها محلي:

  • تشخيص السيرة الذاتية — درجة حتمية من 0 إلى 100 مع شرح لكل فحص (الأثر المُقاس كمياً، الأفعال الضعيفة، الكلمات الطنّانة، الطول، الأقسام الأساسية، بيانات الاتصال). على جانب العميل بالكامل (window.CvDiagnostics) — بلا نموذج لغوي، لا شيء مُختلق، وكل ملاحظة مشروحة كي تقرّر أنت ما تغيّره.
  • قناع الخصوصية — إخفاء البيانات الشخصية (البريد الإلكتروني، الهاتف، الروابط/المعرّفات، عنوان الشارع، واختيارياً اسمك ← الأحرف الأولى) قبل مشاركة سيرتك كنموذج أو لقطة شاشة. يعمل بالكامل في المتصفّح (window.CvPrivacy)؛ ويُبلغ بدقّة عمّا أخفاه ولا يخزّن الأصل أبداً.
  • Make it human / مطابقة النبرة — الصق سطراً أو فقرة جامدة وأعد كتابتها بنبرتك أنت، مبنيةً على جانب الخادم على voice-dna.md و writing-samples/. حاجز صارم: يجوز له إعادة الترتيب والتشذيب وإعادة النبرة، لكنه لا يُدخل أبداً حقيقة أو مقياساً أو إنجازاً غير موجود أصلاً في النص. يعمل مباشرةً عبر سلسلة المزودين المشتركة، أو يعيد لك مطالبة جاهزة للنسخ واللصق دون مفتاح.

جديد: server/lib/routes/cv-studio.mjs (وحدة المسارات العشرون — POST /api/cv-studio/humanizepublic/js/views/cv-studio.js، public/js/lib/cv-diagnostics.js، public/js/lib/cv-privacy.js، PATHS.voiceDna + PATHS.writingSamplesDir. 29 مفتاح i18n جديد عبر جميع الـ 16 locales. الاختبارات: tests/cv-diagnostics.test.mjs، tests/cv-studio-routes.test.mjs. (معرض القوالب، والتصدير إلى Word، وأرشيف PDF للإعلانات تُتابَع كأعمال لاحقة في CV Studio.)

[1.91.0] — 2026-07-04

التواصل المهني والبحث المعمّق عن الشركات (Epic 16). صفحة #/networking جديدة تحوّل شركةً إلى خطة قابلة للتنفيذ للحصول على مقابلة، مبنية على سيرتك الذاتية وملفك الشخصي و two-pager:

  • ملف الشركة — موجز مركّز عمّا تفعله الشركة، والإشارات الحديثة الجديرة بالاقتباس، وخطاطيف «لماذا أنا مناسب» المستخلصة من خلفيتك الحقيقية.
  • بمن تتصل — من 3 إلى 5 شخصيات مستهدفة (مدير التوظيف، مسؤول توظيف داخلي، مساهم فردي أقدم في الفريق، معرفة دافئة/من الخريجين) مع سلسلة بحث LinkedIn محددة للعثور على كل منهم. لا يختلق أبدًا أسماء حقيقية.
  • أدفأ مسار للتعريف — أكثر طريق دافئ واقعي للدخول بالنسبة لخلفيتك أنت (صاحب عمل/مدرسة/مجتمع مشترك، مسار من الدرجة الثانية، أو رسالة مباشرة باردة عالية الإشارة) ولماذا.
  • مسودّات التواصل — رسائل قصيرة ومحددة لأهم الشخصيات، مبنية على نقاط إثباتك الحقيقية.
  • مباشر أو يدوي — يعمل مباشرةً عبر سلسلة المزودين المشتركة بأي مفتاح، أو يعيد لك مطالبة جاهزة للنسخ واللصق (بديل أمين، لا شيء مُختلق). حفظ الخطة يحفظ خطة مكتملة في طبقة المستخدم (networking/net-{company}-{role}-{date}.md)؛ وتعرض الصفحة الخطط المحفوظة وتفتحها وتحذفها.

جديد: server/lib/routes/networking.mjs (وحدة المسارات التاسعة عشرة)، public/js/views/networking.js، PATHS.networkingDir. يعيد استخدام سلسلة server/lib/llm-dispatch.mjs من v1.90.0. 24 مفتاح i18n جديد عبر جميع الـ 16 locales. الاختبارات: tests/networking-routes.test.mjs.

[1.90.0] — 2026-07-04

Mock Interview 2.0 (Epic 15). تحوّل صفحة #/mock-interview الجديدة سيرتك الذاتية وملفك الشخصي والـ two-pager وبنك القصص إلى تدريب مقابلة دوراً بدور:

  • تدريب حواري — حدّد الدور المستهدف (+ الشركة / الوصف الوظيفي اختيارياً) ويفتتح المُحاوِر بسؤال مُركَّز. تحصل كل إجابة تُرسلها على ردّ منظَّم: Feedback (نقاط القوة + الفجوة بمنهج STAR+R)، وScore (N/5)، وNext question يسبر أضعف جزء في إجابتك الأخيرة. كل ذلك مبني على مستندك الحقيقي في الخادم — ولا يختلق أبداً خبرة لا تملكها.
  • واعٍ ببنك القصص — يُضمَّن interview-prep/story-bank.md في المطالبة (بنفس مستوى الثقة الممنوح لـ cv.md) كي تشير الملاحظات إلى أفضل قصصك.
  • مباشر أو يدوي — مع وجود مفتاح مزوّد، يُنفَّذ الدور مباشرةً عبر السلسلة المشتركة (Anthropic → Gemini → OpenAI → Qwen → OpenRouter → GitHub Models)؛ ومن دون مفتاح تحصل على مطالبة جاهزة للنسخ واللصق (احتياطي أمين، بلا إجابات مختلقة).
  • جلسات محفوظة — انقر حفظ النص للاحتفاظ بمقابلة منتهية في طبقة المستخدم (interview-prep/mock-{company}-{role}-{date}.md)؛ وتعرض الصفحة الجلسات المحفوظة وتفتحها وتحذفها.

جديد: server/lib/routes/interview.mjs (وحدة المسار الثامنة عشرة)، public/js/views/mock-interview.js، server/lib/llm-dispatch.mjs (سلسلة المزوّدين المشتركة)، PATHS.storyBank، bundleProjectContext({ extraFiles }). 30 مفتاح i18n جديد في كل 16 لغة. الاختبارات: tests/interview-routes.test.mjs.

[1.89.0] — 2026-07-04

ملاءمة المرشّح للسوق — الـ two-pager (Epic 14). تتيح لك صفحة #/two-pager الجديدة تدوين ما تريده أنت فعلاً من دورك المهني القادم، على غرار «Mnookin two-pager» من كتاب Never Search Alone:

  • مُنشئ موجَّه — سردية بصيغة المتكلّم «من أنا»، وملاحظة «البيئة المستهدفة»، وخمسة محرّرات لقوائم الرقائق: loves وmust-haves وhates وdeal-breakers وnon-negotiables. تُحفظ في طبقة المستخدم للمشروع الأب (config/two-pager.yml) عبر PUT /api/two-pager — ولا تُستبدل أبداً بتحديثات النظام.
  • مساعد التعبئة بالذكاء الاصطناعي (POST /api/two-pager/draft) — يبني موجّه Mnookin جاهزاً للتشغيل مع تضمين سيرتك الذاتية + ملفك الشخصي، لتشغّله في أي نموذج لغوي وتلصق النتيجة مرة أخرى. يستخدم موادك الخاصة فقط؛ ولا يُختلق شيء.
  • شارة الملاءمة-لما-تريد — يُظهر كل إعلان على #/scan الآن درجة ملاءمة ◎ N (من جانب العميل، عبر window.FitScore) تقارن نوع العمل والبلد والحد الأدنى للراتب والانتقال في الإعلان مع الـ two-pager الخاص بك. صادقة بحكم التصميم: عندما لا يقدّم الإعلان إشارة قابلة للمطابقة، لا تُعرض أي شارة (ولا رقم مُختلق أبداً). تُوزَن انتهاكات deal-breakers أكثر من الكراهيات الخفيفة.
  • تُغذّي كل تقييم — يُضمَّن الـ two-pager المحفوظ في bundleProjectContext، بحيث تمزج جميع تقييمات النماذج اللغوية اللاحقة تفضيلاتك المعلَنة مع مطابقة السيرة الذاتية مقابل الوصف الوظيفي.

جديد: server/lib/routes/two-pager.mjs، public/js/views/two-pager.js، public/js/lib/fit-score.js، PATHS.twoPager. 27 مفتاح i18n جديد عبر جميع الـ 16 لغة. الاختبارات: tests/two-pager-routes.test.mjs، tests/fit-score.test.mjs.

[1.88.0] — 2026-07-04

تحسينات المشكلة #29 — فجوات i18n في المسح + نظافة الـ API.

  • تعريب آخر سلاسل المسح المضمّنة بشكل ثابت (خارطة الطريق v1.69.4): تمرّ الآن حبوب ملخّص المصدر (N جديدة / M مطابقة)، وتنبيهات N وظائف جديدة، وشارة reloc عبر t() — 4 مفاتيح جديدة (scan.pillNew، scan.pillMatching، scan.newOffers، scan.relocBadge) عبر جميع الـ 16 لغة. لم يعد المستخدمون غير الناطقين بالإنجليزية يرون نصاً إنجليزياً متناثراً في مسار المسح الأساسي.
  • تعطيل ترويسة X-Powered-By (خارطة الطريق v1.69.5): app.disable('x-powered-by') في createApp() — لم يعد الخادم يُعلن عن Express. (بقيّة تلك الملحمة سبق تسليمها: يزيل parentVersion تعليق release-please الخاص به، ومبدّل السمة في الوضع الفاتح، وإغلاق النوافذ المنبثقة عند تغيير المسار، وتعريب «Score» (rep.score) في التقارير.)

الاختبارات: tests/scan-i18n-gaps.test.mjs + تأكيد غياب X-Powered-By في tests/security-headers.test.mjs.

[1.87.0] — 2026-07-04

4 مزوّدي مسح جدد بلا مصادقة (تكافؤ مع career-ops الأصل v1.16.0). يكبر سجل الماسح من 41 → 45 محوّلاً (40 EN + 5 RU) — جميعها عامة، بلا مصادقة، مثبّتة على المضيف، redirect:'error' (آمنة من SSRF)، ولكل منها اختبار معزول لـ CI:

  • Get on Board (getonbrd) — JSON:API عام على مستوى البوابة (تقنية LATAM/عن بُعد)، يُختار بحسب المزوّد، مع ترقيم صفحات. server/lib/sources/getonbrd.mjs.
  • Amazon (amazon) — JSON بحث عام من amazon.jobs، يُكتشف بحسب المضيف أو provider: amazon، مع ترقيم بالإزاحة. server/lib/sources/amazon.mjs.
  • Avature (avature) — نظام ATS من *.avature.net لكل مستأجر، يُحلَّل من HTML، يُكتشف بحسب المضيف أو provider: avature. server/lib/sources/avature.mjs.
  • SAP SuccessFactors (successfactors) — قائمة بلاطات RMK لكل مستأجر (*.successfactors.eu/.com، jobs2web.com)، تُحلَّل من HTML. server/lib/sources/successfactors.mjs.

يوفّر كلٌّ منها sources/<slug>.mjs (مع meta مُكتشف تلقائياً → قائمة #/scan المنسدلة) و portals/adapters/<slug>.mjs ضمن ALL_ADAPTERS (قاعدة السجلّين) + tests/sources-<slug>.test.mjs. ارتفع عدّ ALL_ADAPTERS وكذلك تأكيدات المعرّفات المرتّبة ومجموعة EN في /api/scan/sources من 36→40؛ ويسرد GET /api/scan/sources الآن 45.

[1.86.0] — 2026-07-03

إحصاءات حسب الأدوار المستهدفة (#/stats) — إحصاءات سوق الوظائف والرواتب لأدوارك المستهدفة. تقرأ صفحة تحليلات جديدة أدوارك المستهدفة من الملف الشخصي (config/profile.yml → غير مضمّنة بشكل ثابت) والوظائف من آخر عملية مسح، ثم تعرض لكل دور وبلد:

  • الوظائف حسب البلد ومتوسط الراتب حسب البلد (USD) — مجمَّعة على جانب العميل (public/js/lib/role-stats.js، مع إعادة استخدام window.Countries) من البيانات المتفرّقة التي تجمعها الماسحات بالفعل. تُطبَّع الرواتب بأي عملة إلى USD عبر جدول FX تقريبي بشكل صريح، مع تنبيه بشأن حجم العينة — ولا تُلفَّق أبداً.
  • مرشِّحات الدور والبلد ومخططات أعمدة واتجاه مضمّنة بصيغة SVG مكتوبة يدوياً (بلا اعتماديات جديدة، آمنة لـ CSP — تستخدم addEventListener فقط).
  • حفظ لقطة (POST /api/stats/snapshot) يحفظ التجميع الحالي في data/role-stats.jsonl؛ ويتتبّع مخطط الاتجاه (GET /api/stats/trend) أعداد الوظائف عبر الزمن — عرض «الديناميكية». هجين صادق: تأتي اللقطات من بيانات مسح محلية، وتُحدَّث عند الطلب.
  • مترجَمة بالكامل في جميع الـ 16 لغة (26 مفتاح i18n جديد).

جديد: server/lib/routes/stats.mjs (وحدة المسارات الـ 16)، وpublic/js/lib/role-stats.js، وpublic/js/views/stats.js، وPATHS.roleStats؛ اختبارات role-stats.test.mjs (7) + stats-routes.test.mjs (5).

[1.85.0] — 2026-07-03

اللغات الألمانية والإيطالية والتركية (تكافؤ اللغات مع المشروع الأصلي career-ops v1.16.0). أصبحت الواجهة الآن متوفّرة بـ 16 لغة — تنضمّ de 🇩🇪 وit 🇮🇹 وtr 🇹🇷 إلى الـ 13 لغة القائمة.

  • ترجمة كاملة للواجهة — تُرجمت جميع مفاتيح i18n الـ 730 في public/js/lib/locales/i18n-dict.{de,it,tr}.js؛ ويسرد مبدّل اللغة Deutsch / Italiano / Türkçe، ويتعرّف الاكتشاف التلقائي للغة المتصفّح على de/it/tr (public/js/lib/i18n.js).
  • دليل المساعدة داخل التطبيق — تُرجمت docs/help/{de,it,tr}.md (البنية الكاملة 19 H2 / 75 H3)، ويقدّمها GET /api/help/:lang.
  • التوثيق — أُضيفت README.{de,it,tr}.md وCHANGELOG.{de,it,tr}.md؛ وتغطّي الآن بوابة تكافؤ لغات CHANGELOG 15 لغة غير EN.
  • سقالة المطالبات — عُرّبت server/lib/prompts.mjs (LOCALE_NAMES + SCAFFOLD_STRINGS) للّغات الثلاث الجديدة، بحيث يتبع مخرج الـ LLM لغة الواجهة.

وُسِّعت جميع بوابات التكافؤ (i18n-locale-files وi18n-coverage وcheck-changelog-parity وlang-switcher-rtl) لتشمل مجموعة الـ 16 لغة.

[1.84.0] — 2026-06-30

cooldown إعادة التقديم + التعويض في pipeline.md (تكافؤ مع المشروع الأصلي career-ops v1.15.0). ترقيتان للماسح:

  • cooldown إعادة التقديم (#1201): يتخطّى مسح EN الآن الأدوار لدى الشركات التي تقدّمت إليها مؤخراً، لتبقى النتائج مُركّزة على الفرص الجديدة. هيّئ نوافذ لكل شركة في config/profile.yml تحت مفتاح re_apply_windows: (last_apply_date وsame_role_days وapplied_to: [roles] ومفتاح cross_role_bucket الاختياري)؛ مطابقة الشركات غير حساسة للترقيم + مبنية على حدود الكلمات (server/lib/cooldown.mjs). معطّلة عند غياب المفتاح؛ ويعرض سجل الفحص Cooldown skipped: N.
  • التعويض في pipeline.md (#1017): تحفظ العروض الممسوحة الآن راتبها في عمود trailing اختياري (url | <salary>) في data/pipeline.md. يبقى الرابط مفتاح إزالة التكرار (يُحذف عمود | comp عند القراءة)، وتُطهَّر قيمة الخلية (دون حقن صفوف/أعمدة وتُبطَّل الصيغ البادئة)، وتبقى ملفات pipeline المحتوية على روابط فقط متوافقة مع الإصدارات السابقة. يشحن مع tests/cooldown.test.mjs + اختبارات تعويض pipeline. عدد المصادر ثابت عند 41 (كلاهما ترقيات لمنطق الفحص، ليستا لوحتَين جديدتَين).

[1.83.0] — 2026-06-30

كاشف إعادة النشر / الوظائف الوهمية (تكافؤ مع المشروع الأصلي career-ops v1.15.0). لوحة جديدة 🔁 الوظائف المُعاد نشرها / الوهمية في #/scan تُحدّد مجموعات الشركة+الوظيفة التي أُعيد إدراجها تحت روابط URL مختلفة خلال نافذة متحرّكة مدتها 90 يوماً — إشارة إلى خطوط توظيف قديمة ووظائف وهمية. يستند إلى مطابق ضبابي لعناوين الوظائف (server/lib/role-matcher.mjs) وكاشف للقراءة فقط (server/lib/detect-reposts.mjs) يعمل على data/scan-history.tsv، ويُعرض عبر GET /api/scan/reposts. كذلك: يُبلّغ parentVersion في /api/health الآن عن رقم semver فقط (يُحذف تعليق release-please # x-release-please-version). يشحن مع tests/detect-reposts.test.mjs. عدد المصادر ثابت عند 41 — الكشف عن إعادة النشر ميزة تحليلية وليس لوحة جديدة.

[1.82.0] — 2026-06-30

مصدر فحص NoDesk (تكافؤ مع المشروع الأصلي career-ops v1.15.0). أصبحت تغذية RSS للوظائف عن بُعد على مستوى اللوحة بأكملها من NoDesk مصدر فحص من الدرجة الأولى — أضف إدخال provider: nodesk ليظهر في قائمة Source المنسدلة في #/scan (41 محوّلًا إجمالًا: 36 إنجليزي + 5 روسي). المضيف مثبّت على nodesk.co مع redirect:'error' (حماية من SSRF)؛ وتُقسَّم العناوين على Role at Company (لا تحتوي NoDesk على وسم للموقع، لذا يبقى الموقع فارغاً)؛ وجميع الصفوف عن بُعد. يشحن مع مجموعة اختبار tests/sources-nodesk.test.mjs معزولة CI؛ مجموعة الاختبارات الوحدوية الكاملة على الأخضر عند 1523.

[1.81.0] — 2026-06-29

تكافؤ مع المشروع الأصلي career-ops — 13 مصدر فحص وظائف جديد. ينقل أحدث دُفعة مزوّدين من main في Fighter90/career-ops إلى الماسح الداخلي للعملية. واجهات API عامة على مستوى اللوحة بأكملها (مُختارة بالمزوّد): Arbeitnow وHimalayas وJobicy وLanding.jobs و4 Day Week وThe Muse وThe Hub وJobspresso (RSS) وHacker News “Who is hiring?” (خطوتان عبر Algolia). لوحات بولندية (تُكتشف عبر المضيف أو provider:): JustJoin.it وNoFluffJobs (بحث POST). ATS لكل مستأجر (يُكتشف تلقائياً من careers_url): Pinpoint (<slug>.pinpointhq.com/postings.json) وRippling (ats.rippling.com/<slug> → لوحة api.rippling.com). كل مصدر مثبّت بالمضيف مع redirect:'error' (حماية من SSRF) وقابل للاختيار في قائمة Source المنسدلة في #/scan — يضم السجل الآن 40 محوّل ماسح (35 إنجليزي + 5 روسي). يضيف 13 مجموعة اختبار معزولة CI لكل مصدر؛ مجموعة الاختبارات الوحدوية الكاملة على الأخضر عند 1513.

[1.80.0] — 2026-06-28

خمسة تحسينات للمسح (أفكار من job-crawler، أُعيدت كتابتها). (1) مصدر Teamtailor — مواقع <slug>.teamtailor.com لكل مستأجر عبر تغذية /jobs.rss العامة، يُكتشف تلقائياً من careers_url (المضيف مثبّت + redirect:'error')؛ يضم السجل الآن 27 محوّلاً. (2) حجر المصادر — المصدر الذي يعيد 404/410 دائمًا يُسجَّل في data/scan-quarantine.json ويُتخطّى في عمليات المسح اللاحقة (إصلاح ذاتي: يُعاد بعد 14 يومًا). (3) الحد لكل مصدر — حقل اختياري في #/scan يحدّ من عدد الوظائف لكل board (∞ افتراضيًا). (4) نُشر خلال — مرشّح عُمر من جهة العميل (24 ساعة / 7 أيام / 30 يومًا). (5) عمليات البحث المحفوظة + ★ المفضلة — سمِّ مجموعات المرشّحات وأعد استخدامها وميّز الوظائف؛ تُحفظ في localStorage مع تحقّق دفاعي (تُعاد تهيئة الذاكرة المؤقتة التالفة بنظافة)؛ وتُعاد تهيئة ذاكرة النتائج قبل كل مسح وتُملأ مباشرةً.

[1.79.0] — 2026-06-28

مصدر فحص WeWorkRemotely (تطابق مع career-ops v1.14.0). أصبحت تغذية RSS للوظائف عن بُعد على مستوى اللوحة من We Work Remotely مصدر فحص من الدرجة الأولى — أضف إدخال provider: weworkremotely ليظهر في قائمة Source في #/scan (26 محوّلًا إجمالًا). المضيف مثبّت على weworkremotely.com مع redirect:'error' (حماية من SSRF)؛ وتُقسَّم العناوين على Company: Role. كذلك تُقتطع كلمات title_filter المفتاحية قبل فحص الطول (parent #1261).

[1.78.2] — 2026-06-27

تحسين i18n وتجربة الاستخدام (متابعات لـ v1.78.1). أصبح الاسم الوصفي للشعار مترجماً في جميع اللغات الـ 13 (nav.logoHome). الضغط على Enter في البحث العام أثناء وجودك في #/scan يفرض إعادة تصيير كي لا يُفقد المصطلح المُعبّأ مسبقاً (حارس المسار نفسه). تُرجم health.title إلى البولندية (Kondycja) والدنماركية (Systemtilstand) — كان سابقاً بالإنجليزية. الاختبارات 1235 → 1238.

[1.78.1] — 2026-06-27

إصلاحات تجربة Scan. يتحدّث جدول نتائج #/scan الآن تلقائياً أثناء الفحص ومرة أخرى عند انتهائه — دون إعادة تحميل يدوية. يعرض البحث العام في الشريط العلوي تلميح Enter، وعند إدخال استعلام ليس رابطاً ينتقل إلى #/scan مع تعبئة حقل البحث مسبقاً (كان #/tracker). أصبح الشعار الآن رابطاً إلى لوحة التحكم (الصفحة الرئيسية).

[1.78.0] — 2026-06-27

مرشّح جغرافي في صفحة Scan — فلترة النتائج حسب الدولة، مع الأعلام. قائمة الدولة الجديدة في #/scan تعرض كل دولة مكتشفة في نتائجك (رمز العلم + العدد)، لتُبقي فقط الوظائف المرتبطة بدولة معينة — إلى جانب مرشّح Remote/Hybrid/Onsite، حتى تبحث عن العمل المرتبط بدولة والعمل عن بُعد معاً. مدعوم بمساعد جديد countries.js يربط موقع الإعلان النصي الحر (أسماء الدول والمرادفات و~100 مدينة رئيسية) بدولة ISO + علم؛ والاكتشاف متحفظ ولا يخمّن أبداً.

[1.77.0] — 2026-06-27

إضافة الدنماركية (Dansk) كلغة الواجهة الثالثة عشرة. ترجمة كاملة لواجهة المستخدم ودليل المساعدة المدمج (19 H2 / 75 H3) وREADME وCHANGELOG. تنضم الدنماركية إلى منتقي اللغات بالأعلام؛ وتغطي الآن منظومة i18n (المُجمِّع، التدقيق، بوابات التكافؤ، اللقطة) 13 لغة.

[1.76.0] — 2026-06-26

تطابق مع career-ops v1.13.0 الأصلي — ست مصادر وظائف جديدة، وتقوية الماسح، وجدول نتائج بلا حد أقصى.

أُضيف

  • ست مصادر ATS لكل مستأجر — BambooHR وBreezy HR وComeet وPersonio وRecruitee وSolidJobs. تُكتشف تلقائيًا من مضيف careers_url (يتطلب Comeet عنوان api: كاملًا)؛ ويُثبَّت كل مضيف بنمط regex مُرسَّخ + redirect:'error' (حماية من SSRF). قابلة للاختيار في قائمة Source في #/scan — يضم السجل الآن 25 محوّلًا (20 إنجليزي + 5 روسي). يضيف مساعد fetchText لتغذية Personio بصيغة XML.
  • trust_filter — تقييم ثقة اختياري (0–100، مستوى high/medium/low، أعلام)، للتعليق فقط. الصفوف دون high تحصل على شارة ⚠ محايدة لغويًا في #/scan؛ ولا تُستبعد أي وظيفة أبدًا.
  • Arbeitsagentur remoteMatch + remoteMaxPages — اكتشاف العمل عن بُعد عبر الإعداد: title أو filter (homeoffice=nv_true من جهة الخادم + ترقيم الصفحات) أو off.

تغيّر

  • لا حد لنتائج المسح. أُزيل حد العرض MAX_STORED_RESULTS (2000) — تُخزَّن كل النتائج المطابقة ويُصفِّحها جدول #/scan (200/صفحة).
  • متانة مرشّح العنوان — الاختصارات القصيرة (COO، SDR…) تُطابَق على حدود الكلمات؛ ولم يعد إعداد title_filter المعطوب يُعطّل المسح. كلا الماسحَين.

اختبارات

  • +32 حالة (1190 → 1222): sources-ats-providers وtitle-filter وarbeitsagentur-remote وtrust-validator وحارس scan-result-cap المُعاد كتابته («بلا حد»).

[1.75.2] — 2026-06-19

docs: تكافؤ توثيقي كامل لمُجمِّعات الماسح في الإصدار v1.75.0 عبر جميع اللغات الـ 12. دون تغيير في الكود — يوائم التوثيق المُوجَّه للمستخدم مع المصادر السبعة التي وصلت في الإصدار v1.75.0:

  • دليل المساعدة (12 لغة). يكتسب §5 كتلة content_filter (تبويب عبر كلمات مفتاحية للوصف/المقتطف، نظير location_filter) وملاحظة عن المُجمِّعات؛ ويُدرج §7 المصادر السبعة الجديدة في جولة الفحص بنقرة واحدة وفي التعداد الكامل للقائمة المنسدلة Source؛ ويُصحَّح عدد المحوّلات في §17 من القيمة القديمة “11 adapters” إلى “19 adapters — 14 English + 5 Russian”. لم يُضَف أي عنوان ##/###، لذا تبقى بنية 19 H2 / 75 H3 المُقفلة دون تغيير.
  • README (9 لغات كاملة). نقطة جديدة “Aggregator boards (v1.75.0)” أسفل مصادر الفحص، إضافةً إلى شارة الإصدار المرفوعة إلى v1.75.2. (ملفات README المختصرة لـ pl/uk/ar ليس لها قائمة لكل مصدر، وهي متروكة عمدًا دون تغيير هناك.)
  • وثائق مرجعية. يكتسب docs/portals-examples.md قسمًا جاهزًا للنسخ واللصق بعنوان “Aggregator boards” يضم كتل تهيئة provider: / <provider>: دقيقة للمصادر السبعة جميعًا؛ ويُحدَّث docs/PROJECT.md إلى 19 adapters؛ ويوثّق docs/sdd/CONVENTIONS.md التمييز بين السجلّين (sources/registry.mjs للقائمة المنسدلة مقابل portals/registry.mjs للجلب)، واختيار المُجمِّع المبني على provider: المُمرَّر بوصفه opts.company، ومُطهِّر كتابة الفحص (scan-sanitize.mjs)، وعدد اختبارات الإصدار v1.75.1 (1190).
  • QA. أُضيف qa/QA-REGRESSION-PROMPT-v1.75.2-FULL.md — مُشغِّل بوابة الإصدار للسطح الكامل، مُحدَّث لدورة مُجمِّعات الفحص في الإصدار v1.75.x.

[1.75.1] — 2026-06-19

fix(scan): صقل المتانة على المصادر المُدارة بالإعدادات في الإصدار v1.75.0. ثلاثة إصلاحات تعزيز صغيرة من المراجعة بعد الإصدار (دون تغيير في السلوك لأي فحص سليم):

  • تأخيرات ترقيم الصفحات المراعية للإلغاء. أصبحت فترات التهذيب بين الصفحات لكل من Glints (300 ms) وJobstreet/SEEK (200 ms) تُحَلّ فورًا عند إطلاق AbortSignal الخاص بالفحص، عبر مساعد جديد delay(ms, signal) في server/lib/http-json.mjs، بحيث لا يستطيع عميل منقطع إبقاء فحص قيد الترقيم مفتوحًا لفترة توقف إضافية.
  • خطأ وصفي عند الاستجابة غير المنسّقة بـ JSON. أصبح fetchJson يغلّف جسم استجابة 2xx غير المنسّق بـ JSON (مثل صفحة صيانة HTML تُقدَّم بالحالة 200) على هيئة non-JSON 2xx response from <url> بدلًا من إظهار SyntaxError مجرّد، بحيث يُسمّي سجل أخطاء الماسح لكل مصدر نقطة النهاية المُختلّة السلوك.
  • تطبيع أقوى لكتابة الفحص. أصبح normalizeScanScalar يطوي علامة الجدولة الرأسية وتغذية الصفحة وفاصلَي السطر/الفقرة في Unicode (\v \f U+2028 U+2029) إضافةً إلى \r \n \t — وهي مجموعة فائقة صارمة، بحيث لا يبقى أي فاصل سجل/سطر قد تعتمده جدول بيانات أو عارض حتى يصل إلى scan-history.tsv.

[1.75.0] — 2026-06-19

feat(scan): نقل التكافؤ مع المشروع الأصلي career-ops v1.12.0 — سبعة مصادر وظائف جديدة، وتصفية المحتوى، وإصلاحات أمنية/جودة. تشغّل web-ui ماسحاتها الخاصة داخل العملية (فهي لا تستدعي scan.mjs الخاص بالمشروع الأصلي عبر shell out)، لذا لا تتدفق تغييرات المزوّد والمسح في الإصدار الأصلي v1.12.0 تلقائيًا — يعيد هذا الإصدار تنفيذ المنطبق منها وفق عقد المحوّلات في web-ui.

  • سبعة مصادر ماسح جديدة. ثلاثة مُجمِّعات للعمل عن بُعد على مستوى اللوحة بأكملها — RemoteOK وRemotive وWorking Nomads — تندرج ضمن النمط المُكتشَف تلقائيًا server/lib/sources/*.mjs (تُختار عبر provider: remoteok / remotive / workingnomads). وأربعة مُجمِّعات إقليمية مُوجَّهة بالتهيئة — careers الخاص بـ IBM وArbeitsagentur (وكالة العمل الاتحادية الألمانية) وGlints (جنوب شرق آسيا) وJobstreet / SEEK — تقرأ كتلة تهيئة <provider>: لكل مُدخَل؛ ويمرّر en-scanner الآن مُدخَل الشركة المُحلَّل إلى كل جالب (fetcher) كي تتمكن من قراءته. تظهر السبعة جميعها تلقائيًا في القائمة المنسدلة للمصادر في #/scan.
  • content_filter (المشروع الأصلي #974). كتلة اختيارية في portals.yml (قائمتا كلمات مفتاحية positive / negative) تُبوِّب الإعلان بناءً على نص وصفه/مقتطفه — تحاكي دلالات location_filter؛ والإعلانات بلا وصف تمرّ دائمًا. مُوصَّلة بكلا الماسحين EN وRU.
  • تقوية كتابة المسح (المشروع الأصلي #1098). تُطهَّر الآن بيانات تعريف الموجزات الخارجية قبل أن تستقر في data/scan-history.tsv وdata/pipeline.md: تُطوى أحرف التحكم (لم يعد بإمكان سطر جديد في اسم الشركة/المسمّى حقن صف TSV)، ويُحيَّد أي = + - @ في البداية ضد حقن صيغ جداول البيانات.
  • secondaryLocations في Ashby (المشروع الأصلي #1073). يطوي مصدر Ashby الآن تسمية منطقة كل موقع ثانوي إضافةً إلى addressLocality / addressCountry البريديَّين داخل سلسلة الموقع (مع إزالة التكرار)، بحيث يظهر دور مؤهَّل للعمل في الاتحاد الأوروبي تقرأ تسميته الأساسية مثلًا “Canada” أمام location_filter.
  • التحقق من شكل تقرير التقييم (المشروع الأصلي #819). تُعلِّم المزوّدات داخل العملية في /api/evaluate (Anthropic / OpenAI / Qwen / OpenRouter / GitHub Models) الآن أي تقرير A–G / SCORE_SUMMARY مُشوَّه على هيئة مصفوفة warnings غير قاتلة؛ ومسار تقييم Gemini يرث هذه الحماية مسبقًا من gemini-eval.mjs الخاص بالمشروع الأصلي.
  • docs: أُضيف Antigravity CLI إلى قوائم المساعدين المدعومين في جميع ملفات README الاثني عشر (يُقابل مزوّد Gemini).

موروث مجانًا من git pull الخاص بالمشروع الأصلي (تستدعي web-ui هذه عبر shell out): الخط الاحتياطي CJK لملفات PDF اليابانية (#1053)، وخطوط PDF الآمنة لأنظمة ATS (#1074)، وحماية CJK لـ LaTeX (#1054)، وإصلاحات tracker/merge/followup/dashboard، وأوضاع modes/zh الصينية (تَسرُد web-ui الأوضاع ديناميكيًا).


[1.74.3] — 2026-06-18

docs(parent-source): توجيه مستودع career-ops الأصلي إلى تفرع Fighter90/career-ops. تشير واجهة web-ui الآن إلى تفرع المشرف بوصفه المشروع الأصلي في كل موضع يُستخدم فيه كمصدر فعلي: القيمة الافتراضية CAREER_OPS_REPO في مُثبِّت bin/setup.sh، وكل رابط git clone / «مبني فوقه» / الإعداد في جميع ملفات README الاثني عشر، ووثائق الوكلاء (CLAUDE.md وAGENTS.md وGEMINI.md و.github/copilot-instructions.md وdocs/). يبقى إسناد التأليف إلى santifer (وإخلاء المسؤولية عن الواجهة غير الرسمية) دون تغيير — انتقلت فقط روابط المصدر/الاستنساخ. يتحقق tests/sh-files.test.mjs الآن من أن المُثبِّت يستنسخ التفرع.


[1.74.2] — 2026-06-17

fix(health): إظهار GITHUB_MODELS_API_KEY كفحص اختياري في #/health وفي /api/status/providers. كان مزوّد GitHub Models في الإصدار v1.74.0 قابلاً للتهيئة في #/config لكنه لم يكن له صف في صفحة Health وكان مفقوداً من سطح المزوّدين keysConfigured. تمت إضافة الفحص الاختياري (بنفس صياغة “set / unset (manual mode)” المستخدمة مع المزوّدين الخمسة الآخرين للتقييم المباشر) وgithub (+ GITHUB_MODELS_MODEL الخاص به) إلى /api/status/providers، بحيث أصبح توجيه المزوّد النشط وصفحة Health يعكسان الآن جميع المزوّدين الستة. تم توسيع اختبار صف health في tests/api.test.mjs ليشمل جميع المزوّدين الستة.


[1.74.1] — 2026-06-17

docs + test: قسم “تثبيت مساعد ذكاء اصطناعي” في README؛ تغطية كاملة للفروع لموصّل Gemini. تمت إضافة جدول التثبيت/تسجيل الدخول إلى README — روابط التثبيت لـ Claude Code / Gemini CLI / Codex / Qwen Code / OpenCode / GitHub Copilot CLI + تعيين مزوّد #/config لكل منها + «سجّل الدخول قبل المتابعة» (يعكس دليل البدء السريع في career-ops.org/docs؛ يوضّح أن واجهة الويب هي البديل المستقل الذي لا يحتاج إلى CLI). يغطّي tests/gemini-connector.test.mjs الجديد (8 حالات) كل فرع من فروع runGemini — بلا مفتاح، نجاح، خطأ في API، إكمال فارغ/محظور، جسم استجابة مشوّه، انتهاء المهلة، خطأ في الشبكة، hasGeminiKey — ليصل server/lib/gemini.mjs إلى 100% من التعليمات البرمجية. التغطية الإجمالية: 96% أسطر / 88% فروع / 96% دوال. مجموعة الاختبارات 1126 → 1134.


[1.74.0] — 2026-06-17

feat(llm): GitHub Models (Copilot) كمزوّد سادس + التوافق القانوني لستة مساعدين. يُدرج career-ops.org/docs ستة مساعدين لترميز الذكاء الاصطناعي — Claude Code، Gemini CLI، Codex، Qwen Code، OpenCode، GitHub Copilot CLI. تدعم web-ui الآن جميع الستة: خمسة منهم يرتبطون بمزوّدين نشطين موجودين (Anthropic / Gemini / OpenAI / Qwen / OpenRouter)، ويحصل GitHub Copilot CLI على موصّل مخصص لـ GitHub Models — runGitHubModels (OpenAI-compatible؛ PAT من GitHub بنطاق models)، قابل للتهيئة في #/config (GITHUB_MODELS_API_KEY + GITHUB_MODELS_MODEL) وقابل للتحديد عبر LLM_PROVIDER=github؛ السادس في ترتيب auto. تُدرج حزم المساعدة وملفات README الآن الستة القانونيين (تمت إعادة تسمية Qwen CLI إلى Qwen Code؛ تمت إضافة Gemini CLI + GitHub Copilot CLI)، ويُضيف README جدولاً كاملاً لمرجع الأوضاع وروابط محوّلات البوابات إلى career-ops.org/docs حتى يمكن تتبّع كل ميزة إلى المشروع الأصل. يُوسّع tests/llm-provider-context.test.mjs مصفوفة حدود الجلب لتشمل جميع المزوّدين الستة (cv.md + profile.yml مضمّنة + العناصر المُعادة)؛ تمت إضافة مفاتيح GITHUB_MODELS_* الجديدة إلى جميع قواميس 12 لغة إقليمية. الحزمة 1125 → 1126.


[1.73.0] — 2026-06-17

feat(llm): موصّل Gemini العام + سياق السيرة الذاتية/الملف الشخصي الموثَّق عبر جميع مزودي الخدمة. تمت إضافة server/lib/gemini.mjs (runGemini) — عميل Gemini generateContent بدون أي تبعيات خارجية يعيد نفس البنية {markdown, usage, error} التي يعيدها العملاء المتوافقون مع Anthropic / OpenAI. الإصلاح: كانت /api/mode/:slug و/api/deep تُمرّران المطالبات عبر gemini-eval.mjs المخصص للتقييم فقط، مما جعل Gemini Run live يعيد تقييماً بدلاً من القطعة المطلوبة (رسالة تقديم، تواصل، موجز). تستدعيان الآن runGemini مع bundleProjectContext، فيُدمَج cv.md + config/profile.yml مضمَّنَين لـ Gemini تماماً كما مع كل مزود آخر — فتصبح الرسائل والموجزات مفصّلة ومخصَّصة. يحاكي tests/llm-provider-context.test.mjs الجديد حدود HTTP لكل مزود ويتحقق من أن المزودين الخمسة جميعهم (Anthropic / Gemini / OpenAI / Qwen / OpenRouter) يُدمجون cv.md + profile.yml مضمَّنَين ويعيدون القطعة (مصفوفة mode + deep + evaluate، 9 حالات). تحتفظ /api/evaluate بـ gemini-eval.mjs المضبوط للوظائف. Suite 1116 → 1125.


[1.72.0] — 2026-06-17

feat(modes): يُعيد Run live الآن القطعة النهائية مباشرةً (عقد إخراج من تنفيذ واحد). كُتبت قوالب modes/<slug>.md الأصلية لجلسات Claude Code التفاعلية — إذ يتوقف بعضها (cover وcontacto وغيرها) لطرح أسئلة توضيحية قبل إنتاج النتيجة، مما كان يجعل Run live في واجهة الويب يُصدر استبياناً بدلاً من القطعة المطلوبة. يُغلّف buildModePrompt الآن كل وضع في عقد إخراج غير تفاعلي: يُجري التحليل (تفكيك وصف الوظيفة، وملاحظات الشركة، وكلمات ATS المفتاحية، والفجوات بين الملف الشخصي↔والوظيفة، وخيارات النبرة/الزاوية) بصمت، ويختار قيمًا افتراضية معقولة من cv.md / config/profile.yml لكل ما كان القالب يسأل عنه عادةً، ولا يُخرج سوى القطعة النهائية — مختتمًا بتذكير خاص بكل وضع «output ONLY {the cover letter / outreach message / …}». وهكذا، يُعيد النقر على Run live في #/cover الآن خطاب التغطية نفسه؛ ويسري الإصلاح ذاته على جميع الأوضاع العامة (cover وcontacto وinterview-prep وproject وtraining وfollowup وpatterns) في كل 12 لغةً محليةً (تُكتب القطعة بلغة الواجهة عبر توجيه اللغة المحلية). Suite 1103 → 1116.


[1.71.2] — 2026-06-17

docs(i18n): ينشر مراجعة اتساق الوثائق. يسرد قسم “Translations of this guide” في كل ملف README الآن جميع اللغات الـ 11 المرافقة (كانت بعضها تحذف English/Français أو تحتوي على رابط ذاتي)، مع استعادة السطر الفارغ قبل فاصل القسم. تمت إعادة تسمية موجّه انحدار ضمان الجودة الكامل إلى الإصدار الحالي، ومزامنة الوثائق (CLAUDE.md، CONVENTIONS، LOCALIZATION، PROJECT-CONTEXT) مع الإصدار الحالي وعدد الاختبارات (1103). لا تغييرات في الكود أو السلوك — وثائق فقط، لذا تظل ترجمات المساعدة/واجهة المستخدم وجميع الميزات من 1.70.0–1.71.1 دون تغيير.


[1.71.1] — 2026-06-17

fix(i18n): دليل المساعدة داخل التطبيق مترجم الآن بالكامل بجميع اللغات الـ 12. تمت إضافة docs/help/{pl,uk,ar}.md (يحتوي كل منها على البنية المُتحقق منها 19 H2 / 75 H3) حتى يقدّم #/help حزمةً أصيلةً باللغات البولندية والأوكرانية والعربية بدلاً من الرجوع إلى الإنجليزية — يعيد GET /api/help/{pl,uk,ar} الآن محتوى اللغة الخاصة به. تم ربطه بجميع بوابات المساعدة (help-ui، help.test، help-ru-config-section، canonical-docs-coverage). كما اكتملت جميع قوائم الترجمة بـ 12 لغة: قسم «Translations of this guide» في ملفات README (9 ملفات README)، وترويسات «Translations:» في ملفات CHANGELOG المترجمة (8 ملفات)، وتم تحديث أعداد التوثيق القديمة. Suite 1100 → 1103.


[1.71.0] — 2026-06-16

feat(cover): إنشاء ملف PDF لخطاب التقديم مباشرةً من #/cover. يُنتج وضع cover (المُضاف في v1.70.0) نص الخطاب؛ وتُقدِّم النتيجة الآن زرَّ Generate PDF الذي يُصيِّر النص عبر مسار markdown→PDF المُضمَّن المشترك (POST /api/stream/pdf/inlinegenerate-pdf.mjs)، وهو المسار ذاته الذي يستخدمه interview-prep. يمكنك الآن إعداد الخطاب وإرسال ملف PDF دون مغادرة SPA.

test/docs: تعزيز مراجعة v1.70.0. تمت إضافة تغطية معزولة عن CI لوضع cover (قائمة السماح + تجميع الطلب)، ومُبدِّل <select> الأعلام + RTL العربية (dirFor/<html dir>)، وtop.langLabel في كل إعداد محلي، وتوصيل PDF خطاب التقديم، وتوجيه الإعداد المحلي في prompts.mjs + السقالات البرمجية لـ fr/pl/uk/ar. تم تحديث المراجع القديمة «جميع الـ 8» إلى 12 إعداداً محلياً في docs/sdd/CONVENTIONS.md وفي طلب انحدار QA للمشروع بأكمله.


[1.70.0] — 2026-06-16

feat(i18n): ثلاث لغات واجهة مستخدم جديدة — البولندية (pl) والأوكرانية (uk) والعربية (ar، مع دعم كامل لـ RTL) — لترتفع بالـ SPA إلى 12 منطقة محلية، لتتطابق مع جميع اللغات الواردة في README مشروع career-ops الأصلي. تأتي كل منطقة محلية جديدة مزودة بقاموس كامل مؤلف من 697 مفتاحاً (public/js/lib/locales/i18n-dict.{pl,uk,ar}.js)، خاضعٍ للتحقق عبر مجموعات الاختبار الحالية للتكافؤ والتغطية وعدم تسرب الحروف اللاتينية وعدم وجود بيانات شخصية. يضيف الدعم العربي توجيهاً حقيقياً من اليمين إلى اليسار: يضبط i18n.js السمة <html dir="rtl"> للمناطق المحلية RTL، فيما يعكس كتلة [dir="rtl"] المحددة النطاق في app.css واجهة التطبيق (الشريط الجانبي، ودرج الإشعارات، والجداول وعروض الاقتباس في markdown، والمسافات المضمّنة) — بينما تبقى المناطق المحلية LTR دون تغيير بايت واحد. يُعرِّف المفتاح الجديد top.langLabel (×12) محدد اللغة لقارئات الشاشة.

feat(ui): يحل محدد اللغة <select> بأيقونات الأعلام محل صف الأزرار الذي كان يلتف على عدة أسطر. مع 12 منطقة محلية، كان صف .lang-btn القديم يمتد على ثلاثة أسطر في الشريط الجانبي؛ أما <select> الأصلي (كل خيار مسبوق برمز تعبيري لعلم) فيتوسع بشكل أنيق، ويدعم لوحة المفاتيح وقارئات الشاشة بدون إعداد إضافي، كما يظل آمناً مع CSP (معالج التغيير عبر addEventListener، دون JS مضمّن). تتراجع الأعلام إلى أحرف المنطقة حين تفتقر المنصة إلى رموز الأعلام، فتبقى تسمية اللغة دائماً المعرِّف الأساسي.

feat(cover): نقل وضع خطاب التقديم من المشروع الأصلي (career-ops v1.10.0 + تحية v1.11.0) إلى الـ SPA. صفحة #/cover جديدة ضمن مجموعة التنقل «الطلبات»، مبنية على منفذ الأوضاع العام: وصف الوظيفة + الشركة/الدور + تحية اختيارية ← خطاب مخصص مُولَّد من cv.md / modes/_profile.md. أُضيف cover إلى MODE_ALLOWLIST في الخادم وكتلة i18n باسم cover.* (×12 منطقة محلية).

chore(compat): تتبع مشروع career-ops الأصلي v1.11.0. تم التحقق من سلامة عقد القراءة/الكتابة — يبقى data/applications.md مصدر الحقيقة بصيغة markdown (فهرس تتبع SQLite في v1.11.0 هو ذاكرة تخزين مؤقت مشتقة)، وتظل أعمدة أداة التتبع معيَّنة بحسب الترويسة. يُبلّغ parentVersion الآن عن الإصدار 1.11.0.

fix(i18n): سد ثغرة كامنة تمثّلت في غياب اللغة الفرنسية (المضافة في v1.61.0) من LOCALE_NAMES وSCAFFOLD_STRINGS في server/lib/prompts.mjs — إذ كانت استدعاءات LLM بالفرنسية تعود صامتةً إلى إخراج باللغة الإنجليزية وسقالات إنجليزية. fr/pl/uk/ar جميعها متصلة الآن بمسار المنطقة المحلية للمطالبات.

متابعات معروفة: يعود دليل المساعدة داخل التطبيق (docs/help/) إلى اللغة الإنجليزية للمناطق pl/uk/ar (واجهة التطبيق نفسها مترجمة بالكامل)؛ كما أن إعداد المقابلة التفاعلي وميزة اكتشاف نظام ATS العكسي ومزودي الفحص الأحدث في المشروع الأصلي لم تُتَح بعدُ في الـ SPA.