Repository navigation
Conversation
Co-authored-by: amirbiron <amirbiron@gmail.com>
Contributor
|
Cursor Agent can help with this pull request. Just |
amirbiron
marked this pull request as ready for review
August 6, 2025 01:15
amirbiron
deleted the
cursor/resolve-dependency-conflict-and-clean-requirements-txt-7572
branch
August 6, 2025 01:15
Closed
Open
amirbiron
pushed a commit
that referenced
this pull request
Sep 28, 2026
…יפים ושכנים ממצא Greptile (P2) על שכבה 2: כשכותרת נחתכת ל-``MAX_TITLE_CHARS``, הדגל ``title_truncated`` הופיע רק במפה ובתשובת הסעיף, אך לא במועמדי ``ambiguous_section``, בתת-הסעיפים ובשכנים. שם השם החתוך הוצג בלי סימן שהוא חלקי, ושתי כותרות שונות שחולקות קידומת מעל התקרה נראו זהות בלי דרך להבחין ביניהן. - ``_title_flag(sec)``: בעלים אחד לדגל בצד ה-mcp_server. ``_section_ref`` (תת-סעיפים ושכנים), מועמדי ``ambiguous_section`` ותשובת הסעיף עוברים דרכו. מותנה — לכותרת אמיתית אינו נדלק, ולכן אפס-דיף נשמר. ``build_toc`` ב-services נושא עותק משלו (השכבה אינה מייבאת מ-mcp_server), אותו שם מפתח ואותו תנאי. - טסטים שנופלים על הקוד הישן (דפוס בדיקות #5): דגל במועמד, בתת-סעיף ובשכן. ונועל-חוזה ל-round-trip: הדבקת רכיב breadcrumb של כותרת מקוצצת ל-``section`` מתנהגת בדיוק כמו הדבקת הכותרת עצמה — מתאימה, או ``ambiguous_section`` כשהשם משותף. הרכיב הוא אותה מחרוזת כמו הכותרת, כי הקיצוב במקור לפני בניית ה-breadcrumb. - תיעוד: ב-mcp-server.rst בלוק על תקרת אורך-הכותרת, ההתנהגות ambiguous החדשה, ושני משפטי ה-breadcrumb (הרכיבים מקוצצים; הדגל יושב על רשומת הכותרת במפה, ושם בודקים אם רכיב חתוך); שורת ``MAX_TITLE_CHARS`` בטבלת הקבועים (מפנה לשם, לא לערך); ותיאור הפרמטר ``section``. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn
amirbiron
added a commit
that referenced
this pull request
Sep 28, 2026
…eeper_get_file (PR ג) (#3470) * fix(md_parser): שורות המסמך הן שורות המקור — טקסט סעיף זהה בית-בית ל-lines= גם ב-CRLF ועם BOM עד היום md_parser בנה את Document.lines מהטקסט המנורמל (BOM הוסר, \r\n הפך ל-\n), ולכן בקובץ CRLF או עם BOM טקסט הסעיף שחוזר לא היה זהה למה ש-lines= מחזיר על אותו טווח. נמדד. התוכנית של PR ג דורשת את הזהות הזאת, וב-codekeeper_get_file היא חוזה: סעיף שנקרא מקובץ שמור הוא לא פעם old_string של codekeeper_edit_file, שדורש התאמה מדויקת. הנרמולים נשארים במה שהפרסר רואה, ורק השורות שחוזרות הן של המקור. מספר השורות ומיקום כל כותרת לא זזים: BOM אינו מעבר שורה, וכל \r\n נושא \n אחד. approx_bytes במפה הוא עכשיו הגודל האמיתי של הקטע. אפס-דיף (scripts/docs_section_zero_diff.py, לפני/אחרי על אותו קורפוס): docs/ — 208 קבצים, 6,010 רשומות, זהה; amir-bug-patterns (75c2793) — 99 קבצים, 5,401 רשומות, זהה. אין בשני הקורפוסים קובץ CRLF או BOM. שלושה טסטים קיבעו את הנרמול של lines ועודכנו; טסט חדש משווה טקסט סעיף ל-apply_line_range (המימוש של lines=) והוכח שהוא נופל על הקוד הקודם. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * refactor(markdown): "מה נחשב Markdown" בכלל אחד ב-services — הוובאפ קורא ממנו, וה-MCP יקרא ממנו הכלל של הוובאפ — שם השפה markdown/md, או סיומת .md/.markdown, בלי תלות ברישיות — ישב ב-_is_markdown_file ב-webapp/app.py, ועוד שני עותקים שלו היו מוטבעים במסלולי השמירה (איסוף התמונות של Markdown), רשימת הסיומות הוקלדה שוב בשמירת קובץ משותף, ורשימת השפות במצב התצוגה המקדימה. שרת ה-MCP אינו מייבא מהוובאפ, וקריאה לפי סעיף ב- codekeeper_get_file צריכה את אותו כלל בדיוק — עותק נוסף שם היה R6. services/markdown_files.py מחזיק את הכלל ואת שתי הרשימות; כל המקומות בוובאפ עוברים דרכו, בלי שינוי התנהגות (הערך שמגיע מהטופס כבר מקוצץ ומוקטן). ההבדל היחיד: שם שפה שאינו מחרוזת מחזיר False במקום להפיל AttributeError. שלושת הכללים האחרים בריפו (הבוט ב-handlers/file_view.py, is_markdown_filename של ניקוי הקוד המודבק, ותיקוני השפה בוובאפ) שונים במשמעות ונשארו כמו שהם. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * feat(mcp): קריאה לפי סעיף בקובץ Markdown שמור — toc ו-section ב-codekeeper_get_file, דרך אותן פונקציות של docs_get_section שני מצבי קריאה חדשים ב-codekeeper_get_file, לצד lines ו-query: toc=true מחזיר את מפת הכותרות (עם approx_bytes לכל סעיף), ו-section="<כותרת או מזהה>" מחזיר סעיף אחד עם breadcrumb, טווח שורות, תת-סעיפים ושכנים, ועם עימוד ב-max_chars/offset. - שימוש חוזר מלא: הפרסור ומיפוי הסירובים חולצו מ-document_from_read ל-docs_handlers.parse_with_refusals (אפס-דיף על docs_get_section), וההתאמה, החיתוך ובניית התשובה הם docs_handlers.answer_section — אותן פונקציות בשני הכלים, וטסט spy מקבע את זה. - ארבעת המצבים אינם מצטברים, וכל זוג נדחה בקוד משלו (toc_and_section, toc_and_query, toc_and_lines, section_and_query, section_and_lines, query_and_lines כמו קודם). max_chars/offset בלי section נדחים (max_chars_without_section, offset_without_section), ו-section ריק הוא empty_section עם הפניה ל-toc=true. הוולידציה עברה ל-file_read_request_error (לשעבר file_query_request_error), ושתי השכבות בונות את הסירוב דרך file_read_refusal. - רק Markdown, לפי הכלל המשותף של services.markdown_files (not_markdown עם הפניה ל-lines/query); תקרת גודל לפני הפרסור, בבתים של UTF-8 מול MAX_FILE_SIZE_FOR_DISPLAY — אותה תקרה ש-_PARSE_COST_BYTES מתקצב (too_large_for_sections); סירובי הפרסר חוזרים כמו ב-docs, עם hint. - toc הוא StrictBool: "true" ו-1 נדחים בגבול ולא הופכים ל-True בשקט. - אנליטיקס: toc נספר כ-outline ו-section כתווית חדשה section. ההערה שטענה שקריאות query "מנפחות" את עמודת הדשבורד תוקנה — ck_mcp_navigation_cost_v2 סופר את העמודות רק ל-codekeeper_get_repo_file (נקרא מהגדרת נקודת הקצה ב-PostHog). - תיאור הכלי מפנה לשני הפרמטרים, והפירוט בתיאורי הפרמטרים; כלל ההתאמה משובץ מ-_SECTION_PARAM_DOC כמו שהוא. get_file הוא עכשיו התיאור הארוך ביותר (1,132 תווים, מתחת ל-1,400) — ההנמקה בטסט עודכנה. tests/test_mcp_file_sections.py: 56 בדיקות דרך ממשק ה-MCP, וכל אחת נופלת תחת מוטציה נקודתית (36 מוטציות, כולן נהרגו, ב-worktree נפרד). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * fix(mcp): תקרת אורך ל-section — section_too_long מעל 4,096 תווים, בפונקציה שבונה את התשובה ולכן בשלושת הכלים section לא עבר שום תקרה, והעבודה שנעשית עליו גדלה עם מה שהקורא שולח: ההתאמה וההצעות של difflib, וההד המלא ב-requested. נמדד (2026-09-28, docs/mcp-server.rst): 4,096 תווים — 2.8ms ותשובה של 25,054 בתים; מיליון תווים — 321ms ותשובה של 1,020,958 בתים, מעל OUTPUT_BYTE_BUDGET, וגוף בקשה של 1MiB נושא אותו. בלי התקרה, הטענה שמגבלת הקצב שומרת עליה (קריאה אחת ≤ WORST_CASE_CPU_SECONDS) מפסיקה להחזיק. - MAX_SECTION_CHARS = 4,096 ב-docs_handlers, נבדק בראש _answer_from_document — codekeeper_docs_get_section, codekeeper_read_batch ו-codekeeper_get_file מקבלים אותו ממקום אחד. הסירוב נוקב בשמו, עם max_chars ו-actual_chars (אותם שדות כמו path_too_long), בלי הד ובלי TOC, ולפני כל התאמה. - המספר נגזר ממדידה: הכותרת הארוכה ביותר — 71 תווים ב-RST של docs/, 87 ב-Markdown של הריפו (623 ב-README מסופק תחת node_modules), 90 ב-amir-bug-patterns; בקבצים השמורים ATX עד 292 וחסם שמרני של 1,219. התקרה היא פי 3.36 מהחסם — אפס כותרות אמיתיות נחסמות, וטסט מוודא את זה על כל עמודי docs/. - תיאור הפרמטר section נוקב בתקרה, נשתלת מהקבוע (ולכן גם התיאור של get_file, שמשבץ אותו). אפס-דיף על docs_get_section: RST 208 קבצים / 6,010 רשומות (sha256 f3b1fb…), amir-bug-patterns 99 / 5,401 (a2d662…) — זהים לבסיס. שש מוטציות, כולן נהרגו. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * docs(mcp): קריאה לפי סעיף בקובץ שמור — עמוד, הבדלי הדיאלקט מול הוובאפ עם דוגמה שנבדקה, ומה הדשבורד באמת סופר - docs/mcp-server.rst: סעיף חדש (mcp-file-sections) — שני המצבים, דוגמאות JSON שהן פלט אמיתי של הכלי, מה משותף עם docs_get_section ומה שונה בכוונה, איזה קובץ נקרא (Markdown לפי הכלל של הוובאפ, גרסה, file_id, הרשאה, תקרה בבתים), טבלת מצבי קצה, וסעיף הדיאלקט (mcp-file-sections-dialect): כותרת בתוך קופסת :::, כותרת מיד אחרי שורת HTML, ו-front matter. הדוגמה של ::: נבדקה על קובץ שמור אמיתי ועל דוגמה סינתטית באותו מבנה, מול md_parser ומול markdown-it 14.1.0 + markdown-it-container 4.0.0 באפשרויות של הוובאפ: טקסט הסעיף כולל את ה-::: הסוגר, ושורת הפתיחה של הקופסה שייכת לסעיף שלפניה; ויישור היה מעלים את הכותרת מהמפה (בוובאפ היא ברמת קינון 1). - אותו עמוד: שורת הכלי, רשימת ה-status הסגורה (toc, section), MAX_SECTION_CHARS בטבלת הקבועים (ומה MAX_FILE_SIZE_FOR_DISPLAY, MAX_LINES ו-MAX_TOKENS מגבילים עכשיו), הצרכן השני של _PARSE_COST_BYTES במודל הריצה, תקרת האורך בסעיף ההתאמה ובחשבון של מגבלת הקצב, ck_read_mode (section, ו-toc כ-outline), ושורות בפתרון תקלות. "78 מסלולי סירוב" — ספירת מופעים שהשינוי הזה מיישן — הוסרה. - ck_read_mode: שתי ההערות שטענו שתווית שגויה "מנפחת" את עמודות הדשבורד (על query ועל לחיצת היד) תוקנו, ב-analytics.py ובעמוד — ck_mcp_navigation_cost_v2 סופר את העמודות רק על $mcp_tool_call של codekeeper_get_repo_file (נקרא מהגדרת נקודת הקצה ב-PostHog). docs/webapp/mcp-analytics.rst אומר את זה במפורש. - whats-new (2026-09-28), FEATURES_SUMMARY.md, mcp_server/README.md. - טסט שקושר את המספר של התקרה בעמוד — בטבלה ובפרוזה — לקבוע. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * fix(mcp): עמוד של סעיף חסום בבתים כפי שהוא נשלח — לא רק בתווים של max_chars max_chars סופר תווים, והתקציב (OUTPUT_BYTE_BUDGET, 256,000) הוא בתים. לכן עמוד מלא של תווים רחבים עבר אותו — נמדד על הקוד הקודם, דרך codekeeper_get_file: עמוד CJK של MAX_CHARS_MAX תווים יצא ב-299,373 בתים, אימוג'י ב-398,132 ותווי בקרה ב-595,650. ושדה אחד לא היה חסום בכלל: subsections — סעיף עם 7,000 תת-סעיפים יצא בכמיליון בתים. (נתפס בריוויו על ה-PR.) התיקון בפונקציה המשותפת (_answer_from_document), ולכן חל על codekeeper_docs_get_section, codekeeper_read_batch ו-codekeeper_get_file: - עמוד שאינו נכנס נגמר מוקדם יותר, על גבול תו — ולא נדחה. truncated נדלק, next_offset הוא איפה שהעמוד באמת נגמר, ו-truncation_reason: "byte_budget" (המילה הקיימת של search_repo ו-unread_reason) — ורק אז. בעימוד לפי offset זה חיתוך בלי אובדן, ויש טסט שמחבר את העמודים בחזרה לסעיף. - המדידה היא על התשובה כולה, באותה סריאליזציה שה-SDK שולח: wire_json עבר מ-read_batch ל-repo_handlers, ליד התקציב, ושני המודולים מודדים בו. get_file שומר מקום ל-found/status (reserve_bytes), כך שהתשובה כפי שהיא יוצאת נכנסת. - כמה תווים נכנסים — חיפוש בינארי שמודד כל קידומת כמחרוזת JSON. הגרסה הראשונה חתכה לפי בתי UTF-8, וטסט תווי הבקרה תפס אותה: תו בקרה הוא בית אחד ב-UTF-8 ושישה ב-JSON, והקיצוץ מחק את כל התוכן. - subsections חסומה ב-_TOC_MAX, עם subsections_truncated רק כשנחתכה. - סעיף שגם עמוד ריק שלו אינו נכנס: section_too_large עם bytes, max ו-line_range (ב-get_file גם hint) — ולא עמוד ריק ש-next_offset שלו לא זז. אפס-דיף: הסוללה של scripts/docs_section_zero_diff.py זהה בית-בית על 208 עמודי RST ועל 99 קבצי amir-bug-patterns, וגם כל סעיף בשני הקורפוסים עם max_chars=MAX_CHARS_MAX (2,812 ו-2,008 קריאות) — אף תשובה אמיתית לא עברה את התקציב, ואף אחת לא השתנתה. ובאותו קומיט: ההערה ליד MAX_SECTION_CHARS טענה ש-OUTPUT_BYTE_BUDGET הוא תקציב "שאף תשובה אחרת של כלי קריאה אינה עוברת" — וזה לא היה נכון (עמודי הסעיף עצמם עברו אותו). תוקנה. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * fix(mcp): המפה, "לא נמצא" ו"כפולה" חסומות בתקציב הבתים — לא רק עמוד של סעיף עד עכשיו OUTPUT_BYTE_BUDGET חל רק על עמוד של סעיף, והרשימות של שלוש הצורות האחרות היו חסומות רק במספר פריטים. תיאור הפרמטר section הבטיח ש"תשובה לעולם אינה עוברת" את התקציב — וזה לא היה נכון לתשובת section_not_found, שנושאת את המפה כולה. כל פריט במפה נושא את כל ה-breadcrumb שלו, ולכן בקינון עמוק המפה גדלה פי כמה מהקובץ: קובץ של 93,906 בתים החזיר מפה של 472,517 בתים. - _fit_lists: כל רשימה נחתכת מהסוף עד שהתשובה כולה נכנסת, במדידה של wire_json (כפי שנשלחת), והדגל שלה נדלק רק כשבאמת נחתך פריט. תשובה שנכנסת חוזרת כמו שהיא. ב-section_not_found המפה נחתכת לפני ההצעות; המועמדים נשארים בסדר המסמך. - answer_too_large: תשובה שגם כשכל הרשימות שלה ריקות אינה נכנסת. הענף ניתן להגעה (update_file_metadata אינו מגביל תגיות, לשם הקובץ אין תקרה בשכבת ה-MCP, ו-includes של RST חסום רק בגודל הקובץ), ולכן סירוב מפורש ולא תנאי מת. get_file מוסיף לו hint. - חיפוש בינארי אחד (_longest_fitting_prefix) לעמוד ולרשימות (R6). - תיאורי הכלים: שתי הסיבות של כל דגל, החיתוך מהסוף והדרך למועמד שנחתך; "at most 50" הפך לקבוע; "never exceeds" הוסר. - טסט לשורה שזורקת TypeError על תוכן שמור שאינו מחרוזת (לא היה מכוסה). אימות: 12 טסטים חדשים, 11 נופלים על הקוד הקודם (e78dd9c); 16 מוטציות נהרגו, וגם 13 המוטציות של הסבב הקודם; אפס-דיף על docs_get_section (RST ו-abp) ועל התשובות המלאות של שלוש הצורות בכל עמודי התיעוד, abp וקובצי ה-Markdown בריפו. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * fix(mcp): כותרת ששמה חוזר אינה נענית בשמה — התיאורים מפנים ל-line_range, להורה ול-query התיאור של toc ב-codekeeper_get_file הבטיח "A heading past the cut is still readable by its name", והעמוד אמר אותו דבר בכמה מקומות. זה לא נכון כשהשם חוזר בקובץ: find_sections מחזיר את כל ההתאמות המלאות, ולכן שאלה בשם הזה היא ambiguous_section תמיד — והמפה שנחתכה כבר אינה מראה את הטווח. שוחזר על c11d428: 400 כותרות ועוד שתיים בשם "סיכום" — המפה 400 מתוך 402, והשם מחזיר ambiguous_section. (שתי הערות ריוויו, על server.py ועל mcp-server.rst.) הדרך הייתה קיימת בקוד ולא הייתה כתובה: - כל מועמד נושא breadcrumb ו-line_range, ו-lines= על הטווח מחזיר בדיוק את שורות הסעיף. - הורה מה-breadcrumb כולל אותו כברירת מחדל — הדרך בכלי התיעוד למי שאינו אדמין, כי codekeeper_get_repo_file (שקורא טווח שורות) הוא לאדמין בלבד. - ומה שחסר גם במועמדים וגם במפה — query מוצא את השורה שלו, ו-lines קורא משם. התיקון בתיאורים ובעמוד, לא בהתנהגות: - _SECTION_PARAM_DOC (משותף לשני הכלים): כל מועמד נושא breadcrumb ו-line_range; שם שחוזר לעולם אינו נענה; מועמד שנחתך מופיע במפה, אלא אם גם היא נחתכה, והשם המלא מגיע אליו רק כשאינו חוזר. - toc ו-section של get_file: התנאי על שם שחוזר, lines= ל-line_range, ו-query למה שחסר בשתי הרשימות. - docs/mcp-server.rst: פסקה חדשה ב-mcp-docs-section, התיקון בכל מקום אחר בעמוד שחזר על ההבטחה, ושורה חדשה בפתרון תקלות. - whats-new וההערה ב-docs_handlers מפנים עכשיו למקום אחד במקום לחזור על הדרך (prose-restates-code-fact). נמדד על הקבצים השמורים, הצרכן של הפיצ'ר (2026-09-28, אגרגציה לקריאה בלבד שמחזירה מספרים, ואומתה באותה ריצה מול md_parser ו-normalize_title על 13 מסמכי בדיקה — 13 מתוך 13 זהים): - כותרת ששמה חוזר ב-87 מתוך 564 הקבצים הפעילים (15%), עד 21 חזרות של שם אחד. - התרחיש של הריוויו קיים: שתי גרסאות עם יותר מ-400 כותרות, כלומר מפה שנחתכת (Bigfile.md עם 589, והגרסה השישית של AI-Mentor-architecture.md עם 577), ובגרסה השישית יש כותרת אחרי החיתוך ששמה חוזר. היא אינה הגרסה האחרונה, כך שמגיעים אליה ב-version=6 או ב-file_id. - אף שם אינו חוזר יותר מ-50 פעמים, ולכן רשימת המועמדים אינה נחתכת היום בקבצים השמורים. הדרך דרך query, למה שחסר גם במועמדים וגם במפה, תיאורטית שם. ובכלי התיעוד (הריפו): ב-amir-bug-patterns 17 קבצים עם שם שחוזר, עד 16 פעמים באותו קובץ; בעמודי ה-RST שלושה. טסטים, באותה שיטה של _SUGGESTION_RULE_SURFACES: שלושה שמריצים את הדרך דרך mcp.call_tool (עוברים גם על c11d428 — ההתנהגות לא השתנתה, והם מחזיקים את התיאורים לה), ובדיקת משטחים שנופלת על הטקסט הישן — כל שמונת הסמנים חסרים שם. שבע מוטציות, כולן נתפסות. אפס-דיף על תשובות כלי התיעוד. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * fix(mcp): מדידת תשובת סעיף מצטברת — לא מסדרלים את התשובה המלאה לפני החיתוך שכבה 1 של SEC-001 מסקירת Han: `_fit_lists` מדד את גודל התשובה בקריאת `wire_json(answer)` על התשובה עם כל הפריטים לפני החיתוך. מפה שכל פריט בה נושא את כל ה-breadcrumb שלו (כותרת-אב חוזרת בכל צאצא) יכולה לנפח את המחרוזת הזאת למאות מגה-בייט על קובץ Markdown שמור שנבנה לכך — מיצוי זיכרון של מופע ה-MCP היחיד. התיקון: מדידה מצטברת. מודדים את התשובה עם רשימות ריקות (קטן) ואת עלות כל פריט לבד; מסדרלים את התשובה המלאה רק כשהחסם-העליון בטווח כמה בתים מהתקציב (לעולם לא במקרה הענק); חותכים מהסוף לפי סדר הוויתור; ומאמתים מדויק על התוצאה החתוכה. המחרוזת הגדולה ביותר שנבנית אי-פעם היא התקציב ועוד פריט אחד. - עלות פר-פריט חולצה ל-`repo_handlers.list_item_cost`, בעלים אחד ליד `wire_json` ותקציב הבתים (R6). `read_batch._entry_cost` מאציל אליה — בלי עותק שני. - `_cut_from_end` הוסר (מת אחרי המעבר למצטבר). `_longest_fitting_prefix` נשאר לחיתוך תוכן העמוד בלבד. - אותה תוצאה בדיוק, ישן מול חדש: על מפה של 725,309 בתים (מעל התקציב) שני המסלולים מחזירים 141/400 פריטים ו-255,482 בתים, אבל שיא ההקצאה יורד מ-725KB ל-275KB (tracemalloc). היחס גדל עם גודל התשובה. - טסט דטרמיניסטי חדש שסופר כל קריאת `wire_json` ומוודא שהמקסימום ≤ תקציב ועוד פריט; הוא נופל על הקוד שלפני התיקון. 474 טסטים קיימים עוברים. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * fix(mcp): תקרת אורך לכותרת בודדת — חוסמת את מגבר ה-breadcrumb בשורש שכבה 2 של SEC-001 מסקירת Han, השורש שמעל שכבה 1: מגבר ה-breadcrumb הוא שכותרת-אב נכתבת שוב בכל צאצא. שכבה 1 חסמה את גודל התשובה; כאן חוסמים את המקור — אורך של כותרת בודדת. - `doc_sections.MAX_TITLE_CHARS` (4,096, שווה ל-`docs_handlers.MAX_SECTION_CHARS` ומקובע בטסט — כותרת ארוכה יותר אינה נגישה בשמה ממילא). הקיצוב ב-`_finalize`, על `sec.title`, **לפני** בניית ה-breadcrumbs — ולכן הוא מכסה בבעלים אחד את הכותרת, כל עותק שלה ב-breadcrumb, `section`, `neighbors` ו-`subsections`. - קיצוץ בטוח (`_truncate_title`): על גבול תו (Python חותך על נקודת-קוד), ומסיר שארית תלויה — סימני צירוף, ZWJ, בוררי-וריאציה — כדי שלא יישאר אשכול שבור. - `title_truncated` בתשובה (מפה וסעיף), מותנה — נדלק רק כשנחתך. הכותרת המוצגת היא גם הצורה השמורה והמותאמת, ולכן הדבקתה חזרה מוצאת את הסעיף (round-trip). כשכמה כותרות חולקות קידומת ארוכה מהתקרה — `ambiguous_section` עם מועמדים. אימות: - אפס-דיף על הקורפוס: 0 מתוך 1,410 כותרות RST (208 קבצים) ו-359 כותרות md חוצות את התקרה — הכותרת הארוכה שנמדדה 71 ב-RST, 68 ב-md, 1,219 בקבצים שמורים. הפלט זהה; הקיצוב מופעל רק על כותרת פתולוגית שאינה בקורפוס. - מוטציה: בלי הקיצוב, כותרת מעל התקרה חוזרת מלאה ובלי דגל (ההתנהגות הישנה). - טסטים חדשים: קיצוב+דגל+round-trip, עותקי breadcrumb נחתכים, קידומת משותפת → מועמדים, וקיצוץ בטוח על עברית עם ניקוד / CJK / אמוג'י ZWJ. 481 טסטים עוברים. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * fix(mcp): דגל title_truncated נלווה לכל כותרת שנפלטת — מועמדים, תת-סעיפים ושכנים ממצא Greptile (P2) על שכבה 2: כשכותרת נחתכת ל-``MAX_TITLE_CHARS``, הדגל ``title_truncated`` הופיע רק במפה ובתשובת הסעיף, אך לא במועמדי ``ambiguous_section``, בתת-הסעיפים ובשכנים. שם השם החתוך הוצג בלי סימן שהוא חלקי, ושתי כותרות שונות שחולקות קידומת מעל התקרה נראו זהות בלי דרך להבחין ביניהן. - ``_title_flag(sec)``: בעלים אחד לדגל בצד ה-mcp_server. ``_section_ref`` (תת-סעיפים ושכנים), מועמדי ``ambiguous_section`` ותשובת הסעיף עוברים דרכו. מותנה — לכותרת אמיתית אינו נדלק, ולכן אפס-דיף נשמר. ``build_toc`` ב-services נושא עותק משלו (השכבה אינה מייבאת מ-mcp_server), אותו שם מפתח ואותו תנאי. - טסטים שנופלים על הקוד הישן (דפוס בדיקות #5): דגל במועמד, בתת-סעיף ובשכן. ונועל-חוזה ל-round-trip: הדבקת רכיב breadcrumb של כותרת מקוצצת ל-``section`` מתנהגת בדיוק כמו הדבקת הכותרת עצמה — מתאימה, או ``ambiguous_section`` כשהשם משותף. הרכיב הוא אותה מחרוזת כמו הכותרת, כי הקיצוב במקור לפני בניית ה-breadcrumb. - תיעוד: ב-mcp-server.rst בלוק על תקרת אורך-הכותרת, ההתנהגות ambiguous החדשה, ושני משפטי ה-breadcrumb (הרכיבים מקוצצים; הדגל יושב על רשומת הכותרת במפה, ושם בודקים אם רכיב חתוך); שורת ``MAX_TITLE_CHARS`` בטבלת הקבועים (מפנה לשם, לא לערך); ותיאור הפרמטר ``section``. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * fix(mcp): תקרת עבודה ל-suggest — עלות ה-difflib נכנסת לתקציב המעבד (WARN-001) ממצא WARN-001 מסקירת Han: עלות צעד ה-``suggest`` (difflib) במסלול ה-not-found לא נכללה בתקציב המעבד שממנו נגזר ה-rate limit. אומת בהרצה (לא הונח, כמו ה-~1.7s שהסוכן העריך ולא הריץ): שאילתה שאינה נמצאת על קובץ 512KB של כותרות עוינות (אלפבית ~100 ששורד את ה-autojunk של difflib, כותרות באורך התקרה) עלתה ~1.5 שניות-מעבד, ובמשלב עם ציטוט עמוק ~2.4 — פי 2–4 מהפרסור בלבד. - ``doc_sections.SUGGEST_WORK_BUDGET`` + ``_within_work_budget``: לפני ``get_close_matches`` חוסמים את המועמדים לפי ``len(cand)·Q`` — ``ratio()`` רץ רק על מועמד תואם-אורך (``real_quick_ratio ≥ 0.5`` ⇔ ``len ∈ [Q/3, 3Q]``, ממקור difflib 3.11), ומסמנים ``suggestions_truncated``. אפס-דיף על ההצעות: על 242 קובצי הקורפוס המטען המקסימלי הוא 99,674 — פי ~1,500 מרווח, והדגל לא נדלק על אף קובץ אמיתי. - ``limits.py``: ``DEFAULT_RATE_LIMIT_PER_MINUTE`` 45→40. הגרוע-לקריאה אינו הפרסור לבדו — מסלול ה-not-found מוסיף suggest חסום ובניית מפה, ונמדד פי ``_NOT_FOUND_CPU_FACTOR`` (1.11) מהפרסור (0.88 מול 0.79 שניות-מעבד, max מ-7, מבודד). ``40 × 0.73 = 29.2 ≤ 30``. - מדידה משולבת (חיפוש יריב, לא הנחה): המיקס הגרוע גבוה משני הקצוות הנקיים — parse ו-suggest מתחברים, כי כותרות עולות ~4 טוקנים ומתחרות בפרסור רק על שורות/בתים. - ``docs/mcp-server.rst``: החשבון המעודכן, ותיקון המשפט — מכסת 0.5 CPU היא של **כל השירות** (מוחזקת ע"י ``numInstances: 1``), לא ערובה מול כמה זהויות במקביל. - טסטים: חסימה על קובץ עוין (נופל על הקוד הישן — ``get_close_matches`` מקבל את כל המפתחות), guard שקובץ אמיתי לא נחתך, ועדכון החשבון בתיעוד ובתצורה לפי הקבועים. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * refactor(mcp): _SUGGEST_CUTOFF בעלים אחד — הסף וטווח תואמי-האורך (R6) הסף 0.5 של difflib הופיע פעמיים: כארגומנט ל-get_close_matches, וכבסיס לגזירת _LEN_COMPATIBLE_LO/HI. עותק שני של כלל שהיה נסחף בשינוי הסף (R6). מאוחד תחת _SUGGEST_CUTOFF, וטווח תואמי-האורך נגזר מהנוסחה c/(2-c)..(2-c)/c במקום מספרים מוקלדים. בלי שינוי התנהגות (0.5 → [Q/3, 3Q] כמו קודם), 155 טסטים עוברים. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn * test(mcp): טסט מכסת המעבד מחיל את גורם ה-not-found (בלי עיגול) ממצא מסקירת קוד: `test_one_identity_cannot_spend_more_cpu_than_the_service_has` חישב `rate × WORST_CASE_CPU_SECONDS` — עלות הפרסור בלבד — בעוד שהחסם שהקוד עצמו מתעד (docstring של `DEFAULT_RATE_LIMIT_PER_MINUTE`) הוא `rate × WORST_CASE_CPU_SECONDS × _NOT_FOUND_CPU_FACTOR`, כי הגרוע-לקריאה הוא מסלול ה-not-found (פרסור + מפה + suggest חסום + התאמת-תקציב). כלומר שומר שאינו יכול ליפול: אם עלות ה-not-found תגדל, החשבון האמיתי יחרוג מהמכסה והטסט ימשיך לעבור. התיקון מחיל את `_NOT_FOUND_CPU_FACTOR` גם על טסט הבטיחות, **בלי עיגול** — זו הבדיקה של השמורה האמיתית, לא החשבון המעוגל לקריאה שהתיעוד מציג (שם `round(...,2)` נשאר, כדי להתאים למחרוזת בעמוד). אומת: על קבועי היום עובר (40 × 0.66 × 1.11 = 29.304 ≤ 30, מרווח 0.696); עם גורם מנופח (1.2) שחורג מהמכסה — הטסט המתוקן נופל, בעוד הנוסחה הישנה (פרסור-בלבד, 26.4 ≤ 30) הייתה נשארת ירוקה. גם תוקן ניסוח שהתיישן ("ירדה מ-60 ל-45"; המגבלה היום 40 אחרי WARN-001) — עכשיו בלי לנעוץ את ערך היעד. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uy69S9WWwe2uJuR15Y8rEn --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upgrade
safetyand remove duplicate dependencies to resolve CI/CD issues and cleanrequirements.txt.The
safety==2.3.5package conflicted withblackdue to incompatiblepackagingrequirements. Upgradingsafetyresolves this, allowingpipto find a compatible set of dependencies.