Repository navigation
fix: ארבעה תיקונים מדוח האימות של #3394 — -B בתת-תהליך, כפילות ב-configuration, מספר תלוי-סביבה, ו-.pyc בגיט - #3395
Conversation
אחד-עשר קבצי ``__pycache__/*.cpython-313.pyc`` נכנסו לגיט ב-8098b67e (#3239) למרות ש-``.gitignore`` שורה 2 חוסם את התיקייה. ``git rm --cached`` מוציא אותם מהאינדקס ומשאיר אותם על הדיסק, וה-``.gitignore`` הקיים דואג שהם לא יחזרו כ-untracked. נבדק שאין תלות: כל עשרת האזכורים של ``__pycache__`` בריפו הם החרגות בלבד — ``.ruff.toml``, ``bandit.yaml``, ``.yamllint.yaml``, ``services/code_indexer.py``, ``scripts/audit_config_definitions.py``, ``scripts/find_duplicates.py``, ``github_menu_handler.py``, ``repo_analyzer.py`` ושני טסטים. אף אחד מהם אינו קורא מהתיקייה. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P1AYmWhLGtpsr7sSdRZVD3
``test_doc_sections_pulls_no_heavy_modules`` הריץ ``subprocess.run([sys.executable, "-c", code], cwd=_ROOT)`` — כלומר תת-תהליך שכותב ``__pycache__`` לתוך ``services/``, וזה טסט שכותב לעץ המקור. התקדים הנכון כבר קיים בריפו, ב-``tests/test_rst_parser.py``, ושם ההערה מנמקת את שני הדגלים במפורש. השורש היה שהייבוא נשען על ה-``cwd``: הוא עבד רק מפני שהתהליך רץ בשורש הריפו. לכן ``-B`` לבדו לא הספיק — ``sys.path.insert`` עם נתיב מוחלט מנתק את המדידה מעץ העבודה, ורק אז ``cwd=tmp_path`` אפשרי. נמדד, ולא הונח. בקרה מבודדת בלי pytest, על ``services/__pycache__`` ריק: הצורה הישנה כותבת שלושה קבצים (``__init__``, ``backoff_state``, ``doc_sections``), הצורה החדשה כותבת אפס, ותיקיית ה-tmp נשארת ריקה גם היא. ומה שלא השתנה, כדי שלא ייקרא אחרת: בריצת pytest רגילה הדלתא הייתה אפס גם קודם, כי האיסוף עצמו מייבא את אותם מודולים וכותב את אותם קבצים. כלומר זו הפרה של מוסכמה ולא באג נצפה, והערך שלה הוא ביום שבו עץ המקור יהיה לקריאה בלבד — כלל 8 ב-CLAUDE.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P1AYmWhLGtpsr7sSdRZVD3
שלושת הסעיפים "Databases and Cache (Pooling/Timeouts)", "HTTP Clients" ו-"שימוש ב-http_sync" הופיעו פעמיים בקובץ. שניהם נכנסו ב**קומיט אחד**, 171d8f6 (#1015, 23.10.2025), שהכניס את אותו בלוק פעם אחרי "Environment variables" ופעם אחרי "Security" — לפניו הכותרות לא היו בקובץ כלל. המחיר נמדד: מתוך 45 תשובות ``ambiguous_section`` שהכלי ``docs_get_section`` מחזיר על כל 208 קובצי ה-RST, 25 הגיעו מהקובץ הזה לבדו. הכפילות גם חצתה את נרמול הכותרות — ``normalize_title`` מאחד מקפים, ולכן ``שימוש ב‑http_sync`` עם U+2011 ו-``שימוש ב-http_sync`` עם מקף ASCII התנגשו זה בזה. **שני העותקים הושוו שורה-שורה לפני המחיקה, והם אינם זהים** — אבל אף הבדל אינו הבדל בתוכן: פיסוק ב-``REDIS_URL``, ניסוח ב-``.. note::`` ("לא להשתמש" מול "אל תשתמשו", גרשיים עבריים מול מרכאה), והמקף בכותרת. נשאר העותק הראשון **מילה במילה**, כי הוא במקום הנכון מבנית — עם שאר סעיפי הקונפיגורציה, והוא היחיד שמלווה ב-"שימוש ב-http_async" שאין לו כפילות. שבע-עשרה מתוך תשע-עשרה השורות שנמחקו קיימות בו כלשונן, והשתיים הנותרות הן שתי וריאציות הניסוח. עוגנים — נבדק ולא הונח: ``autosectionlabel`` אינו מופעל ב-``conf.py``, ולכן לכותרות אין לייבלים אוטומטיים. העוגן המפורש היחיד בקובץ הוא ``.. _config-error-signatures:``, שיושב הרבה מתחת לבלוק שנמחק, ואליו מפנה ``docs/observability/log-aggregator.rst``. שאר ההפניות הנכנסות הן ``:doc:`` לעמוד כולו. אימות: הספירה הורצה מחדש על כל 208 הקבצים — הקובץ הזה ירד מ-25 ל-**0**, והסך הכול מ-45 ל-20 (הנותרים הם ``development/tools.rst`` ו- ``observability/query-performance-profiler.rst``, שלא נגענו בהם). ובנייה מלאה של Sphinx עם ``-W --keep-going`` עברה באפס אזהרות. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P1AYmWhLGtpsr7sSdRZVD3
ה-docstring של ``outline_scanners/__init__.py`` קבע ש-``from services import rst_parser`` מושך "**ארבעה** מודולים מהריפו", ומנה אותם בשמם — וסגר ב"מהריפו רק מודולים שרשומים כאן בשמם". שתי הטענות אינן נכונות בסביבה שבה ``structlog`` מותקן. נמדד בשתי סביבות באותו עץ: - **בלי** ``structlog``: 73 מודולים, ומהריפו ארבעה — שרשרת ה-``services``. - **עם** ``structlog``: 330 מודולים, ומהריפו שבעה — אותם ארבעה ועוד ``observability``, ``monitoring`` ו-``monitoring.error_signatures``. השרשרת: ``services/__init__.py`` מייבא ``state`` מ-``backoff_state``, ששורה 16 בו **מנסה** לייבא ``observability``; ו-``observability.py`` מייבא ``structlog`` בקשיחות בשורה 22 ומנסה ``monitoring.error_signatures`` בשורה 28. כלומר הזנב הזה תלוי במה שמותקן בסביבה, לא בשורת קוד — וזה בדיוק המבחן שב-``docs/doc-authoring.rst``: מספר שיכול להשתנות בלי שאף שורת קוד תשתנה אינו ערך שנאכף. הניסוח החדש מתאר מה נטען ולמה — חלק קבוע וזנב מותנה — ואינו קובע מספר ואינו סוגר רשימה. האינווריאנט הנושא נשאר, והוא זה שנמדד בשתי הסביבות: **אפס מודולים כבדים**. ותוקן גם דיוק שני: הטסט שאוכף את האינווריאנט מודד את ``services.doc_sections``, לא את ``rst_parser``, וזה נכתב עכשיו כפער מוצהר במקום להשתמע ככיסוי. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P1AYmWhLGtpsr7sSdRZVD3
There was a problem hiding this comment.
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 4 days and 18 hours by commenting @sourcery-ai review. Upgrade to get a review now.
|
ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
🧯 Dangerous deletes guard reportPolicy: see .cursorrules — dangerous deletions are blocked unless wrapped safely. Summary:
Flagged findings (file:line:snippet): Excluded matches (by path pattern) |
Reviewer's Guideהתיקון מטפל בארבעה ממצאי אימות ללא שינוי בקוד ייצור: מבודד תת-תהליך בדיקה ומונע כתיבה לעץ המקור, מסיר כפילות מתיעוד הקונפיגורציה, מחליף ספירות תלויות-סביבה בתיעוד האינווריאנט של הייבוא, ומנקה קובצי Sequence diagram for isolated documentation import testsequenceDiagram
participant Test as test_doc_sections
participant Subprocess as Python subprocess
participant Temp as tmp_path
participant Source as services
Test->>Subprocess: subprocess.run([sys.executable, -B, -c, code], cwd=tmp_path)
Subprocess->>Subprocess: sys.path.insert(0, absolute_source_path)
Subprocess->>Source: import services.doc_sections
Source-->>Subprocess: import completes without writing __pycache__
Subprocess-->>Test: return code 0
Flow diagram for environment-independent import invariantflowchart TD
Start[Import services] --> Chain[Load services backoff_state rst_parser and doc_sections]
Chain --> Optional{structlog installed?}
Optional -->|Yes| Tail[Try observability and monitoring.error_signatures]
Optional -->|No| NoTail[Optional tail is not loaded]
Tail --> Invariant[Zero heavy modules]
NoTail --> Invariant
Flow diagram for documentation and Git hygiene fixesflowchart LR
Config[Duplicate configuration.rst sections] -->|remove duplicate block| Single[Single canonical configuration block]
Pyc[Tracked __pycache__ and .pyc files] -->|git rm --cached| Ignored[Ignored files remain on disk and untracked state stays clean]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (11)
📒 Files selected for processing (3)
💤 Files with no reviewable changes (1)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughהשינוי מסיר תיעוד כפול, מעדכן את תיעוד חוזה הייבוא, ומבודד את בדיקת התת-תהליך מעץ המקור ומקבצי Changesתיעוד ותיקוף
Priority: ⬇️ Low Estimated code review effort: 1 (Trivial) | ~5 minutes Change: Other Merge Risk: ⚪ Minimal · up to The documentation and test-isolation updates do not change production behavior and are ready to merge. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. תיעוד כפול יורד מן הדף Comment |
⏱️ Performance report(No performance test durations collected. Mark tests with |
📖 Documentation PreviewThe documentation has been built successfully!
To view locally:
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
.gitignore מכיל __pycache__/ מזמן, והוא עובד בדיוק במקרה אחד: קובץ untracked ש-git add מדלג עליו. הוא אינו חל על קובץ שכבר ב-index, ויש לפחות שני מסלולים שמכניסים אותו בכל זאת: 1. git checkout <ref> -- . מעתיק מה-ref אל העץ ואל ה-index בלי להתייעץ עם .gitignore — הוא חל על הוספת קבצים חדשים, לא על שחזור מ-ref. אם ה-ref מיושן, כל מה שהיה בו חוזר. זה מה שקרה בסשן של #3401: ניסוי שהשווה אזהרות ruff בין main לענף הריץ git checkout origin/main -- . כשה-origin/main המקומי היה snapshot מלפני #3395, והוא החזיר את 11 קבצי ה-.pyc שאותו PR הסיר. ה-git add -A שאחריו קומיט אותם בשקט. 2. git add -f, או קובץ שנכנס לפני שהדפוס נוסף ל-.gitignore. בשני המסלולים אין שגיאה ואין אזהרה, ושום בדיקה קיימת לא התריעה. זו גם הפעם השנייה: #3395 הסיר את אותם קבצים, והם חזרו פחות משבוע אחרי — כלומר מחלקה ולא מופע. scripts/check_no_committed_pyc.py הוא מקור האמת היחיד, ושלושת הצרכנים קוראים לו: צעד חוסם ב-code-quality (בניגוד לרוב הצעדים שם שמסתיימים ב-|| true), hook מקומי ב-pre-commit, וטסט. הודעת הכשל מסבירה למה .gitignore לא עזר ונותנת את git rm -r --cached, כי מי שרואה ".pyc בגיט" ומוצא __pycache__/ ב-.gitignore מסיק שהבדיקה שבורה. אומת על שני הקצוות ובריפו האמיתי: ריפו גיט תחת tmp_path עם .pyc אחד מפיל את הבדיקה, main של הפרויקט (0 קבצים, אחרי fetch) עובר, וה-hook נבדק בפועל מול .pyc ב-staging של הריפו הזה. שבע מוטציות הורצו וכל אחת הפילה את הטסט ששומר עליה — ביניהן שתיים שחשפו כיסוי מדומה: הכלל על __pycache__ מעולם לא נבדק בפועל כי כל הקבצים בבדיקות נשאו ממילא סיומת .pyc, וכשל של git עצמו דווח כ"נקי". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DwGtJt7SXsCvxXHVNBszFW
…קיים (#3401) * feat(mcp): codekeeper_update_file_description — רענון תיאור של קובץ קיים עד היום התיאור נקבע רק ב-codekeeper_save_file, שמסרב לקובץ שכבר קיים, ול-codekeeper_edit_file ול-codekeeper_append_file אין פרמטר description — הם מעתיקים את הקיים לגרסה החדשה. כלומר תיאור שהתיישן ביחס לתוכן היה ניתן לתיקון רק מהדפדפן. הכלי מקבל file_name ו-description בלבד, ומחזיר את התיאור הקודם, את החדש ואת מספר הגרסה שלא השתנה. הוא אינו יוצר גרסה, ולכן התיאור הקודם אינו נשמר בהיסטוריה; רק הגרסה האחרונה מתעדכנת. שתי ההצהרות כתובות בתיאור הכלי עצמו. תיאור ארוך מהמותר נדחה ואינו נחתך, כי סוכן אינו רואה את התוצאה. מסלול כתיבה אחד ולא שניים: הראוט quick-update בוובאפ החזיק update_one משלו מול code_snippets, ולא עבר דרך שכבת ה-repository. הלוגיקה חולצה ל- database.repository.update_file_metadata_in ושני הערוצים קוראים לה. ההתנהגות כלפי הדפדפן לא זזה — אותו 404, אותה חתימת updated_at (רק כשהתיאור נכלל), ואותם שדות בתשובה. בדרך נסגר חלון TOCTOU: הבעלות נבדקה ב-find_one נפרד לפני ה-update_one, והיא עברה לתוך הפילטר של find_one_and_update עצמו. fix: invalidate_file_related לא ניקתה את search_code, שה-cache שלו 300 שניות — כלומר החיפוש המשיך להגיש את התיאור הישן עד חמש דקות אחרי העדכון. נוספו search_code, regular_files ו-files_by_repo: שלוש הרשימות שמחזירות description של הגרסה האחרונה. הכלי מסווג ככותב דרך readOnlyHint בלבד, בלי רשימת שמות נפרדת, ולכן הוא עובר בתור הכתיבה הקיים. אינו שולח התראת Web Push — לא נשמר קובץ. אימות: 16 טסטים חדשים, כולם דרך mcp.call_tool ומול מונגו אמיתי; 20 מוטציות הורצו וכל אחת הפילה את הטסט שאמור לשמור עליה. נמדד ש-find_one_and_update בלי sort מחזיר את הגרסה הנכונה רק בזכות idx_snippets_latest_version, ולכן המיון נבדק על צורת השאילתה ולא על התוצאה. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DwGtJt7SXsCvxXHVNBszFW * perf(cache): סריקה אחת לכל דפוסי הניקוי, במקום אחת לכל דפוס invalidate_file_related ו-invalidate_user_cache קראו ל-delete_pattern בלולאה, וכל קריאה הריצה SCAN משלה. שלוש תוצאות, ושתיים נמדדו מול Redis 7 אמיתי על keyspace של 100,000 מפתחות: 1. פי N round-trips. עשרה דפוסים = 2,000 פקודות SCAN מול 200 בסריקה אחת. על localhost זה נבלע — שם הצורה הישנה אפילו מהירה ב-0.9x, כי MATCH מסנן בשרת וחוסך פרסור — אבל ה-RTT מכריע: ב-1ms זה 2.0 שניות מול 0.2, וב-5ms זה 10 מול 1. 2. תקציב הזמן הוקצה פר-דפוס. CACHE_DELETE_PATTERN_BUDGET_SECONDS הוא 5 שניות, וכל קריאה קיבלה תקציב משלה — עשרה דפוסים יכלו לחסום את התהליך עד 50 שניות. זו התנהגות, לא אופטימיזציה. 3. MATCH של Redis מסנן אחרי השליפה מהאוסף, ולכן דפוס אינו מקצר את הסריקה — עשרה דפוסים היו עשרה מעברים מלאים ולא עשרה חיפושים ממוקדים. delete_patterns היא המימוש, ו-delete_pattern נשארת כמקרה פרטי שלו (יש לה קוראים רבים בריפו). ההתאמה עוברת לרג'קס אחד מאוחד מאותו fnmatch.translate שהקוד ממילא הריץ על כל מפתח, כלומר הסמנטיקה זהה. רשימה ריקה יוצאת מוקדם: רג'קס מאפס דפוסים הוא מחרוזת ריקה, שמתאימה לכל מפתח. נמדד על הקוד עצמו: invalidate_file_related יורדת מ-2,000 ל-200 פקודות SCAN, וכל שבעת המפתחות הרלוונטיים עדיין נמחקים. docs: עדכון README והקדמת mcp-server.rst בעקבות ריוויו — codekeeper_save_file מתואר כיצירת קובץ חדש בלבד (הוא חוסם שם תפוס מאז #3396), הבטחת "עדכון תמיד יוצר גרסה חדשה" מסויגת לעדכוני תוכן, והמונה "תשעת כלי הכתיבה" הוסר במקום לעדכנו: ספירת מופעים מתיישנת בלי שאף בדיקה תתריע, וזה בדיוק מה שקרה. test: הפיקסצ'ר עובר ל-monkeypatch.setenv, כדי ש-REDIS_URL לא ידלוף לבדיקות הבאות באותה ריצה. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DwGtJt7SXsCvxXHVNBszFW * docs(mcp): קיצור תיאור codekeeper_update_file_description ל-451 תווים התיאור עמד על 1,001 תווים ונשא שישה פרטים שאינם משנים לסוכן בזמן שהוא בוחר כלי: מה הוובאפ מציע (הוא לא שם), ש-codekeeper_list_versions לא יראה את השינוי (נגזר מ"לא נוצרת גרסה"), מה תחזיר קריאה של גרסה ישנה (פירוט יתר של המשפט על הגרסה האחרונה), שתגיות אינן נוגעות (שם הכלי אומר description), שאין התראת שמירה (לא קיים בעולם של הסוכן), ושתיאור ארוך נדחה עם המגבלה (הודעת השגיאה אומרת זאת כשהיא מגיעה). נשאר מה שמכריע בבחירה: מה הכלי עושה, מתי לבחור בו על פני האחרים, ומה בלתי הפיך. הפירוט המלא ממשיך לחיות ב-docs/mcp-server.rst. test: הבדיקה על שלוש ההצהרות הייתה כבולה לניסוח — היא חיפשה "NO NEW VERSION IS CREATED" באותיות גדולות, ולכן הקיצור הפיל אותה אף ששלוש העובדות שרדו בו. ההשוואה עברה לחסרת-רישיות על הליבה של כל משפט. נוספה תקרה מקומית של 600 תווים לכלי הזה: התקרה הכללית (1,400) מונעת חיתוך אצל הלקוח ואינה מונעת תיאור שנכנס במלואו ועדיין מבזבז תשומת לב. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DwGtJt7SXsCvxXHVNBszFW * ci: חסימת קבצי bytecode במעקב גיט, ב-CI וב-pre-commit .gitignore מכיל __pycache__/ מזמן, והוא עובד בדיוק במקרה אחד: קובץ untracked ש-git add מדלג עליו. הוא אינו חל על קובץ שכבר ב-index, ויש לפחות שני מסלולים שמכניסים אותו בכל זאת: 1. git checkout <ref> -- . מעתיק מה-ref אל העץ ואל ה-index בלי להתייעץ עם .gitignore — הוא חל על הוספת קבצים חדשים, לא על שחזור מ-ref. אם ה-ref מיושן, כל מה שהיה בו חוזר. זה מה שקרה בסשן של #3401: ניסוי שהשווה אזהרות ruff בין main לענף הריץ git checkout origin/main -- . כשה-origin/main המקומי היה snapshot מלפני #3395, והוא החזיר את 11 קבצי ה-.pyc שאותו PR הסיר. ה-git add -A שאחריו קומיט אותם בשקט. 2. git add -f, או קובץ שנכנס לפני שהדפוס נוסף ל-.gitignore. בשני המסלולים אין שגיאה ואין אזהרה, ושום בדיקה קיימת לא התריעה. זו גם הפעם השנייה: #3395 הסיר את אותם קבצים, והם חזרו פחות משבוע אחרי — כלומר מחלקה ולא מופע. scripts/check_no_committed_pyc.py הוא מקור האמת היחיד, ושלושת הצרכנים קוראים לו: צעד חוסם ב-code-quality (בניגוד לרוב הצעדים שם שמסתיימים ב-|| true), hook מקומי ב-pre-commit, וטסט. הודעת הכשל מסבירה למה .gitignore לא עזר ונותנת את git rm -r --cached, כי מי שרואה ".pyc בגיט" ומוצא __pycache__/ ב-.gitignore מסיק שהבדיקה שבורה. אומת על שני הקצוות ובריפו האמיתי: ריפו גיט תחת tmp_path עם .pyc אחד מפיל את הבדיקה, main של הפרויקט (0 קבצים, אחרי fetch) עובר, וה-hook נבדק בפועל מול .pyc ב-staging של הריפו הזה. שבע מוטציות הורצו וכל אחת הפילה את הטסט ששומר עליה — ביניהן שתיים שחשפו כיסוי מדומה: הכלל על __pycache__ מעולם לא נבדק בפועל כי כל הקבצים בבדיקות נשאו ממילא סיומת .pyc, וכשל של git עצמו דווח כ"נקי". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DwGtJt7SXsCvxXHVNBszFW * fix(cache): סריקה לכל דפוס עם MATCH בשרת, batch שנשטף לפני כל יציאה, ושני סירובים שלושה ממצאי code review, והשני הוא הכבד. (1) delete_patterns קיבל Sequence[str], ומחרוזת עומדת בזה: "file_content:*" התפרק לדפוס לכל תו, והתו "*" לבדו מתאים לכל מפתח — טעות של תו אחד בין delete_pattern ל-delete_patterns הייתה מוחקת את המסד כולו, כולל מוני flask-limiter. עכשיו: str/bytes זורק TypeError, ודפוס בלי אף תו מילולי ("*", "**", "?*") זורק ValueError עם לוג שמפנה ל-clear_all. שניהם לפני ה-try, אחרת ה-except הכללי היה בולע אותם ומחזיר 0 — "לא היה מה למחוק" על שגיאת קורא. הקריטריון הוא "אין תו מילולי בכלל" ולא "אין תו לפני הכוכבית הראשונה": *:user:{uid}:* ב-invalidate_user_cache מתחיל בכוכבית ואינו מתאים לכל מפתח, והצורה השנייה הייתה פוסלת אותו. הקורא היחיד שמעביר "*" — remediation_manager._clear_internal_cache — מגיע לשם רק ב-else של hasattr(_cache, "clear_all"), ו-_cache הוא CacheManager שיש לו clear_all; הענף מת בפרודקשן ולא נגע בו. (2) הסריקה האחת בלי MATCH הייתה רגרסיה. נמדד מול Redis 7 עם 200,000 מפתחות (limiter, סשנים וקאש באותו מסד): delete_pattern עם דפוס יחיד משך 200,007 מפתחות לפייתון כדי למחוק אחד — ויש 73 קוראים כאלה, וב-MCP הם רצים על ה-worker היחיד של _WRITE_POOL. MATCH אינו מקצר את המעבר של Redis, אבל הוא קובע מה חוצה את הרשת. חזרה לסריקה לכל דפוס עם match בשרת; מה שנשאר מאוחד הוא התקציב (אחד לקריאה ולא אחד לדפוס), ה-batch והספירה. אחרי: 1 מפתח במקום 200,007 לדפוס יחיד, 7 במקום 200,007 לעשרה דפוסים, 175ms במקום 495ms. ובנוסף: ה-batch החלקי נשטף לפני כל יציאה. הצורה הקודמת (גם זו שמלפני ה-PR) שטפה רק כש-time.time() <= deadline, כלומר מיצוי תקציב זרק עד 199 מפתחות שכבר הותאמו בלי DEL — בדיוק הקאש הישן שהניקוי בא להסיר. עכשיו finally, ומיצוי תקציב נרשם ב-WARNING עם "חלקי" ומספר הדפוסים שהושלמו. (3) הטסט על מיקום הייבוא פתח את webapp/app.py בנתיב יחסי ל-cwd, וטען על שורה עם שמונה רווחים — כלומר שינוי הזחה בראוט היה מפיל אותו. עכשיו Path(__file__) ו-ast: העץ יודע באיזו פונקציה כל ייבוא יושב. אימות: 15 טסטים בקובץ הקאש (8 חדשים), 6/6 מוטציות נתפסו — כולל הקריטריון השגוי על הכוכבית, ה-batch שנזרק, והאזהרה שחסרה. טסט ה-ast: 2/2 מוטציות נתפסו, וריפקטור-בקרה שמשנה הזחה עבר כפי שצריך. mypy על cache_manager ירד מ-2 ל-1: ה-type: ignore הישן הוחלף באנוטציה. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DwGtJt7SXsCvxXHVNBszFW * perf(cache): SCAN count 500 → 2000, עם הנימוק והצפי לייצור על הקבוע תקציב הניקוי (5 שניות) נמדד בזמן, ובייצור הזמן הוא roundtrips × RTT. roundtrips לתבנית = DBSIZE / count, ו-count היה 500: ב-200K מפתחות זה 400 לתבנית ו-4,000 ל-invalidate_file_related — שב-RTT של מילי-שנייה אחת הם 5.0 שניות, כלומר מיצוי התקציב בכל קריאה וניקוי חלקי קבוע. נמדד מול Redis 7 עם 200,000 מפתחות, INFO commandstats, לקוח redis-py אמיתי (ואומת מול המקור ש-scan_iter מעביר count לשרת): - ה-CPU של Redis לקריאה של עשר תבניות הוא ~900ms בכל count — זה מחיר עשרה מעברים מלאים על ה-keyspace, ו-count רק קובע לכמה פקודות הוא מתחלק: 0.24ms לפקודה ב-500, 0.92ms ב-2,000, 2.15ms ב-5,000. - 2,000 הוא הערך שבו פקודה בודדת עדיין מתחת למילי-שנייה בשרת (Redis הוא חוט יחיד שמשרת גם את flask-limiter), ומספר ה-roundtrips קטן פי ארבעה: 1,000 במקום 4,000. ב-RTT של 1ms על 200K: ~1.9s במקום ~5.0s. - ב-RTT של 5ms, או מעל ~500K מפתחות, גם 2,000 ממצה. שם התשובה אינה count גבוה יותר אלא ניקוי בלי SCAN — #3402. ההפרש 1731ms מול 1426ms שנראה במדידה הראשונה של ה-PR: רעש. בחמש חזרות משולבות ה"אחרי" יצא 1503ms median מול 1517ms של המקורי. התקציב עצמו לא שונה. שני המספרים שהצפי תלוי בהם — RTT ו-DBSIZE בייצור — אינם בריפו: אין מדידת ping ל-Redis (זו של /api/health היא ל-Mongo), ו-render.yaml אינו מגדיר Redis. הטבלה המלאה בגוף ה-PR. test: הדמה מתעדת גם את count, וטסט חדש טוען שהוא נשלח ושהוא לא נסוג מתחת לאלף. שתי מוטציות (10, None) נתפסו. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DwGtJt7SXsCvxXHVNBszFW * fix(ci): קריאת הנתיבים מגיט ב-NUL (-z), כי גיט מצטט שמות עם שורה חדשה או עברית בלי -z גיט מצטט נתיב שמכיל תו מיוחד בסגנון C, עם המירכאות כחלק מהפלט: "line\nbreak.pyc" לשורה חדשה, ו-"\327\251\327\234\327\225\327\235.pyc" לכל שם שאינו ASCII — כלומר לכל שם בעברית, כש-core.quotePath בברירת המחדל. splitlines() החזיר את המחרוזת המצוטטת, endswith(".pyc") נכשל על המירכאה הסוגרת, והקובץ עבר. אומת בריצת בקרה על הגרסה הקודמת: שני הקבצים האלה עברו אותה ב-exit 0, גם ב-index וגם ב---ref HEAD. המקרה העברי הוא הריאלי בריפו הזה. git ls-files -z ו-git ls-tree -r -z --name-only מוציאים NUL בין נתיבים בלי שום ציטוט, והפרסור הוא split(b"\0"). הפענוח ב-surrogateescape כדי ששם שאינו UTF-8 תקין יגיע ל-offending_paths במקום להפיל את הבדיקה. ההפרדה בין tracked_files ל-offending_paths נשמרה. נתיב שאינו printable מוצג ב-repr כדי ששורה חדשה בשם לא תשבור את רשימת הכשלים לשתי שורות. הוסרו ארבעה תווי LRM (U+200E) ממחרוזות ההודעה — אותה מחלקה כמו RLM (hebrew-source H1), ruff PLE2502 תופס אותם. test: פיקסצ'ר עם line\nbreak.pyc ו-שלום.pyc, מקומטים ב-git add -f; הטסט מפורמט על [] ועל --ref HEAD, כי אלה שתי פקודות git שונות וכל אחת צריכה את ה--z שלה. מוטציה: הגרסה הישנה של הסקריפט מול הטסט — נופל. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DwGtJt7SXsCvxXHVNBszFW --------- Co-authored-by: Claude <noreply@anthropic.com>
✨ תיאור קצר
ארבעה תיקונים נפרדים שעלו מדוח אימות בלתי-תלוי של PR #3394 (squash
dea1b9e). אין שינוי התנהגות בקוד ייצור: שניים מהם תיעוד, אחד מוסכמת טסטים, ואחד היגיינת גיט. כל אחד נמדד לפני ואחרי ולא הונח.📦 שינויים עיקריים
פירוט נקודות:
tests/test_doc_sections.py— התת-תהליך יוצא מעץ המקור.test_doc_sections_pulls_no_heavy_modulesהריץsubprocess.run([sys.executable, "-c", code], cwd=_ROOT), כלומר תת-תהליך שכותב__pycache__לתוךservices/. השורש היה שהייבוא נשען על ה-cwd— הוא עבד רק מפני שהתהליך רץ בשורש הריפו, ולכן-Bלבדו לא הספיק:sys.path.insertעם נתיב מוחלט מנתק את המדידה מעץ העבודה, ורק אזcwd=tmp_pathאפשרי. זה בדיוק החיווט שכבר קיים ב-tests/test_rst_parser.py, וההערה שם מנמקת אותו.docs/configuration.rst— הסרת בלוק כפול. שלושת הסעיפים "Databases and Cache (Pooling/Timeouts)", "HTTP Clients" ו-"שימוש ב-http_sync" הופיעו פעמיים.mcp_server/outline_scanners/__init__.py— מספר תלוי-סביבה יוצא מה-docstring. הוא קבע "ארבעה מודולים מהריפו" ומנה אותם בשמם; בסביבה עםstructlogהם שבעה.__pycache__/*.pyc— יוצאים מניהול גיט. אחד-עשר קבצים שנכנסו ב-8098b67e(fix(security): דפדפן הקוד חסום לאדמינים בלבד #3239) למרות ש-.gitignoreחוסם אותם.git rm --cachedבלבד — הם נשארים על הדיסק.הכפילות ב-
configuration.rst— מה נמצא ומה הוכרעשני העותקים נכנסו בקומיט אחד,
171d8f66(#1015, 23.10.2025), שהכניס את אותו בלוק פעם אחרי "Environment variables" ופעם אחרי "Security". לפניו הכותרות לא היו בקובץ כלל.שני העותקים הושוו שורה-שורה לפני המחיקה, והם אינם זהים — אבל אף הבדל אינו הבדל בתוכן:
Redis connection string. Use ``rediss://`` …Redis connection string (use ``rediss://`` …)ע״י(U+05F4) + "לא להשתמש"ע"י(ASCII) + "אל תשתמשו"שימוש ב‑http_sync(U+2011)שימוש ב-http_sync(ASCII)נשאר עותק א' מילה במילה, כי הוא במקום הנכון מבנית — עם שאר סעיפי הקונפיגורציה — והוא היחיד שמלווה ב-"שימוש ב-http_async", שאין לו כפילות כלל. שבע-עשרה מתוך תשע-עשרה השורות שנמחקו קיימות בעותק ששרד כלשונן, והשתיים הנותרות הן שתי וריאציות הניסוח שבטבלה.
עוגנים — נבדק ולא הונח.
autosectionlabelאינו מופעל ב-docs/conf.py, ולכן לכותרות אין לייבלים אוטומטיים ואף:ref:לא יכול להצביע עליהן. העוגן המפורש היחיד בקובץ הוא.. _config-error-signatures:, שיושב הרבה מתחת לבלוק שנמחק ואליו מפנהdocs/observability/log-aggregator.rst. שאר ההפניות הנכנסות הן:doc:לעמוד כולו. לא נשבר אף עוגן, ולכן לפיdocs/versioning-stable-anchors.rstאין כאן שינוי שובר-תיעוד שמחייב רשומה ב-whats-new.משקל הייבוא — המדידה שהחליפה את המספר
structlogservicesstructlogobservability,monitoring,monitoring.error_signaturesהשרשרת:
services/__init__.pyמייבאstateמ-backoff_state, ששורה 16 בו מנסה לייבאobservability; ו-observability.pyמייבאstructlogבקשיחות בשורה 22 ומנסהmonitoring.error_signaturesבשורה 28. כלומר הזנב תלוי במה שמותקן בסביבה ולא בשורת קוד — וזה בדיוק המבחן ב-docs/doc-authoring.rst: מספר שיכול להשתנות בלי שאף שורת קוד תשתנה אינו ערך שנאכף, ואין לכתוב אותו. הניסוח החדש מתאר חלק קבוע וזנב מותנה, בלי מספר ובלי רשימה סגורה. האינווריאנט הנושא נשאר וזה מה שנמדד בשתי הסביבות: אפס מודולים כבדים.תוקן גם דיוק שני באותו docstring: הטסט שאוכף את האינווריאנט מודד את
services.doc_sections, לא אתrst_parser. זה נכתב עכשיו כפער מוצהר במקום להשתמע ככיסוי.🧪 בדיקות
413 טסטים עברו, אפס נכשלו —
test_doc_sections,test_rst_parser,test_mcp_docs_handlers,test_mcp_outline,test_doc_summary_style,test_docs_literalinclude_anchors.flake8נקי על שני קובצי הפייתון שהשתנו.בקרה שמוכיחה שתיקון ה-
-Bאינו no-op. התת-תהליך הורץ מבודד, בלי pytest, עלservices/__pycache__ריק:services/__pycache__-B,cwd=שורש הריפו__init__,backoff_state,doc_sections)-B,cwd=tmp_pathומה שלא השתנה, כדי שלא ייקרא אחרת: בריצת pytest רגילה הדלתא הייתה אפס גם לפני התיקון, כי האיסוף עצמו מייבא את אותם מודולים וכותב את אותם קבצים. נמדד אחרי התיקון: איסוף=4, הטסט לבדו=4, דלתא=0. כלומר זו הפרה של מוסכמה ולא באג נצפה, והערך שלה הוא ביום שבו עץ המקור יהיה לקריאה בלבד — כלל 8 ב-
CLAUDE.md.ambiguous_section— הורץ מחדש על כל 208 קובצי ה-RST:docs/configuration.rstdocs/development/tools.rstdocs/observability/query-performance-profiler.rstשני הקבצים הנותרים לא נגענו בהם, והם כפילויות נפרדות.
בניית תיעוד:
python -m sphinx -b html -W --keep-goingעל כל האתר —build succeeded, קוד יציאה 0, אפס אזהרות. בניית עמוד בודד הוחלפה בבנייה מלאה בכוונה: היא מייצרת אזהרהsearch index couldn't be loadedשהיא ארטיפקט של הבנייה החלקית עצמה ולא ממצא, והבנייה המלאה היא ממילא מה ש-RTD מריץ..pyc— נבדק שאין תלות: כל עשרת האזכורים של__pycache__בריפו הם החרגות בלבד (.ruff.toml,bandit.yaml,.yamllint.yaml,services/code_indexer.py,scripts/audit_config_definitions.py,scripts/find_duplicates.py,github_menu_handler.py,repo_analyzer.pyושני טסטים). אף אחד אינו קורא מהתיקייה. אחריgit rm --cachedהם נשארו על הדיסק ואינם מופיעים כ-untracked, כי.gitignore:2חוסם אותם.🧪 בדיקות נדרשות ב‑PR
📝 סוג שינוי
✅ צ'קליסט
configuration.rstהוא כפילות מדויקת של בלוק ששורד באותו קובץ, ולכן אף משתנה לא ירד מהתיעודreadlink -fמול allowlist, לפיCLAUDE.mddocs/doc-authoring.rst,docs/versioning-stable-anchors.rst| המשפט: "אם המספר יכול להשתנות בלי שאף שורת קוד תשתנה — הוא ספירת מופעים, ואין לכתוב אותו" ו-"לא לשבור מזהי עוגנים קיימים (refs) בין גרסאות"🧩 השפעות/סיכונים
אפס שינוי בקוד ייצור. שני קבצי התיעוד הם פרוזה, קובץ הטסט הוא מוסכמת בידוד, וה-
.pycהם היגיינת גיט. הסיכון היחיד שנשקל הוא אובדן תוכן מ-configuration.rst, והוא נסגר במדידה: 17 מ-19 השורות שנמחקו קיימות כלשונן בעותק ששרד, והשתיים הנותרות הן ניסוח חלופי של אותו מידע.סטייה מהמוסכמה, מדווחת:
CLAUDE.mdו-docs/doc-authoring.rstאומרים לא לבנות את התיעוד מקומית עבור פרוזה בעמוד קיים, כי RTD תופס. נתבקשתי לבנות עם אזהרות כשגיאות, ולכן בניתי — ובניתי את האתר המלא ולא עמוד בודד, כי הצורה החלקית מייצרת אזהרת שווא משלה.🔗 קישורים
dea1b9e, בסיס06983a8)docs/doc-authoring.rst,docs/versioning-stable-anchors.rst🧯 סיכון / החזרה לאחור (Rollback)
git revertלכל אחד מארבעת הקומיטים בנפרד — הם עצמאיים לגמרי ואינם נוגעים באותם קבצים. ה-.pycשיחזרו ב-revert יהיו זהים לאלה שעל הדיסק.🤖 Generated with Claude Code
https://claude.ai/code/session_01P1AYmWhLGtpsr7sSdRZVD3
Generated by Claude Code
Summary by Sourcery
Clean up documentation, test isolation, and repository artifacts identified during validation without changing production behavior.
Bug Fixes:
Enhancements:
Documentation:
Tests:
Chores: