Repository navigation
HaramLite 0.2.9 — «الحوافّ المكشوفة»
حالة هذه الوثيقة: منشورة. الإصدار
v0.2.9منشور عامّاً على GitHub بتاريخ
2026-09-25 (publishedAt = 2026-09-25T13:02:51Z· غير مسودّة · غير prerelease)، وهذا نصّ
النشرة نفسه. والوسمv0.2.9مؤشّر إلى الالتزامfca5882(مقيس: كائن الوسم
8b454c0⇒fca5882). والأرقام أدناه مقيسة محلياً على التزام ساكن
(git statusفارغ قبل القياس وبعده) ولا تُنسب إلى CI إلا حيث نُصَّ على خلافه.المصدر:
git log main..0db0ade— ٥٥ التزاماً، والدمج منmain = ada7b2c
(مقيس:git rev-list --count main..0db0ade= 55). كل بند أدناه مسنود ببصمة التزامه،
لا بذاكرة كاتب.
| الإصدار | 0.2.9 — موحَّد في package.json · src-tauri/tauri.conf.json · src-tauri/Cargo.toml · src-tauri/Cargo.lock (pnpm versions:check ⇒ ✓ · exit 0) |
| إضافة المتصفّح | 1.1.9 — دورة نشر مستقلّة، لا تُوحَّد مع التطبيق (browser-extension/manifest.json) |
| رأس فرع التكامل | 0db0ade |
| قاعدة النطاق | main = ada7b2c (git merge-base main rehearsal/owner-test = ada7b2c) |
| عنصر النشر | Release #36136806340 على 3ff9d95 ⇒ completed · success (بناء على عدّاءات GitHub). وخرج خمسة أصول: HaramLite_0.2.9_x64-setup.exe 196,944,377 بايت (187.8 MiB) · HaramLite_0.2.9_x64_en-US.msi 230,002,688 بايت (219.3 MiB) · SHA256SUMS.txt 194 · build-info.json 3,578 · sbom.cdx.json 422,856 |
١) ما أُصلح
١-١ · الإضافة: الوضع واللغة واللغتان معاً
المشكلة المقيسة: زرّ المشاهدة كان يرسل الطلب بلا وضع، فيقع التطبيق على افتراضيّه
(song) — أي أن الوضع المعروض في الواجهة لم يكن الوضع المُرسَل. وزرّ اللغة لم يكن
موجوداً أصلاً، وكانت اللغة تتبع navigator.languages وحدها.
9d1c1e4—feat(ext): 1.1.9 - the mode choice reaches the player, named bridge errors stop reading as empty replies, and the language has a visible switchd2e3bd3—fix(ext): the displayed mode is the mode that gets sent - the watch request carried none, so the app used its own default (song)2fabfba— زرّ المشاهدة يعود إلى المسار المؤقّت، والأوضاع الصريحة تُحفَظ، وقائمة سماح تمنع الإفراط في الإسقاطa2d1656— جواب جاسوس مستقلّ: الاتجاه عند التبديل الحيّ · مزلاج البدء · ومسربا HTML كانا يعيدان فتح Trusted Typesc800cff— انتظار القراءة الأولى قبل إرسال الوضع · متابعة تغييرات التبويب الآخر · بناء قائمة السياق مرّة واحدة · وطباعة الوضع المُرسَل0521402—i18n(ext): لغة واحدة لكل نصّ — الجرد أولاً، ثم إعادة كتابة الإنجليزية، وقائمة سماح بالأسماء تمنع الرجوع2b1d8c2— إعادة قياس صفوف المتصفّح بعد 1.1.9 (إبقاء المصفوفة صادقة)
القياس: pnpm ext:guard أخضر · pnpm ext:mutants (بوّابة سالبة) بلا ثقوب · صفوف المتصفّح الثلاثة بمكافآتها الرقمية وآثارها الملتزَمة في qa/browser-rows/.
١-٢ · البوت بلغتين + /lang
النصوص كانت عربية مكتوبة في telegram.rs بلا جدول ترجمة. الآن للبوت لغة، و/lang يبدّلها فعلاً.
c11fe88—feat(telegram): the bot speaks English too, and /lang switches for real5d42327—fix(telegram): «نصّا المالك الحيّان كانا نصوصي المرحليّة المتوازية» (تصحيح ذاتي مُعلَن)756d07a— قياس الحدّ الذي سمّاه الجاسوس على النصّ الخام: ليس موجوداً (نفي بالقياس لا بالرأي)
١-٣ · نافذة/شاشة الإعدادات
العطل المقيس: شاشة الإعدادات كانت شريطاً بطول ٦٧ بكسل داخل ترويسة مموّهة
(backdrop-filter ينشئ containing block فيحبس العنصر fixed)، ثم صارت نافذة ثانية، ثم
أُلغيت النافذة الثانية لصالح شاشة داخل النافذة في سبعة تبويبات.
db4e686—feat(ui): an independent settings window opened from Rust, covered by the existing capability2085794—feat(ui): the settings screen moves into the window, in seven tabsa5fcb1e—refactor(ui): drop the second window, settings become an in-window screen03bb637— الشاشة تأخذ الوضع من الرست، و CSS‑ي يكفّ عن إبطالhidden5e5ce0f—fix(ui): the settings screen was a 67px strip inside the blurred header(علّةbackdrop-filter)10d8dfa—fix(settings): unnest the seven tab panels so every tab shows its own group5190f22—test(settings): حكم على خريطةid → تبويبالمعلَنة، لا على حضور DOM9a49379—ci(layout): قياس شاشة الإعدادات لا القائمة المنسدلة وحدهاa00c8d5/ba916f5/3f4e823/73d2b6b— انظر ١-٤
١-٤ · بوّابتا الحرّاس: الأسماء + مُفسَدات رست
الفجوة المقيسة: خطّ الأساس كان يقارن العدد وحده ⇒ اختبار يُحذف ويُوضع مكانه اختبار
تفاهي يمرّ. والحارس الذي لا يسقط قط ليس حارساً.
a00c8d5—feat(gate): compare the rust test *names*, not just the counts— جردُ أسماء فيqa/rust-baselines.json؛ اسم غاب ⇒ يسقط البناءba916f5—feat(qa): a baseline of test *names*, and the two measured holes as mutants— الثقبان المقيسان (نزع حارس واختبار تافه مكانه · اختبار صار#[ignore]) صارا مُفسَدَين3f4e823—feat(qa): a negative gate for rust guards— نظيرext:mutantsللجانب الرست73d2b6b—ci(gate): run the rust mutant gate in the existing job— موصولة بـCI (وإلا فهي نصيحة، قاعدة المشروع §١٠)8252a9d— تثبيت خطّ الأساس بعد إصلاح الجسر
القياس (على 2da7093): pnpm rust:gates ⇒ clippy ١٤/١٤ موضعاً فريداً · ٤١٨ ناجح (الأساس ٤١٨) · ٠ فاشل · ٥ مُهمَل · الجرد ٤١٨/٤١٨ اسماً والغائب صفر · exit 0 — قيس مرّتين بنفس النتيجة.
١-٥ · جرد التبويبات
الشاشة سبعة تبويبات، ولكل معرّف تبويبٌ معلَن في خريطة، والحارس يحكم على الخريطة لا على
حضور العنصر في DOM (وهو ما كان يمرّ على عنصر مخفيّ).
f2114a5,3ce2baf,9fa296c,2471286,96ab31e— فرعagent/settings2(غير مدموج بعد؛ انظر §٣): حذف مسار#advanced-panelالميت · تثبيت الحارس على العنوان · حذف توصيل «المعاينة السريعة» ومستهلكيه · توسيع حارس المسارات الميتة إلى كلsrc5190f22— التبويبات وidالمعلَنة (مدمج)
١-٦ · حذف المسارات الميتة
2471286—refactor(queue): delete the dead quality-select wiring, and widen the dead-path guard to all of src9fa296c—refactor(main): delete the dead quick-preview DOM wiring, and drop its allowlistf2114a5—refactor(settings): delete the dead #advanced-panel path and guard against its return96ab31e—refactor(preview): delete the quick-preview setting and all of its consumers
حدّ أمانة: هذه الأربعة على
agent/settings2ولم تُدمج في فرع التكامل بعد (مقيس:
git rev-list --count 0db0ade..agent/settings2= 5). فحذف المسارات الميتة ليس في 0.2.9
حتى تُدمج — أُثبته هنا ولا أدّعي خلافه.
١-٧ · subcode وأعطال المحرّك
المشكلة: ردّ فشل بلا code يُقرأ في الإضافة «ردّ فارغ» فيضيع السبب.
e5ee662—fix(slots,telegram,bridge): an occupancy connector, a t3 that measures its own resource, and engine subcodes02ef68a—test(bridge): a raw engine message is never a valid subcode824e211—fix(bridge): carry watch and mode together so an explicit clip survives the request file
القياس: pnpm bridge:codes ⇒ ٧ رموز مُصدَرة في ١٧ موضع خطأ، ولكلٍّ مدخل وحالة ترجمة في
ملفَّي الإضافة (٢١ فحصاً · ٠ فاشل) · exit 0.
١-٨ · ترجمة رسائل الواجهة
العطل المقيس: رسائل يصوغها التطبيق (لا الإضافة) تُعرض عربية في واجهة إنجليزية،
وdirection="ltr" — وزيادة مفاتيح ترجمة لا تُصلحه لأن النصّ لا يأتي من الإضافة.
3beea55—fix(i18n): read the backend's error through its stable code, not the raw text58845e2—fix(ui): every displayed backend error goes through errTexta273f08—test(ui): no raw .error read escapes errText, and the codes match Rust both ways34e2dfa—test(ui): عدّString(<catch binding>)عند مصرف العرض أيضاً — مُفسَد هو الذي وجد الثقب3ceb424— ربط عيّنة الإلغاء بالنصّ الذي يبنيهseparator.rsاليوم (وكان التعليق تاريخياً)
حدّ أمانة: هذه الخمسة على
agent/texts2/agent/ui-textsولم تُدمج (مقيس:
0db0ade..agent/texts2= 6). ويبقىط-١٢مفتوحاً فيdocs/BACKLOG-0.3.md:353:
تفصيل المحرّك{e}يبقى عربياً داخل جملة إنجليزية.
١-٩ · إصلاح stderr والتنزيل
العطل المقيس (بلاغ المالك): تنزيل يفشل بعد ١٠٠٪ والرسالة والسجلّ يحملان تقدّماً فقط بلا
سطر الخطأ. السبب الجذري بالقراءة: yt_dlp.rs كان يصرّف stderr إلى بالوعة تُرمى،
ويبني الذيل من stdout وحده — وyt-dlp يكتب ERROR: على stderr. ثم أُعيد إنتاجه بنصّه:
ERROR: unable to download video data: HTTP Error 403: Forbidden.
68b7f5e—fix(ytdlp): stop discarding stderr — a failed download now carries its causeb0d8f2d—feat(ytdlp): retry a transient download failure once — measured, logged, boundedba5a852—fix(ytdlp): a download failure is no longer labelled "ملفات:" (file error)0e9da97—fix(ytdlp): never retry a permanent failure, never spend a valid result36f2e62—fix(qa): the rust baseline gate now compares tests_ignored too124ee13—fix(ytdlp): judge the failure from its ERROR line, and rescue a valid slot on stall6e07746—style(ytdlp): إزاحة توثيق لا تغيير كود
حدّ أمانة: السبعة على
agent/x2-dlولم تُدمج (مقيس:0db0ade..agent/x2-dl= 13).
أي أن إصلاحstderrوالتنزيل ليس في 0.2.9 حتى تُدمج.
١-١٠ · الإلغاء
العطل المقيس: الإلغاء من الواجهة كان يُسجَّل ERROR pipe: خطأ استدلال النموذج: تم إلغاء المعالجة من قبل المستخدم — أي إلغاء المستخدم يُقرأ عطلاً.
705ae1b—fix(pipe): a user's cancel is not a failure — no ERROR line on the engine path1ca3b0d—fix(sep): type the stop in demix, and guard that the type is the one production builds1661a70—test(pipe): إغلاق تفادي الحارس بالتعليق/التجزئة، وحراسة مساري المحرّك والأداة438ac78—test(sep): حصر الحارس النصّي على متنdemix، وجعل الحارس الحيّ غير قابل للتخطّي الصامت9375383— إيقاف الحارس عن رفض الكود الصحيح، ومنع الحارس الحيّ من المرور بلا تنفيذfb9f250— علم المشاهدة يعود إلى المسار المؤقّتb348c38—test(layout): الحارس يقيس الحالة التي يفتحها المستخدم فعلاً (agent/x4-cancel، غير مدموج:0db0ade..agent/x4-cancel= 1)
الحدّ المعلَن للإلغاء: نداء الاستدلال حاجب ولا يُقطع داخل العملية ⇒ نقلُه إلى عملية
عاملة بندٌ مسجَّل في 0.3 (ARCHIVE/0.2.9PLAN.md:333، القرار ٢).
١-١١ · SSRF — ⚠️ ليس من عمل 0.2.9
طلب المشرف إدراج SSRF في «ما أُصلح»، والقياس ينقض ذلك:
$ git diff --stat main..0db0ade -- src-tauri/src/yt_dlp.rs
(فارغ — 0 بايت)
$ git merge-base --is-ancestor 011c5eb main ⇒ True
حارس المضيفات المحلية/الخاصة (validate_download_url · private_scope، وفيه التغطية
المُعلَنة 127.0.0.0/8 · 10/8 · 172.16/12 · 192.168/16 · 0.0.0.0 · ::1 · fc00::/7
· fe80::/10 · localhost، والصور المتكافئة [::ffff:127.0.0.1] و[::ffff:7f00:1]
و[::ffff:c0a8:105]) قائم في src-tauri/src/yt_dlp.rs:51 و:125، وأُدخل في 011c5eb
وهو سلفٌ لـmain (main..011c5eb = 0). ⇒ صفر تغيير على yt_dlp.rs في نطاق 0.2.9،
ولا يجوز أن يُدرَج بنداً في نشرة 0.2.9. الموجود فيه اليوم اختباران: ٩ تأكيدات
(local_and_private_targets_are_rejected، :1537) و٢ تأكيدات
(malformed_urls_are_rejected_before_any_process_runs، :1639).
٢) ما لم يُقَس (حدود معلَنة — لا تُقرأ كإنجاز)
منقولة بالاسم من تقارير
ARCHIVE/*-report.md؛ والقاعدة قاعدة المشروع §٤.
| ما لم يُقَس | التقرير · القسم · السطر |
|---|---|
عرض تلغرام في عميل حقيقي — لا توكن بوت ولا شبكة خارجية؛ كل قياس «ما يصل السلك» على خادم Bot API وهمي محلّي عبر TcpListener حقيقي. ومحرفا الشريط ▰/▱ غير مُختبَرين على عميل حقيقي (احتمال تباعد غير أحاديّ لم يُقَس) |
x3-tg-report.md §٨ · سطر 269 |
| التزامن الحيّ داخل الصفحة (المشغّل) — لا متصفّح ولا GPU ⇒ كل أحكام التزامن والتسريع بنيوية من قراءة الكود؛ التأكيد النهائي بجدول حكم بيد المالك | review-0.2.9-cline.md §٦ · سطر 241 |
مقطع حقيقي فيه صمت — مسار clip نُفِّذ فعلاً على يوتيوب حقيقي وأنتج ملفاً كامل الطول وkept=[] لأن المقطع بلا صمت ≥٨٠٠ مللي؛ فالقصّ نفسه مقيس بـffmpeg (full=6.000s kept_sum=4.300s served=4.300s diff=0.0000s) ولم يُقَس مقطع حيّ فيه صمت |
docs/BACKLOG-0.3.md:349 (ط-٧) |
| VRAM بثلاث مهامّ متزامنة — «ثالثٌ يُتوقَّع أن يتجاوز البطاقة ولم يُقَس عمداً»؛ والقياسان المتزامنان 4734 MiB (12 ث · CUDA) و7943 MiB من 8192 (180 ث · DirectML · هامش 249 MiB) | docs/BACKLOG-0.3.md:314 و:319-325 |
| تثبيت MSI على جهاز نظيف — يحتاج Windows Sandbox/آلة المالك؛ ولا صفّ مصفوفة مملوء لـ«تشغيل فصل بـCUDA على تثبيت NSIS وحده» مقابل MSI | docs/BACKLOG-0.3.md:143 (ن-٣) · 0.2.9PLAN.md:212 (الطبقة ب) |
سلوك الإضافة محمَّلة من المانيفست — كروم ١٥٣ لا يحترم --load-extension؛ فكل قياس حيّ حقنٌ عبر CDP للملف المشحون. غير المقيس: تسجيل الخدمة الخلفية وربط المانيفست، ونقرٌ حقيقيّ على مدخل قائمة السياق، وchrome.storage الحقيقي |
x1-ext-report.md §٧ · سطر 545 (بنود ١–٣) |
| جودة الترجمة الإنجليزية تحتاج مراجعة بشرية — «الحارس يشترط الوجود والتكافؤ لا المعنى»، و«الترجمة الإنجليزية صحيحة لغوياً» لم يُتحقَّق منها | ui-texts-report.md §٧ · سطر 147 (الفقرة ٤) |
ما لم يُقَس في آخر جولات التنزيل — (أ) هل الـ403 على المقطع الأخير أم على الطلب الكامل؟ (ب) هل استُنفدت --retries/--fragment-retries؟ (ج) أن إعادةً واحدة كانت ستُنجح فشل المالك بعينه (المقيس: نفس الأمر نجح 18 من 19) (د) زمن انتظار التصريف (1000ms) على شبكة/قرص بطيء (هـ) أن الواجهة/تلغرام تتصرف صحيحاً بعد التغيير (لم يُشغَّل التطبيق) |
x2-dl-report.md §٥ · سطر 177 (بنود ٢–٦) · و§٨-٦ سطر 310 · و§٩-٤ سطر 429 |
لا جلسة WebView2 ولا لقطة للتطبيق العامل — حارس الهندسة يقود كروم بلا واجهة على dist/ عبر CDP: نفس DOM ونفس CSS، لا داخل WebView2؛ وعدد النوافذ=١ مقيس بنيوياً لا بتشغيل |
settings2-report.md §٦ · سطر 244 (بنود ١–٢) |
حارس الهندسة يقرأ نصّاً عربياً مشحوناً لا الجدول (i18n table NOT readable from src/i18n.ts — measuring GEOMETRY ONLY) ⇒ أثر تبديل اللغة على عرض التسميات غير مقيس |
settings2-report.md §٦ · سطر 250 (بند ٣) |
لا عدّاء CI — كل الأزمنة محلية، وكلفة البوّابة على windows-latest (بناء بارد) لم تُقَس |
gates-report.md §٥ · سطر 138 |
G4 لم يُغلق — الاختبار المعطَّل بـreturn داخل جسمه لا يراه الجرد، وrust:mutants يقيس ما يغطّيه سجلّ التحويلات وحده |
gates-report.md §٥ · سطر 138 · قاعدة المشروع:٣٤٣ |
ci.yml لم يُشغَّل على GitHub (لا دفع) ⇒ الخطوات الجديدة مقيسة محلياً فقط |
gates-report.md §٥ · سطر 138 |
ui:live لم تُقَس في هذه الدفعة — منفذ التنقيح 9222 لم يستمع رغم WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS='--remote-debugging-port=9222'، بطريقتين مختلفتين لحقن المتغيّر ($env: قبل Start-Process، وStart-Process -Environment) ⇒ لا صفحة على CDP ⇒ لا قياس حيّ للتطبيق العامل. والتطبيق أقلع فعلاً (نافذة + ١٣ عملية WebView2 + سجلّ إقلاع) لكن بلا منفذ. ولم تُقَس على ثنائي 0.2.8 القديم عمداً — فذلك يقيس غير ما نُسلِّم |
قياس هذه الدفعة · ARCHIVE/deliver-report.md §التقرير النهائي |
release:verify --strict لا وجود له — العلم --strict غير معرَّف في check-release-artifact.cjs (يعطي exit 2: «وسيط غير معروف»). المقيس هو pnpm release:verify مجرّداً ⇒ exit 0 |
قياس هذه الدفعة |
| مصفوفة الاختبار اليدوي لم تُمَلأ (تحتاج ويندوز + بوتاً حقيقيّاً + هاتفاً) | x3-tg-report.md §٨ · سطر 269 |
تذبذب مقيس في اختبار قائم — telegram::tests::t3_two_users_run_in_parallel_up_to_the_global_cap: سببُه التنازع على الجهاز لا المنتج (٤٢ تشغيلاً ساكناً ⇒ صفر فشل؛ وتحت تنازع حقيقي ١ فشل من ٣٦) ⇒ «صفر فاشل» ليس قابلاً لإعادة الإنتاج بثقة، ولم يُغلق |
x4-cancel-report.md §٧ · سطر 603 (بند ٨) · docs/BACKLOG-0.3.md:359 (ك-١) |
٢.١) قياس بعد الدفع (لا يُقرأ كخضرة CI)
| ما قِيس | النتيجة |
|---|---|
Quality gate على fca5882 |
failure (مقروء 2026-10-03 · التشغيل 36136136810). Rust baseline gate ⇒ «✗ اختبارات فاشلة: 7 · ✗ اختبارات ناجحة: 440 < الأساس 447 · ✗ اختبارات غابت عن التشغيل: 7 من 447». والأسماء الغائبة السبعة موجودة في src-tauri/src على fca5882 نفسه (متحقَّق: git grep على الالتزام) ⇒ ليست اختبارات محذوفة بل لم تُشغَّل. ولم أُعالج العطب: القرار للمالك |
Quality gate بعد تثبيت الأداة |
التشغيلان 37144671050 و37145722314: سطر clippy سقط من قائمة الفشل في الاثنين (مقيس بالعدّ: صفر مطابقة لـ«تحذيرات clippy») وكان 15 موضعاً فريداً > الأساس 14 قبل التثبيت ⇒ الرقم صار قابلاً لإعادة الإنتاج |
| نشر الموقع | pages build and deployment على fca5882 ⇒ completed success · وعلى c0bf651 وa8747a6 ⇒ completed success أيضاً (التزاما تحديث الموقع، انظر §٥) |
| حماية الفرع | الدفع أعلن Bypassed rule violations: Required status check "gate" is expected ⇒ الفحص المطلوب اسمه gate، وقد تجاوزه الدفع بصلاحية المالك. ولا خضرة CI مدّعاة: التشغيل جارٍ، ولم تُقرأ نتيجته |
release.yml قابل للتشغيل بـworkflow_dispatch |
نعم — لا تعطيل مقصود: صفر if: false · صفر continue-on-error · صفر خطوة معلَّقة. يبني المثبِّت بـtauri-apps/tauri-action@v0 وينشر إصدار GitHub (releaseDraft: false · permissions: contents: write) ثم ينشر SHA256SUMS.txt·build-info.json·sbom.cdx.json ويرفع أصول الإصلاح الذاتي إلى assets-v1 |
| أسرار التوقيع | غير مطلوبة الآن: سطرا TAURI_SIGNING_* محذوفان عمداً (قرار 2026-09-05، لأن سلسلة فارغة تُفشل المفكِّك) وincludeUpdaterJson: false ⇒ يعمل بلا سرّ، ويكفيه GITHUB_TOKEN التلقائي |
| مدّته المتوقَّعة | لم تُقَس على عدّاء. المرجعان: بناء Rust محليّ بذاكرة دافئة ٥د٠٥ث (مقيس هنا) · وسجلّ المالك ١٥–٢٠ دقيقة نجاحاً و١٣٥٫٥ فشلاً |
| شرطه الخارجي | يحتاج assets-v1 قائماً: bin/yt-dlp.exe وmodels/ غير متتبَّعين في git، وfetch_redist.js --verify يتحقّق من بصماتها ⇒ بلا تلك الأصول يفشل التشغيل |
للتشغيل من المتصفّح: Actions → Release → Run workflow (الفرع main) — GitHub يبنيه على عدّاءاته وجهازك مطفأ. وtagName: v__VERSION__ سيستهدف v0.2.9 (الإصدار غير موجود على GitHub بعد ⇒ سينشئه).
٢.٢) تصنيف اختبارات كان يُظنّ أنها «متذبذبة» — قِيس فتبيّن أنه نقص بيئة (2026-10-03)
كُتب بعد أن صار ممكناً تشغيل المجموعة محلياً:
bin/وmodels/كانتا فارغتين في كل شجرة،
فجُمعت الموارد منtarget/(vc_redistوالنموذج) ومنffmpeg-lgpl-staging(ffmpeg/ffprobe)،
وبُصمة كل ملف طابقت المثبَّتة فيrepair.rs(node scripts/fetch_redist.js --verify⇒
«4 مكوّناً + vc_redist.x64.exe موجودة وبصماتها مطابقة»).
الفجوة المقيسة: ci.yml كان يقول إن اختبارات ffmpeg/ffprobe «تتخطّى نفسها عند غياب
الأدوات» — وهذا غير صحيح لهذه المجموعة: yt_dlp لا يكتفي بثنائي yt-dlp، بل يحكم على
«خانة صالحة» عبر slot_usable ⇒ media::probe ⇒ ffprobe؛ وبلا ffprobe تسقط لا تتخطّى.
وbin/ في CI stubs فقط (ci.yml: تُنشأ ملفات .stub)، فالبوّابة كانت تسقط لنقص بيئة
ويُقرأ ذلك «عطب اختبارات».
| # | الاختبار | الحكم | الدليل المقيس |
|---|---|---|---|
| ١ | yt_dlp::tests::a_transient_failure_is_retried_once_and_succeeds |
نقص بيئة (ffprobe) — لا يُهمَل |
بلا ffprobe: سقط برسالة الحارس «yt-dlp نجح دون ملف ناتج صالح — أعد المحاولة» والنداءان وقعا · وبـffprobe: نجح |
| ٢ | yt_dlp::tests::a_transient_failure_with_a_valid_slot_spends_no_retry |
نقص بيئة | سقط بـ«نداءات: 2» بدل ١ لأن الحكم على الخانة لم يجد ffprobe |
| ٣ | yt_dlp::tests::a_valid_slot_is_success_even_when_ytdlp_exits_nonzero |
نقص بيئة | سقط · ونجح بالمجموعة كاملة |
| ٤ | yt_dlp::tests::a_stalled_download_with_a_valid_slot_is_rescued |
نقص بيئة | سقط · ونجح بالمجموعة كاملة |
| ٥ | yt_dlp::tests::a_truncated_container_is_refused_while_a_complete_one_passes |
نقص بيئة | سقط عند media::probe(&truncated) — الموضع نفسه يسمّي ffprobe |
| ٦ | telegram::tests::t4_a_user_with_four_files_does_not_block_another_user |
سليم محلياً · متذبذب على العدّاء |
نجح محلياً في كل قياس، وسقط على العدّاء في التشغيلات الثلاثة الأخيرة ⇒ ليس نقص بيئة بل حسّاسية توقيت |
| ٧ | telegram::tests::t9_the_intro_is_pinned_once_at_the_moment_the_bot_is_added |
سليم محلياً · متذبذب على العدّاء |
نجح محلياً في كل قياس · وسقط على العدّاء في التشغيلات الثلاثة الأخيرة |
| ٨ | slots::tests::a_free_slot_is_taken_without_waiting_and_a_taken_one_reports_its_wait |
سليم محلياً · متذبذب على العدّاء |
نجح محلياً · وسقط على العدّاء في تشغيل ونجح في آخر ⇒ المجموعة تتناوب (2 ثم 3) على الشجرة نفسها |
وحدّ أمانة على هذا الجدول: الصفوف ٦–٨ ليست «سليمة» بلا قيد — نجاحها المحلي مقيس، وسقوطها
على العدّاء مقيس أيضاً، وكلها حسّاسة للتوقيت والتزامن (Instant::now/sleep/TcpListener
ومهامّ متزامنة). ولم تُهمَل ولم تُقنَّع: هي الخطوة الحمراء الوحيدة الباقية، والقرار في شأنها
للمالك (تهدئة المهلة الداخلية؟ إعادة محاولة؟ أم إعلانها متذبذبة موثَّقةً؟) — ولا أُصلحها تخميناً
لأنني لا أستطيع إعادة إنتاج سقوطها محلياً، و«إصلاح» لا يُقاس على العلّة لبب نفسه |
العلاج: ci.yml صار يجلب ffmpeg وffprobe مع النموذج في الخطوة المتحقِّقة نفسها
(--only=model,ffmpeg,ffprobe). ولم يُهمَل اختبار واحد ولم يُعدَّل خطّ الأساس، لأن القياس
أثبت أن الاختبارات سليمة والبيئة ناقصة.
والقياس الختامي محلياً (1.95.0 المثبَّتة · المجموعة كاملة): cargo test --lib ⇒
447 ناجحاً · ٠ فاشلاً · ٥ مُهمَلة — وهو رقم خطّ الأساس بعينه، و8/8 للثمانية أعلاه.
وأثر الإصلاح على العدّاء مقيس (ثلاثة تشغيلات بعد الدفع): 8 ساقطة ⇒ 2 ثم 3،
وffmpeg/ffprobe نُزّلا وتحقّقت بصمتاهما في السجلّ ⇒ خمسة اختبارات yt_dlp أُصلحت
فعلاً، وسطر clippy غائب في كلها. والباقي ٢–٣ هي الصفوف ٦–٨ أعلاه.
وما جرى بعد ذلك (2026-10-03 · تكملة): أُصلح عطلان في الأداة والقياس، فقُلص العدد من ٨ إلى ٣:
- البوّابة صارت تسمّي الساقط: كانت تحفظ مخرَج
cargo testثم تطبع الملخّص وحده ⇒
صفر سطر... FAILEDوصفرpanicked atفي أربعة سجلات — فالعدّاء لم يكن يتكلّم.
الآن تطبع الاسم · الموضع · نصّ الفشل لكل ساقط. وأول ما نطقت به كان الجواب:
t4علىtelegram.rs:10909بـ«لم تبدأ المهامّ الخمس: [1000, 1002]» — مهمّتان من خمس
خلال ٦٠ ثانية كاملة، وكان ذلك يُقرأ «عطب ترتيب» وهو مهلة.
والقياس المحلي: خمس تشغيلات ⇒ ٥٫٢٥–٥٫٣٠ ث للمهمّات الخمس مع اكتمالها. - وُسِّعت مهلتان مُقيَّدتان — والمهلة سقفُ فشل لا شرط نجاح، فالدعاوى كما هي:
مهلة انتظار البدء ٦٠ ⇒ ٣٠٠ ث · ومهلة قراءة الخادم المزيّف (أحاديّ الخيط، وt4معه
خمسة عمّال) ٥ ⇒ ٦٠ ث — إذ كانت تعود قبل أن يُرسل العميل بايتاته حين لا يكون العامل
قد جُدوِل بعد، فيُخدَم «طلبٌ لم يُقرأ».
والنتيجة النهائية المُعلَنة — بلا خضرة: Quality gate يبقى أحمر على خطوة خطّ الأساس
وحدها بـثلاثة اختبارات، كلٌّ باسمه وموضعه ونصّ فشله:
| الاختبار | الموضع | نصّ الفشل الحرفي | الدعوى |
|---|---|---|---|
telegram::tests::t4_a_user_with_four_files_does_not_block_another_user |
telegram.rs:10936 |
«لم تبدأ المهامّ الخمس: [1000, 1002]» | كل المهامّ بدأت |
telegram::tests::t9_the_intro_is_pinned_once_at_the_moment_the_bot_is_added |
telegram.rs:11158 |
«النصّ سطران كما في التصميم: …» | شكل نصّ التعريف |
slots::tests::a_free_slot_is_taken_without_waiting_and_a_taken_one_reports_its_wait |
slots.rs:3697 |
«فتحة محجوزة ⇒ انتظار فعلي، ولا بد أن يُعلَن» | إعلان زمن الانتظار |
وثلاثتها دعاوى توقيت، وثلاثتها محلياً خضراء في كل قياس، والمجموعة كاملة محلياً
447 ناجحاً · ٠ فاشلاً · ٥ مُهمَلة (128.75 ث). ⇒ حسّاسية توقيت على عدّاء مشترك، ولم تُهمَل
ولم تُقنَّع ولم يُعدَّل خطّ أساس، ولا ادّعاء خضرة.
وفرضية التنظيف فُحِصت وسقطت — بالقياس لا بالرأي (2026-10-03): قِيل إن «يتامى يُبقون خادماً
مشغولاً». والقياس: (١) لا منفذ ثابت في telegram/slots — الربط كلّه
bind("127.0.0.1:0") (منفذ عابر يختاره النظام) ⇒ لا تصادم منافذ. (٢) ولا أبناء يتامى:
بعد تشغيل الثلاثة صفر عملية HaramLite أو haramlite_rs_lib باقية، وصفر مقبس متروك.
(٣) والجدول: ثلاث تشغيلات «مع يتامى» وثلاث «بعد قتلهم» ⇒ 6/6 نجحت في 17.7 ث،
و**orphans_before = 0 في الستّة جميعاً** — فلم يكن هناك يتامى يُقاس عليهم أصلاً، والفرضية
مردودة. (٤) وJobQueue::pop() يحجب على شرط، وshutdown() يوقظ كل المحجوبين
(notify_all) ⇒ لا عمّال عالقون عند الإنهاء.
وعلوقٌ واحد أُعلن بلا تفسير مُدَّعى: في قياس واحد علِق الثنائي (t4 وt9 معاً) واستهلك
1960 ثانية معالج حتى أوقفتُه، والسجلّ يقول has been running for over 60 seconds — وهي
إشارة علوق لا فشل. ولم يتكرّر في ٦+٦ تشغيلات بعده، ولم أُعدّل شيئاً بسببه: علّةٌ لا
تُعاد إنتاجها لا تُصلَح تخميناً، والفرضية **مطروحة للفحص
٣) المؤجَّل (بندٌ بند · بسببه)
| البند | رقم السطر | سبب التأجيل |
|---|---|---|
| سقف طوابير تلغرام لكل مستخدم — الافتراضيّ ١ والسقف المسموح ٢ | ARCHIVE/0.2.9PLAN.md:341 (القرار ١١) · docs/BACKLOG-0.3.md:281 (ز-٩/٩) و:299 (ز-١٠/٢) |
يحتاج إعادة هيكلة (مُوزِّع FIFO لكل user_id بدل مسار عالمي واحد)، والسقف يُقاس قبل تثبيته: أسوأ حالة معروفة 7943 MiB من 8192 (DirectML · ملف طويل) ⇒ الهامش 249 MiB، والثالث لم يُقَس. فالتثبيت على رقم غير مقيس ممنوع |
توسيع pace_group_send للوسائط |
src-tauri/src/telegram.rs:797 (والتعليل في :788-796) |
الكلفة زمن انتظار لا يُقاس بلا مجموعة حقيقية (يحتاج بوتاً وchat حقيقيّاً = x3-tg-report.md §٨)، والتصميم الحالي يضع الانتظار على خيط الكاتب وحده فلا يحجب /kill — وتوسيعه للوسائط يغيّر ترتيب ما يصل السلك (وهو سلوك مُختبَر) |
تكرار stopPoll (content.js:306-307) |
ARCHIVE/review-0.2.9-cline.md:259 و:233 (البند ١١) |
«نظافة تحرير» بلا أثر سلوكي مقيس؛ ويحتاج مُفسَداً («حذف النداء الصحيح») حتى لا يكون الحذف تخميناً — والملف محروس فتغييره يستلزم تحديث الحارس معه |
| توافق قديم/جديد في الإضافة (إضافة قديمة + تطبيق جديد · وتطبيق قديم + إضافة جديدة) | ARCHIVE/0.2.9PLAN.md:178-181 و:197-198 (صفّا المصفوفة) |
الإضافة تراكمية في 0.2.9 (page_kept وmode حقول جديدة، وkept يحفظ معناه) ⇒ التقادم آمن الآن. وتغيير معنى حقل مستقبلاً يلزمه state_version في حالة الجسر — وصفّا المصفوفة يحتاجان جهازاً ويد المالك، فلم يُملآ |
| نقل الاستدلال إلى عملية عاملة (ليُقطع نداء ONNX الجاري) | ARCHIVE/0.2.9PLAN.md:333 (القرار ٢) |
قطع نداء ONNX غير ممكن داخل العملية؛ والإلغاء اليوم تعاوني عند حدود المراحل مع قتل فوري لـffmpeg/yt-dlp. ويستلزم عملية عاملة + سِجلّ PIDs + إثبات «صفر عمليات يتيمة» |
٤) ما لا تقوله هذه الوثيقة (تحديثات صريحة)
- نُشر الإصدار فعلاً — وُضع الوسم
v0.2.9علىfca5882، وبنىRelease
(#36136806340) المثبِّتَ على عدّاءات GitHub ⇒completed·success، ونُشر على GitHub
خمسة أصول بأسمائها وأحجامها في جدول الترويسة أعلاه. والبصمات تحقّقت ثلاث مرات: نصّ
SHA256SUMS.txtالمنشور · وdigestفي واجهة GitHub · وحساب SHA-256 محلياً بعد تنزيل
الأصلَين (تحقّق 2026-10-03) ⇒ الثلاثة متطابقة. mainتقدّم على التزام البناءfca5882بعد النشر — والفرق موثَّق لا مُفترَض
(مقيس 2026-10-03 بـgit diff --name-only fca5882..HEAD): ملفّاتdocs/وحدها، ومثلها
تعليقٌ فيscripts/check-ui-live.mjs(8 أسطر مضافة، صفر أسطر محذوفة، وكلّها نصّ
تعليق يوثّق أن وصفة منفذ التنقيح لا تعمل على 0.2.9). وصفر تغيير علىsrc/
وsrc-tauri/وbrowser-extension/:git diff --numstat fca5882..HEAD -- src src-tauri browser-extensionفارغ. ⇒ فالأصول المنشورة مبنية من كود لم يتغيّر بعده.- بنود §١-٥ · §١-٦ · §١-٨ · §١-٩ · §١-١٠ (جزء
b348c38) ليست في فرع التكامل بعد،
وقد ذُكرت بأرقام فروعها صراحةً. ومعنى ذلك أن 0.2.9 كما هو اليوم لا يحمل حذف المسارات
الميتة ولا إصلاحstderrولا ترجمة رسائل الواجهة. - SSRF ليس من عمل 0.2.9 (§١-١١) — أُثبت بفرق فارغ على
yt_dlp.rsوبأن مُدخِله سلفٌ لـmain. - الأرقام محلية على هذا الجهاز (
cargo 1.95.0·clippy 0.1.95) ولا تُنسب إلى CI.
٥) تحديث الموقع بعد النشر (c0bf651 · a8747a6)
العطل المقيس: الرئيسية تقرأ releases/latest عند فتحها، فكان الزائر بجافاسكربت وشبكة
يرى 0.2.9 أصلاً — والمكسور هو المكتوب في الشجرة: احتياطي 0.2.8 (٢٢ موضعاً في
docs/index.html) · بصمتا 0.2.8 · مقاسان (187.6 MB و364.7 MB) · وقسم «الجديد» الذي كان
محتوى 0.2.8 تحت عنوان يُبنى من {v} فيقرأ 0.2.9.
ما صار: ٣٣ موضعاً في ستّة ملفات — الرقم والروابط الثلاثة إلى v0.2.9 والبصمتان المقيسان
والحجمان المقيسان (187.8 MB من 196,944,377 بايت · 219.3 MB من 230,002,688 بايت)
ومداخل «الجديد» من §١ أعلاه · وثلاثة أزرار تحميل كانت مجمَّدة على v0.2.4/0.2.7 صارت
<span class="js-release-tag"> حيّة فلا تتقادم. والناتج: 0.2.8 = 0 · 0.2.9 = 33.
القياس: pnpm site:check ⇒ exit 0 · check-site-latest-trust.cjs و--selfcheck ⇒ exit 0
· وpages build and deployment على الالتزامين ⇒ completed success · والصفحة الحيّة
https://haramlite.com ⇒ 200 و33 × 0.2.9 · 0 × 0.2.8.