Skip to content

fix(mcp): מראה בדיסק של שירות ה-MCP בלי רשומה ב-repo_metadata נמחקת בסוף מעבר ה-autosync - #3520

Merged
amirbiron merged 2 commits into
mainfrom
claude/eloquent-newton-l3qnbz
Oct 4, 2026
Merged

amirbiron merged 2 commits into
mainfrom
claude/eloquent-newton-l3qnbz

Conversation

@amirbiron

@amirbiron amirbiron commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

✨ תיאור קצר

ריפו שהוסר בממשק נשאר לנצח בדיסק של שירות ה-MCP, וכלי הקריאה המשיכו להגיש אותו. כך קרה ל-codekeeper-plugin: אין לו רשומה ב-repo_metadata, ובכל זאת codekeeper_list_repo_tree מחזיר עליו עץ, מקומיט 6ec18f2. עכשיו בסוף כל מעבר של ה-autosync, כל מראה בדיסק של השירות שאין לה רשומה ב-repo_metadata נמחקת. כך הדיסק משקף את Mongo, והמראה של codekeeper-plugin תימחק במעבר הראשון שאחרי הפריסה, בלי טיפול ידני.

השורש: הסרה בוובאפ (unmirror_repo) מוחקת את המראה שבדיסק של הוובאפ ואת הרשומות, ולדיסק של ה-MCP היא לא מגיעה. ב-refresh_once לא היה שום שלב שמסיר מראה מקומית שאין לה רשומה. זה בדיוק הדפוס state-record-without-state-change §3: הרשומה נמחקה, והמשאב נשאר.

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

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

פירוט:

  • mcp_server/repo_autosync.py — שלב התאמה חדש, _prune_orphans, שרץ אחרי המעבר על הרשומות ועל אותה קריאה של repo_metadata. הקריאה נעשית מה-primary במפורש (ReadPreference.PRIMARY). המחיקה עוברת ב-delete_mirror (_safe_rmtree), בלי rmtree חדש. לא נמחק:
    • שום דבר כשהשאילתה נכשלה. זה היה המצב גם קודם, כי הפונקציה חוזרת מוקדם.
    • שום דבר כשאף שם ב-repo_metadata אינו שם של מראה שבדיסק: קריאה ריקה, שמות שאינם שמות של מראה, או מסד עם ריפואים אחרים. כשיש בדיסק מראות שנחסכו כך, נרשמת אזהרה.
    • תיקייה שאינה מראה שהשירות יוצר: שם ש-_validate_repo_name דוחה, או קישור סמלי. את ההחלטה הזו מקבל שירות המראות (list_managed_mirror_names), לא ה-autosync.
    • ריפו ש-is_refreshing מדווח עליו.
  • התלות של refresh_once בשירות המראות מוגדרת ב-Protocol (LocalMirrors), כך שה-autosync לא נוגע בפונקציות פרטיות שלו.
  • מונה pruned נוסף ל-stats, ושורת repo autosync pass נכתבת גם כשהדבר היחיד שקרה הוא מחיקה. לכל מראה שנמחקה יש שורת info עם השם. כשל של delete_mirror (בערך החזרה או בחריגה), או תיקיית מראות שאי אפשר לקרוא, נספרים ב-errors ונרשמים כאזהרה. המראה נשארת עד המעבר הבא, והמעבר ממשיך לשאר המראות.
  • services/git_mirror_service.py — list_mirror_names() מחזירה את כל המראות בדיסק, ו-list_managed_mirror_names() רק את אלה שמותר למחוק. שתיהן נשענות על סריקה אחת ב-os.scandir, ההיפוך של _get_repo_path. הסיומת .git עברה לקבוע אחד (MIRROR_DIR_SUFFIX). תיקייה שחסרה, או שאי אפשר לקרוא, זורקת OSError ולא מחזירה רשימה ריקה.
  • services/mirror_credentials.py — ה-sweep משתמש ב-list_mirror_names. עד עכשיו המנייה הייתה עותק בתוך ה-sweep, וזה איחוד לפי R6: כל תיקיית *.git עדיין נבדקת, גם עם שם לא תקין. שינוי אחד: תיקיית מראות שאי אפשר לקרוא היא עכשיו כשל עם שם. במקום שורה של checked=0 failed=0, שנראית כמו "הכול נקי", ה-sweep זורק.
  • תיעוד: תת-הסעיף "רענון אוטומטי" ב-docs/mcp-server.rst (עם עוגן חדש, mcp-repo-autosync), הסעיף על ה-sweep, שורת repo_not_mirrored בטבלת התקלות, MCP_REPO_AUTOSYNC ב-environment-variables.rst, ה-README של mcp_server, ו-whats-new.

🔎 ממצאי המחקר שלפני הקוד

  • הבאג אומת מול השירות החי: codekeeper_list_repos מחזיר 4 ריפואים, ו-codekeeper_list_repo_tree על codekeeper-plugin מחזיר עץ עם ref: "HEAD", כי בלי רשומה הענף הראשי נופל ל-HEAD.
  • תיקיות זמניות: אין כאלה. init_mirror משכפל ישר לתוך <name>.git. במקור של git v2.43.0 (builtin/clone.c), --mirror מדליק option_bare, תיקיית היעד היא ה-git dir עצמו, ו-remove_junk מוחק בכשל בדיוק אותה. זה נמדד גם עם strace על git clone --mirror: כל קובץ נעילה או קובץ זמני (config.lock, packed-refs.new) נוצר בתוך התיקייה, ושום רשומה לא נוצרת לידה. retry_cleanup הוא אותה תיקייה, ו-fetch_updates רץ בתוכה. בשירות ה-MCP init_mirror נקרא רק מ-refresh_once, וזה נבדק ב-grep.
  • השלכה שכדאי לדעת: גם codekeeper_docs_get_section קורא מהמראות האלה. ריפו תיעוד שיוסר מ-repo_metadata יחזיר מעכשיו repo_not_mirrored, במקום להגיש עותק ישן שאיש לא מעדכן.

↔️ סטיות מהמפרט, ולמה

  1. דילוג על קישור סמלי. זו תוספת. _safe_rmtree עושה resolve לפני המחיקה, ולכן מחיקה של alias.git → alpha.git הייתה מוחקת את alpha.git ומשאירה את הקישור.
  2. קריאה חוזרת אחרי המחיקה. זו תוספת. מראה נספרת ב-pruned רק אחרי ש-mirror_exists מראה שהיא איננה. ההצלחה ש-delete_mirror מדווחת היא דיווח, לא מצב (K11).
  3. "תוצאה ריקה" הורחבה ל"אין חפיפה". במפרט: תוצאה ריקה לא מוחקת. בסבב הריוויו התברר שזה לא מספיק, ולכן ההגנה היא עכשיו: כשאף שם בקריאה אינו שם של מראה שבדיסק, לא מוחקים. זה כולל את המקרה הריק. האזהרה נרשמת רק כשיש בדיסק מראות שנחסכו.
  4. שורת repo autosync pass עברה מ-_loop אל refresh_once. כך אפשר לבדוק בטסט שהיא נכתבת כש-pruned הוא הדבר היחיד שקרה.
  5. המנייה המשותפת (list_mirror_names / list_managed_mirror_names) — לא היה במפרט, ונובע מ-R6 ומהריוויו.

🔁 סבב ריוויו (cubic) — מה תוקן

  • P1, שם לא תקין עוקף את ההגנה: תקף. ההגנה בדקה "יש שם כלשהו" במקום "הקריאה מכירה משהו מהדיסק", כך שרשומה אחת עם שם לא תקין הייתה מוחקת את כל המראות. התיקון: הגנת חפיפה, שמכסה גם קריאה ריקה וגם מסד אחר.
  • P2, קריאה ממשני מפגר: הקריאה מה-primary אומצה. סף על "פער גדול" לא אומץ, והנימוק בשרשור.
  • P2, גישה לפונקציות פרטיות: list_managed_mirror_names בשירות, ו-Protocol (LocalMirrors) לתלות.
  • P2, glob בולע PermissionError: המנייה עברה ל-os.scandir, ותיקייה שאי אפשר לקרוא היא עכשיו כשל ולא רשימה ריקה. זה חל גם על ה-sweep.
  • P3 ×2, דיוק בתיעוד: אזהרה נרשמת רק כשיש מראות בדיסק, ומחיקה שנכשלה משאירה את המראה עד המעבר הבא. עודכנו כל העותקים: rst, README, whats-new ו-docstrings.

🧪 בדיקות

  • Unit, ב-tests/test_mcp_repo_autosync.py, מול מראות bare אמיתיות ב-tmp_path ו-GitMirrorService אמיתי. ההנחות נבדקות על הדיסק עצמו. המקרים:
    • מראה בלי רשומה נמחקת, ומראות עם רשומה נשארות (כולל שורת הלוג).
    • קריאה שלא מכירה אף מראה, בארבעה מקרים: ריקה, רשומות בלי שם, שמות שאף מראה לא יכולה לשאת, ומסד אחר. שום דבר לא נמחק, ונרשמת אזהרה. ובלי מראות בדיסק — בלי אזהרה.
    • הקריאה של repo_metadata מבקשת ReadPreference.PRIMARY.
    • שאילתה שנכשלת: שום דבר לא נמחק.
    • ריפו ב-is_refreshing לא נמחק.
    • כשל של delete_mirror, בערך החזרה או בחריגה, מגדיל את errors, והמעבר ממשיך.
    • רשומה שה-fetch שלה נכשל שומרת על המראה שלה.
    • קישור סמלי, שם לא תקין, קובץ ותיקייה בלי סיומת: לא נוגעים בהם.
    • מחיקה שמדווחת הצלחה ומשאירה את התיקייה נספרת כשגיאה.
    • תיקיית מראות שאי אפשר לקרוא (דרך os.scandir האמיתי של המנייה): שום דבר לא נמחק, ו-errors גדל.
  • tests/test_git_mirror_service.py: שתי הרשימות, ותיקייה חסרה או לא קריאה שזורקות.
  • tests/test_git_mirror_credentials.py: ה-sweep על תיקייה לא קריאה זורק, ולא כותב שורת checked=.
  • הדמות _Mirror הושלמה בלי להרחיב שום except. ה-except סביב המנייה תופס רק OSError, ולכן דמות חסרה נופלת בקול (T3).
  • ⚠️ לא הורץ מקומית, לפי בקשה: ה-CI יריץ. ספציפית, לא הרצתי את הטסטים על הקוד שלפני התיקון. לפי הקוד שלפני הריוויו, המקרים "שמות שאף מראה לא יכולה לשאת" ו"מסד אחר" היו מוחקים את שתי המראות, ולכן הם אמורים ליפול שם — אבל זה נימוק, לא מדידה.
  • מה שכן נמדד: הבאג מול השירות החי, התנהגות git clone --mirror ב-strace, והרצה אחת של הטסטים הקיימים, שהראתה את הדמות נופלת בקול כמו שציפיתי.

🚀 אימות אחרי פריסה

  • בלוג של שירות ה-MCP מופיעה שורת repo autosync pass עם 'pruned': 1, ושורה pruned the local mirror of codekeeper-plugin.
  • בעלייה הבאה של השירות, ה-sweep מראה checked=4. בעלייה הראשונה הוא עדיין יראה 5, כי הוא רץ לפני המעבר הראשון.
  • codekeeper_list_repo_tree על codekeeper-plugin מחזיר repo_not_mirrored.
  • Rollback: חזרה לקוד הקודם לא מחזירה מראה שנמחקה, אבל גם לא צריכה אותה. נמחקות רק מראות בלי רשומה, ומראה של ריפו עם רשומה משוכפלת מחדש במעבר הבא.

🧪 בדיקות נדרשות ב‑PR

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

📝 סוג שינוי

  • feat: פיצ'ר חדש
  • fix: תיקון באג
  • docs: שינוי תיעוד בלבד
  • refactor: שינוי קוד ללא שינוי התנהגות
  • perf: שיפור ביצועים
  • chore/ci: תשתית/CI
  • breaking change: שינוי שובר תאימות

📚 עיון בתיעוד (CodeBot – Project Docs)

כן. עיינתי ב:

  • AI-MAP.md
  • docs/mcp-server.rst: "דפדפן הריפו — איך זה עובד" כולו, "הפעלה — צעד אחר צעד", וטבלת התקלות.
  • docs/testing.rst, docs/doc-authoring.rst, docs/versioning-stable-anchors.rst, docs/style-glossary.rst.
  • docs/environment-variables.rst: MCP_REPO_AUTOSYNC ו-REPO_MIRROR_PATH.
  • docs/whats-new.rst.
  • docs/dev/sticky_notes_extending.rst: העיקרון "שאילתה שנכשלה אינה ריק".

ב-amir-bug-patterns: K11, K13, K16, U1, U3, R6, state-record-without-state-change, return-value-failure-unchecked, write-from-cached-read, race-toctou, external-input-isinstance, prose-restates-code-fact, line-number-coupling, secret-in-derived-text, silent-fallback-to-worse-path, side-effect-riding-on-log-line, TESTING-PATTERNS.md, claude-md-snippets/testing.md, widened-exception-scope, ו-BY-STACK/cron-jobs.md דפוס 10.

✅ צ'קליסט

  • הקוד עוקב אחרי הסגנון — בדיקת תחביר בלבד (ast.parse); flake8/mypy ירוצו ב-CI
  • בדיקות רצות ועוברות — ממתין ל-CI (לא הורץ מקומית)
  • תיעוד עודכן (README/Docs)
  • לא נוספו ג'ובים חדשים
  • לא נוספו ולא שונו משתני סביבה. רק התיאור של MCP_REPO_AUTOSYNC הורחב
  • אין סודות/מפתחות בקוד
  • אין מחיקות מסוכנות: המחיקה עוברת רק ב-delete_mirror (_safe_rmtree, מוגבל ל-base_path)
  • הודעת הקומיט תואמת Conventional Commits
  • CHANGELOG (whats-new) עודכן

🤖 Generated with Claude Code

https://claude.ai/code/session_016LWehkLW2ZDpXQuXuDqh5X


Generated by Claude Code

&lt;source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"&gt;&lt;source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"&gt;&lt;img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"&gt;

Summary by Sourcery

Prune stale MCP repository mirrors during autosync so the service’s local disk stays consistent with repository metadata.

Bug Fixes:

  • Remove orphaned MCP repository mirrors at the end of autosync passes so repositories removed from the web application are no longer served from stale local copies.

Enhancements:

  • Make autosync pruning safety-aware by preserving mirrors when metadata reads fail or identify no local repositories, while skipping active refreshes, invalid names, and symbolic links.
  • Add mirror-directory discovery APIs and use them for both orphan pruning and credential sweeps, with explicit handling for unreadable mirror directories.
  • Track pruned mirrors in autosync statistics and log pass summaries, deletion outcomes, and verification failures.

Documentation:

  • Document MCP autosync orphan pruning behavior, safeguards, configuration details, and the resulting repository-not-mirrored behavior.

Tests:

  • Add coverage for orphan pruning, safety guards, deletion failures, unreadable directories, mirror discovery, and credential-sweep behavior.

…סוף מעבר ה-autosync

ריפו שהוסר בממשק (unmirror_repo) נמחק מהדיסק של הוובאפ ומ-Mongo, אבל
המראה שלו בדיסק של שירות ה-MCP נשארה לנצח, וכלי הקריאה המשיכו להגיש
אותה (codekeeper-plugin, קומיט 6ec18f2).

- refresh_once: שלב התאמה (_prune_orphans) אחרי המעבר על הרשומות, על
  אותה קריאה של repo_metadata; המחיקה דרך delete_mirror (_safe_rmtree).
- לא נמחק: כשהשאילתה נכשלה, כשאין אף שם ריפו (עם אזהרה), שם שאינו עובר
  _validate_repo_name, קישור סמלי, וריפו ש-is_refreshing מדווח עליו.
- מראה נספרת ב-pruned רק אחרי ש-mirror_exists מראה שהיא איננה; כשל של
  delete_mirror נספר ב-errors, והמעבר ממשיך לשאר המראות.
- שורת "repo autosync pass" עברה ל-refresh_once, והתנאי שלה כולל pruned.
- GitMirrorService.list_mirror_names: מניית המראות בדיסק, משותפת ל-sweep
  של ה-credentials ולהתאמה (עד עכשיו עותק בתוך ה-sweep). הסיומת .git
  בקבוע אחד, ש-_get_repo_path קורא גם הוא.
- טסטים מול מראות bare אמיתיות ב-tmp_path; תיעוד: mcp-server.rst,
  README של mcp_server, environment-variables, whats-new.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016LWehkLW2ZDpXQuXuDqh5X
@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.

@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

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

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

@github-actions

github-actions Bot commented Oct 4, 2026

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)

@sourcery-ai

sourcery-ai Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

The PR makes MCP autosync reconcile the local mirror directory with repository metadata by safely deleting orphaned mirrors after successful passes, adds shared mirror enumeration, records and logs pruning outcomes, tests the safety and failure paths, and documents the behavior.

Sequence diagram for MCP autosync orphan pruning

sequenceDiagram
    participant Autosync as refresh_once
    participant Mongo as repo_metadata
    participant Mirror as GitMirrorService
    participant Disk as Local mirror disk

    Autosync->>Mongo: find
    alt query fails
        Mongo-->>Autosync: failure
        Autosync-->>Autosync: return stats
    else query succeeds
        Mongo-->>Autosync: repo records
        Autosync->>Mirror: refresh known repositories
        Autosync->>Mirror: list_mirror_names
        Mirror->>Disk: enumerate *.git directories
        Disk-->>Mirror: local mirror names
        Mirror-->>Autosync: on_disk names
        loop orphan mirrors
            Autosync->>Mirror: is_refreshing(name)
            alt safe to delete
                Autosync->>Mirror: delete_mirror(name)
                Mirror->>Disk: _safe_rmtree
                Autosync->>Mirror: mirror_exists(name)
                Mirror-->>Autosync: mirror absent
                Autosync-->>Autosync: increment pruned
            else refreshing, invalid, or symlink
                Autosync-->>Autosync: skip mirror
            end
        end
        Autosync-->>Autosync: log repo autosync pass
    end
Loading

File-Level Changes

Change Details Files
Prune local MCP mirrors that no longer have a valid repository metadata record after each autosync pass.
  • Collect repository names from the successful metadata query and prune unmatched on-disk mirrors afterward.
  • Skip pruning for empty/invalid metadata results, invalid mirror names, symlinks, refreshing repositories, and listing failures.
  • Route deletion through delete_mirror, verify the mirror is actually gone, continue after per-mirror failures, and record pruned/error statistics.
  • Move autosync pass summary logging into refresh_once so pruning-only passes are reported.
mcp_server/repo_autosync.py
Add a shared API for enumerating mirror directories and reuse it across mirror maintenance.
  • Introduce MIRROR_DIR_SUFFIX and list_mirror_names as the inverse of mirror path construction.
  • Preserve enumeration of all direct *.git directories, including invalid names and symlinked directories.
  • Replace the credential sweep's duplicated filesystem enumeration with the shared API.
services/git_mirror_service.py
services/mirror_credentials.py
Add coverage for orphan pruning, safety conditions, failure handling, and mirror enumeration.
  • Test pruning against real bare mirrors and verify disk state, logs, and statistics.
  • Cover failed queries, empty metadata, active refreshes, failed deletions, failed refreshes, symlinks, invalid names, and unlistable directories.
  • Test list_mirror_names filtering and behavior when the base directory is missing.
tests/test_mcp_repo_autosync.py
tests/test_git_mirror_service.py
Document the new autosync cleanup behavior and its operational safeguards.
  • Explain orphan pruning and its exclusions in MCP server documentation and README.
  • Update environment-variable documentation and release notes to describe autosync behavior.
docs/environment-variables.rst
docs/mcp-server.rst
docs/whats-new.rst
mcp_server/README.md

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 Oct 4, 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: 8ce8368a-19ed-426d-98a0-8a8f151b667b
📥 Commits

Reviewing files that changed from the base of the PR and between a736a3e and fd37c3d.

📒 Files selected for processing (9)
  • docs/environment-variables.rst
  • docs/mcp-server.rst
  • docs/whats-new.rst
  • mcp_server/README.md
  • mcp_server/repo_autosync.py
  • services/git_mirror_service.py
  • services/mirror_credentials.py
  • tests/test_git_mirror_service.py
  • tests/test_mcp_repo_autosync.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

נוסף ניקוי של מראות מקומיות שאין להן רשומה ב-repo_metadata. השירות מונה שמות מראות, ו-autosync מוחק מראות יתומות בתום המעבר בכפוף לתנאי דילוג ואימות. נוספו בדיקות ותיעוד להתנהגות זו.

Changes

ניקוי מראות MCP יתומות

שכבה / קבצים סיכום
מניית שמות מראות
services/git_mirror_service.py, services/mirror_credentials.py, tests/test_git_mirror_service.py
נוספה list_mirror_names למניית תיקיות מראה ישירות תחת ספריית הבסיס. scrub_stored_credentials משתמשת בה, והבדיקות מכסות מראות, קישורים סמליים וספריית בסיס חסרה.
ניקוי מראות בסבב autosync
mcp_server/repo_autosync.py, tests/test_mcp_repo_autosync.py, docs/environment-variables.rst, docs/mcp-server.rst, docs/whats-new.rst, mcp_server/README.md
refresh_once מוחקת מראות ללא רשומת מאגר, בכפוף לתנאי הדילוג ולאימות המחיקה. נוספו מונה pruned, רישום סיכום, בדיקות לתנאי הצלחה וכשל ותיעוד המדיניות.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant refresh_once
  participant repo_metadata
  participant GitMirrorService
  refresh_once->>repo_metadata: שאילתת שמות מאגרים
  repo_metadata-->>refresh_once: שמות מאגרים רשומים
  refresh_once->>GitMirrorService: list_mirror_names
  GitMirrorService-->>refresh_once: שמות מראות מקומיות
  refresh_once->>GitMirrorService: delete_mirror למראה ללא רשומה
Loading

Merge Risk: ⚪ Minimal · up to fd37c

No identified issue prevents merging after normal checks. The new tests still need to run in CI.

Security Architecture Review

Security architecture risk: 🔵 Low · up to fd37c

Automatic cleanup reduces stale repository availability and includes safeguards against accidental deletion. Remaining uncertainty concerns concurrent filesystem changes and who can modify the repository-copy directory.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The deletion authority spans eligible direct mirror directories under the configured MCP base, using the complete metadata-name set rather than a per-request or per-tenant selection. The examined containment checks reject resolved targets outside that base. Repository browsing is documented as admin-only; its authorization enforcement was not independently inspected in this pass.

Trust Boundaries and Controls

  • observed — Locally enumerated names are not trusted directly: pruning validates names and skips ordinary symlinks before calling delete_mirror, which validates again. The existing deletion helper applies resolved-path containment checks before recursive removal.
  • inferred — Symlink exclusion is not atomic with deletion. A concurrent writer able to replace an orphan entry after the check could redirect the existing resolving helper to another in-root mirror. The helper predates this PR, but periodic pruning adds a caller. Filesystem writer permissions and any independent attacker capability remain unestablished, so this is conditional boundary exposure rather than a verified attack path.

Resilience and Maintainability Implications

  • observed — The cleanup policy favors avoiding destructive false positives when metadata cannot be read or contains no names. Per-mirror failures and verified deletion counts provide operational visibility without treating a failed clone or fetch as loss of repository ownership.

Hardening Proposals

  • proposed — Confirm exclusive ownership of the MCP mirror directory. If concurrent or less-trusted writers are supported, enforce non-following deletion at the deletion primitive rather than relying only on the caller's earlier symlink check.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 39 functions across 5 files. (4 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed הכותרת מתארת במדויק את השינוי המרכזי: מחיקת מראות MCP מקומיות ללא רשומה ב־repo_metadata בסוף מעבר autosync.
Description check ✅ Passed התיאור מפורט ומכסה את עיקרי השינויים, הבדיקות, הסיכונים, תוכנית החזרה לאחור והתיעוד בהתאם לתבנית. הוא מציין במפורש שהבדיקות המקומיות לא הורצו ושהן ממתינות ל־CI.
Full details: Docstring Coverage

