Repository navigation
fix(mcp): codekeeper_note_str_replace מודד את התוצאה לפני שהוא בונה אותה — max_size חובה ב-_apply_edit, ו-call-arg חוסם ב-mypy של CI (#3495) - #3517
Conversation
…ותה — max_size חובה ב-_apply_edit, ו-call-arg חוסם ב-mypy של CI (#3495) עד היום הכלי בנה את גוף הפתק החדש ורק אז השווה אותו ל-MAX_NOTE_CHARS, ולכן replace_all על מחרוזת קצרה עם new_string ארוך בנה בזיכרון מחרוזת באורך מספר המופעים כפול אורך ההחלפה לפני הסירוב: בקשה של כ-10KB — שיא של 20,019,845 בתים (tracemalloc דרך call_tool), ופתק מלא — 200,015,625. בתקרות הייצור (גוף של 2MiB) כ-40GB לפני הסירוב. - _apply_edit: max_size חובה, בלי ברירת מחדל; הבדיקה המוקדמת רצה תמיד. - note_str_replace: max_size=MAX_NOTE_CONTENT, ו-code_too_large נענה content_too_long עם max — אותה תשובה, רק לפני ההקצאה. הבדיקה שאחרי ההחלפה נשארת כרשת על מה שנכתב. קצה: רווחים בלבד מעל התקרה הם content_too_long. - ci.yml: השער של mypy חוסם גם call-arg, כדי שקורא ששכח את max_size ייתפס סטטית. main.py: _Ctx השני ב-setup_bot_data הוא _CtxPredictive — המופע היחיד של call-arg בריפו (שתי מחלקות באותו שם באותה פונקציה). - טסטים: שיא ההקצאה מתחת לגבול שאינו תלוי במספר המופעים, צורת הסירוב זהה להיום, הגבול בתו, הקצה של רווחים בלבד, והחשבון ביחידה. - תיעוד: docs/mcp-server.rst (שורת הכלי והפסקה על הגודל), docs/whats-new.rst. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhRqpoqm8WFN2KP7cJSKXJ
|
ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing |
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 1 day and 8 hours by commenting @sourcery-ai review. Upgrade to get a review now.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
Reviewer's GuideThe PR closes a memory-amplification vulnerability in note-wide string replacement by enforcing an exact pre-allocation size check, makes the ceiling mandatory for all Sequence diagram for bounded note string replacementsequenceDiagram
participant Client
participant MCP as note_str_replace
participant Edit as _apply_edit
participant Store as update_note
Client->>MCP: note_str_replace
MCP->>Edit: _apply_edit(body, old_clean, new_clean, replace_all, max_size=MAX_NOTE_CONTENT)
Edit->>Edit: str.count(old_string)
alt computed result exceeds MAX_NOTE_CONTENT
Edit-->>MCP: code_too_large
MCP-->>Client: content_too_long with max
else result fits ceiling
Edit->>Edit: str.replace(old_string, new_string)
Edit-->>MCP: new_body
MCP->>Store: update_note
Store-->>MCP: saved note
MCP-->>Client: ok
end
Flow diagram for mandatory edit size ceilingsflowchart LR
A[_apply_edit requires max_size] --> B{All callers provide a ceiling}
B -->|yes| C[Pre-allocation size check]
B -->|no| D[mypy call-arg error]
C --> E[CI gate blocks error]
C --> F[Oversized result rejected before str.replace]
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
🧯 Dangerous deletes guard reportPolicy: see .cursorrules — dangerous deletions are blocked unless wrapped safely. Summary:
Flagged findings (file:line:snippet): Excluded matches (by path pattern) |
|
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
📒 Files selected for processing (9)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughהעדכון מחשב את גודל תוצאת החלפת תוכן בפתק לפני יצירתה, ומחזיר Changesהחלפת תוכן בפתק
Priority: ⬆️ High Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: High Merge Risk: ⚪ Minimal · up to The size-limit change has no confirmed merge-blocking issue; the production edit callers pass the required limit. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change strengthens protection against oversized replacements without an observed expansion of write access. No new material security concern was established. Deployed size limits, memory capacity, and service-wide exposure remain unverified. Retained concerns Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 65.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 20 functions across 6 files. (3 skipped: 3 unsupported.)
✨ Finishing Touches 💡 1📝 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 |
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (3 files)
Previous Review Summaries (2 snapshots, latest commit 6ba68c0)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 6ba68c0)Status: No Issues Found | Recommendation: Merge Files Reviewed (4 files)
Previous review (commit 59f3a25)Status: No Issues Found | Recommendation: Merge Files Reviewed (9 files)
Reviewed by nemotron-3-ultra-550b-a55b:free · Input: 155.9K · Output: 1.6K · Cached: 177.1K |
📖 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! |
There was a problem hiding this comment.
All reported issues were addressed across 9 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…ים בלבד כן השתנה (#3495) ריוויו (cubic, P3) על docs/mcp-server.rst: "אותה תשובה שהכלי החזיר תמיד" אינו נכון. בקוד שלפני #3495 בדיקת הריקות רצה לפני בדיקת האורך, ולכן תוצאה של רווחים בלבד שארוכה מהתקרה נענתה empty_content. אותה הכללה הופיעה בעוד מקומות שה-PR כתב, ותוקנה בכולם: - docs/mcp-server.rst: מתאר את ההתנהגות של היום במלואה — תוצאה ארוכה מהתקרה, גם של רווחים בלבד, נענית content_too_long — בלי טענה על העבר (ההיסטוריה ב-whats-new). - handlers.py (docstring של note_str_replace): "התשובה שנתן גם לפני #3495 על תוצאה ארוכה מדי", והקצה היחיד שהשתנה נקוב בשמו. - docs/whats-new.rst: "התשובה על תוצאה ארוכה מדי היא אותה תשובה". - tests/test_mcp_new_note_tools.py: הטענה מוגבלת למקרה שהטסט בודק, ומפנה לטסט של הקצה. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhRqpoqm8WFN2KP7cJSKXJ
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
…שם הטסט (#3495) המשך לריוויו של cubic (P3). בתשובה בשרשור דחיתי את הנוסח "אותה תשובה שהכלי החזיר על תוצאה שחרגה מהתקרה", כי תוצאה של רווחים בלבד שחרגה נענתה עד #3495 empty_content. אבל באותו היקף נשארה הטענה ב-docstring של note_str_replace וב-whats-new, והחריג הופיע שם רק במשפט שאחריה. ושם הטסט test_a_result_over_the_ceiling_is_the_same_refusal_as_before טען אותו דבר על כל תוצאה מעל התקרה, בלי חריג בכלל (והסיכום של Sourcery העתיק ממנו "unchanged rejection responses"). - handlers.py ו-whats-new.rst: "על תוצאה ארוכה מדי שאינה רווחים בלבד". - הטסט נקרא עכשיו test_a_result_over_the_ceiling_that_is_not_only_whitespace_is_the_same_refusal_as_before. נבדק בהרצה ולא רק בקריאה: אותה מטריצה של 14 סוגי קלט רצה על הבסיס (d2f9e3b) ועל 6ba68c0, כל אחד ב-worktree נפרד. השתנו רק תוצאות של רווחים בלבד מעל התקרה (רווחים, ירידות שורה, ותו אחד מעל התקרה): עד #3495 empty_content, ועכשיו content_too_long, ובשני המקרים לא נכתב דבר. כל השאר זהה במילון המלא, כולל ambiguous_match על תוצאה שהייתה חורגת, ורווחים בלבד בדיוק בתקרה. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhRqpoqm8WFN2KP7cJSKXJ
There was a problem hiding this comment.
Your trial has ended. Reactivate Greptile to resume code reviews.
Code Review ✅ Approved🟡 Medium risk · Note replacements reject oversized output before allocation; whitespace-only errors also change. Fixes note replacement memory exhaustion by checking content size before building the result, preventing massive intermediate allocations on production. Makes OptionsAuto-apply is off → Gitar will not commit updates to this branch. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Gitar |
תבנית Pull Request
✨ תיאור קצר
codekeeper_note_str_replaceבודק עכשיו את אורך התוצאה לפני שהוא בונה אותה. עד היום הוא בנה את גוף הפתק החדש ורק אז מדד אותו, ולכןreplace_allעל מחרוזת קצרה עםnew_stringארוך בנה בזיכרון מחרוזת באורך "מספר המופעים כפול אורך ההחלפה" לפני הסירוב. על שרת חי, בקשה של כ-10KB הקפיצה את שיא הזיכרון של התהליך בכ-19MiB, ועל פתק מלא בכ-172MiB. בתקרות של הייצור זה כ-40GB — בקשה אחת שמפילה את שירות ה-MCP לכל המשתמשים. התשובה לסוכן על תוצאה ארוכה מדי שאינה רווחים בלבד לא השתנתה: אותוcontent_too_longעםmax, רק לפני ההקצאה. הקצה היחיד שהשתנה: תוצאה של רווחים בלבד מעל התקרה, שנענתה עד היוםempty_content.max_sizeשל_apply_editהפך לחובה, בלי ברירת מחדל, ו-call-argנוסף לשער החוסם של mypy ב-CI. כך כלי עתידי שיחליף טקסט דרך_apply_editוישכח את התקרה ייעצר ב-CI, ולא בייצור.📦 שינויים עיקריים
main.py, בלי שינוי התנהגות (פירוט למטה)פירוט נקודות:
mcp_server/handlers.py—_apply_edit:max_size: intחובה (במקוםint | None = None), והבדיקה המוקדמת רצה תמיד. ה-docstring מסביר למה, ומוסיף מקור לחשבון: זה בדיוק החשבון שלפיוstr.replaceעצמו מקצה את התוצאה (new_size = slen + n * (len2 - len1)ב-replace,Objects/unicodeobject.c, נקרא ב-CPython 3.11.15; הייצור רץ על 3.11 —Dockerfile,.python-version).mcp_server/handlers.py—note_str_replace: מעבירmax_size=MAX_NOTE_CONTENT, ו-code_too_largeשחוזר משם נענה{"ok": false, "error": "content_too_long", "max": MAX_NOTE_CONTENT}— בדיוק התשובה של היום על תוצאה ארוכה מדי שאינה רווחים בלבד (הקצה של רווחים בלבד — ב"השפעות/סיכונים"). הבדיקה שאחרי ההחלפה נשארה כרשת על מה שנכתב (ProductionBackend.update_noteאינו בודק אורך, נבדק), והיא אינה ההגנה: היא לא נדלקת כל עוד החשבון ב-_apply_editמדויק..github/workflows/ci.yml: השער של המעבר הרחב של mypy חוסם עכשיו גםcall-arg(attr-defined|return-value|call-arg). עד היוםcall-argהודפס ביומן ולא עצר דבר.main.py: המחלקה המקומית השנייה בשם_Ctxב-setup_bot_dataנקראת עכשיו_CtxPredictive(כמו_CtxMaintו-_CtxBkpשלידה). זה היה המופע היחיד שלcall-argבריפו — false positive של mypy, כי שתי מחלקות באותו שם באותה פונקציה — ובלי התיקון השער החדש היה אדום מהיום הראשון. שינוי שם בלבד.docs/mcp-server.rst— בשורתcodekeeper_note_str_replaceבטבלת הכלים (הגודל נבדק לפני הבנייה, כמו ב-codekeeper_edit_file), ובפסקה "גודל, לפני שהתוצאה נבנית".docs/whats-new.rst— שתי שורות: התיקון (כולל שינוי הקצה שלempty_content), ושער ה-CI.אתרי הקריאה של
_apply_edit(grep -rn "_apply_edit(")mcp_server/handlers.py—edit_filemax_size=max_code_size()(כבר מ-#3494)mcp_server/handlers.py—multi_edit_filemax_size=max_code_size()(כבר מ-#3494)mcp_server/handlers.py—note_str_replacemax_size=MAX_NOTE_CONTENT— חדשtests/test_mcp_edit_append.pymax_size, ואחת בכוונה בלעדיו — מוודאתTypeErrortests/test_mcp_server_build.pymax_size=MAX_NOTE_CONTENTהאם mypy באמת תופס קורא ששכח? נמדד, לא הונח: אחרי שהפכתי את
max_sizeלחובה ולפני שתיקנתי אתnote_str_replace, הפקודה של CI (mypy . --ignore-missing-imports --show-error-codes) החזירהmcp_server/handlers.py:2095: error: Missing named argument "max_size" for "_apply_edit" [call-arg]— גם ב-mypy 2.4.0 (מה שה-CI מתקין, בלי נעיצה) וגם ב-1.18.2 (הנעוץ ב-requirements/development.txt). אבל השער של CI לא חסםcall-arg, כלומר החובה הייתה נבדקת רק בזמן ריצה. לכן השער הורחב. הטסטים מוחרגים מ-mypy (mypy.ini) — שם הקריאות נבדקות בהרצה.תקרה אחת
MAX_NOTE_CONTENTב-mcp_server/handlers.pyהואfrom sticky_notes_target import MAX_NOTE_CHARS as MAX_NOTE_CONTENT— אותו אובייקט, לא עותק — ו-tests/test_note_boards_api.py::test_the_limit_has_a_single_sourceכבר מצמיד אותו לוובאפ. לא נוסף עותק שלישי: הטסטים והתיעוד נוקבים בשם, לא במספר.החשבון באישו, מתוקן
באישו כתוב "גוף בקשה של 1MiB — כ-20GB". תקרת הגוף נגזרת מ-
MAX_CODE_SIZE(request_bytes_forב-mcp_server/limits.py): על ברירת המחדל שבקוד (100,000) היא 1MiB, ועל 300,000 — הערך בייצור כפי שמסרת (את הסביבה ב-Render לא קראתי) — היא 2MiB (נמדד:request_bytes_for(300_000) == 2_097_152). כלומר פתק שלMAX_NOTE_CHARSתווים כפולnew_stringשל כ-2MiB — כ-40GB. המסקנה זהה. תיקנתי את אותו מספר גם במסמך ההצעה ב-Ck (הצעת-דפוס-תקרה-אחרי-הגברה.md); את גוף האישו לא ערכתי.סריקה לאותו דפוס (
mcp_server/handlers.py,mcp_server/backend.py)סריקת AST של כל
*,.replace,.join,.subודומיהם בשני הקבצים:_apply_edit(code.replace(old, new)), ועכשיו כל שלושת הקוראים שלו מוגנים._sanitize_note_text—.replaceו-re.subרק מקצרים.apply_line_range—"\n".joinעל פרוסה של הקובץ, לא יותר ממנו.append_file—code + sep + contentנבנה לפני ש-_resave_editedמודד אותו, אבל זה סכום ולא מכפלה: חסום בתקרת הגוף. לא הדפוס.backend.py— אין בוjoin,replaceאוsubבכלל; ה-+=שם מספריים.mcp_server/*.py: אין מופע נוסף (redaction.pyמחליף התאמה במסמן קבוע, ובמקרה הגרוע גדל פי שניים בערך — לא בשליטת הבקשה).🧪 בדיקות
טסטים חדשים:
tests/test_mcp_multi_edit.py::test_a_note_replace_all_is_checked_before_its_result_is_built— דרךcall_toolעלbuild_mcpאמיתי, מעלProductionBackendאמיתי (הפתק באוסףsticky_notesשל הדמה המשותפת), בדגם שלtest_a_single_replace_all_is_checked_before_its_result_is_built. בודק את התשובה, שהגבול על שיא ההקצאה אינו תלוי במספר המופעים —peak < 20 × request_bytes— ושבמסד שום דבר לא נכתב (הפתק נקרא מהאוסף, ואין צילום ב-sticky_note_versions). פרמטרי: 2,000 מופעים (האישו) ופתק מלא עד התקרה.tests/test_mcp_new_note_tools.py— על תוצאה שאינה רווחים בלבד צורת הסירוב זהה להיום (שוויון מלא, בלי שדות נוספים; עובר גם על הקוד הישן, וזו הכוונה); הגבול בתו (MAX_NOTE_CONTENTנכתב, תו אחד יותר נדחה); ושינוי הקצה של רווחים בלבד.tests/test_mcp_edit_append.py— החשבון מדויק בתו (6 עובר, 5 נדחה), ו-_apply_editבליmax_sizeהואTypeError.לפני ואחרי —
tracemallocדרךcall_tool(בקשה של 10,097 בתים, גבול 201,940):MAX_NOTE_CHARSמופעים)וטסט הקצה של רווחים בלבד נופל על הקוד הישן (
empty_content), כמצופה.שרת חי —
uvicorn mcp_server.app:appבמצב PAT, מעל mongod 8.0.15 אמיתי, ולקוח MCP אמיתי (streamable_http_clientשלmcp 1.28.1) על HTTP. אותו תסריט על שני עצים; הזיכרון הואVmHWMשל תהליך השרת (/proc/<pid>/status) לפני ואחרי כל קריאה, ומה שבמסד נקרא אחרי כל קריאה ב-pymongo:content_too_long, שיא ה-RSS +19,184KBcontent_too_long, +92KBcontent_too_long, +175,900KBcontent_too_long, +56KBempty_contentcontent_too_longok, במסד 20,000 תווים, גרסה אחתok, במסד 20,000 תווים, גרסה אחתבכל הסירובים הפתק במסד נשאר כמו שהיה ולא נכתבה גרסה. שיא ה-RSS של השרת אחרי כל הקריאות: 285,276KB לפני, 90,748KB אחרי.
מוטציות — כולן ב-
git worktreeנפרד, וכולן נתפסו:note_str_replaceבליmax_size_apply_editnote_str_replaceעם תקרה ענקית>=במקום>MAX_NOTE_CONTENT - 1max_sizeחזרהtest_apply_edit_has_no_default_ceilingcontent_too_longmax(M1 ו-M2 אינם מפילים את טסט הצורה ואת טסט הגבול — הרשת שאחרי ההחלפה עונה שם נכון. הם נתפסים בשיא ההקצאה, וזה התפקיד של טסט הזיכרון.)
שאר הבדיקות:
-n auto --dist=loadscope, כמו ב-CI), על הקוד הסופי: 7,203 עברו, 313 דולגו, 0 נכשלו (Python 3.11.15; הדילוגים הם מה שמדלג גם ב-CI — מונגו אינו נגיש ובדיקות הדפדפן). ובקבצים שנגעתי בהם גם ב-Python 3.12.3, השנייה במטריצה: 379 עברו, והשיא בטסט הפתק 17,916 ו-16,652 בתים.note_str_replaceרצה על d2f9e3b ועל 6ba68c0, כל צד ב-git worktreeנפרד. השתנו רק תוצאות של רווחים בלבד מעל התקרה (רווחים, ירידות שורה, ותו אחד מעל התקרה): עד היוםempty_content, עכשיוcontent_too_long, ובשני המקרים לא נכתב דבר. כל השאר זהה במילון המלא, כוללambiguous_matchעל תוצאה שהייתה חורגת, ורווחים בלבד בדיוק בתקרה (empty_contentבשניהם).call-arg/attr-defined/return-valueגם ב-1.18.2.scripts/generate_ai_map.py --check— עדכני.toctree— ה-check של RTD יתפוס אזהרות.🧪 בדיקות נדרשות ב‑PR
📝 סוג שינוי
call-argבשער של mypy)✅ צ'קליסט
docs/whats-new.rst)docs/mcp-server.rst| המשפט: "אותה בדיקה חלה גם עלcodekeeper_edit_file:replace_allעםnew_stringארוך נדחה לפני שהתוצאה נבנית."עיון ב-CodeBot – Project Docs: כן.
AI-MAP.md;docs/mcp-server.rst(טבלת הכלים, "כמה עריכות בגרסה אחת" ו"גודל, לפני שהתוצאה נבנית", "קריאת פתק בודד", "היסטוריית פתקים", פתרון תקלות);docs/whats-new.rst;docs/doc-authoring.rst;docs/versioning-stable-anchors.rst;docs/style-glossary.rst;docs/testing.rst;docs/dev/sticky_notes_extending.rst("התיעוד למשתמש הוא חלק מהפיצ'ר");docs/user/sticky_notes.rst— נבדק ש"מעבר לתקרה השמירה נדחית ומוחזרcontent_too_long" נשאר נכון (ועכשיו גם בקצה של רווחים בלבד), ולכן העמוד לא שונה.דפוסי
amir-bug-patternsשנדלקו ונקראו: U3 ו-external-input-isinstance(תקרה היא סירוב עם קוד משלו, ואינה תחליף לתקרת הגוף ולהגבלת הקצב); H6 (התקרה בתווים, החשבון בתווים,maxבתווים — אין ערבוב יחידות);silent-truncation-at-sink;prose-restates-code-fact(grep עלmax_size,_apply_edit,note_str_replace,empty_content,content_too_longבכל הפרוזה; שמות ולא ערכים);claude-md-snippets/testing.mdו-TESTING-PATTERNS.md(דרך הממשק של הצרכן, בתצורת הייצור, ונופל על הקוד הישן);widened-exception-scope;line-number-coupling;host-metric-in-container(העלות נמדדת בשיא —tracemallocו-VmHWM— ולא אחרי); K15 ו-lazy-init-guard-publish-order("לפני שמסירים התנהגות מנוונת — מודדים מי נשען עליה":Noneכ"בלי תקרה" — קורא ייצור אחד ושבע קריאות בטסטים, כולם עודכנו); R6;write-from-cached-read(הקריאה של הפתק היאfind_oneישיר, לא קאש); K11. ובנוסף ההצעה ב-Ck:הצעת-דפוס-תקרה-אחרי-הגברה.md— אין לה פתקים.🧩 השפעות/סיכונים
content_too_longולאempty_content. שני הסירובים אינם כותבים דבר.call-argייכשל ב-"🔍 Code Quality & Security". זו הכוונה. היום אין אף אחת בריפו (mypy 2.4.0). ה-CI מתקין mypy בלי נעיצה, ולכן גרסה חדשה שתזהה מקרים חדשים יכולה להאדים את השער — אותו סיכון שכבר קיים ל-attr-definedו-return-value.str.countאחד שכבר רץ קודם — אין עבודה נוספת.🔗 קישורים
docs/mcp-server.rst— "גודל, לפני שהתוצאה נבנית"🧯 סיכון / החזרה לאחור (Rollback)
סטיות מהתוכנית
call-arg, ושינוי השם ב-main.py. ביקשת לוודא ש-mypy ב-CI תופסmax_sizeחסר — הוא סימן אותו, אבל השער לא חסם עליו. בלי זה "חובה" הייתה נבדקת רק בזמן ריצה.backend.update_noteאינו בודק אורך, וכל שאר כותבי הפתקים בודקים בגבול הכתיבה.🤖 Generated with Claude Code
https://claude.ai/code/session_01DhRqpoqm8WFN2KP7cJSKXJ
Generated by Claude Code
<source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg">Summary by Sourcery
Reject oversized note replacements before constructing their results and enforce explicit edit size limits through mypy and CI.
Bug Fixes:
Enhancements:
CI:
Documentation:
Tests: