Skip to content

לינקר: היסטוריית "שם" עוברת שלמה בגבולות המנות (מפת הכותרות), ושחזורים בלי היסטוריה - #12

Merged
Y-PLONI merged 5 commits into
perf/ibid-gate-history-exactfrom
fix/ibid-title-map-across-seams
Oct 28, 2026
Merged

Y-PLONI merged 5 commits into
perf/ibid-gate-history-exactfrom
fix/ibid-title-map-across-seams

Conversation

@Y-PLONI

@Y-PLONI Y-PLONI commented Oct 26, 2026

Copy link
Copy Markdown
Member

מבוסס על #11. כשהוא ימוזג וה-branch שלו יימחק, GitHub יעביר את ה-PR הזה ל-main אוטומטית.
משנה פלט ← relink מלא (ראו "אחרי המיזוג").

הבאג

ספר עם "שם" מקושר במנות (transport batches): 100 שורות מקומית, 25 ב-Kaggle. כל מנה היא קריאת bulk_link נפרדת, וספריא מאפסת בה את היסטוריית ה-ibid. IbidContext אמור להעביר את ההיסטוריה מעבר לגבול, כך שהספר יתנהג כרצף אחד, אבל הוא העביר רק את 3 ה-refs האחרונים.

IbidHistory של ספריא שומרת גם מפת כותרות: כותרת → ה-ref האחרון מאותו ספר, עד 3 כותרות. המפה משמשת לציטוט שמזכיר שם ספר ("ברכות שם", או כותרת עם כתובת חלקית). השחזור מ-3 ה-refs בנה אותה מחדש רק מהם, ולכן כל כותרת שצוטטה לפניהם נעלמה בגבול:

שורה 98:  ברכות ב.      refs=[ברכות ב.]               מפה={ברכות}
שורה 99:  שבת ה:        refs=[ברכות ב., שבת ה:]       מפה={ברכות, שבת}
שורה 100: שבת ו., שבת ז. refs=[שבת ה:, שבת ו., שבת ז.] מפה={ברכות, שבת}
──────── גבול: שחזור מ-3 ה-refs → מפה={שבת} ────────
שורה 101: "ברכות שם ע״ב" → ברכות כבר לא במפה

התוצאה: הקישורים של ספר היו תלויים ב-BATCH_LINES/BATCH_CHARS (מקומי מול Kaggle), ובמקום שאליו עריכה הזיזה את הגבולות. גם סדר _last_titles (סדר הופעה ראשונה, לא אחרונה) השתבש, ולכן מאוחר יותר נזרקה כותרת אחרת.

השינויים

  1. IbidContext מעביר את ההיסטוריה כולה (95172ad): ה-refs, הכותרות לפי הסדר, ומפת הכותרות, בדיוק כמו ב-IbidHistory המוצמד.
    • ה-sidecar של ה-checkpoint עובר ל-ibid state schema 4, עם title_refs.
    • sidecar של schema 3 נדחה, ואז המנה מקושרת מחדש במקום לאמץ היסטוריה בלי כותרות.
    • חוזה ה-API הפרטי נבדק בקול: חמשת השדות של IbidHistory, החסמים last_n_*, והאינווריאנט "קבוצת הכותרות = מפתחות המפה".
  2. השחזורים נפתרים בלי היסטוריה (371e295): parenthesized_address ו-chapter_led_citation קוראים ל-resolve_raw_ref אחרי שכל המנה כבר נפתרה. ההיסטוריה של ספריא כוללת אז את הציטוטים שאחרי הציטוט שנכשל, עד סוף המנה, כלומר עתיד שתלוי בפריסה.
    • parenthesized_address קיבל גם הקשר IBID ממנה, כי הוא דוחה רק CURRENT_BOOK.
    • ב-chapter_led_citation היא יכלה לגזום (prune) את הקריאה ה-context-free.
    • עכשיו שניהם רצים עם היסטוריה ריקה (resolve_alone), וזו המשמעות של "resolved alone" שבתיעוד שלהם. אובייקט ההיסטוריה של ה-resolver מוחזר למקומו.
  3. תשובה קצרה מ-bulk_link (db982c3, נמצא ב-QA, קיים מקודם): בדיקת האורך רצה אחרי ש-IbidContext כבר שמר את מצב סוף המנה. ה-replay שורה-שורה התחיל אז מהיסטוריה של שורות מאוחרות, ו"שם" בשורה הראשונה יכול היה להצביע קדימה. הבדיקה עברה לתוך הקריאה, לפני השמירה.
  4. בדיקת ה-seams של לינקר: ספר עם "שם" עובר NER רק על השורות שהשתנו, כשההיסטוריה לא יכולה לזוז #11 נשארת, ועכשיו היא רק שמרנית. אפשר להסיר אותה בהמשך בשינוי output-neutral, ואז יותר ספרים ייכנסו למסלול ה-probe.

בדיקות

  • python3 -m unittest discover -s tests: ‏470 טסטים, 12 דילוגים, הכול עובר.
  • tests/test_ibid_layout.py (חדש):
    • רצף מול מנות: ספרים אקראיים עם seed (5 כותרות, ציטוט מפורש, "שם" לפי כותרת, "שם" סתמי, ציטוט עמום, וציטוט שנכשל ומאפס היסטוריה) מקושרים פעם כרצף אחד ופעם בכל פריסה קטנה. בחלק מהריצות ה-worker ממוחזר דרך ה-sidecar ב-JSON. ה-fake הוא העתק של IbidHistory המוצמד.
    • מקרים ממוקדים: כותרת לפני 3 ה-refs; סדר הכותרות; תשובה קצרה; תוכן ה-sidecar; חוזה schema 4.
  • מוטציות: כל טסט חדש נכשל כשמחזירים את הקוד הישן של התיקון שלו.
  • fuzz (לא בקומיט): 2,400 ספרים אקראיים בפריסות 1–12 עם recycle אקראי, 0 הבדלים. על הקוד הקודם: 19 הבדלים מתוך 600.
  • ספריא אמיתית על ה-runner:
    הרצה מבודדת ליד ה-stack, בלי לגעת בו: עותק של Mongo בפורט נפרד, NER בפורט נפרד ו-PYTHONHASHSEED=26. הדגימה כללה 60 ספרים עם "שם" (36 מספריא, 18 מ-Dicta ו-6 מאחרים), 20,412 שורות. ה-NER רץ פעם אחת, והפלט שלו הוזן מחדש לכל וריאנט.
וריאנט (קוד חדש) רשומות מול רצף אחד
רצף אחד 12,939 —
מנות של 100 12,939 זהה
מנות של 25 12,939 זהה
100, עם recycle אחרי כל מנה 12,939 זהה
  • ה-recycle: 219 שחזורים מ-JSON עם Refs אמיתיים, 144 מהם עם כותרות, וכל אחד מדויק. גם Ref(normal).index.title שווה לכותרת בכל המקרים.
  • בדגימה הזו הקוד הישן לא הראה הבדל בין הפריסות, וגם resolve_alone לא שינה אף רשומה. הבאג מוכח ב-fake המדויק, אבל בפועל הוא נדיר. ההשפעה של ה-relink על הקישורים צפויה להיות קטנה.

QA

שני סוכני QA בלתי תלויים, בלי חוסמים.

  • הראשון (נכונות ההיסטוריה):
    • מבחן הפרשים משלו, כ-10,600 ריצות, 0 הבדלים. הוא כיסה: כשל באמצע מנה עם replay שורה-שורה, _ibid_candidate מעבר לגבול, AltStruct, ו-sidecars משובשים.
    • מצא את בעיית התשובה הקצרה, שתוקנה ב-db982c3.
    • העיר ש-sidecar פסול מעורר book is recycling without progress במקום replay, בגלל committed_shard_count. זו בעיה קיימת מקודם. sidecar של schema 3 לא יכול לפגוש את הקוד החדש בייצור, כי שחזור checkpoint מקומי מחייב את אותו head_sha (ci/validate_local_checkpoint_source.py). לא תוקן כאן.
  • השני (שחזורים ופייפליין):
    • אישר מול ספריא המוצמדת שהשחזורים קראו היסטוריה של סוף המנה.
    • מצא שמוטציה שמחזירה את הקריאה הישירה עברה את כל הטסטים, ולכן נוספו טסטים דרך שתי הפונקציות (07b3e2a).
    • מצא את סכנת סדר המיזוג מול לינקר: ספר עם "שם" עובר NER רק על השורות שהשתנו, כשההיסטוריה לא יכולה לזוז #11, ולכן נרשמו שתי רשומות (c7195ad).
    • recall אפשרי: שחזור שהסתמך במקרה על הקשר IBID מסוף המנה לא ייפתר עוד. זה היה נכון רק במקרה, כי ההקשר הגיע מסוף המנה. ב-runner זה לא שינה אף רשומה בדגימה (למעלה).

אחרי המיזוג: relink מלא

  • engine_src: עובר ל-c9f0a1b83cc87198 ונרשם ב-output_affecting_fingerprint_changes.json. יש שתי רשומות: מה-lineage שפורסם (e3599195a9566a5c), ומהמנוע של לינקר: ספר עם "שם" עובר NER רק על השורות שהשתנו, כשההיסטוריה לא יכולה לזוז #11 (17df3c79a33074d5). כך הסדר לא משנה, ובדקתי את שני המצבים.
  • אסור להשתמש ב-adopt_fingerprint.
  • target=local מאפשר relink מלא (תקרה של 48 שעות). ב-kaggle/server תחת build ממתין הוא אסור ונכשל מהר. SERIAL_LINKER_TARGET הוא local.
  • מומלץ להריץ relink standalone ידני (target=local, בלי library_run_id) מיד אחרי המיזוג, ולא להשאיר את זה ל-build הבא, שיחכה כ-12 שעות או יותר.

לא בתיקון הזה

ספרים בלי "שם": ספריא משתמשת בהיסטוריה גם לציטוט בלי כותרת ("פרק ג" לבד) ולעידון ציטוט עם כותרת, וזה לא תלוי בטוקן "שם". ספר בלי "שם" לא מקבל IbidContext: ההיסטוריה שלו מתאפסת בכל מנה, ובתוכנית שורות חלקית היא נבנית רק מהשורות שנשלחו. שער ה-ibid מבוסס על הטוקן ולכן אינו שמרני ביחס לספריא. זה קיים מקודם. נמדד על ה-runner, 40 ספרים בלי "שם" (12,025 שורות):

  • מנות של 100 מול רצף אחד: הבדל ברשומה אחת. בגבול מנה אבד Shabbat 61.
  • שורה-שורה מול רצף אחד: 13 רשומות ב-6 ספרים.

שורה-שורה היא קירוב של עריכה בודדת בתוכנית שורות. הפתרון ייקר: להכניס את כל הספרים לשער ה-context-probe.

IbidContext replayed only the last three refs at each transport seam, which rebuilt
IbidHistory's title map from those refs alone. A title cited before them ("Berakhot
2a ... Shabbat, Shabbat, Shabbat ... X שם" naming Berakhot) was gone after a seam, so
a book's links depended on BATCH_LINES/BATCH_CHARS (100 locally, 25 on Kaggle) and on
where an inserted line pushed the seams.

The seam now restores the exact state: the last refs, the titles in first-seen order
and the title map. The checkpoint sidecar goes to ibid state schema 4 with
title_refs; a schema-3 sidecar is refused, so a resumed batch replays instead of
adopting a history without titles. The context-probe seam check stays, now only
conservative.

tests/test_ibid_layout.py links seeded random books whole and under every small
layout, with worker recycling through the JSON state, against a copy of the pinned
IbidHistory; it fails on the previous code.
…story

parenthesized_address and chapter_led_citation call resolve_raw_ref after bulk_link
has finished the whole transport batch, so Sefaria's ibid history then holds the
citations after the failed one, up to wherever the batch happens to end. An IBID
context from it (accepted by parenthesized_address) or a context match that prunes
the context-free reading made the recovery depend on later lines and on the layout.

Both now resolve with an empty history (resolve_alone), which is what "resolved
alone" meant, and the resolver's history object is put back afterwards.
A mutation that called resolve_raw_ref directly again passed every test: the
recoveries are now checked through parenthesized_address and chapter_led_citation
themselves.
The length check ran after IbidContext.resolve had captured the batch's end state,
so the line-by-line replay of a short reply began from the history of lines after
it (a bare "שם" on the first line could point forward). The check now raises
inside the call, before the capture.
…fecting

engine_src goes to c9f0a1b83cc87198 and needs a full relink. It is registered from
the published lineage (e3599195a9566a5c) and from PR #11's output-neutral engine
(17df3c79a33074d5), so either merge order keeps the lineage guard exact.
@Y-PLONI
Y-PLONI merged commit f571e92 into perf/ibid-gate-history-exact Oct 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant