Skip to content

fix: ארבעה תיקונים מדוח האימות של #3394 — ‎-B בתת-תהליך, כפילות ב-configuration, מספר תלוי-סביבה, ו-.pyc בגיט - #3395

Merged
amirbiron merged 4 commits into
mainfrom
claude/laughing-mayer-wazq5m
Sep 16, 2026

Conversation

@amirbiron

@amirbiron amirbiron commented Sep 16, 2026 •

Copy link
Copy Markdown
Owner

✨ תיאור קצר

ארבעה תיקונים נפרדים שעלו מדוח אימות בלתי-תלוי של PR #3394 (squash dea1b9e). אין שינוי התנהגות בקוד ייצור: שניים מהם תיעוד, אחד מוסכמת טסטים, ואחד היגיינת גיט. כל אחד נמדד לפני ואחרי ולא הונח.

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

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

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

  • 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". לפניו הכותרות לא היו בקובץ כלל.

שני העותקים הושוו שורה-שורה לפני המחיקה, והם אינם זהים — אבל אף הבדל אינו הבדל בתוכן:

עותק א' (13–58) עותק ב' (85–111) סוג
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.

משקל הייבוא — המדידה שהחליפה את המספר

סביבה סה"כ מודולים מהריפו כבדים
בלי structlog 73 ארבעה — שרשרת ה-services 0
עם structlog 330 שבעה — ועוד observability, monitoring, monitoring.error_signatures 0

השרשרת: 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. זה נכתב עכשיו כפער מוצהר במקום להשתמע ככיסוי.

🧪 בדיקות

  • Unit
  • Integration
  • Manual

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= שורש הריפו 3 קבצים (__init__, backoff_state, doc_sections)
חדשה — עם -B, cwd=tmp_path 0, ותיקיית ה-tmp נשארת ריקה גם היא

ומה שלא השתנה, כדי שלא ייקרא אחרת: בריצת pytest רגילה הדלתא הייתה אפס גם לפני התיקון, כי האיסוף עצמו מייבא את אותם מודולים וכותב את אותם קבצים. נמדד אחרי התיקון: איסוף=4, הטסט לבדו=4, דלתא=0. כלומר זו הפרה של מוסכמה ולא באג נצפה, והערך שלה הוא ביום שבו עץ המקור יהיה לקריאה בלבד — כלל 8 ב-CLAUDE.md.

ambiguous_section — הורץ מחדש על כל 208 קובצי ה-RST:

קובץ לפני אחרי
docs/configuration.rst 25 0
docs/development/tools.rst 12 12
docs/observability/query-performance-profiler.rst 8 8
סה"כ 45 20

שני הקבצים הנותרים לא נגענו בהם, והם כפילויות נפרדות.

בניית תיעוד: 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

  • 🔍 Code Quality & Security
  • Unit Tests (3.11)
  • Unit Tests (3.12)

📝 סוג שינוי

  • feat
  • fix: תיקון באג
  • docs: שינוי תיעוד בלבד
  • refactor
  • perf
  • chore/ci: תשתית
  • breaking change

✅ צ'קליסט

  • הקוד עוקב אחרי הסגנון (flake8 נקי)
  • בדיקות רצות ועוברות
  • תיעוד עודכן
  • ג'ובים חדשים — לא רלוונטי
  • משתני סביבה — לא נוספו ולא שונו. הבלוק שנמחק מ-configuration.rst הוא כפילות מדויקת של בלוק ששורד באותו קובץ, ולכן אף משתנה לא ירד מהתיעוד
  • טוקנים/ערכות נושא — לא רלוונטי
  • אין סודות/מפתחות בקוד
  • אין מחיקות מסוכנות — כל מחיקה זמנית בסשן עברה דרך מעטפת שמאמתת readlink -f מול allowlist, לפי CLAUDE.md
  • הודעות הקומיט תואמות Conventional Commits
  • CHANGELOG — אין קובץ כזה בשורש
  • עיינתי במסמכי אתר התיעוד — נתיב: docs/doc-authoring.rst, docs/versioning-stable-anchors.rst | המשפט: "אם המספר יכול להשתנות בלי שאף שורת קוד תשתנה — הוא ספירת מופעים, ואין לכתוב אותו" ו-"לא לשבור מזהי עוגנים קיימים (refs) בין גרסאות"

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

אפס שינוי בקוד ייצור. שני קבצי התיעוד הם פרוזה, קובץ הטסט הוא מוסכמת בידוד, וה-.pyc הם היגיינת גיט. הסיכון היחיד שנשקל הוא אובדן תוכן מ-configuration.rst, והוא נסגר במדידה: 17 מ-19 השורות שנמחקו קיימות כלשונן בעותק ששרד, והשתיים הנותרות הן ניסוח חלופי של אותו מידע.

סטייה מהמוסכמה, מדווחת: CLAUDE.md ו-docs/doc-authoring.rst אומרים לא לבנות את התיעוד מקומית עבור פרוזה בעמוד קיים, כי RTD תופס. נתבקשתי לבנות עם אזהרות כשגיאות, ולכן בניתי — ובניתי את האתר המלא ולא עמוד בודד, כי הצורה החלקית מייצרת אזהרת שווא משלה.

🔗 קישורים

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

git revert לכל אחד מארבעת הקומיטים בנפרד — הם עצמאיים לגמרי ואינם נוגעים באותם קבצים. ה-.pyc שיחזרו ב-revert יהיו זהים לאלה שעל הדיסק.


🤖 Generated with Claude Code

https://claude.ai/code/session_01P1AYmWhLGtpsr7sSdRZVD3


Generated by Claude Code

Review in cubic

Summary by Sourcery

Clean up documentation, test isolation, and repository artifacts identified during validation without changing production behavior.

Bug Fixes:

  • Prevent the documentation import test from writing bytecode into the source tree by isolating its subprocess and disabling bytecode generation.

Enhancements:

  • Make outline scanner documentation describe the stable no-heavy-import invariant without environment-dependent module counts or incomplete module lists.

Documentation:

  • Remove the duplicated configuration guidance while retaining the complete canonical section.

Tests:

  • Update the subprocess-based import test to use an isolated working directory and repository path.

Chores:

  • Remove tracked Python bytecode artifacts from Git while preserving the files locally.

אחד-עשר קבצי ``__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

@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 4 days and 18 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@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.

@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): 129

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

Excluded matches (by path pattern)
./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]
./docs/DOCUMENTATION_GUIDE.md:453:rm -rf _build
./docs/Makefile:24:	rm -rf $(BUILDDIR)
./Dockerfile:42:    rm -rf /var/lib/apt/lists/*
./Dockerfile:121:    rm -rf /var/lib/apt/lists/*
./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/katex/src/fonts/Makefile:139:	rm -rf pfa ff otf ttf woff woff2
./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.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/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/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:842:find . -name "__pycache__" -exec rm -rf {} +

@sourcery-ai

sourcery-ai Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

התיקון מטפל בארבעה ממצאי אימות ללא שינוי בקוד ייצור: מבודד תת-תהליך בדיקה ומונע כתיבה לעץ המקור, מסיר כפילות מתיעוד הקונפיגורציה, מחליף ספירות תלויות-סביבה בתיעוד האינווריאנט של הייבוא, ומנקה קובצי .pyc שנעקבו בטעות מ-Git.

Sequence diagram for isolated documentation import test

sequenceDiagram
    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
Loading

Flow diagram for environment-independent import invariant

flowchart 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
Loading

Flow diagram for documentation and Git hygiene fixes

flowchart 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]
Loading

File-Level Changes

Change Details Files
בודד את בדיקת ייבוא תיעוד התת-תהליכית מעץ המקור ומנע יצירת קבצי bytecode.
  • הוספת נתיב מוחלט של שורש הריפו ל-sys.path.
  • הרצת תת-התהליך עם -B ובספריית עבודה זמנית וייחודית.
  • הרחבת הטסט לקבלת tmp_path ואימות שמירת האינווריאנט של היעדר מודולים כבדים.
tests/test_doc_sections.py
עדכן את תיעוד מבנה הייבוא כך שלא יכיל ספירות או רשימות תלויות-סביבה.
  • הסרת ספירת מודולי הריפו ורשימתם כרשימה סגורה.
  • תיעוד ההבחנה בין שרשרת services הקבועה לזנב המותנה ב-structlog.
  • הבהרת שאפס מודולים כבדים הוא האינווריאנט הנמדד, וכן מהו כיסוי הטסט בפועל.
mcp_server/outline_scanners/__init__.py
הסרת תוכן תיעודי כפול בלי לשנות את המידע הקנוני או את העוגנים.
  • מחיקת העותק הכפול של סעיפי מסדי הנתונים, לקוחות HTTP ו-http_sync.
  • השארת העותק המבני המלא לצד סעיף http_async.
docs/configuration.rst
הוצאת קבצי bytecode שנעקבו בטעות מניהול Git.
  • הסרת 11 קובצי __pycache__/*.pyc מה-index בלבד.
  • השארת הקבצים על הדיסק תחת הגנת .gitignore.
__pycache__/*.pyc

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

@coderabbitai

coderabbitai Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

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: c5165880-9612-4182-a6d2-3e148c2757c5

📥 Commits

Reviewing files that changed from the base of the PR and between dea1b9e and 3e58fb0.

⛔ Files ignored due to path filters (11)
  • __pycache__/advanced_bot_handlers.cpython-313.pyc is excluded by !**/*.pyc
  • __pycache__/bot_handlers.cpython-313.pyc is excluded by !**/*.pyc
  • __pycache__/code_processor.cpython-313.pyc is excluded by !**/*.pyc
  • __pycache__/conversation_handlers.cpython-313.pyc is excluded by !**/*.pyc
  • __pycache__/database.cpython-313.pyc is excluded by !**/*.pyc
  • __pycache__/file_manager.cpython-313.pyc is excluded by !**/*.pyc
  • __pycache__/github_menu_handler.cpython-313.pyc is excluded by !**/*.pyc
  • __pycache__/integrations.cpython-313.pyc is excluded by !**/*.pyc
  • __pycache__/main.cpython-313.pyc is excluded by !**/*.pyc
  • __pycache__/search_engine.cpython-313.pyc is excluded by !**/*.pyc
  • __pycache__/utils.cpython-313.pyc is excluded by !**/*.pyc
📒 Files selected for processing (3)
  • docs/configuration.rst
  • mcp_server/outline_scanners/__init__.py
  • tests/test_doc_sections.py
💤 Files with no reviewable changes (1)
  • docs/configuration.rst

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


📝 Walkthrough

Walkthrough

השינוי מסיר תיעוד כפול, מעדכן את תיעוד חוזה הייבוא, ומבודד את בדיקת התת-תהליך מעץ המקור ומקבצי __pycache__.

Changes

תיעוד ותיקוף

Layer / File(s) Summary
ניקוי ועדכון תיעוד
docs/configuration.rst, mcp_server/outline_scanners/__init__.py
הוסר קטע תיעוד כפול. בדוק-הסטרינג מתאר כעת את האינווריאנט, את כיסוי הבדיקות ואת ההבדלים בין סביבות structlog.
בידוד בדיקת התת-תהליך
tests/test_doc_sections.py
הבדיקה מוסיפה נתיב מוחלט ל־sys.path, משתמשת ב־cwd זמני ומריצה את Python עם ‎-B‎. Claude Code שמר על שינוי ממוקד בבדיקת הסביבה.

Priority: ⬇️ Low

Estimated code review effort: 1 (Trivial) | ~5 minutes

Change: Other

Merge Risk: ⚪ Minimal · up to 3e58f

The documentation and test-isolation updates do not change production behavior and are ready to merge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed הכותרת מתארת באופן ברור ומדויק את ארבעת השינויים המרכזיים: בידוד תת-התהליך, הסרת כפילות בתיעוד, תיקון תיאור תלוי-סביבה והסרת קובצי .pyc מניהול Git.
Description check ✅ Passed התיאור מפורט ומכסה את מטרת השינוי, השינויים העיקריים, הבדיקות, הסיכונים, הקישורים ותוכנית ה-Rollback. הוא מציין 413 טסטים שעברו, בניית Sphinx מוצלחת ובדיקות flake8. חסרים רק סימונים מפורשים לכך שכל ה-…
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/laughing-mayer-wazq5m

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

תיעוד כפול יורד מן הדף
חוזה הייבוא נעשה מדויק
תהליך הבדיקה נשאר מבודד
Claude Code טיפח שינוי מסודר
CodeKeeper forever 💫

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

@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 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@amirbiron
amirbiron merged commit b439fb3 into main Sep 16, 2026
31 checks passed
amirbiron pushed a commit that referenced this pull request Sep 16, 2026
.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
amirbiron added a commit that referenced this pull request Sep 16, 2026
…קיים (#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>
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.

2 participants