Explanation

Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 39 functions across 5 files. (4 skipped: 4 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

⏱️ Performance report

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

@github-actions

github-actions Bot commented Oct 4, 2026 •

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

@kilo-code-bot

kilo-code-bot Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (10 files)
  • mcp_server/repo_autosync.py - Core pruning logic with primary read, Protocol typing, safer orphan detection
  • services/git_mirror_service.py - New list_managed_mirror_names() and _scan_mirror_dirs() with os.scandir
  • services/mirror_credentials.py - Updated docstring for unreadable directory behavior
  • tests/test_mcp_repo_autosync.py - 11 pruning tests including safety guards and primary read verification
  • tests/test_git_mirror_service.py - 3 tests for mirror enumeration and error surfacing
  • tests/test_git_mirror_credentials.py - 1 test for sweep on unreadable directory
  • docs/mcp-server.rst - Updated autosync documentation
  • docs/whats-new.rst - Updated release notes
  • mcp_server/README.md - Updated autosync description
  • scripts/sweep_mirror_credentials.py - Updated exit code documentation
Previous Review Summary (commit fd37c3d)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit fd37c3d)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (9 files)
  • mcp_server/repo_autosync.py - Core pruning logic with safety guards
  • services/git_mirror_service.py - Shared list_mirror_names() and MIRROR_DIR_SUFFIX
  • services/mirror_credentials.py - Updated to use shared enumeration
  • tests/test_mcp_repo_autosync.py - 11 comprehensive pruning tests against real bare mirrors
  • tests/test_git_mirror_service.py - 2 tests for list_mirror_names()
  • docs/mcp-server.rst - Detailed autosync documentation with anchor
  • docs/environment-variables.rst - Updated MCP_REPO_AUTOSYNC description
  • docs/whats-new.rst - Release note
  • mcp_server/README.md - Updated autosync description

Reviewed by nemotron-3-ultra-550b-a55b:free · Input: 423.6K · Output: 13.3K · Cached: 427.7K

@codecov

codecov Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.09677% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mcp_server/repo_autosync.py 82.60% 1 Missing and 7 partials ⚠️

📢 Thoughts on this report? Let us know!

@cubic-dev-ai cubic-dev-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.

All reported issues were addressed across 9 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread mcp_server/repo_autosync.py
Comment thread mcp_server/repo_autosync.py Outdated
Comment thread services/git_mirror_service.py Outdated
Comment thread mcp_server/repo_autosync.py
Comment thread docs/mcp-server.rst Outdated
Comment thread mcp_server/README.md Outdated
…, ושירות המראות מחליט מה מותר למחוק

תיקוני ריוויו ל-#3520.

- הגנת חפיפה במקום הגנת "ריק": לא מוחקים כלום כשאף שם ב-repo_metadata
  אינו שם של מראה שבדיסק. ההגנה הקודמת בדקה רק שיש שם כלשהו, ולכן רשומה
  אחת עם שם לא תקין הייתה מוחקת את כל המראות. אותה הגנה תופסת גם קריאה
  ריקה ומסד עם ריפואים אחרים; האזהרה נרשמת רק כשיש מראות שנחסכו.
- repo_metadata נקרא מה-primary במפורש (ReadPreference.PRIMARY, כמו
  Repository.find_version_by_id): משני מפגר היה משמיט רשומה, והמראה שלה
  הייתה נמחקת.
- GitMirrorService.list_managed_mirror_names: השירות מחליט מה נחשב מראה
  שמותר למחוק (שם תקין, לא קישור סמלי). repo_autosync כבר לא נוגע
  ב-_validate_repo_name וב-_get_repo_path, והתלות מוגדרת ב-Protocol
  (LocalMirrors).
- מניית המראות ב-os.scandir במקום Path.glob, שבלע PermissionError והחזיר
  רשימה ריקה. תיקייה לא קריאה היא עכשיו כשל: בהתאמה נספר ב-errors, ובניקוי
  ה-credentials עולה כחריגה במקום שורת checked=0 failed=0.
- תיעוד: האזהרה רק כשיש מראות בדיסק, ומחיקה שנכשלה משאירה את המראה עד
  המעבר הבא (mcp-server.rst, README, whats-new, docstrings).

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

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

@gitar-bot

gitar-bot Bot commented Oct 4, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

🔴 High risk · Autosync now deletes on-disk repository mirrors absent from Mongo metadata.

Prunes orphaned MCP repository mirrors during autosync passes so the service's local disk stays consistent with repo_metadata. Repositories removed from the web interface are no longer served from stale local copies, with safeguards to skip failed metadata reads, active refreshes, invalid names, and symbolic links. Adds mirror discovery APIs shared between orphan pruning and credential sweeps, updates autosync statistics and logging, and documents the new behavior and repo_not_mirrored outcome. Comprehensive test coverage validates pruning logic, safety guards, and edge cases against real bare repositories. No issues found.

Review coverage

📋 Rules No rules evaluated

🧪 Functional validation Not enabled · Set up

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@amirbiron
amirbiron merged commit 72581aa into main Oct 4, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants