docs: add note boards and expand sticky notes documentation - #3256
Open
dosubot[bot] wants to merge 67 commits into
Open
docs: add note boards and expand sticky notes documentation#3256dosubot[bot] wants to merge 67 commits into
dosubot[bot] wants to merge 67 commits into
Conversation
אירוע אמיתי מפרודקשן (CODEKEEPER-2Z) הראה את הטוקן המלא בתוך breadcrumbs של httplib — הערוץ שה-before_send העמוק מכסה, אבל רק באירועי שגיאה. אירועי ביצועים (transactions, נדגמים ב-10%/5%) נושאים את אותם URL-ים ב-spans ועוקפים את before_send לחלוטין. נוסף before_send_transaction עם אותו ניקוי fail-closed בשני השירותים, וטסט על אירוע בצורת transaction (spans + breadcrumbs + request.url). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שלושה ממצאי ריוויו על שכפולים שנוצרו תוך כדי הסבבים: - נוסף scrub_sentry_event ב-telegram_api — הניקוי העמוק + fail-closed במקום אחד, ושני ה-before_send (בוט ווובאפ) מאצילים אליו במקום להחזיק עותקים שכבר התחילו להתפצל. - הדפוס נחשף כ-BOT_TOKEN_RE ציבורי, ו-SensitiveDataFilter מייבא אותו ישירות בלי עותק fallback מקומי — עותק כזה מתפצל בשקט מהמקור, וזה גרוע יותר מכשל ייבוא קולני (telegram_api תלוי רק בספריה הסטנדרטית). - שלושת טסטי ה-Formatter חלקו תשתית של 13 שורות — אוחדה ל- _filtered_record_for, וכל טסט מספק רק את החריגה ואת ה-asserts. נוסף טסט ל-scrub_sentry_event: ניקוי תקין + החזרת None בכשל. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
…ר טוקן repr(None) לעולם לא מכיל טוקן, כך שהבדיקה הקודמת הייתה עוברת גם אם הפונקציה הייתה מפילה כל אירוע. עכשיו מאומת שהאירוע חזר ושההודעה נוקתה לערך המדויק. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
המונים ("1529 files" בכותרת ובתחתית הסיידבר) רונדרו בשרת פעם אחת בזמן
טעינת הדף, מתוך `metadata` שנטען ב-`repo_index`. `updateRepoDisplay` —
הפונקציה שמרעננת את ה-UI בהחלפת ריפו — עדכנה שם ריפו, dropdown ופריט
פעיל, ולא נגעה בהם. לכן בכל ריפו שעברו אליו בלי לרענן את הדף הוצג מספר
הקבצים של הריפו הראשון.
זה דפוס U5 / `linked-field-atomicity`: פעולה לוגית אחת דורשת עדכון של
קבוצת שדות מקושרים, והקוד מעדכן חלק מהם ושוכח את השאר.
**התיקון**
הוספת שתי שורות ל-`updateRepoDisplay` הייתה מתקנת את התסמין ומשאירה את
השורש — המונים ממולאים משני מסלולים נפרדים, ואף אחד לא מחייב שיישארו
מסונכרנים. במקום זה כל אלמנט מונה מסומן ב-`data-repo-stat`, ופונקציה
אחת (`renderRepoStats`) היא נקודת העדכון היחידה אחרי טעינת הדף. מונה
חדש שיסומן בתבנית יתעדכן מאליו.
הערכים נלקחים מ-`repoMetadataByName` שכבר יושב בזיכרון מ-`/api/repos`,
ולכן העדכון סינכרוני. בדקתי לפני שכתבתי: אין `await` במסלול, ולכן דפוס
U2§5 (תגובה מאוחרת שדורסת חדשה) לא חל וההגנה שתכננתי מיותרת.
כשאין מטא-דאטה לריפו הפונקציה לא נוגעת בכלום, כדי שכשל רגעי של
`/api/repos` לא ימחק ערך תקין שהשרת רינדר.
**הסרת מוני השפות**
"979 48" בתחתית היו מספר קבצי Python ו-JavaScript, עם `title` בלבד —
ובטאבלט אין hover, אז הם נראו כמו מספרים אקראיים. הוסרו לבקשת המשתמש.
בעקבות זה האגרגציה ב-`repo_index` שחישבה אותם הפכה לקוד מת והוסרה,
מה שחוסך שאילתת aggregation ב-MongoDB בכל טעינת דף. `/api/file-types`
נשאר — הוא משרת את סרגל הסינון ולא קשור.
**בדיקות**
`tests/test_repo_browser_stats_sync.py`. הסימונים נגזרים מה-HTML שנוצר
ולא מרשימה קשיחה, לפי כלל 1 ב-`claude-md-snippets/testing.md`.
כל ארבעת השומרים אומתו מול הקוד שלפני התיקון ונפלו: הסרת הקריאה
מ-`updateRepoDisplay`, מונה מסומן שאיש לא מטפל בו, חזרת מוני השפות,
והסרת היציאה המוקדמת. הבדיקות סטטיות ולא מריצות דפדפן — המגבלה מתועדת
בקובץ עצמו.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
הקומיטים שבענף המרוחק מוזגו ל-main ב-#3237 (squash), ולכן ה-SHA שלהם שונה אף שהתוכן כבר שם. אימתתי: before_send_transaction ו-scrub_sentry_event קיימים ב-main. ה-merge הזה מאחד את ההיסטוריות בלי להחזיר את התוכן הישן, כדי לא לדרוס את הענף המרוחק ב-force push.
שלושה ממצאים מ-cubic, כולם אומתו מול הקוד ותוקנו.
**1. `Number(null)` הוא 0**
`getRepoTotalFiles` השתמשה ב-`Number()` לפני `Number.isFinite`. אימתתי:
`Number(null)`, `Number(false)` ו-`Number('')` כולם מחזירים 0, ו-`isFinite`
מאשר אותם. ריפו שסונכרן חלקית היה מציג "0 files" — מספר שנראה אמיתי ואינו.
עכשיו נבדק `typeof === 'number'` לפני הכל.
לא אימצתי את ההצעה לתמוך גם במחרוזות: הערך נכתב ב-`repo_sync_service` וב-
`git_mirror_service` כ-`len(...)`, כלומר `int` בלבד, וטיפול במחרוזות היה
קוד ספקולטיבי לתרחיש שלא קיים.
**2. הערה שהבטיחה יותר ממה שהקוד עושה**
ההערה טענה שמונה חדש שיסומן בתבנית "יתעדכן מכאן מאליו", אבל `values` ממפה
`total_files` בלבד — מונה בלי מיפוי יוצג כ-`—`. ההערה תוקנה לתאר את שתי
הפעולות הנדרשות בפועל.
**3. הטסטים בדקו צורה ולא התנהגות**
זה הממצא המשמעותי. `assert "renderRepoStats" in body` היה עובר גם אם הקריאה
מופיעה רק בהערה, וכל הסוויטה הייתה ירוקה מול רגרסיה ששומרת על הצורה ושוברת
את הפלט.
`tests/test_repo_browser_stats_behavior.py` מריץ את `renderRepoStats` ואת
`getRepoTotalFiles` עצמן, כפי שהן כתובות בקובץ, מול DOM מינימלי ב-Node.
שתיהן נוגעות רק ב-`querySelectorAll`, `dataset` ו-`textContent`, ולכן כפיל
קטן מספיק. לא נעשה שימוש ב-Playwright: הוא לא מותקן וה-CI לא מריץ אותו,
כך שטסט כזה לא היה מגן על כלום.
מדדתי את ההפרש על שלוש רגרסיות אמיתיות — כמה טסטים נופלים בכל שכבה:
| רגרסיה | סטטי | התנהגותי |
|---|---|---|
| חזרה ל-`Number()` | 0 | 4 |
| סלקטור שגוי | 0 | 10 |
| קריאת שדה שגוי | 0 | 3 |
השכבה הסטטית נשארת — היא תופסת מונה שנוסף לתבנית בלי מיפוי, וזה דבר
שהתנהגותי לא רואה. המגבלה שנותרה מתועדת בקובץ: פריסה בדפדפן, CSS וסדר
טעינה דורשים דפדפן אמיתי.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
הצד השני של סגירת הלולאה: הדפוס תועד ב-amir-bug-patterns PR #12, וכאן נוספת שורת הטריגר שתגרום לו להיקרא בזמן המימוש. דפוס בלי טריגר הוא דפוס שלא ייקרא. הטריגר שלו הוא פעולת עריכה ולא נושא, ולכן לא היה מכוסה באף שורה קיימת: מי שמתקן ממצא PII בלוג לא מחפש בטבלה "לוגים" — הוא פשוט מוחק שורה. הדפוס השני מאותו PR (אובייקט SDK עצל) כבר מכוסה בשורת "PyGithub / קריאות SDK חיצוני", ולכן לא נוספה עבורו שורה. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
הניסוח הקודם ("וירידה ל-debug") היה לא מדויק: ב-Python הארגומנטים של
קריאת הלוג מוערכים לפני הקריאה, גם כשהרמה מנוטרלת — ולכן ירידה ל-debug
לבדה אינה מסירה את תופעת הלוואי. ה-guard היחיד שכן עוצר את ההערכה הוא
`if logger.isEnabledFor(...)`, והוא הטריגר הנכון.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
`isEnabledFor` הוא ה-guard הנפוץ אבל לא היחיד: `if verbose:`, דגל פיצ'ר, או כל תנאי חוסם מונעים את הערכת הארגומנטים באותה מידה. הניסוח הקודם היה מפספס אותם. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
# Conflicts: # CLAUDE.md
בתיקיית ``workflows`` היו ארבע זרימות — שמירה, חיפוש, ריפקטורינג וגיבוי — ולא הייתה זרימת Gist, אף שהיא אחת מזרימות המשתמש המרכזיות. ``autodoc`` לא יכול לכסות את הפער: הידע מפוזר על פני ארבעה handlers, ``integrations`` ו-``database/repository``, ולא נמצא באף docstring בודד. הדף מכסה את מה שהיה חוסר את הבאג ב-PR #3241: - ארבע נקודות הכניסה, וכולן דרך ``asyncio.to_thread`` - תרשים רצף מלא כולל שלושת מסלולי הכשל - שלושת מצבי ``auth_failed`` ולמה זה לא בוליאני - למה הסיווג נעשה לפי תת-מחלקות של PyGithub ולא לפי קוד סטטוס - למה ``_ = user.login`` אינו קוד מת, ומה קרה כשהוא נמחק כל התוכן אומת מול הקוד ב-main. הבילד נבדק עם ``sphinx -W`` (כמו RTD) ועובר עם אפס אזהרות; הדף רשום ב-``docs/workflows/index.rst``. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
חמישה ממצאים, כולם אומתו מול הקוד ב-main לפני התיקון:
- ``_export_gist`` קורא ל-``create_gist_multi``, לא ל-``create_gist``.
התרשים הציג מתודה אחת לכל המסלולים ויצר חוזה שגוי לייצוא ריפקטורינג.
- ``_export_gist`` בודק ``not result or not result.get("url")``. התיעוד
תיאר רק את בדיקת ``result``.
- ``auth_failed`` הוא ``Optional[bool]``. הניסוח "לא בוליאני" מטעה.
- ה-fail-closed תואר כמוחלט. בפועל ``create_gist`` בונה את מילון התוצאה
אחרי שה-``POST /gists`` כבר הצליח, ולכן כשל בשלב הזה מחזיר ``None``
בעוד שה-Gist כבר קיים. ניסיון חוזר ייצור כפילות. הניסוח שונה
ל"אין קישור" במקום "אין Gist", והחלון תועד במפורש.
- ``logger.exception`` לא מבטיח הגעה ל-Sentry: בלי DSN, ``init_sentry``
יוצאת מוקדם ו-``LoggingIntegration`` לא מותקנת. ההבטחה סויגה ללוג.
בנוסף: ההפניה ל-amir-bug-patterns הפכה לקישור אמיתי במקום טקסט.
הבילד נבדק שוב עם ``sphinx -W`` — קוד יציאה 0, אפס אזהרות.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
`_share_to_gist_multi` קורא ל-`create_gist_multi` (bot_handlers.py:5238), לא ל-`create_gist`. בתיקון הקודם מופה רק מסלול הריפקטורינג והמסלול השלישי לא נבדק מחדש. עכשיו טבלת נקודות הכניסה כוללת עמודת "מתודת היצירה" כך שהמיפוי מפורש לכל ארבעת המסלולים, והתרשים מפצל קובץ בודד ← create_gist, מרובה וריפקטורינג ← create_gist_multi. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שלבים 0–1 של תוכנית עיגון התיעוד (אושרה 19.8).
הבעיה: עשרה בלוקי literalinclude ב-conversation-handlers.rst מוענו לפי
מספרי שורות. שמונה מהם כבר הציגו קוד לא קשור — הבולט שבהם הצביע על
main.py:739 ("הגדרת upload_conv_handler") והציג פנימיות של notify_admins,
במרחק 2,900 שורות מהיעד האמיתי (3686). דפוס line-number-coupling.
שלב 0 — תיקון עשרת הבלוקים:
- שבעה יעדים שהם פונקציה/מתודה שלמה ← ``:pyobject:`` (נמשך לפי שם בכל
בנייה; שינוי שם מפיל את הבנייה ברעש).
- שלושה יעדים שהם קטע בתוך פונקציה ← הערות סימון ``# docs:<שם>:start/end``
בקוד, ומיעון ``:start-after:``/``:end-before:``. הערת הסימון אומרת
שהתיעוד תלוי בה ובאיזה דף, כדי שלא תוסר כהערה סתומה.
- המוסכמה תועדה ב-docs/doc-authoring.rst.
שלב 1 — tests/test_docs_literalinclude_anchors.py:
- אוסר ``:lines:`` בכל docs/**/*.rst, עם הודעת כשל שמסבירה את החלופות.
- מאמת שכל marker שהתיעוד מפנה אליו קיים בקובץ המקור פעם אחת בדיוק —
חסר מפיל את הבנייה ממילא, אבל כפול היה מרנדר קטע שגוי בשקט.
- בדיקת אי-ריקנות: אם כל המיעונים ייעלמו, הבדיקה לא תעבור על כלום.
אימות (משמעת testing.md):
- הטסט נופל על ה-rst שלפני התיקון (test_no_line_number_addressing) ועל
מוטציית marker כפול (exists_exactly_once) — שניהם הורצו בפועל.
- הבנייה עם ``sphinx -W`` עוברת נקי, וכל 11 הבלוקים נבדקו מול ה-HTML
המרונדר: כל כיתוב פותח בקוד שהוא מבטיח.
- py_compile ו-flake8 נקיים על השורות שהשתנו; השינוי בקבצי המקור הוא
הערות בלבד.
גבול מוצהר: הפניות שורה בפרוזה (כמו קישורי #L123) אינן מכוסות בטסט
ונשארות בטריגר של line-number-coupling.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
…h' into claude/bot-hacked-messages-comsrh
שלב 2 של תוכנית עיגון התיעוד. באתר כ-240 עמודים וסוכן שמחפש תשובה גורר את כולם או מוותר; AI-MAP.md נותן שורה לכל עמוד ידני — נתיב, כותרת ותקציר — כדי שהחיפוש יתחיל מקובץ אחד. - scripts/generate_ai_map.py — הולך על ה-toctree מ-docs/index.rst (מקור האמת של Sphinx), שולף כותרת ופסקת פרוזה ראשונה תוך דילוג על directives/admonitions/רשימות, מנקה תחביר RST, ומסנן עמודי פיגום autodoc (55 כאלה) עם שורת סיכום שמשאירה את ההשמטה גלויה. - AI-MAP.md בשורש הריפו, לא ב-docs/: MyST פעיל וכל md בתוך docs/ נכנס לבניית Sphinx — עמוד יתום מפיל את RTD. - .github/workflows/ai-map.yml — מרענן את המפה אחרי כל שינוי תיעוד שהגיע ל-main ומקמט בעצמו (בתבנית של monthly-redeploy). - tests/test_ai_map_generator.py — דטרמיניזם בייט-בבייט, כיסוי עמודים ידועים, סינון פיגומים, ואיסור חותמות זמן בשורות שהמחולל מחבר. אין השוואת רעננות מול הקובץ בריפו — בכוונה; את הרענון עושה ה-Action. סטייה מהתוכנית המאושרת: התוכנית אמרה "Action מקמט לענף ה-PR". שונה לרענון על main אחרי מיזוג, משתי סיבות: (1) דחיפה של GITHUB_TOKEN לענף PR אינה מפעילה מחדש את בדיקות החובה וה-PR היה נתקע ממתין להן; (2) למפה נגזרת אין ערך ריוויו ב-diff של PR. התוצאה זהה: המפה מתעדכנת בלי שאף אחד נדרש לטרמינל. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שישה ממצאי ריוויו על שלב 2, כולם אומתו לפני מגע: 1. (P1, תקף) עמוד פיגום עם toctree מקונן חסם את הירידה לילדיו — מדדתי: 9 עמודי פיגום כאלה, ועמודים ידניים כמו api/handlers.documents נעלמו מהמפה. התיקון הראשון שכתבתי היה no-op (הוספתי ל-visited לפני הקריאה ל-walk, שפותחת בבדיקת visited) — המספרים חשפו את זה: 190/55 לא זזו. אחרי התיקון האמיתי: 228 נסרקו, 92 פיגומים, והדפים החבויים מופיעים עם תקציר. 2. (P2, תקף) גדר קוד בתוך תקציר שברה את ה-Markdown של שורת המפה — איסוף הפסקה נעצר עכשיו על ```/:::/טבלה, ו-backticks שנותרו מנוקים. 3. (P2, חלקי) הרצה מקומית דורסת קובץ tracked — נוספו --out ו---check. ברירת המחדל נשארת AI-MAP.md בשורש בכוונה: זה תוצר נגזר שדריסתו היא המטרה, כמו lockfile. 4. (P2, תקף) concurrency משותף בין refs — הרצה ידנית מענף אחרת ביטלה רענון רץ של main. עכשיו group לפי github.ref. 5. (P2, תקף) git diff --quiet מפספס קובץ untracked — הוחלף ב-status --porcelain. 6. (P2, תקף-בספק) הגנת ענף עלולה לדחות push ל-main. בדקתי את הראיות: branch-protect.yml הוא workflow_dispatch בלבד ודורש PAT, אין עדות שרץ אי-פעם — אבל גם אין עדות ש-push של actions ל-main עבד אי-פעם (.build-bust של monthly-redeploy לא קיים ב-main, ואפס קומיטים של github-actions בהיסטוריה; ה-|| true שלו מסתיר כשל). לכן נוספה נפילה מבוקרת: אם הדחיפה נדחית — ענף automation/ai-map-refresh קבוע ו-PR שמתעדכן במקום להיערם. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
…Action
תשעה פתילים פתוחים, כולם אומתו מול הקוד והפלט לפני מגע. שמונה תוקנו,
ואחד הוא חזרה שלי מהחלטה קודמת:
המחולל (scripts/generate_ai_map.py):
- ילדי פיגום מסונן נכתבו בעומק depth+1 אף שהפיגום עצמו לא נכתב —
ההזחה קפצה רמה (MD005). עכשיו הם תופסים את מקומו באותו עומק, ותקרת
ההזחה min(depth,2) שיצרה אחים לא-עקביים הוסרה. אומת בסריקת קפיצות
הזחה על המפה כולה.
- קישור RST חיצוני השאיר "_" יתום אחרי הסרת ה-backticks
(docs/mcp-server.rst). נרמול `label <url>`_ ← label לפני הניקוי.
- הוסרו שני תווי כיווניות נסתרים (U+200F) שהפילו את CodeFactor.
- חזרה מהחלטה: בסבב הקודם השארתי כתיבה ל-AI-MAP.md כברירת מחדל
בנימוק "תוצר נגזר כמו lockfile". שני ריוויוארים חזרו על הממצא
והצביעו על סעיף המדיניות המפורש ("אל תכתוב ב-root של הפרויקט").
המדיניות גוברת על האנלוגיה שלי: בלי --out שום קובץ לא נכתב (המפה
מודפסת ל-stdout), --check משווה בלי לכתוב, וה-workflow מעביר
--out AI-MAP.md במפורש.
הטסט (test_docs_literalinclude_anchors.py):
- ה-regex דרש שלושה רווחים בדיוק לפני אופציות — בלוק :lines: בהזחת
ארבעה רווחים חמק מהבדיקה כולה. הורחב ל-[ \t]+ ונוספה בדיקת רגרסיה
שמזינה בלוק כזה דרך tmp_path.
ה-Action (ai-map.yml):
- אחרי git pull --rebase המפה שנוצרה כבר מיושנת, והקומיט החוזר לא
היה מפעיל ריצה נוספת (נוגע רק ב-AI-MAP.md שאינו ב-paths) — עכשיו
מייצרים מחדש ומעדכנים את הקומיט לפני הדחיפה השנייה.
- PR שנוצר ב-GITHUB_TOKEN לא מפעיל בדיקות חובה: אם הסוד
REPO_ADMIN_TOKEN מוגדר משתמשים בו (branch-protect.yml כבר מצפה לו),
ואם לא — גוף ה-PR מסביר איך להפעיל את הבדיקות ידנית. בלי העמדת פנים.
- כשל אמיתי של gh pr create כבר לא נבלע: בדיקת קיום מפורשת עם
gh pr view, וכשל יצירה מפיל את הריצה ברעש.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
…סטים ל-CLI סבב ריוויו שלישי — ארבעה ממצאים, כולם תקפים: 1. (P2, החמור שבהם) עמוד RST שנפתח בטבלה תקע את המחולל בלולאה אינסופית: "|" עצר את איסוף הפסקה לפני קידום האינדקס, ואותה שורה נבדקה שוב לנצח. שוחזר בהרצה עם SIGALRM לפני התיקון, ואומת אחריו. שום דף נוכחי לא נפתח בטבלה — הדף הראשון שכן היה תוקע את ה-Action בלי שגיאה. נוספה בדיקת רגרסיה. 2. (P1) הרצה ידנית מ-ref שאינו main הייתה, בכשל דחיפה, פותחת PR שגורר קומיטים זרים לתוך main. הג'וב מוגבל עכשיו ל-main. 3. (P2) קבוצות concurrency לפי ref עם ענף אוטומציה משותף אפשרו לריצה אחת לדרוס PR של אחרת — נסגר מאליו על ידי הגבלת ה-main: כל הריצות באותה קבוצה, בזו אחר זו. 4. (P2) מצבי ה-CLI לא היו מכוסים: נוספו טסטים ל-stdout בלי כתיבה (כולל אימות שאף קובץ בשורש לא השתנה), --out לנתיב נתון, וקודי היציאה של --check על קובץ תואם וסוטה. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שבעה ממצאים (סבב רביעי, ultrareview), כולם אומתו ותוקנו: - (P1) actions/checkout שומר את GITHUB_TOKEN בקונפיג של git, ולכן git push לענף האוטומציה התעלם מ-GH_TOKEN — אירוע ה-synchronize לא היה מפעיל את בדיקות החובה גם כש-REPO_ADMIN_TOKEN מוגדר. הטוקן עבר ל-checkout עצמו, ועכשיו כל הדחיפות משתמשות בו. - (P2) rebase כושל השאיר את עץ העבודה באמצע rebase וכל פקודה הבאה נכשלה בשגיאה מבלבלת — נוסף git rebase --abort לפני ה-fallback. - (P2) ענף האוטומציה נבנה עכשיו מ-origin/main נקי והמפה מיוצרת בו מחדש — קומיט אחד בדיוק, בלי היסטוריה מקומית חלקית, וכולל בדיקה שהמפה בכלל עוד נדרשת אחרי הסנכרון. - (P2) gh pr view החזיר "אין PR" גם על שגיאת API/הרשאה — הוחלף ב-gh pr list שמחזיר רשימה ריקה כשאין ונכשל ברעש על שגיאה אמיתית. - (Minor) ערך שמופיע פעמיים באותו בלוק toctree נכתב פעמיים — בדיקת visited נוספה בתוך הלולאה. - (Minor) בלוק literalinclude בסוף קובץ בלי newline סופי חמק מהסריקה — ה-regex הורחב ל-(?:\n|$) ונוספה בדיקת רגרסיה. - (Minor) הטסטים של ה-CLI רצים עכשיו מ-tmp_path (לא משורש הפרויקט), ובדיקת הלולאה האינסופית עברה מ-SIGALRM (לא קיים ב-Windows) לת'רד עם join-timeout. 18 טסטים עוברים; flake8 נקי על הקבצים שהשתנו; --check עדכני. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
הסריקה הקודמת ניקתה שניים אבל תו שלישי נכנס עם טקסט העזרה החדש. נבדק עכשיו על כל טווח תווי הכיווניות, לא רק RLM/LRM. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שני ממצאים (סבב חמישי), שניהם תקפים: - (P2) טסט הלולאה האינסופית הוכיח אי-תלייה אבל לא את דילוג הטבלה: שורות הטבלה בו היו קצרות מסף 20 התווים, ולכן מוטציה שמסירה את הטיפול ב-"|" עברה אותו (אומת בהרצת המוטציה — עברה). חוזק: שורות טבלה ארוכות מהסף + אסרט שהתקציר אינו מכיל "|". המוטציה עכשיו נופלת (אומת), והקוד התקין עובר. - (P1) בלי REPO_ADMIN_TOKEN, ה-PR שה-fallback יוצר תקוע: בדיקות החובה לא רצות עליו וזה קרה בשקט, בריצה ירוקה. עכשיו במצב הזה הריצה נכשלת בכוונה עם ::error שמסביר בדיוק מה לעשות — PR חסום כבר לא נשאר ברירת מחדל שקטה. עם PAT — הכול רץ וירוק כרגיל. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שני ממצאים (סבב שישי), שניהם תקפים ושניהם אותו דפוס — מסלול שקט שמתחמק מעיקרון שכבר נקבע: - (P1) התיקון הקודם הפיל את הריצה בלי PAT רק במסלול יצירת PR חדש; במסלול "PR כבר קיים" הריצה יצאה ירוקה אחרי דחיפה שלא תפעיל בדיקות. אותה בדיקת HAS_PAT הוחלה גם שם. - (P2) הרצה עם --check ו---out יחד קיבלה את שניהם והתעלמה בשקט מ---out — הקורא ביקש כתיבה וקיבל השוואה בלבד. השילוב נדחה עכשיו במפורש עם parser.error (אומת: קוד יציאה 2 והודעה ברורה). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שני ממצאים אחרונים: - (P1, אבטחה) checkout שמר את REPO_ADMIN_TOKEN (בעל הרשאות אדמין) ב-.git/config לאורך כל הריצה — כולל בזמן שהמחולל רץ. עכשיו persist-credentials: false, והאישור מוזרק נקודתית ב-remote set-url בתוך שלב הדחיפה בלבד. המחולל רץ בשלב קודם, בלי הטוקן בסביבה ובלי שום דבר שמור בדיסק. שארית מתועדת: הרגנרציה שבתוך שלב ה-fallback רצה עם GH_TOKEN בסביבת השלב — קוד שלנו, שנסקר כאן. - (P3) ה-docstring עוד הצהיר שהפלט נכתב לשורש ללא תנאי — עודכן להתנהגות של --out. הממצא השלישי ברשימת cubic (טסט הטבלה) תוקן כבר ב-d3bde37 ואומת במוטציה — מסומן addressed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שישה ממצאים (סבב שמיני). שני המרכזיים נפתרו יחד בחילוץ: - (P1, אבטחה, cubic) התיקון הקודם השאיר את הרגנרציה שבתוך שלב הקומיט רצה עם GH_TOKEN בסביבה ועם הטוקן ב-remote — persist-credentials: false לא באמת בודד את קוד הריפו מהסוד. עכשיו: הדחיפות עוברות ב-URL אינליין (שום דבר לא נכתב לדיסק), וכל הרצה של המחולל עוברת env -u GH_TOKEN — קוד הריפו לא רואה את הסוד גם דרך הסביבה. - (P2, תחזוקתיות, cubic) שלב הקומיט תפח לחמש אחריות שזורות. חולץ ל-.github/scripts/publish-ai-map.sh עם פונקציות ממוקדות (regen, fail_without_pat); ה-workflow ירד ל-56 שורות והשלב לשורה אחת. - (Major, CodeRabbit) הזרימה רצה עכשיו כולה תחת set -euo pipefail — כשל של כל פקודת פרסום מפיל את הריצה, כולל המקרה שדחיפת הענף נכשלת ו-PR קיים היה מדווח "עודכן" על כלום. (ברירת המחדל של Actions היא bash -e, אבל מפורש עדיף על מרומז — ועם -u ו-pipefail שאין בברירת המחדל.) - (Minor×2, שני הבוטים) הודעת השגיאה הציגה ---out (שלושה מקפים) בגלל התנגשות מקף-עברי עם שם הדגל — נוסחה מחדש בלי ההתנגשות. - (P3, cubic) הסרת ה-backticks מחקה גם inline-code מאוזן ולגיטימי. עכשיו מסירים רק כשהמספר אי-זוגי; מונחים כמו `/deploy` שרדו במפה. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
חמישה ממצאים חדשים (סבב תשיעי). שלושת המרכזיים נפתרו בשינוי אחד. **סטייה מדווחת: cubic הציע פתרון אחר ל-P0 ולא יישמתי אותו.** הוא הציע להעביר את הפרסום מסקריפט בריפו ל-run: inline ב-workflow. זה לא מוסיף הגנה: קובץ ה-workflow יושב באותו ריפו ומשתנה באותו PR בדיוק כמו הסקריפט, ובאירוע push על main רץ הקוד שאוחד בשני המקרים. הפתרון השורשי הוא לא להסתיר את הסוד מהקוד אלא לא להזדקק לו: - הפרסום עבר מ-git ל-GitHub Contents API. קריאת PUT אחת מחליפה את commit+push+rebase+ענף-אוטומציה+PR, ולכן contents:write של GITHUB_TOKEN מספיק. REPO_ADMIN_TOKEN הוסר מה-workflow לחלוטין — אין יותר סוד בעל admin:repo שנחשף לקוד שרץ מהריפו (P0 נסגר). - המפה נוצרת ב-mktemp -d ונמחקת ב-trap; אין שום כתיבה ב-root של ה-checkout, בהתאם לכלל "עבוד רק על תיקיות זמניות" (P2 נסגר). - git status שנכשל כבר לא נראה כמו "אין שינויים" — אין git בזרימה; ההשוואה היא diff -q בין הקובץ ב-checkout לקובץ הזמני (P2 נסגר). - ה-sha של הקובץ הקיים משמש כנעילה אופטימית: שינוי מקביל מחזיר 409 והריצה נכשלת ברעש במקום לדרוס. - הסקריפט ירד מ-94 שורות ל-45; ה-workflow ל-55. שני הנותרים: - (P2) טבלת grid ב-RST (+---+) לא זוהתה ותחביר הטבלה חזר כתקציר — נוסף _GRID_ROW_RE, בשני מקומות (איסוף הפסקה ומסלול הדילוג). - (P3) בדיקת הלולאה האינסופית עברה מת'רד daemon לתת-תהליך: ת'רד שנתקע היה ממשיך לסובב על ליבה שלמה עד סוף ריצת pytest, וחריגה בתוכו הופיעה כ-KeyError מבלבל. עכשיו נהרג ב-timeout ומחזיר את החריגה המקורית. הבדיקה מכסה את שני סוגי הטבלאות, ואומתה במוטציה. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
הסעיף נכנס למחולל ולא לקובץ: AI-MAP.md נוצר אוטומטית, ועריכה ידנית בו גם נדרסת בהרצה הבאה וגם מפילה את test_ai_map_freshness. למה במפה ולא רק בפריימר: את הקובץ הזה קורא גם מי שלא קיבל את הפריימר — סוכן בכלי אחר, או grep מקרי. זה הכלל שהכי יקר לפספס, כי כל סבבי הריוויו כאן מצאו טענות בפרוזה שסתרו את הקוד. נוסף שומר שנופל אם הסעיף יוסר מהמחולל. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
הבדיקה שנוספה בקומיט הקודם השוותה מול הטקסט המלא של המפה, ולכן ``assert "autodoc" in content`` היה ריק מתוכן: המילה מופיעה ממילא ב-preamble של המפה ובשורת הסיכום של הפיגומים. נמדד — מחיקת משפט החריגים מהסעיף השאירה את הטענה עוברת. ``literalinclude`` היה יחיד היום ולכן עבד במקרה, אבל מספיק שעמוד אחד יזכיר אותו בתקציר כדי שגם הוא יתרוקן. עכשיו הבדיקה מבודדת את הבלוק שמתחיל ב-``## סמכות התיעוד`` עד הכותרת הבאה, ובודקת בתוכו. טענות הכותרת והטקסט נשמרו כפי שהיו. אומת בשתי מוטציות: הסרת משפט החריגים מפילה — וזו בדיוק המוטציה שהגרסה הקודמת שרדה — והסרת הסעיף כולו מפילה. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
הצעד הראשון בלוח הפתקים, ובלי שינוי גלוי למשתמש. היום שישה מודולים כותבים ישירות ל-sticky_notes, ואין שום שכבה שאוכפת לאיזה משטח פתק שייך. כל עוד זה המצב, "אילוץ בשכבת הוולידציה" הוא הצהרה ריקה. sticky_notes_target.py הוא השכבה: טהור, בלי Flask ובלי pymongo, כדי ש-webapp, mcp_server ו-services יוכלו לייבא אותו — בדיוק כמו sticky_notes_scope שלצידו. build_note_target מקבלת כוונה ומחזירה שדות יעד, ומריצה ולידציה לפני ההחזרה, כך שהקורא אינו יכול לייצר מסמך לא חוקי. פתק לוח שמקבל scope_id או file_name נדחה — זה באג של הקורא, לא קלט שיש להשלים בשקט. check_note_quota דוחה גם כשהספירה נכשלה, במכוון בניגוד ל- mcp_server/backend שמתייחס לכשל כאילו אין פתקים ומעביר את היצירה. תקרה שנפתחת לרווחה כשהמסד מתקשה היא לא תקרה. אדמין פטור, לפי ההחלטה שהתקבלה. 21 בדיקות. ארבע מוטציות אומתו ומפילות בדיוק את הבדיקה שלהן: דילוג על הוולידציה בבנאי, כשל ספירה שעובר, מחרוזת ריקה כיעד תקין, ופתק לוח שמקבל מטא-דאטה של קובץ. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
flake8 תפס F841 — הבדיקה בנתה שאילתת לוח ולא נגעה בה, כלומר חצי מהטענה שלה לא נבדק בכלל. עכשיו שני הכיוונים נטענים במפורש. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
הפיצ'ר של לוח הפתקים צריך את is_admin לפטור מתקרת הפתקים, אבל webapp/app מייבא את sticky_notes_api — כך שייבוא הפוך היה יוצר מעגל. בדרך לפתרון התגלה משהו גדול יותר. webapp/code_tools_api.py:15-30 החזיק עותק מדויק, מילה במילה, של webapp/app.py:4104-4119. שני עותקים של לוגיקת הרשאות הם שני מקומות שיכולים לסטות, ורק אחד מהם ייבדק. user_roles.py הוא עכשיו המקור היחיד — טהור, קורא os.environ ותו לא, ולכן ניתן לייבוא מכל מקום. שני העותקים הוסרו, ו-app ממשיך לייצא את השמות כדי ש-themes_api, routes/repo_browser ו- routes/auth_routes ימשיכו לעבוד בלי שינוי. ההתנהגות נשמרה: רשומה לא-ספרתית מדולגת בשקט, מינוס נדחה על ידי isdigit כמו קודם, ומזהה כמחרוזת עדיין עובד. נוסף fail-closed גם ל-is_admin, שלא היה לו try/except — בלי שינוי מעשי, כי הקלט מוגן ב-isdigit. 8 בדיקות, כולל אחת שטוענת ששני הצרכנים מצביעים לאותו אובייקט (is ולא ==) — היא נופלת אם מישהו יחזיר מימוש מקומי. 31 בדיקות ההרשאות הקיימות עוברות. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
השלמת שלב 0. בלי שינוי גלוי למשתמש, אבל עם שני תיקונים אמיתיים.
note_boards.py — לוח ברירת המחדל, זיהוי לפי is_default ולא לפי שם.
זו סטייה מכוונת מ"שולחן עבודה" באוספים, ומשתי סיבות: האפיון מרשה
לשנות את שם הלוח (וזיהוי לפי מחרוזת היה נשבר בפעולה חוקית), ובלוח
מחיקה שקטה מייתמת פתקים — הלוח הוא המקום היחיד שלהם, בניגוד
לאוסף שהקבצים שלו שורדים ב-code_snippets.
המרוץ שבו שתי בקשות מקבילות יוצרות לוח ברירת מחדל נסגר במסד ולא
בקוד: אינדקס ייחודי-חלקי one_default_per_user. הקוד קורא שוב אחרי
הכתיבה ומחזיר את מה שהמנצח יצר.
באג רדום ראשון — mcp_server/backend._notes_scope_filter החזיר
{"user_id": uid} כשאין scope ואין related, כלומר את **כל** הפתקים
של המשתמש במקום את הפתקים של הקובץ. היום זה לא נגיש; עם פתקי לוח
פתק ללא קובץ היה נשאב לתשובה. הטסט הקיים קיבע את הבאג — עודכן.
באג רדום שני — לשאילתת ה-list הראשית לא היה אינדקס על ענף ה-scope
ב-webapp כלל (הוא נוצר רק אגב-אורחא ב-MCP). נוסף.
עוד: אינדקס (user_id, board_id), שומר שמונע קריאת _resolve_scope
מיותרת בכל עדכון של פתק לוח (בשני המקומות), וסקריפט מיגרציה
שברירת המחדל שלו היא דוח בלבד.
112 בדיקות. חמש מוטציות אומתו. אחת מהן חשפה שהאימות בקריאה חוזרת
ב-ensure_default_board לא היה מכוסה — נוסף טסט שבו הכתיבה מדווחת
הצלחה ולא נקלטת, והוא נופל בלי האימות.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שלב 1+2. מכאן הפיצ'ר אמיתי בצד השרת. webapp/note_boards_api.py — רשימה, יצירה, שינוי שם, מחיקה. ראוטי הפתקים של הלוח נוספו לתוך sticky_notes_api ולא לקובץ נפרד, כי require_auth, notes_rate_limit, _sanitize_text, _coerce_int ו-_as_note_response כבר שם. המחיקה היא הפעולה היחידה שיכולה לאבד נתונים, ולכן הסדר בה לא מקרי: ברירת מחדל חסומה ב-409; ensure_default_board נקרא ולא מונח; ספירה לפני ההעברה כי modified_count מדווח בחסר; ואחרי ההעברה ספירה חוזרת — אם נשארו פתקים, עוצרים ולא מוחקים. הלוח שורד, אפשר לנסות שוב, ואף פתק לא מתייתם. התקרות נאכפות עכשיו באמת — 200 ללוח ו-1000 למשתמש — דרך check_note_quota, שדוחה גם כשהספירה נכשלה. אדמין פטור. עד היום 200 נאכף רק ב-MCP וכ-soft-cap, ו-1000 לא נאכף בשום מקום למרות שהוא מתועד. עוד: שדה mode עם ולידציה (surface/screen), board_id ו-mode ב-_as_note_response, ואימות שהלוח קיים ושייך למשתמש לפני כל יצירה או רשימה — בלעדיו אפשר היה ליצור פתקים על board_id שרירותי, פתקים שאינם נראים בשום ממשק אבל נספרים בתקרה. 133 בדיקות. חמש מוטציות אומתו ומפילות בדיוק את הבדיקה שלהן: מחיקה בלי ספירה חוזרת, ברירת מחדל בלי חסימה, יצירה שמסיקה מ-inserted_id, כשל ספירה שעובר, וניתוב פתקי לוח דרך _resolve_scope. אומת גם שהראוטים רשומים באפליקציה האמיתית ולא רק ב-stub — הרישום עטוף ב-try/except שבולע כשלים, כך שייבוא שבור היה מדלג בשקט. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שלב 3. כ-85% מ-sticky-notes.js עובר בירושה כמו שהוא: גרירה,
שינוי גודל, תור השמירה עם ה-debounce וה-409, keepalive, מודל
התזכורות, מזעור, מחיקה וקאש. זה גם החלק הכי מסובך, ופיצול
לקובץ שני היה מכפיל אותו.
ארבעה תפרים:
- קונסטרקטור שמקבל גם אובייקט. מחרוזת ממשיכה להיקרא כמזהה קובץ,
ולכן md_preview.html לא משתנה בכלל.
- this._scopeUrl מחליף שני מופעים בלבד — loadNotes ו-createNote.
/note/<id> ו-/batch כבר אגנוסטיים ליעד.
- this.container מחליף את document.body בשלושה מקומות, כולל
ה-querySelectorAll ב-_reflowWithinViewport — בלעדיו שני מנהלים
באותו עמוד נלחמים על אותם אלמנטים.
- this._anchorHost מחליף את שמונת מופעי getElementById('md-content').
בלוח הוא null, ו-_setupDomObservers יוצא מעצמו.
_resolveMode מחליף את התנאי המוטמע: שדה mode מנצח כשהוא קיים
(פתקי לוח), ואחרת נגזר מהסנטינלים בדיוק כמו קודם. התנאי ל-anchored
מותנה ב-!boardId ולא בקיום ה-host, כדי שמסלול הקובץ יישאר זהה גם
במקרה הקצה שבו md-content חסר.
מפתח הקאש של לוח נפרד. בלעדיו לוח וקובץ עם אותה מחרוזת מזהה היו
חולקים localStorage ומרנדרים זה את הפתקים של זה.
14 בדיקות JS חדשות בתבנית tests/md-anchors.test.js. ארבע מוטציות
אומתו. ובנוסף הרצתי השוואה התנהגותית מול התנאי המקורי על 12
צירופים של anchor_id/line_start — זהה בכולם.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שלב 4. מכאן הפיצ'ר נראה במסך.
webapp/boards_ui.py בתבנית collections_ui: הראוטים מגישים HTML
בלבד, והנתונים נשלפים מה-API בצד הלקוח. שתי תבניות — רשימה
ומשטח — בבלוקים של base.html ועם ?v={{ static_version }}.
רשת הנקודות היא CSS טהור: radial-gradient עם background-size של
24px. אפס נכסים, אפס ווטרמארק, ואפס בייטים להוריד — וגם מקבלת
את צבע ערכת הנושא הפעילה, מה שאיור סטטי לא היה עושה.
background-attachment: local כדי שהנקודות יזוזו עם המשטח בגלילה
ולא יחליקו מתחתיו.
המשטח הוא position: relative, וזה מה שהופך פתק במצב surface
ל-absolute יחסית ללוח ולא למסמך.
כניסה מכפתור קיצור הדרך בנאבבר, אחרי "שולחן עבודה".
הבדיקות מרנדרות את התבניות באפליקציה האמיתית ולא ב-Flask
מינימלי, כי הרישום עטוף ב-try/except שבולע כשלים — תבנית חסרה
או Jinja שבור היו מדלגים בשקט.
שלוש מוטציות אומתו: החלפת tojson בהדבקת מחרוזת (XSS דרך הנתיב),
הסרת הפריט מהנאבבר, וויתור על next בהפניה להתחברות.
118 בדיקות פייתון, 14 JS. לא הורץ Prettier על התבניות (כלל Jinja
ב-CLAUDE.md) — נערכו ידנית.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שלב 5. האפיון מסמן את זה כמקור הבאג הצפוי, והניסוח שלו מדויק: לחיצה על צ'קבוקס היא כתיבה למסד. הכשל הצפוי הוא שהתצוגה מתהפכת, השמירה נכשלת בשקט, וברענון הסימון חוזר אחורה בלי שדבר דיווח. sticky_notes_tasks.py — שתי פונקציות טהורות, בלי Flask ובלי מסד. ההתאמה היא לפי מספר סידורי ולא לפי טקסט: שלוש שורות "- [ ] לבדוק" זהות חייבות להיות ניתנות לסימון בנפרד, ו-replace היה מסמן תמיד את הראשונה. POST /api/sticky-notes/note/<id>/task. הראוט קיים כדי שאפשר יהיה לאמת: שליחת התוכן המלא ב-PUT הופכת כל קליק לדריסת last-writer-wins, ובעיקר הופכת אימות לבלתי אפשרי — אפשר לאמת רק שכתבנו את מה ששלחנו. הבקשה נושאת כוונה (סידורי + מצב), וזה מה שניתן לאמת מול המסד. הסדר: 404 → 409 על prev_updated_at → 409 על סידורי חסר (לא 200!) → 200 בלי כתיבה אם כבר במצב המבוקש → compare-and-swap עם התוכן הקודם בפילטר → **קריאה חוזרת** ובדיקה שהתו השתנה. לא modified_count ולא ok:true. בלקוח: flush לפני הכול (בלעדיו _queueSave שממתין ב-debounce היה נוחת אחרי ה-toggle ומבטל אותו), רינדור מחדש מהתוכן שהשרת החזיר, ובכשל — חזרה אחורה עם חיווי גלוי. 37 בדיקות. חמש מוטציות אומתו. אחת מהן חשפה שענף ה-CRLF ב- _split_lines הוא קוד מת: פיצול על \n מתגלגל חזרה נכון בעצמו. הוסר, ובמקומו נשאר הענף ל-\r בלבד — שם זה כן משנה, ויש עליו בדיקה שנופלת בלעדיו. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שלב 6. הבעיה: כל צרכן הרכיב את יעד ההתראה בעצמו מהמזהים שהיו לו ביד. sw.js בנה /md/<file_id>, והפעמון ב-base.html עשה אותו דבר מאחורי if (fileId). לפתק לוח אין file_id — ולכן ההתראה נחתה בשורש האתר, ולחיצה על הפעמון פשוט לא עשתה כלום. תיקון השורש הוא בונה אחד: GET /note/<note_id>. השרת יודע אם הפתק יושב על קובץ או על לוח ומפנה בהתאם, והצרכנים לא צריכים להכיר את סוגי הפתקים — כולל סוג שלישי שיתווסף בעתיד. פתק שנמחק או של משתמש אחר מקבלים את אותה תשובה, כדי לא להדליף קיום. ארבע נקודות החיכוך בתזכורות: board_id נכתב למסמך התזכורת, מוחזר ב-reminders/list (עם השלמה מהפתק לתזכורות ישנות), ונכנס ל-data של ה-push. _ensure_user_owns_note ו-_coerce_preview לא נגעו — הם מזהים לפי _id + user_id. sw.js פותח /note/<id> ומשאיר את /md/ כפולבק להתראות שכבר בתור. SW_VERSION הועלה ל-2.1.0, אחרת אי אפשר לאשש בפרודקשן שהגרסה החדשה נטענה. ונקודה חמישית שלא הייתה ברשימה: personal_backup_service דילג בשקט על כל פתק בלי file_name, כלומר פתקי לוח לא שרדו גיבוי-שחזור בכלל. עכשיו הם מיוצאים עם **שם** הלוח (ה-_id לא תקף בסביבה אחרת) ומשוחזרים לפיו, ובהיעדר התאמה — ללוח ברירת המחדל. שדות היעד עוברים דרך build_note_target ולא נכתבים ביד. 148 בדיקות. ארבע מוטציות אומתו, כולל אחת שמחזירה את הגיבוי למצב שבו פתקי לוח נעלמים. flake8: 44 ממצאים לפני, 44 אחרי — אפס חדשים (כולם קדמו לשינוי). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
שלב 7, האחרון. docs/user/sticky_notes.rst — פרק לוחות: יצירה, לוח ברירת מחדל שלא נמחק, מחיקה שמעבירה פתקים, שני מצבי המיקום, צ'קבוקסים ותזכורות. ההצהרה על 200/1000 תוקנה — היא הייתה מתועדת ולא נאכפה בשום מקום ב-webapp. ותוך כדי כתיבת המשפט הזה גיליתי שאני עומד לכתוב טענה חצי-נכונה: תקרת ה-1000 למשתמש נאכפה רק במסלול הלוח. במקום לצמצם את הניסוח הוספתי את האכיפה גם ליצירת פתק על קובץ, כך שהתיעוד נכון כמו שהוא. יש על כך בדיקה. הכשל שהאכיפה החדשה גרמה בטסט קיים היה מלמד: ה-stub לא מימש count_documents, ולכן הספירה "נכשלה" והתנהגות ה-fail-closed חסמה. ה-stub היה חסר, לא הקוד — הושלם. docs/database/detailed-schema.rst — שני אוספים: sticky_notes עם האילוץ "בדיוק אחד" ו-note_boards, כולל הסבר למה האינדקס הייחודי-החלקי הוא מה שסוגר את המרוץ ולמה הזיהוי הוא is_default ולא השם. 221 בדיקות פייתון, 19 JS, sphinx -W עם אפס אזהרות, המפה עדכנית, flake8 נקי. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YULCppaQRPYN1RgeY6NBu
Contributor
Reviewer's GuideDocuments the new note boards feature alongside existing sticky notes, including UI flows, API endpoints, backup behavior, and note limits, with expanded Hebrew guides and feature summary updates. Sequence diagram for reattach_orphan_notes on note boards page loadsequenceDiagram
actor User
participant WebApp
participant NoteBoardsAPI
User ->> WebApp: open /note-boards
WebApp ->> NoteBoardsAPI: GET /api/note-boards
WebApp ->> NoteBoardsAPI: reattach_orphan_notes
NoteBoardsAPI --> WebApp: updated boards with reattached notes
WebApp --> User: render note boards with healed orphan notes
Entity relationship diagram for sticky_notes and note_boardserDiagram
sticky_notes {
int id
int board_id
string file_id
string content
}
note_boards {
int id
string board_name
bool is_default
}
note_boards ||--o{ sticky_notes : has
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Contributor
🧯 Dangerous deletes guard reportPolicy: see .cursorrules — dangerous deletions are blocked unless wrapped safely. Summary:
Flagged findings (file:line:snippet): Excluded matches (by path pattern) |
Contributor
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- In
PERSONAL_BACKUP_FEATURE_GUIDE.md, consider moving the "שים לב" blockquote about board notes out of the table area (placing it after a blank line following the table) so the markdown table renders consistently across viewers. - In
webapp/USER_GUIDE.mdunder "מגבלות פתקים", the note "עד גרסה זו הן היו מתועדות אך נאכפו רק חלקית" is more of a historical/changelog detail; consider removing or relocating it to a changelog to keep the user guide focused on the current behavior only.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `PERSONAL_BACKUP_FEATURE_GUIDE.md`, consider moving the "שים לב" blockquote about board notes out of the table area (placing it after a blank line following the table) so the markdown table renders consistently across viewers.
- In `webapp/USER_GUIDE.md` under "מגבלות פתקים", the note "עד גרסה זו הן היו מתועדות אך נאכפו רק חלקית" is more of a historical/changelog detail; consider removing or relocating it to a changelog to keep the user guide focused on the current behavior only.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Contributor
⏱️ Performance report
(No performance test durations collected. Mark tests with |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
amirbiron
force-pushed
the
claude/bot-hacked-messages-comsrh
branch
8 times, most recently
from
August 27, 2026 14:48
fb9f11d to
a66a2d2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Update documentation for #3255
Updated the Hebrew USER_GUIDE to introduce “לוחות פתקים” (note boards), including creation, default board behavior, deletion/transfer rules, and two board positioning modes. Added documentation for interactive checkbox tasks in notes, clarified that reminders work for both file notes and board notes, and documented note limits. Expanded FEATURES_SUMMARY and README to reflect the new note boards feature, related UI pages, and new API endpoints, and updated the backup guide to note that backups include both file notes and board notes (with board_name for restore).
Generated by Dosu
Summary by Sourcery
Document note boards and expand sticky-note guidance across the project's user-facing and backup documentation.
New Features:
Enhancements:
Documentation: