Skip to content

fix(mcp): ניקיון שאריות #3470 — toc כדגל רגיל, סירובים שנכנסים בתקציב, והפניה במקום העתק (#3472) - #3490

Merged
amirbiron merged 1 commit into
mainfrom
claude/happy-mendel-bhwrn5
Sep 29, 2026
Merged

amirbiron merged 1 commit into
mainfrom
claude/happy-mendel-bhwrn5

Conversation

@amirbiron

@amirbiron amirbiron commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

✨ תיאור קצר

📦 שינויים עיקריים

  • קוד (Backend)
  • בוט טלגרם
  • מסד נתונים/מיגרציות
  • תיעוד (docs/)
  • DevOps/CI/CD

פירוט נקודות:

  • WARN-002 — toc הוא bool רגיל, כמו outline, ו-StrictBool נמחק. "true", "yes" ו-1 נותנים את המפה; "false", "no" ו-0 את הקובץ המלא; "maybe", 2, null ו-"" עדיין נדחים בשגיאת ולידציה. למה לא strict: המחרוזת "true" מגיעה לכלי כמו שנשלחה — pre_parse_json (mcp 1.28.1) מפענח אותה ל-True, ו-True הוא int, ולכן הוא זורק את הפענוח — כך ש-strict על דגל לא מונע שום קריאה שגויה, רק חוסם לקוח ששולח דגלים כמחרוזות (עד היום toc="true" נדחה בזמן ש-outline="true" עבד). ב-lines זה הפוך: שם strict מונע [true, 5] שנקרא כשורות 1 עד 5, ולכן StrictInt/StrictLines נשארים. הנימוק כתוב ליד StrictInt, במצבי הקצה של mcp-file-sections, ובמוסכמה ב-Handoff — כדי ש-StrictBool לא ייווצר שוב. רשת isinstance ב-file_read_request_error נשארה, לקורא שעוקף את הסכימה.
  • תיקון באג שקיים היום באנליטיקס, ב-outline — ו-toc רק מצטרף אליו. ck_read_mode בדק אמת-שקר של פייתון, שבה מחרוזת לא ריקה היא אמת, ולכן outline="false" — שהכלי קורא כקובץ מלא — תויג outline. זה לא "תופעת לוואי" של WARN-002: הבאג קיים ב-main ב-codekeeper_get_repo_file, ו-toc היה נכנס אליו ברגע שהפסיק להיות strict. עכשיו הקולבק קורא דגל באותה ולידציה של הכלי (TypeAdapter(bool) ברמת המודול; נקרא במקור של pydantic 2.12.3 / pydantic-core 2.41.4 שה-validator נבנה במלואו בבנאי ושאין מצב שמשתנה בקריאה — K15): מה שהכלי קורא true — outline; false — לפי שאר הפרמטרים; ומה שהכלי דוחה — כולל null שנשלח — outline, כמו זוג מצבים שנדחה (הקריאה לא קראה תוכן). מפתח שלא נשלח אינו בקשה.
  • SUGG-002 — כל סירוב שנושא file נכנס בתקציב. עד היום תגית ענקית (אין תקרה על מספר התגיות ועל האורך שלהן) שלחה סירוב של כ-270KB, מעל OUTPUT_BYTE_BUDGET, בלי סימן. עכשיו כל סירוב של מצבי הסעיף יוצא דרך fit_refusal — אותו _fit_lists שחותך את המפה ואת ההצעות, לא פונקציה חדשה — ש-file.tags נחתכות בו מהסוף, עם file.tags_truncated: true. ב-section_not_found וב-ambiguous_section התגיות נחתכות ראשונות, לפני המפה, ההצעות והמועמדים. תשובה מוצלחת לעולם אינה חותכת מטא-דאטה. מחרוזות (תיאור, שם) אינן נחתכות — השורש בכותבים נפתח בנפרד: שדות מטא-דאטה של קובץ שחסומים אצל חלק מהכותבים בלבד — תגיות ותיאור (derived-field-added-to-one-writer) #3489.
  • _fit_lists למד לרדת רמה אחת (("file", "tags")), כשעלות כל פריט נמדדת בעומק שבו הוא יושב (list_item_cost(item, depth=...), nonempty_list_bytes(depth)). כפי שנדרש באישור: טסט שמריץ את אותם קלטים דרך הצורה החדשה בעומק 0 ודרך עותק קפוא של _fit_lists מ-main, על כל תקציב בית-בית, ומשווה החלטה-החלטה — ושלוש מוטציות בחישוב העומק מפילות אותו.
  • SUGG-008 — max בסירובי התקציב הוא OUTPUT_BYTE_BUDGET עצמו (256,000), ו-bytes כולל את המקום ל-found/status, כך ש-bytes > max הוא בדיוק ההחלטה שנפלה. עד היום max היה 255,964 במפה ו-255,960 בסעיף — מספר שלא הופיע בשום תיעוד.
  • SUGG-001 — שינוי התנהגות בקלט קיצוני, ולא רק ניקיון: section של רווחים בלבד שארוך מ-4,096 תווים היה empty_section ב-codekeeper_get_file ו-section_too_long ב-codekeeper_docs_get_section — שני כלים שעונים באותה פונקציה, ושתי שגיאות לאותו קלט. עכשיו "קודם אורך, אחר כך תוכן" גם כאן: section_too_long, עם המטא-דאטה של הקובץ. רווחים קצרים — empty_section כמו קודם.
  • WARN-003 — קריאה בכמה שלבים צריכה file_id. שני התיאורים (toc ו-section) נפתחים במשפט שקריאות המשך (סעיף, העמוד הבא, lines על line_range) צריכות להעביר file_id=file.id — ההוראה נגמרת בתו 103, בתוך 120 התווים שלקוח מקצר אליהם. לפי file_name כל קריאה קוראת את הגרסה האחרונה, וגרסה ששמר סשן אחר באמצע נותנת עמוד שנפתח באמצע מילה. גם בעמוד התיעוד.
  • YAGNI-002 — הפניה במקום העתק. תיאור section ב-get_file שיבץ את _SECTION_PARAM_DOC כמו שהוא (כ-3,000 תווים בכל tools/list). עכשיו יש הפניה לפרמטר section של codekeeper_docs_get_section (השם נשתל מ-SECTION_TOOL_NAME), ונשאר רק מה שייחודי לקובץ שמור. התיאור ירד מ-5,254 ל-2,516 תווים, ו-tools/list (בלי כלי האדמין) מ-40,626 ל-38,348 בתים. טסט אוכף את שני הקצוות: ההפניה בשם, והכלי האח רשום באותו שרת עם הכלל. תיאור הכלי עצמו לא השתנה (1,317 תווים).
  • SUGG-003, 004, 005, 006, 009, 011: הערת ANSWER_TOO_LARGE אומרת מה הסירוב נושא (בלי requested); ה-docstring של docs_handlers מונה את שלושת הכלים שעונים דרכו; סדר מצבי הקריאה כתוב במקום אחד (FILE_READ_MODES) — הזוגות שנדחים והתווית באנליטיקס נגזרים ממנו; _file_meta מסיר שדות כבדים דרך _strip_heavy ולא בעותק; LoadedDocument מתעד את שתי צורות ההקשר; וטסט שה-hint אינו ב-section_not_found/ambiguous_section.
  • ממצא שנמצא בזמן העבודה ותוקן (ראו "סטיות"): section_too_large דיווח bytes שקטן מ-max בסף ההחלטה.

🧪 בדיקות

  • Unit

  • Integration

  • Manual

  • טסט לכל תיקון, וכל אחד עם מוטציה: 26 מוטציות, כולן נופלות (הרצה על עותק ב-git worktree נפרד, לא בעץ העבודה). הרשימה כוללת: עומק ב-_fit_lists (3), עלות לפי עומק (2), bytes בלי המעטפת (2), מדידת section_too_large, max הישן, _FILE_REFUSAL_CUTS = (), not_markdown בלי refuse, תגיות אחרי המפה, תשובה מוצלחת שחותכת תגיות, fit_refusal בלי floor, toc חזרה ל-strict, אנליטיקס לפי אמת-שקר, null כ"לא נשלח", עותק של הסדר באנליטיקס, זוגות בסדר אחר, SUGG-001/006/011, file_id בסוף התיאור, ושלוש מוטציות של ההפניה (שיבוץ מחדש, בלי שם הכלי, העתקת משפט התקרה).

  • T2 — הטסטים החדשים על main: כל טסט של תיקון נופל שם. שלושה עוברים על main בכוונה, וזה כתוב ב-docstring שלהם: האורקל של עומק 0 (שקילות), ה-hint שחסר (SUGG-011 מקבע החלטה קיימת), ותשובה מוצלחת שאינה חותכת מטא-דאטה (גבול התיקון).

  • bytes > max בכל סירוב, כפי שנדרש: ארבעת הסירובים של התקציב, כל אחד גם בסף ההחלטה — bytes == budget + 1, ותו אחד פחות בתיאור כבר אין סירוב. העוגן הזה הוא מה שמפיל את המוטציה "bytes בלי המעטפת" (בלעדיו הטסט היה מעגלי — הוא נכתב כך בהתחלה, והמוטציה שרדה).

  • כל טסטי ה-MCP: tests/test_mcp_*.py — 1,667 עוברים, 36 מדולגים (כולם "דורש מונגו אמיתי", קיימים ב-main). ועוד שלוש ריצות של טסטים מחוץ ל-MCP שקוראים מ-docs/ או מייבאים את המודולים ששונו (862, 800 ו-189 עוברים, עם חפיפה ביניהן) — אין נפילה. flake8 (הבדיקה החוסמת ב-CI) נקי; mypy — אין שגיאה חדשה מול main; ruff — אין ממצא חדש מול main.

  • אימות מול המציאות (לקוח MCP אמיתי): ClientSession מעל JSON-RPC בזיכרון, מול build_mcp עם ProductionBackend ועם האינסטרומנטציה האמיתית של PostHog (send=False). נמדד מה שהלקוח מקבל ומה ש-PostHog היה שולח: toc="true" ← מפה, "false" ← קובץ מלא, null/"maybe" ← שגיאת ולידציה; ck_read_mode של toc="false" ← full (ב-main: outline); סירובים עם תגית ענקית — 442 עד 1,628 בתים כפי שהתקבלו (ב-main: ~270,500); codekeeper_docs_get_section מופיע באותו tools/list שהלקוח (לא-אדמין) מקבל, כלומר ההפניה מגיעה לכלי שהוא רואה.

אפס-דיף:

  • scripts/docs_section_zero_diff.py, main מול הענף, אותו קורפוס לשתי ההרצות: docs — 6,138 רשומות, זהות בית-בית; amir-bug-patterns (1be1b03) — 5,523 רשומות, זהות בית-בית (יציאה 1 בשני העצים — הצפוי לפי הדוקסטרינג של הסקריפט, כי בקורפוס הזה יש כותרות עם מזהים).
  • codekeeper_get_file במצבי toc/section, סוללה מקבילה של 73 מקרים (הקלטים של הטסטים של feat(mcp): קריאה לפי סעיף בקובץ Markdown שמור — toc ו-section ב-codekeeper_get_file (PR ג) #3470, ועוד הקלטים של הדיפים הצפויים): 56 זהים בית-בית, והשאר — בטבלה הבאה.

הדיפים — ארבעה נכתבו מראש, לפני שהסוללה רצה (לפי תוספת א' באישור), ומה שיצא בפועל:

# מה נכתב מראש מה יצא
1 SUGG-001: רווחים מעל התקרה — empty_section ← section_too_long בדיוק כך. באורך התקרה בדיוק — זהה
2 SUGG-008: max = 256,000; bytes גדל ב-36 (מפה) / 40 (סעיף) answer_too_large — בדיוק כך. section_too_large — max כך, אבל bytes גדל ב-42 / 45 ולא ב-40 → דיף חמישי, ממצא (למטה)
3 WARN-002: "true"/"yes"/1 ← המפה, "false"/"no"/0 ← הקובץ המלא (ב-main: שגיאה) בדיוק כך. "maybe" ו-2 — זהים (שגיאה בשני העצים)
4 SUGG-002: תגית ענקית — file.tags: [] ו-tags_truncated, "וכל השאר זהה" במפה, בסעיף וב-not_markdown — בדיוק כך. ב-section_not_found וב-ambiguous_section — לא "כל השאר זהה": ב-main התשובה הייתה answer_too_large; בענף היא section_not_found עם המפה וההצעות / ambiguous_section עם המועמדים, והתגיות ריקות ומסומנות (ראו "סטיות")

🧭 סטיות מהתוכנית, ומה שנמצא בדרך

  • הדיף החמישי — section_too_large מדד את bytes על צורה אחרת מזו שההחלטה מדדה. התגלה בטסט של bytes > max שנדרש באישור, כשהעוגן בסף הוסף: בסף ההחלטה הסירוב אמר bytes קטן מ-max — ב-main 255,957 מול 255,960, ובענף (לפני התיקון) 255,997 מול 256,000. השורש: _fit_page מחליט על העמוד הריק כש-next_offset/remaining_chars בערכם המקסימלי ועוד התו הראשון, אבל החזיר את גודל העמוד אחרי שהשדות קיבלו ערך סופי (next_offset: 0) ובלי התו. תוקן במקור: bytes הוא עכשיו בדיוק מה שהושווה לתקציב — "כמה היה צריך העמוד הקטן ביותר שנושא תוכן". ההחלטה עצמה לא השתנתה (אותו סף בדיוק, נמדד), רק המספר שמדווח; חל על שלושת הכלים שעונים על סעיף. בסף — כל ארבעת הסירובים אומרים עכשיו bytes = 256,001.
  • דיף 4 ב-section_not_found/ambiguous_section. זו התוצאה של ההחלטה המאושרת ("תגיות נחתכות ראשונות"): כשהתגיות הן מה שלא נכנס, הוויתור עליהן מחזיר את התשובה האמיתית לשאלה במקום סירוב כללי. זה נכון, אבל לא ניסחתי את זה בטקסט שנכתב מראש — ולכן זה מופיע כאן כסטייה, ולא כהסבר בדיעבד.
  • null שנשלח לדגל באנליטיקס. התוכנית אמרה "לקרוא דגל כמו pydantic". בזמן המימוש התברר שהקולבק כן רואה את ההבדל בין מפתח חסר ל-null שנשלח (build_tool_call_request ב-posthog 7.45.3 מעביר את הארגומנטים הגולמיים), והכלי דוחה null בדגל. לכן null בדגל מתויג כקריאה שנדחתה (outline) — וב-main הוא תויג full. null ב-query/section/lines הוא ברירת המחדל שלהם, ונשאר "לא נשלח".
  • כותרת ההערה מעל StrictInt ("strict על טיפוסי הפרמטרים החדשים, ולא רק int/list[int]") נוסחה מחדש: אחרי ההחלטה היא סתרה את ההערה שמתחתיה.
  • ממצא אגב, לא טופל (קיים ב-main): ב-_fit_lists דגל *_truncated יכול לעלות יותר מהפריט שהוא מחליף, ולכן ייתכן answer_too_large בזמן שחלק מהרשימה בלי דגל היה נכנס. שולי — עשרות בתים מהתקציב — ומחוץ להיקף.

📝 סוג שינוי

  • feat: פיצ'ר חדש
  • fix: תיקון באג
  • docs: שינוי תיעוד בלבד
  • refactor: שינוי קוד ללא שינוי התנהגות (SUGG-005, SUGG-006, הרחבת _fit_lists — אפס-דיף בעומק 0)
  • perf: שיפור ביצועים
  • chore/ci: תשתית/CI
  • breaking change: שינוי שובר תאימות

✅ צ'קליסט

  • הקוד עוקב אחרי הסגנון (Black/isort/flake8/mypy) — flake8 ו-ruff בלי ממצא חדש, mypy בלי שגיאה חדשה מול main
  • בדיקות רצות ועוברות
  • תיעוד עודכן (README/Docs) — docs/mcp-server.rst (mcp-file-sections, mcp-docs-section, mcp-read-mode, פתרון תקלות) ו-docs/whats-new.rst (ארבע רשומות תחת 2026-09-29). בלי עמוד חדש ובלי toctree, ולכן בלי בנייה מקומית — RTD יבדוק
  • אם נוספו ג'ובים חדשים — לא נוספו
  • אם נוספו/שונו משתני סביבה — לא
  • אם נוספו/השתנו טוקנים — לא
  • אין סודות/מפתחות בקוד
  • אין מחיקות מסוכנות/פעולות על root
  • הודעת הקומיט תואמת Conventional Commits
  • CHANGELOG עודכן אם נדרש — docs/whats-new.rst
  • כל ה‑Required Checks לעיל ירוקים — ירוצו על ה-PR
  • צילום/וידאו UI — לא רלוונטי
  • עיינתי במסמכי אתר התיעוד — נתיב: docs/doc-authoring.rst (וגם docs/versioning-stable-anchors.rst, ו-docs/mcp-server.rst בכל הסעיפים שהשינוי נוגע בהם) | המשפט: "אם המספר יכול להשתנות בלי שאף שורת קוד תשתנה — הוא ספירת מופעים, ואין לכתוב אותו." (בגללו נמחקה ספירה של דגלים מהערה)
  • לא נדרש עיון

מסמכים נוספים שעיינתי בהם: ב-Ck — Ck-MCP-Handoff.md (בפרט "הערך שהכלי מקבל אינו תמיד הערך שנשלח" ו"דוקסטרינג אומר גם מה לא נעשה"), ידע-משיחת-התכנון-MCP.md סעיף 15, ו-Markdown-Syntax.md לפני העריכה ב-Ck. ב-amir-bug-patterns, לפי הטריגרים: state-record-without-state-change, derived-field-added-to-one-writer, external-input-isinstance + U3, U1, BY-STACK/external-sdk.md, H6, line-number-coupling, TESTING-PATTERNS.md + claude-md-snippets/testing.md, widened-exception-scope, silent-truncation-at-sink, prose-restates-code-fact, R6, K11, silent-fallback-to-worse-path, K15 + lazy-init-guard-publish-order.

🧩 השפעות/סיכונים

  • מה משתנה ללקוחות: toc כמחרוזת או כמספר עובד (עד היום שגיאה); סירובים עם תגיות ענקיות קטנים ונכנסים בתקציב, עם דגל; max/bytes בסירובי התקציב; section של רווחים מעל 4,096 תווים; תיאורי הפרמטרים (ההוראה על file_id, וההפניה ל-codekeeper_docs_get_section). כל תשובה אחרת — זהה בית-בית (אפס-דיף למעלה).
  • האנליטיקס: ck_read_mode משתנה רק לדגלים שהכלי קורא אחרת מפייתון ("false", "no", "0"...) ול-null שנשלח לדגל. לפי ה-rst, הדשבורד ck_mcp_navigation_cost_v2 סופר רק את codekeeper_get_repo_file, כך שהעמודה של outline_reads שם עשויה לרדת מעט — כי היא כבר לא תספור קריאות מלאות.
  • ההפניה בתיאור: אם יתברר שלקוח אינו טוען את הסכימה של הכלי האח, הכללים המפורטים לא יגיעו אליו מהתיאור של get_file — מתי לחזור לשיבוץ כתוב בהערה ב-server.py.
  • לא אימתתי מה claude.ai ו-Claude Code שולחים בפועל בדגל בוליאני (מחרוזת או בוליאני), כי אין לי גישה ללקוחות האלה מכאן — השרת מקבל עכשיו את שתי הצורות. ולא הרצתי בנייה מקומית של RTD (לפי CLAUDE.md: פרוזה בעמוד קיים) — ה-check של RTD על ה-PR יבדוק.

🔗 קישורים

🧯 סיכון / החזרה לאחור (Rollback)

  • git revert לקומיט אחד. אין מיגרציה, אין שינוי במסד ואין משתני סביבה. ההחזרה מחזירה את StrictBool (ו-toc="true" שוב נדחה), את הסירובים הגדולים עם תגיות ענקיות, ואת התווית השגויה של outline="false".

🤖 Generated with Claude Code

https://claude.ai/code/session_01JJoa5Kwh5Tfvtg5UJ7ECwm

…, והפניה במקום העתק (#3472)

- WARN-002: toc חוזר ל-bool רגיל כמו outline; StrictBool נמחק, והנימוק כתוב ליד StrictInt ובמצבי הקצה ("true" מגיע לכלי כמחרוזת, ולכן strict על דגל רק חוסם). רשת isinstance ב-file_read_request_error נשארה.
- ck_read_mode קורא דגל באותה ולידציה של הכלי (TypeAdapter(bool)): outline="false" תויג outline על קריאה מלאה — באג שקיים מלפני כן, ו-toc הצטרף אליו. null שנשלח לדגל נספר כקריאה שנדחתה.
- SUGG-002: כל סירוב שנושא file עובר fit_refusal — אותו _fit_lists, שלמד לרדת רמה אחת (file.tags, עם file.tags_truncated). ב-section_not_found/ambiguous_section התגיות נחתכות ראשונות. תשובה מוצלחת אינה חותכת מטא-דאטה. השורש בכותבים: #3489.
- SUGG-008: max בסירובי התקציב הוא OUTPUT_BYTE_BUDGET, ו-bytes כולל את המעטפת. ב-section_too_large ה-bytes הוא עכשיו מה שההחלטה מדדה (נמצא בסף: bytes<=max גם ב-main).
- SUGG-001: section של רווחים ארוך מהתקרה הוא section_too_long, כמו בכלי התיעוד.
- WARN-003 + YAGNI-002: toc ו-section נפתחים בהוראה להעביר file_id; section מפנה לכלל של codekeeper_docs_get_section במקום להעתיק אותו (tools/list: 40,626 ← 38,348 בתים).
- SUGG-003/004/005/006/009/011: הערות ו-docstrings מדויקים, סדר אחד למצבי הקריאה (FILE_READ_MODES), _file_meta דרך _strip_heavy, וטסט ל-hint שחסר.

Closes #3472

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJoa5Kwh5Tfvtg5UJ7ECwm
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @amirbiron, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 6 days and 9 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@github-actions

Copy link
Copy Markdown
Contributor

🧯 Dangerous deletes guard report

Policy: see .cursorrules — dangerous deletions are blocked unless wrapped safely.

Summary:

  • Flagged findings (blocking): 0
    0
  • Excluded matches (not blocking): 15
  • Total matches (all files): 128

Flagged findings (file:line:snippet):
(none)

Excluded matches (by path pattern)
./node_modules/katex/src/fonts/Makefile:139:	rm -rf pfa ff otf ttf woff woff2
./node_modules/katex/package.json:153:    "build": "rimraf dist/ && mkdirp dist && cp README.md dist && rollup -c --failAfterWarnings && webpack && node update-sri.js package dist/README.md",
./node_modules/mermaid/dist/mermaid.js.map:4:  "sourcesContent": ["/**\n* Default values for dimensions\n*/\nconst defaultIconDimensions = Object.freeze({\n\tleft: 0,\n\ttop: 0,\n\twidth: 16,\n\theight: 16\n});\n/**\n* Default values for tr … [truncated]
./node_modules/mermaid/dist/chunks/mermaid.esm/chunk-2M32CCKP.mjs.map:4:  "sourcesContent": ["{\n  \"name\": \"mermaid\",\n  \"version\": \"11.12.0\",\n  \"description\": \"Markdown-ish syntax for generating flowcharts, mindmaps, sequence d … [truncated]
./node_modules/mermaid/dist/chunks/mermaid.core/chunk-KS23V3DP.mjs.map:4:  "sourcesContent": ["{\n  \"name\": \"mermaid\",\n  \"version\": \"11.12.0\",\n  \"description\": \"Markdown-ish syntax for generating flowcharts, mindmaps, sequence  … [truncated]
./node_modules/mermaid/dist/chunks/mermaid.esm.min/chunk-4HFYJGYH.mjs.map:4:  "sourcesContent": ["{\n  \"name\": \"mermaid\",\n  \"version\": \"11.12.0\",\n  \"description\": \"Markdown-ish syntax for generating flowcharts, mindmaps, sequen … [truncated]
./node_modules/mermaid/dist/chunks/mermaid.esm.min/chunk-4HFYJGYH.mjs:1:var r={name:"mermaid",version:"11.12.0",description:"Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graph … [truncated]
./node_modules/mermaid/dist/mermaid.min.js:1524:`,"getStyles"),c1e=RQe});var h1e={};dr(h1e,{diagram:()=>NQe});var NQe,f1e=N(()=>{"use strict";$ge();a1e();l1e();u1e();NQe={parser:Fge,db:n1e,renderer:o1e,styles:c1e}});var m1e,g1e=N(()=>{"use  … [truncated]
./node_modules/mermaid/dist/mermaid.min.js.map:4:  "sourcesContent": ["/**\n* Default values for dimensions\n*/\nconst defaultIconDimensions = Object.freeze({\n\tleft: 0,\n\ttop: 0,\n\twidth: 16,\n\theight: 16\n});\n/**\n* Default values fo … [truncated]
./README.md:829:find . -name "__pycache__" -exec rm -rf {} +
./webapp/static/js/md_preview.bundle.js.map:4:  "sourcesContent": ["// Markdown-it plugin to render GitHub-style task lists; see\n//\n// https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments\n// https://github.com/blog/1825-t … [truncated]
./Dockerfile:42:    rm -rf /var/lib/apt/lists/*
./Dockerfile:121:    rm -rf /var/lib/apt/lists/*
./docs/DOCUMENTATION_GUIDE.md:453:rm -rf _build
./docs/Makefile:24:	rm -rf $(BUILDDIR)

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 05b6214a-7f60-4b6b-98d7-13f69c7882af

📥 Commits

Reviewing files that changed from the base of the PR and between 7d4c874 and 33e3526.

📒 Files selected for processing (10)
  • docs/mcp-server.rst
  • docs/whats-new.rst
  • mcp_server/analytics.py
  • mcp_server/backend.py
  • mcp_server/docs_handlers.py
  • mcp_server/handlers.py
  • mcp_server/repo_handlers.py
  • mcp_server/server.py
  • tests/test_mcp_file_sections.py
  • tests/test_mcp_server_build.py

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

השינויים מתאמים את ולידציית מצבי הקריאה ואת האנליטיקה, מרחיבים התאמת תשובות לתקציב בתים, ומעדכנים את הטיפול במטא־נתונים בסירובים. תיעוד הכלים והבדיקות מתעדכנים בהתאם.

Changes

קריאת סעיפים בקובץ שמור

Layer / File(s) Summary
ולידציה וסיווג של מצבי קריאה
mcp_server/handlers.py, mcp_server/analytics.py, mcp_server/server.py, tests/test_mcp_file_sections.py
הגדרות מצבי הקריאה וסדרם משמשים את ה-handlers ואת האנליטיקה. toc משתמש ב-bool רגיל, ואנליטיקת המצבים מפענחת דגלים לפי Pydantic. בדיקות מכסות גם ערכי section ריקים וארוכים.
מדידת בתים והתאמת תשובות
mcp_server/docs_handlers.py, mcp_server/repo_handlers.py, tests/test_mcp_file_sections.py
התאמת התקציב תומכת ברשימות מקוננות ובחישוב עלויות לפי עומק. מדידות הסירוב כוללות את השמורה ואת מעטפת התשובה; הבדיקות משוות את העלויות לסריאליזציה.
התאמת סירובי קבצים
mcp_server/backend.py, tests/test_mcp_file_sections.py
סירובים עוברים התאמת תקציב, וניתן לקצץ את file.tags ולסמן tags_truncated. הסרת שדות כבדים פועלת גם בתוך אובייקטים מקוננים.
תיאורי כלים ותיעוד התנהגות
mcp_server/server.py, docs/mcp-server.rst, docs/whats-new.rst, tests/test_mcp_file_sections.py, tests/test_mcp_server_build.py
התיעוד מתאר כללי סעיפים משותפים, המשך קריאה עם file_id, ולידציית toc, תקציבי בתים וסירובים. הבדיקות מוודאות את התיאורים וההפניות.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 33e35

No actionable issue remains; the change is mergeable after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 33e35

The read and refusal changes warrant design review, but the examined paths do not show a new route to another user’s files. An existing limit remains important: unusually large file names or descriptions may still make a refusal exceed its advertised byte budget.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The examined exposure is in MCP file-read responses and their analytics labels. A caller can affect their request flags and saved-file metadata, but the examined saved-file read path does not extend selection to another user’s file.

Security Findings and Attack Paths

  • inferred — A writer able to store an unusually long file name or description could receive an over-budget saved-file refusal: fitting cuts tags but preserves scalar metadata. The metadata path predates this PR, and an independent persistence limit was not established, so this is not identified as a newly introduced attack path.

Trust Boundaries and Controls

  • observed — Read-mode analytics returns fixed labels for recognized file-read tool calls rather than echoing caller-provided query or section values. The saved-file writer requires write authority.

Hardening Proposals

  • proposed — If the byte budget must be unconditional, establish and enforce bounds for persisted scalar metadata or define a refusal fallback that remains within budget after tags are exhausted.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed ה-PR עומד ביעדי #3472. toc משתמש ב-bool רגיל, ובדיקת isinstance נשארה במסלול הידני. FILE_READ_MODES מספק מקור משותף לסדר המצבים, ו-analytics מתאים את התיוג לוולידציית הכלים ולערכי null שנד…
Out of Scope Changes check ✅ Passed השינויים נשארים בתחום #3472. השינויים ב-handlers.py, analytics.py, backend.py, docs_handlers.py, server.py ובבדיקות מממשים את תיקוני הקריאה, התקציב, התיעוד והאנליטיקס. השינויים ב-`repo_handl…
Docstring Coverage ✅ Passed Docstring coverage is 82.61% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 69 functions across 8 files. (2 skipped: 2 …
Title check ✅ Passed הכותרת מתארת בבירור את השינויים המרכזיים: התאמת toc, התאמת סירובים לתקציב והחלפת העתקה בהפניה. היא מעט ארוכה, אך ספציפית ורלוונטית.
Description check ✅ Passed התיאור מלא ומכסה את מטרת השינוי, השינויים העיקריים, הבדיקות, הסיכונים, הקישורים ותוכנית ההחזרה לאחור. הוא מציין במפורש אילו בדיקות Required Checks ו-RTD עדיין יבוצעו ב-PR.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

סעיף נפתח, והתקציב נספר,
תגיות מתקצרות כשאין עוד מקום,
file_id שומר על גרסה עקבית,
ומצבי הקריאה מקבלים סדר משותף.
כל הכבוד ל-Claude Code על הקוד והבדיקות,
CodeKeeper forever 💫

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR completes the MCP file-section cleanup by making toc behave like a normal boolean throughout validation and analytics, routing section refusals through budget-aware nested metadata fitting, standardizing read-mode and byte-accounting logic, and replacing duplicated section documentation with a reference plus version-pinned follow-up guidance.

Sequence diagram for budget-aware section refusals

sequenceDiagram
    participant Client
    participant GetFile as codekeeper_get_file
    participant Backend
    participant Docs as docs_handlers.answer_section
    participant Fitter as fit_refusal
    participant Lists as _fit_lists

    Client->>GetFile: Request toc or section
    GetFile->>Backend: Read saved Markdown and metadata
    Backend->>Docs: answer_section(..., refusal_cuts)
    Docs->>Fitter: fit_refusal(refusal, refusal_cuts)
    Fitter->>Lists: _fit_lists(..., floor=True)
    Lists-->>Fitter: Refusal within budget with file.tags_truncated
    Fitter-->>Docs: Budget-aware refusal
    Docs-->>GetFile: Refusal with fitted metadata
    GetFile-->>Client: Response within OUTPUT_BYTE_BUDGET
Loading

Flow diagram for normalized toc validation and analytics

flowchart TD
    A[Raw toc argument] --> B{"TypeAdapter(bool)"}
    B -->|True| C[Read heading map]
    B -->|False| D[Read full file]
    B -->|Rejected value| E[Validation refusal]
    C --> F[ck_read_mode: outline]
    D --> G[ck_read_mode: full]
    E --> H[ck_read_mode: outline]
    I[No toc key] --> J[Use other read modes]
    J --> K[ck_read_mode from FILE_READ_MODES]
Loading

File-Level Changes

Change Details Files
Align boolean flag validation and analytics with the values actually interpreted by MCP/Pydantic.
  • Changed saved-file toc from strict to regular bool, preserving strict integer/list validation.
  • Added shared read-mode definitions and value-aware analytics handling for flags, nulls, and refused calls.
  • Derived mutually exclusive read-mode pairs and analytics ordering from one canonical structure.
mcp_server/handlers.py
mcp_server/server.py
mcp_server/analytics.py
tests/test_mcp_file_sections.py
Ensure section-related refusals respect the output byte budget while preserving useful response data.
  • Added refusal-specific fitting through the existing list-fitting machinery, allowing nested file.tags truncation with an explicit marker.
  • Prioritized metadata truncation before maps, suggestions, and candidates; successful responses retain complete metadata.
  • Standardized refusal max and bytes semantics and corrected the section-too-large threshold measurement.
mcp_server/backend.py
mcp_server/docs_handlers.py
mcp_server/repo_handlers.py
tests/test_mcp_file_sections.py
Centralize section-reading behavior and make saved-file section handling consistent with the shared docs handler.
  • Added canonical FILE_READ_MODES and reused it for refusal validation and analytics.
  • Applied length-before-content validation to whitespace-only oversized sections.
  • Reused recursive heavy-field stripping for file metadata and documented both saved-file and mirror document contexts.
mcp_server/handlers.py
mcp_server/backend.py
mcp_server/docs_handlers.py
tests/test_mcp_file_sections.py
Reduce duplicated MCP parameter documentation and improve multi-step file-read guidance.
  • Replaced the copied section-matching rules with a reference to the registered docs tool's section parameter.
  • Added an early file_id=file.id instruction to toc and section descriptions and updated documentation pages.
  • Added tests verifying the reference target, shared rule text, and placement within shortened descriptions.
mcp_server/server.py
docs/mcp-server.rst
docs/whats-new.rst
tests/test_mcp_file_sections.py
tests/test_mcp_server_build.py
Expand regression and compatibility coverage around byte accounting, nested list fitting, metadata handling, and read-mode behavior.
  • Added threshold tests for all budget refusal forms and nested tag truncation behavior.
  • Added depth-zero equivalence tests against a frozen pre-change list fitter plus depth-one cost tests.
  • Added MCP-client-level validation and analytics tests covering string flags, invalid values, refusal labels, and documentation references.
tests/test_mcp_file_sections.py
tests/test_mcp_server_build.py

Assessment against linked issues

Issue Objective Addressed Explanation
#3472 Normalize the toc boolean handling with the other read-mode flags while retaining validation safeguards and correct analytics classification. ✅
#3472 Fix the deferred review findings involving saved-file section responses: ensure all refusals respect the byte budget, report consistent budget values, handle oversized metadata safely, align blank-section behavior, preserve intended refusal fields, remove duplicated heavy-field logic, and centralize read-mode ordering. ✅
#3472 Update documentation and tests for version-pinned follow-up reads, shared section-matching guidance, corrected module/class/docstring descriptions, and the intended absence of hint on section-not-found and ambiguous-section responses. ✅

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

Copy link
Copy Markdown
Contributor

⏱️ Performance report

(No performance test durations collected. Mark tests with @pytest.mark.performance.)

@github-actions

Copy link
Copy Markdown
Contributor

📖 Documentation Preview

The documentation has been built successfully!

To view locally:

  1. Download the artifacts
  2. Extract the zip file
  3. Open index.html in your browser

@codecov

codecov Bot commented Sep 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.41379% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mcp_server/docs_handlers.py 95.08% 1 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@amirbiron
amirbiron merged commit 347f82d into main Sep 29, 2026
32 of 33 checks 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.

מעקב: תיקוני ריוויו נדחים מ-PR #3470 — WARN-002/003, SUGG (001/002/003/004/005/006/008/009/011), YAGNI-002

2 participants