From 8f708adb075e7dd7059b776725eb5ed804f34032 Mon Sep 17 00:00:00 2001 From: Sad Lang Dev Date: Tue, 1 Sep 2026 10:12:47 +0300 Subject: [PATCH] =?UTF-8?q?=D8=AF=D9=84=D9=8A=D9=84:=20=D8=AD=D8=A7=D8=B1?= =?UTF-8?q?=D8=B3=D9=8C=20=D9=8A=D9=82=D9=8A=D8=B3=D9=8F=20=D9=83=D9=84?= =?UTF-8?q?=D9=8E=D9=91=20=D9=81=D8=B5=D9=84=D9=8D=D8=8C=20=D9=88=D9=81?= =?UTF-8?q?=D8=AD=D8=B5=D9=8C=20=D9=8A=D9=82=D9=8A=D8=B3=D9=8F=20=D9=85?= =?UTF-8?q?=D8=B1=D8=AC=D8=B9=D9=8E=20=D8=A7=D9=84=D9=82=D9=81=D9=84=D9=90?= =?UTF-8?q?=20=D9=84=D8=A7=20=D9=88=D8=B3=D9=85=D9=8B=D8=A7=20=D8=B3=D9=88?= =?UTF-8?q?=D8=A7=D9=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit عطبان في أداةِ المزامنةِ نفسِها — لا في الفصول: ١) `cmd_guard_lock` كان يمرّرُ بصمةً تقدّمت إن عُدِّل **أيُّ** فصلٍ يستشهدُ بمصدرها. فمفتاحٌ مشتركٌ (أربعةٌ اليوم: `scripts/codegen` · `_schemas` · `language-truth/grammar` · `ast_visitor.h`) كان يَسِمُ الفصلَ الثانيَ طازجًا وما قرأه أحد — أي أنّ الحارسَ يكتمُ ما بُنيَ لِيَكشِفَه. صار يشترطُ **كلَّ** الفصول، ويسمّي المتروكَ منها والمشتركَ معه. ٢) `sync-check` الأسبوعيُّ كان يقيسُ الانجرافَ عند «آخرِ وسمِ إصدار» بينما القفلُ مبصومٌ عند `dev`. وv1.0.0 وسمُ ٢٠٢٦-٠٣-٠٩، سابقٌ لـ`language-truth/` و`scripts/codegen`، فأعلنَ التقريرُ حذفَ ٣٠ مسارًا وتعذُّرَ ٤٧ (القضيّة #1) وكلُّها قائمةٌ سليمةٌ على dev — إنذارٌ دائمٌ لا يُقرأ، وتحته تعفّنَت أحدَ عشرَ فصلًا بلا كاشف. صار الافتراضيُّ مرجعَ القفلِ نفسَه، ويُرفَعُ تحفّظٌ صريحٌ — على المعيار وفي جسمِ القضيّة — كلّما اختلفَ المرجعان. برهانان (خارج المستودع): مفتاحٌ مشتركٌ بفصلٍ واحدٍ يُرَدُّ وبفصلَيه يمرّ · لافتةُ المرجعِ تظهرُ عند الاختلافِ وتغيبُ عند التطابق. `--validate` سليم. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/sync-check.yml | 29 +++++++++++++-------- scripts/check_sync.py | 43 +++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/.github/workflows/sync-check.yml b/.github/workflows/sync-check.yml index 6ed7b62..78285e4 100644 --- a/.github/workflows/sync-check.yml +++ b/.github/workflows/sync-check.yml @@ -1,8 +1,13 @@ # ============================================================================ -# فحص مزامنة الدليل مع كود اللغة (drift detection) — مربوطٌ بإصدارات اللغة. -# يقيس الانجراف تجاه **آخر وسم إصدار** للغة (لا تجاه dev المتقلّب)، فتعكس النتيجة -# «هل يطابق الدليلُ الإصدارَ المنشور؟». يعمل: عند إصدار لغة جديد (repository_dispatch -# من مستودع اللغة)، أسبوعيًّا، ويدويًّا. عند الانجراف يفتح/يحدّث قضيّة بالفصول المتأثّرة. +# فحص مزامنة الدليل مع كود اللغة (drift detection). +# يقيس الانجراف تجاه **مرجع القفل نفسِه** (`sources.lock.json`.ref)، لأنّ البصمات أُخذت +# عنده، فقياسُها عند مرجعٍ سواه يقارن شيئًا بغيره. كان الافتراضُ «آخر وسم إصدار»، +# وv1.0.0 وسمُ 2026-03-09 سابقٌ لـ`language-truth/` و`scripts/codegen`، فكان التقريرُ يعلن +# حذفَ 30 مسارًا وتعذّرَ 47 (القضيّة #1)، وكلّها قائمةٌ سليمةٌ على dev — فصار الإنذارُ ضجيجًا +# لا يُقرأ. ولقياس الدليل تجاه إصدارٍ منشور: أعِد البصم عند وسمِه +# (`--ref vX --update --set-version X`)، ثمّ يصير هو مرجعَ القفل تلقائيًّا. +# يعمل: عند إصدار لغة جديد (repository_dispatch)، أسبوعيًّا، ويدويًّا. +# عند الانجراف يفتح/يحدّث قضيّة بالفصول المتأثّرة. # ============================================================================ name: sync-check @@ -12,7 +17,7 @@ on: workflow_dispatch: inputs: ref: - description: "ref/وسم لقياس الانجراف تجاهه (افتراضيًّا آخر إصدار)" + description: "ref/وسم لقياس الانجراف تجاهه (افتراضيًّا مرجعُ القفل)" required: false repository_dispatch: types: [language-release] # يطلقه مستودع اللغة عند نشر إصدار @@ -32,16 +37,15 @@ jobs: python-version: "3.12" - run: pip install pyyaml - - name: تحديد الـref (وسم الإصدار > مُدخَل يدويّ > آخر إصدار > dev) + - name: تحديد الـref (وسم الإصدار > مُدخَل يدويّ > مرجع القفل > dev) id: ref - env: - GH_TOKEN: ${{ github.token }} run: | REF="${{ github.event.client_payload.tag }}" [ -z "$REF" ] && REF="${{ github.event.inputs.ref }}" + # (AR) الافتراضيّ: المرجعُ الذي أُخذت عنده البصماتُ نفسُه — وحده + # يجعل «تغيّر/حُذف» تعفّنًا فعليًّا لا فرقَ مرجعين. if [ -z "$REF" ]; then - REF=$(gh api repos/sadlang/s-programming-language/releases/latest \ - --jq .tag_name 2>/dev/null || true) + REF=$(python -c "import json;print(json.load(open('sync/sources.lock.json',encoding='utf-8')).get('ref',''))" || true) fi [ -z "$REF" ] && REF="dev" echo "ref=$REF" >> "$GITHUB_OUTPUT" @@ -69,6 +73,11 @@ jobs: L = [f"رصد فحص المزامنة انجرافًا بين الدليل وكود اللغة عند **`{ref}`** " f"(النسخة المُغطّاة في الدليل: `{r.get('covers_version')}`).\n", "أي أن مصادر تغيّرت في اللغة وقد لا يطابقها الدليل بعد.\n"] + lock_ref = r.get("lock_ref") + if lock_ref and lock_ref != ref: + L.append(f"> ⚠️ البصماتُ المخزّنة أُخذت عند `{lock_ref}` لا عند " + f"`{ref}`؛ فـ«حُذف» و«متعذّر الوصول» أدناه تعنيان «غير " + f"موجودٍ عند `{ref}`» لا «أُزيل من اللغة».\n") def sec(t, items): if items: L.append(f"### {t}") diff --git a/scripts/check_sync.py b/scripts/check_sync.py index 0609f69..5f946cc 100644 --- a/scripts/check_sync.py +++ b/scripts/check_sync.py @@ -14,6 +14,10 @@ --json تقرير JSON للأتمتة. --update يثبّت البصمات الحاليّة (بعد مراجعة الدليل). --ref REF يتجاوز ref البيان (مثلًا وسم إصدار: v1.2.0) — لربط المزامنة بالإصدارات. + تنبيه: البصماتُ المخزّنة أُخذت عند `lock.ref`، فقياسُها عند مرجعٍ سواه + يجعل «حُذف/متعذّر» تعني «غير موجودٍ عند ذلك المرجع» لا «أُزيل من اللغة»؛ + لذا يرفع الفحصُ لافتةً على المعيار حين يختلف المرجعان. ولربط الدليل + بإصدارٍ منشور: أعِد البصم عنده (--ref vX --update --set-version X). --set-version V مع --update: يكتب covers_version=V في القفل (ارفعه عند مراجعة إصدار). --validate حارس بلا شبكة: يتحقّق من سلامة البيان (مسارات الفصول، تكرار المفاتيح، صيغة الأسطر، وأن كل فصل تقنيّ في SUMMARY مسجَّل). @@ -238,21 +242,33 @@ def cmd_guard_lock(manifest: dict, base_path: str, changed_path: str) -> int: changed_files = {l.strip().replace("\\", "/") for l in Path(changed_path).read_text(encoding="utf-8").splitlines() if l.strip()} + # (AR) القاعدة: **كلّ** فصلٍ يستشهد بالمصدر يجب أن يُعدَّل، لا أيّ فصلٍ منها. + # المفتاحُ المشترك (`scripts/codegen` بين codegen.md وphilosophy.md مثلًا) + # كان يمرّ بتعديل أحدهما فَيَسِمُ الآخرَ طازجًا دون أن يقرأه أحد — أي أنّ الحارسَ + # نفسَه كان يكتم ما بُني لِيَكشِفَه. (أربعة مفاتيحَ مشتركةٍ اليوم.) offenders = [] for key in advanced + added: chapters = chapters_for_key(manifest, key) - if not any(c in changed_files for c in chapters): - offenders.append((key, chapters)) + untouched = [c for c in chapters if c not in changed_files] + if untouched or not chapters: + offenders.append((key, chapters, untouched)) if offenders: print("❌ كتمٌ صامت مرصود: تقدّمت بصماتٌ في القفل دون تعديل فصولها:\n") - for key, chapters in offenders: - print(f" • المصدر `{key}` تقدّم، لكن لم يُعدَّل أيّ من: " - f"{', '.join(chapters) or '(لا فصل!)'}") + for key, chapters, untouched in offenders: + if not chapters: + print(f" • المصدر `{key}` تقدّم، ولا فصلَ يستشهد به أصلًا!") + continue + shared = [c for c in chapters if c not in untouched] + print(f" • المصدر `{key}` تقدّم، ولم يُعدَّل: " + f"{', '.join(untouched)}") + if shared: + print(f" (مشتركٌ مع فصولٍ عُدّلت: " + f"{', '.join(shared)} — تعديلُ أحدها لا يُبرّئُ البقية)") print("\nالقاعدة: لا تثبّت بصمةً جديدة (`--update`) إلّا بعد مراجعة الفصل " "المرتبط وتعديله فعليًّا. راجع الفصل، عدّله، ثم أعِد --update.") return 1 print(f"✅ الحارس مرّ: كل بصمةٍ تقدّمت ({len(advanced)+len(added)}) رافقها " - "تعديلُ فصلها.") + "تعديلُ **كلّ** فصلٍ يستشهد بها.") return 0 @@ -266,6 +282,20 @@ def cmd_check(manifest: dict, ref: str, as_json: bool) -> int: lock = json.loads(LOCKFILE.read_text(encoding="utf-8")) old = lock.get("sources", {}) + # (AR) البصماتُ المخزّنة أُخذت عند `lock["ref"]`. قياسُها عند مرجعٍ آخر + # يقارن شيئًا بشيءٍ سواه: مسارٌ لم يكن قد وُجد بعدُ عند وسمٍ قديم + # يُقرأ «حُذف». (قِيس: v1.0.0 وسمُ 2026-03-09، وهو قبل `language-truth/` + # و`scripts/codegen`، فكان التقريرُ الأسبوعيّ يعلن حذفَ 30 مسارًا + # وتعذّرَ 47، وكلّها قائمةٌ سليمةٌ على dev. القضيّة #1 شاهدُها.) + lock_ref = lock.get("ref") + if lock_ref and lock_ref != ref: + print(f"⚠️ المرجع المقيس `{ref}` يخالف مرجع القفل `{lock_ref}`.\n" + f" «حُذف» و«متعذّر الوصول» أدناه تعني «غير موجودٍ عند `{ref}`» " + f"لا «أُزيل من اللغة».\n" + f" لقياس تعفّنٍ حقيقيّ: قِس عند مرجع القفل (--ref {lock_ref})، " + f"أو أعِد البصم عند `{ref}` (--ref {ref} --update).\n", + file=sys.stderr) + changed = [k for k, s in shas.items() if k in old and old[k] != s] added = [k for k in shas if k not in old] removed = [k for k in old if k not in shas] @@ -275,6 +305,7 @@ def cmd_check(manifest: dict, ref: str, as_json: bool) -> int: report = { "drift": bool(drift_keys or missing or added), "ref": ref, + "lock_ref": lock_ref, "changed": sorted(changed), "added": sorted(added), "removed": sorted(removed),