Imported from dolevrokach08-a11y/finance-tracker (
AGENTS.md). Install upstream withnpx skills add dolevrokach08-a11y/finance-tracker. Copyright stays with the author.
עבודה על הריפו הזה
מסמך אחד לכל סוכן שעובד כאן — Codex/GPT, Claude, או כל אחד אחר. קרא אותו לפני העריכה הראשונה.
הכלל הראשון והחשוב ביותר:
אף פעם לא עובדים ישירות על
main. תמיד ענף, תמיד מיזוג אחרי סקירה.
מה זה
אפליקציית ווב עברית (RTL) לניהול פיננסי אישי. vanilla HTML/CSS/JS, בלי
framework ובלי שלב build — למעט שני קבצים מיוצרים (ראה למטה). מתפרסמת מ-main
ל-GitHub Pages כמו שהיא.
חמישה מסכים: app.html (מעטפת + דף בית), portfolio.html, finance.html,
mortgage.html, tax-optimizer.html.
חלוקת עבודה
| אחראי | |
|---|---|
| עיצוב, פריסה, טיפוגרפיה, צבע, מיקרו-אינטראקציות | GPT |
| חישובים פיננסיים, שכבת הנתונים, סנכרון, מיזוג | Claude |
מי בודק את מי
לא סימטרי, כי הביקורת שווה משהו רק כשהבודק יכול באמת לראות את הכשל.
| מה | מי כותב | מי בודק |
|---|---|---|
| חישובים, נתונים, סנכרון | Claude | בדיקות אוטומטיות + סעיף "מה נגע במספר" בפתק |
| עיצוב עצמאי — CSS, טוקנים, מסך שלם | GPT | Claude סוקר וממזג |
| התנהגות ופריסה בתוך קוד מרותך ללוגיקה | Claude מחווט, GPT מאפיין | GPT — את האפליקציה הרצה, לא את הדיף |
שתי נקודות שמחזיקות את זה:
- ביקורת מספרית על Claude לא נופלת על GPT. הוא לא בודק חישובים ולא אמור. ההגנה שם היא בדיקות אוטומטיות ותיעוד מפורש של כל מה שנגע במספר.
- GPT בודק את המסך, לא את הדיף. שם הערך שלו. סבב אחד כזה מצא ליקוי נגישות, התנהגות אקורדיון לא רצויה וגלישה אופקית — כולם בהסתכלות, לא בקריאת קוד.
איך טענה הופכת למוסכמת
הנוהל הזה נכתב אחרי סבב שבו מחשבון עמלת הפירעון ירד מ-63,026 ל-15,390 מול 15,315 בדף הבנק. הוא מנוסח מהתקלות שקרו בו בפועל, לא מהתקלות שדמיינו.
- הסוכן הראשון מציג תוצאה, את ההנחות שמאחוריה, ותוצאה נצפית צפויה. התחזית היא העיקר ולא קישוט: "המסך יראה 15,389 מול 15,315 בדף" — נאמר לפני שמסתכלים, אחרת זה דיווח ולא תחזית.
- הסוכן השני מחשב מחדש ממקורות הגלם ומנסה להפריך. לא לקרוא את הדיף ולהנהן — לבנות את המספר מחדש מהמקור ולחפש דוגמה שסותרת. ומה שנוסה ולא הפריך נכתב. הסכמה בלי רשימת ניסיונות היא שני סוכנים מהנהנים; שלוש קונבנציות שנרשמו ככישלון הן מה שאיפשר לצד השני לקפוץ ישר לחלופה החסרה במקום לחזור על אותה דרך.
- נדרשת ראיה חיצונית. צילום ממשק, הרצת workflow, השוואה למסמך בנק, או תרחיש ידוע מראש. שני סוכנים שמסכימים אינם ראיה.
- רק אז מותר לסמן "שנינו מסכימים".
- וגם זה פירושו "מוכן לאישור", לא "מותר למזג".
מה כל שכבה תופסת — ומה היא לא
הטבלה הזאת היא מאותו סבב, ולכן היא לא תיאורטית:
| מי | מה תפס |
|---|---|
| הסוכן השני | החלופה החסרה בנוסחת הצו · מקור ראשוני למדרגות ההפחתה · חיתוך תווית · שדיווח על באג שכבר לא קיים |
| בדיקות אוטומטיות | תאריך 2026-21-04 שנוצר מ-locale של runner · off-by-one בתקופה שנותרה |
| דולב | מספר שגוי על המסך, פעמיים · מטמון SW שלא בומפה · תלות לא ישימה · דרישה להריץ workflow ידנית |
הפילוח הוא הנקודה. הסוכן השני תופס דברים שגויים בתוצר. דולב תפס דברים שגויים בשיפוט על מה להציג — שורה שכתוב עליה "תקרה" והיא נמוכה מהאמת, מספר סביר שנשען על קלט מנוחש. שני סוכנים יכולים להסכים לחלוטין על שניהם.
לכן שלב 5 אינו פורמליות. ביקורת אדברסרית היא שכבת הגנה מצוינת ואינה תחליף לראיה חיצונית ולשער אישור אנושי.
מה מותר להעביר בלי אישור
מותר: שינוי שלא יכול לשנות מה מספר אומר, או מה נקרא כמספר.
אסור בלי אישור דולב: חישובים פיננסיים · main · נתונים · פריסה.
שתי מלכודות בגבול הזה, שתיהן קרו:
- תיעוד שמתאר תהליך ירוק. תיעוד שמצהיר על תוצאה — לא. פתק בארכיון שכתוב בו "0.49% מהבנק" מטעה את הסבב הבא בדיוק כמו קוד שגוי.
- CSS אינו בטוח מעצם היותו CSS. שינוי ריפוד עבר בשלום בכרטיס שזה עתה נוספה לו שורת שדות שנייה — כי הסלקטור היה מדויק ומישהו בדק, לא כי CSS לא שובר מספרים.
הגבול שאסור לחצות
שינוי עיצובי לא משנה אף מספר.
מותר לשנות איך מספר נראה — גודל, צבע, מיקום, פורמט תצוגה. אסור לשנות איך הוא מחושב, מאיפה הוא נקרא, או מתי הוא מוצג במקום מספר אחר.
אם נראה לך שחישוב שגוי — דווח, אל תתקן. כתוב את זה בסיכום ההעברה ותן לזה לעבור סקירה. מספר שגוי שנראה סביר גרוע ממספר שגוי שצועק.
בסבב עיצוב קודם שונתה בשקט הנוסחה של תשואת המדדים: במקום לדלג על snapshot בלי
שער מטבע נעול, הוכנס שער "מה-snapshot הקרוב ביותר", ובברירת מחדל אחרונה קבוע
3.5. זה שינה מספרים שהמשתמש מסתמך עליהם, בתוך commit שכותרתו עיצוב.
מוקשים
firebase-config.js — אל תיגע
מסומן skip-worktree. הגרסה המקומית מכילה הגנות פיתוח שאסור שיגיעו לפרודקשן, ו-git
פשוט יתעלם מהשינויים שלך בו. אם משהו נראה שם שבור — דווח.
קבצים מיוצרים
tax-optimizer.app.js ו-finance.tailwind.css נוצרים מקוד מקור:
| מקור | פלט |
|---|---|
tax-optimizer.src.jsx |
tax-optimizer.app.js |
finance.html (מחלקות Tailwind) |
finance.tailwind.css |
ערוך את המקור, אף פעם לא את הפלט. אחרי עריכה:
node tools/build-assets.mjs
pre-commit hook חוסם commit עם פלט מיושן. להפעיל אותו פעם אחת לכל clone:
npm --prefix tools install
git config core.hooksPath tools/hooks
לעולם לא --no-verify.
סודות — לא בדפדפן
מפתח Anthropic של העוזר יושב ב-Cloudflare Worker (/api/ai/chat), לא ב-localStorage.
הדפדפן מזדהה עם ה-Firebase ID token שכבר יש לו. אל תחזיר מפתח לקוד לקוח —
גם לא "רק לבדיקה". התחברות היא Google popup פתוח, ולכן טוקן תקין הוא לא אישור
להוציא כסף: AI_ALLOWED_UIDS ב-Worker הוא השער.
קבצים שהם הרצה מקומית בלבד
package.json · vite.config.js · package-lock.json · run-dev.cmd
לא לערוך, לא ל-stage. ראה tools/README.md.
מקורות אמת יחידים
הקבצים האלה קיימים כדי ששני מסכים לא יחלקו על אותו מספר. אל תשכפל את הלוגיקה שלהם למסך מסוים — קרא להם:
| קובץ | מה |
|---|---|
shared/finance-summary.js |
סיכום חודשי בבסיס מזומן (כולל ניכוי כפילויות ומעשר) |
shared/portfolio-twr.js |
TWR |
shared/accrual-rules.js |
לאיזה חודש שייכת שורה בצבירה — דגלים, תבניות וכללים זכורים לפי בית עסק |
shared/backup.js |
גיבוי ושחזור של כל החשבון |
shared/data.js |
נורמליזציה של מערכי נתונים |
shared/user-storage.js |
בידוד localStorage לפי משתמש |
מודול דף הבית חישב מחדש דברים שכבר היו מחושבים, וכל פער בהגדרה הפך לפער על המסך:
- יתרת משכנתא קראה
mortgage.routesבזמן שהנתון נשמר תחתtranches→ הוצג ₪0 - קרן חירום סיננה לפי
a.typeבזמן שהאפליקציה כותבתa.category→ כל נכס נספר כנזיל, דירה כולל - שורת ההשוואה למדד קראה
bm.TA125בזמן שה-cache שומרbm.indices.TA125→ מעולם לא הוצגה
מוסכמות עיצוב
- RTL היא ברירת המחדל. מספרים, מטבעות ותאריכים תמיד בכיוון LTR מבודד.
- ירוק = רווח, אדום = הפסד. בעקביות, ולא לשום שימוש אחר.
- אחוז עם סימן
+/-הוא שינוי. רמה או התקדמות (יחס החזר, אחוז מהיעד, נזילות) מוצגת בלי סימן. 0הוא מספר,—הוא היעדר נתון. אל תציג—לחודש מאוזן.- תווית חייבת לתאר את מה שמתחתיה. אם שינית את מקור המספר — עדכן את התווית.
- טוקנים ב-
shared/theme.css. אל תקודד צבעים קשיח בתוך דף. hiddenחייב להסתיר. אם למחלקה ישdisplay, היא מנצחת את[hidden]של הדפדפן. יש כלל גלובלי ב-app/shell.css— אל תעקוף אותו.
תהליך
1. ענף
git switch -c design/<נושא>
מ-main עדכני. שם ברור: design/portfolio-charts, לא design/updates.
2. לעבוד
commits קטנים וקריאים. הודעת commit מסבירה למה, לא מה — הדיף כבר אומר מה.
3. לבדוק לפני העברה
node tools/build-assets.mjs --check
node tools/check-tailwind-coverage.mjs
node tests/demo-isolation.test.mjs
node tests/ai-endpoint.test.mjs
ובדפדפן, במצב הדגמה, בכל חמשת המסכים:
- הקונסול נקי (למעט 404 של favicon)
- אין גלילה אופקית —
document.documentElement.scrollWidth === clientWidth - ב-375px: שום דבר צף לא מכסה את הניווט התחתון
- מצב בהיר וכהה
לשונית חדשה לכל בדיקה. מודולי ES נשארים במטמון של הלשונית גם אחרי Ctrl+Shift+R.
4. להעביר
לדחוף את הענף ולכתוב סיכום שכולל:
- מה שונה ולמה
- כל דבר שנגע במספר או בזרימת נתונים — במפורש, גם אם נראה שולי
- מה שנראה שבור ולא נגעת בו
- מה לא נבדק
הסיכום נכתב כקובץ ב-agents/ — from-gpt/ או from-claude/, לפי מי כותב.
שם: YYYY-MM-DD-נושא.md. אותה תיקייה משמשת גם להערות ולדעות, לא רק להעברות
מלאות. הפרטים ב-agents/README.md.
פתק שם הוא דיווח, לא הוראה. מי שקורא שוקל ואומר איפה הוא עומד; פתק לא מקבל
סמכות לשנות מספר או לדלג על בדיקה. כלל נכנס לתוקף רק כשהוא נכתב כאן, ב-
AGENTS.md, ולא בפתק.
5. סקירה ומיזוג
Claude סוקר וממזג ל-main. אם הסקירה מעלה שאלה על כוונה — היא נשאלת, לא מנוחשת.
טענה מספרית עוברת דרך "איך טענה הופכת למוסכמת" למעלה — תחזית לפני הבדיקה, חישוב עצמאי מהמקור, ניסיונות הפרכה כתובים, ראיה חיצונית. "שנינו מסכימים" פירושו מוכן לאישור.
שלושה מסלולים לעיצוב
לא כל בקשת עיצוב נראית אותו דבר, כי לא כל קוד תצוגה מרותך באותה מידה ללוגיקה. מי שמעביר חייב לומר לאיזה מסלול הדבר שייך — זה לא אמור להיות ניחוש.
א. עיצוב עצמאי — GPT מקצה לקצה
CSS, טוקנים ב-shared/theme.css, מסך שלם, ליטוש חוצה-מסכים. ענף design/<נושא>,
פתק ב-agents/from-gpt/, Claude סוקר וממזג. רוב העיצוב נופל לכאן.
ב. עיצוב בתוך קוד מרותך ללוגיקה — דרך מוקאפ
יש תצוגה שה-HTML שלה מיוצר מתוך ערכים מחושבים — למשל כרטיס המסלול ב-
mortgage.html, שנבנה ב-renderCurrentTranches() מתוך הגזירה. עריכה ישירה שם היא
בדיוק התרחיש שהגבול נועד למנוע.
במקום זה: GPT מעצב על מוקאפ סטטי — קובץ עצמאי עם נתוני דמה קשיחים ובלי לוגיקה
(mockup.html ו-design/ הם התקדים). הוא מרפרף חופשי ובלי סיכון, ואז Claude מטמיע
את התוצאה ברנדרר החי ומוודא שאף מספר לא זז.
היתרון החשוב: GPT אף פעם לא חסום בהמתנה ל-Claude.
ג. עיצוב שדורש מספר שעדיין לא קיים
GPT מבקש בפתק, Claude מוסיף את הערך הנגזר, GPT מעצב מולו. הוא לא ממציא מספר, Claude לא ממציא עיצוב.
לעבוד במקביל
שני סוכנים באותה תיקייה דורסים זה את זה. אם צריך במקביל — worktree נפרד:
git worktree add ../ft-design design/<נושא>
tools/node_modules לא עובר ל-worktree חדש; להעתיק או להתקין מחדש לפני build.
פקודות
npx http-server . -p 3470 -c-1 # שרת סטטי, הכי קרוב ל-GitHub Pages
node tools/build-assets.mjs # לבנות מיוצרים
node tools/build-assets.mjs --check # לוודא טריות
node tools/check-tailwind-coverage.mjs
node tests/demo-isolation.test.mjs
node tests/ai-endpoint.test.mjs
node tests/mortgage-schedule.test.mjs
node tests/mortgage-penalty.test.mjs
node tests/boi-rates.test.mjs
node tests/accrual-rules.test.mjs
node tools/fetch-boi-rates.mjs # לרענן את ריביות בנק ישראל
node tools/fetch-boi-rates.mjs --check
node tools/agent-relay.mjs --status # מי חייב תגובה למי
node tools/agent-relay.mjs --once
node tools/agent-relay.mjs --prime # לסמן את הקיים כנקרא, בלי לשלוח
node tools/agent-relay.mjs --reset-note <שם>
node tools/agent-relay.mjs --selftest claude|codex
tools/agent-relay.mjs — הדוור
מנטר את agents/from-*/ ומעיר את הצד שחייב תגובה, כדי שדולב לא יהיה השליח.
הוא לא ממזג, לא דוחף, ולא נוגע ב-main, והסוכן שהוא מעיר מתודרך לסקור ולענות,
לא ליישם. סבב מסתיים בענף relay/* במצב "מוכן לאישור".
כל סבב רץ ב-clone נפרד, לא ב-worktree — ולכן אין לו מצביע ל-refs של הרפו והוא לא
יכול להזיז את main גם בטעות. הענף נמשך חזרה לכאן בסיום, וה-clone נמחק.
מה מוכל ומה לא — במפורש, כי זה נטען פעם בלי שנבדק:
| נתיב | הכלה |
|---|---|
| codex | sandbox של workspace-write. הודגם שהוא מסרב לכתיבה מחוץ לתיקייה — כולל ל-.git |
| claude | אין sandbox. הרשימה כוללת Bash(node:*), ו-node כותב לכל מקום ופותח רשת |
התשובה נכתבת על ידי הסוכן ומקומטת על ידי הדוור, מחוץ ל-sandbox — codex לא יכול
לקמט בעצמו, ולכן קיימת codex apply. רק agents/ נכנס ל-stage; קובץ מחוץ לזה
הוא ממצא לדווח, לא תרומה. וסבב שמחזיק משהו לא מקומט לא נמחק.
סבב נחשב מוצלח רק אם הפתק חזר שונה מהבייטים שנמסרו ויש בו כותרת תגובה. קומיט לבדו אינו תשובה — הדוור מקמט את הפתק שהוא עצמו מסר, וזה כבר נראה פעם כמו הצלחה.
פתק מזוהה לפי hash של תוכנו ולכן מעיר פעם אחת, ושרשור נעצר אחרי חמישה סבבים
אוטומטיים. קובץ שמתחיל ב-_ שייך לדוור ולא נסרק.
הדוור דורש ש-CLI יהיה מחובר — claude auth status ו-codex login status אומרים
אם כן. אם לא: claude auth login, או claude setup-token למשהו שרץ לבד.
--selftest claude ו---selftest codex בודקים נתיב אחד כל אחד — הם בינאריים
שונים עם דגלים שונים, ובדיקה של אחד לא אומרת דבר על השני.
לפני --watch: --prime מסמן את הפתקים הפתוחים כנקראים, אחרת הם יוצאים כולם
בבת אחת. --reset-note <שם> משכיח פתק אחד; --reset משכיח הכל וכמעט תמיד אינו
מה שהתכוונת.
איך שרשור נגמר. שורת מצב: בראש הפתק שמתחילה ב-נסגר או סגור אומרת שאיש אינו
חייב תגובה, והדוור מפסיק לשגר אותו. הכלל נקרא מהכותרת בלבד ומחוץ לבלוקי קוד — פתק
שמצטט את המוסכמה אינו משתמש בה. בלי זה שרשור נעצר רק כשנגמרים חמשת הסבבים, וזה תקציב
ולא מסקנה.
צופה שנסגר לבד. --idle <דקות> מוסיף שני תנאי עצירה: כשאין מה לעשות במשך הזמן
הזה, וכשכל השרשורים נענו, נסגרו או מיצו את הסבבים. שניהם מדפיסים שורה שמתחילה ב-■
ויוצאים בהצלחה. בלי הדגל הוא רץ עד Ctrl-C.
node tools/agent-relay.mjs --watch 60 --idle 60
המודל שעונה בסבב. מקובע ל-gpt-5.6-sol, ולא נגרר אחרי ~/.codex/config.toml —
מודל שנבחר באפליקציה למשהו אחר לא אמור לשנות בשקט מי סוקר את הקוד. לשינוי חד־פעמי:
RELAY_CODEX_MODEL=<שם> node tools/agent-relay.mjs --once
RELAY_CODEX_MODEL=config node tools/agent-relay.mjs --once # בלי לציין מודל בכלל
כשל שהוא תשובה לא מנוסה שוב. מכסה שנגמרה, CLI ישן מהמודל, או חוסר התחברות — שלושתם יחזרו זהים בעוד שתי דקות. הדוור מדווח פעם אחת ומה לעשות, ולא שורף שלושה ניסיונות בדקה. תקלה חולפת, כמו timeout, ממשיכה לקבל את שלושת הניסיונות.
מלכודת ששווה להכיר: כשמצוין מודל במפורש, מכסה שנגמרה חוזרת כ-
The model is not supported when using Codex with a ChatGPT account. זה נשמע כמו
בעיית הגדרות ואינו כזה. RELAY_CODEX_MODEL=config יגרום ל-codex לומר את האמת.
שני דוורים במקביל — נעילה. כל פקודה שכותבת תופסת .agent-relay-lock.json תחילה,
ולכן דוור שני מסרב במקום להתחרות. --status אינו תופס נעילה, כי הוא לא משנה כלום.
נעילה שנשארה אחרי קריסה נמחקת אוטומטית כשמתברר שהתהליך אינו קיים; נעילה ממחשב אחר
מכובדת כמות שהיא.
פתק שנערך תוך כדי סבב אינו נשלח לפי הגרסה הישנה. הסריקה מצלמת את כל הפתקים בהתחלה, וסבב יכול לקחת עשרים דקות, ולכן כל פתק נקרא שוב רגע לפני השיגור. אם השתנה — הוא ממתין לסבב הבא. שינוי סיומות שורה אינו עריכה, כאן כמו בכל שאר המקומות.
data/boi-mortgage-rates.json — נתון חיצוני, לא לערוך ביד
הריביות הממוצעות של בנק ישראל, שמהן נגזרת עמלת הפירעון המוקדם. נוצר מקובץ ה-xls
של בנק ישראל על ידי tools/fetch-boi-rates.mjs. בלי תלויות — tools/lib/xls-cells.mjs
קורא את הקובץ ישירות; אין npm install ואין LibreOffice.
בנק ישראל מפרסם חודשית, ולכן השורה העליונה מתיישנת. --check נכשל כשיצא פרסום חדש,
ו-workflow חודשי (16 ו-23 לחודש) מרענן לבד. הסדרה היא שקלי לא צמוד בלבד — מסלול
צמוד לא מקבל מילוי אוטומטי.
מצב הדגמה: login.html ← כפתור ההדגמה. נתונים פיקטיביים, מבודדים מהחשבון האמיתי,
וכתיבה לענן חסומה. כל בדיקה נעשית שם, אף פעם לא על נתונים אמיתיים.
