CH3-005 · אובייקט החלטת CIO גרסאי
גרסה 1.0 · 9.8.2026, 14:58 · P1 · Locked & Approved — מאושר לביצוע
🔒 v1.0 Locked & Approved — מאושר לביצוע
כל שבעת התיקונים המהותיים הוטמעו ונבדקו. אין הערות תוכניות נוספות. המפרט נעול ומאושר לביצוע. CH3-005: סגור.
החלטת ה-CIO מפוזרת כיום בין שדות Trade (symbol, side, quantity, price, score, reasoning) ואינה מהווה אובייקט עצמאי גרסאי. אין יכולת להקפיא Snapshot של כל הקלטים ברגע ההחלטה (מחירי ייחוס, נתוני שוק, ניתוח סוכן, הגדרות מערכת). אין מנגנון להחליף החלטה ישנה בחדשה תוך שמירת היסטוריה. אין הפרדה ברורה בין "ההחלטה" (מה ה-CIO החליט) לבין "הפקודה" (מה בוצע בפועל). חסר decision_context (ENTRY/POSITION/EXIT) ופעולות ניהול פוזיציה (REJECT, REDUCE, INCREASE, EXIT_REVIEW, REJECT_EXIT). שדות החובה אינם תלויי פעולה — quantity ו-reference_price נדרשים גם ב-WAIT/HOLD שבהן אינם רלוונטיים, וב-BUY חסרים שדות חובה כגון הפסד מרבי ויחס סיכון-סיכוי. אין decision_family_id יציב לצד decision_id ייחודי לכל גרסה, והחלפת גרסה אינה אטומית — קיים סיכון לשתי גרסאות ACTIVE. ה-Snapshot אינו מתאים לפרק 5 (חסר snapshot_id, גרסת Schema, Hash, input_refs) ושומר "פלט LLM מלא" ללא סינון Secrets. assertDecisionActive נאכף רק ב-refreshPortfolioPrices ולא בגבול הביצוע המשותף (tradeExecution/executeTrade). ה-try/catch מסוכן: במצב Enforced כשל ביצירת Decision עלול לחזור בשקט ליצירת Trade ישירה. חסרים שדות עקיבות וגרסאות (schema_version, policy_version, model_version, prompt_version, agent_profile_version, correlation_id). אין approval_required ואין הפרדה בין החלטה לבין תוצאת Risk Gate ואישור משתמש שנוצרים לאחריה. מצב העבודה נקרא semi_hard במקום hard_semi, ואינו מודגש כפרופיל משנה. cancelDecision משתמש בטקסט חופשי במקום reason_code קנוני. בנוסף: INCREASE מופיע תחת ENTRY בניגוד לפרק 2 (צריך להיות תחת POSITION), ו-WAIT חסר בחלק מההקשרים. מעבר DRAFT→ACTIVE דורש Risk Gate שעבר, בניגוד לסדר הנכון: Decision ACTIVE → Risk Gate → Approval → Execution. שדות Risk Gate ו-Approval מתעדכנים בתוך Decision לאחר פרסום, בניגוד לעיקרון האי-שינוי — צריכות לחיות ברשומות נפרדות. supersedeDecision אינו מוגדר כאטומי מלא. אין כלל לעסקאות ישנות ללא decision_id. TIMEOUT ו-SUPERSEDED מופיעים כ-cancel_reason_code למרות שהם מעברי מצב (EXPIRED ו-SUPERSEDED בהתאמה).
ישות Decision עצמאית, גרסאית, עם decision_context (ENTRY/POSITION/EXIT), פעולות מורחבות, שדות חובה תלויי פעולה, decision_family_id יציב וגרסאות אטומיות (CAS). Snapshot קפוא מתאים לפרק 5 (snapshot_id, schema_version, Hash, input_refs, סינון Secrets). assertDecisionActive נאכף בגבול הביצוע המשותף (tradeExecution/executeTrade). try/catch: Enforced=חסימה על כשל, Shadow=נפילה למסלול הישן. שדות עקיבות מלאים (schema/policy/model/prompt/agent_profile version, correlation_id). approval_required + Risk Gate ו-Approval כרשומות נפרדות המקושרות ל-Decision. hard_semi כפרופיל משנה (לא מצב רביעי). TTL תלוי הקשר (אסטרטגיה, סוג עסקה), נפרד מ-Approval/Proposal TTL. cancel_reason_code קנוני + טקסט חופשי משלים. INCREASE תחת POSITION, WAIT בכל ההקשרים (ENTRY/POSITION/EXIT). DRAFT→ACTIVE אינו דורש Risk Gate — הסדר: Decision ACTIVE → Risk Gate → Approval → Execution. Decision אינה משתנה לאחר פרסום — Risk Gate ו-Approval חיים ברשומות נפרדות (RiskVerdict, ApprovalRecord) המפנות ל-decision_id + decision_version. supersedeDecision אטומי מלא — גם בקריאות מקבילות נשארת גרסה ACTIVE אחת. כלל Legacy לעסקאות ישנות ללא decision_id. הפרדה בין cancel_reason_code לבין מעברי מצב (TIMEOUT→EXPIRED, SUPERSEDED→מעבר SUPERSEDED). אין מיגרציית נתונים semi_hard→hard_semi ב-CH3-005 — משימה נפרדת.
Decision אינה משתנה לאחר פרסום. שדה יחיד ב-Decision:
רשומות חיצוניות המפנות ל-decision_id + decision_version (אינן משנות את ה-Decision):
מצבים טרמינליים: EXPIREDSUPERSEDEDCANCELLED
רק Decision במצב ACTIVE יכול לזרום ל-Risk Gate → Approval → Execution.
Snapshot מוקפא במעבר DRAFT→ACTIVE: snapshot_frozen=true, snapshot_id, snapshot_hash, snapshot_schema_version, input_refs ו-snapshot_data מאוכלסים. לא ניתן לשנותם לאחר ההקפאה.
snapshot_data חייב סינון Secrets: אין לשמור "פלט LLM מלא" ללא הסרת מידע רגיש (API keys, פרטי חשבון, סיסמאות).
גרסה חדשה: decision_version עולה, parent_decision_id מקשר לקודמת, הקודמת מסומנת SUPERSEDED עם superseded_by. פעולה אטומית ב-CAS (version_cas) + idempotency_key — מונע שתי גרסאות ACTIVE של אותה משפחה.
decision_family_id יציב: זהה לכל גרסאות אותה החלטה. decision_id ייחודי לכל גרסה. חיפוש גרסה פעילה — לפי decision_family_id + status=ACTIVE.
expires_at נאכף: TTL תלוי הקשר (ttl_context). ב-Paper: 24h ברירת מחדל. ב-Live: לפי אסטרטגיה וסוג עסקה. נפרד מ-approval_ttl_hours ו-proposal_ttl_hours.
Trade מקבל הפניה ל-decision_id ו-decision_version — לא מעתיק את שדות ההחלטה לתוכו.
רק הגרסה ה-ACTIVE האחרונה של Decision תקפה לביצוע. גרסאות קודמות (SUPERSEDED) אינן ברות-ביצוע.
EXPIRED/SUPERSEDED/CANCELLED אינן יכולות ליצור או לשנות Trade.
CIO Shadow (CH4-008) קורא רשומות Decision לצורך השוואה — קריאה בלבד.
Decision נוצר על-ידי ה-CIO (runAgentScan) לפני יצירת Trade proposal.
Risk Gate מקבל את reference_price מה-Decision לבדיקת סטיית מחיר (CH3-007).
Decision עם confidence < auto_execute_threshold אינו יכול לעבור אישור אוטומטי.
cancelDecision מקבל cancel_reason_code (קנוני) + cancel_reason_text (חופשי, משלים). טקסט חופשי לבדו אינו מספיק.
assertDecisionActive נאכף בגבול הביצוע המשותף: tradeExecution.ts ו-executeTrade — לא רק ב-refreshPortfolioPrices. כל נתיב ביצוע עובר דרך גבול זה.
try/catch לפי מצב תכונה: Enforced (DECISION_OBJECT_ENABLED=true) — כשל ביצירת Decision חוסם את העסקה, אין נפילה למסלול ישן. Shadow (DECISION_OBJECT_ENABLED="shadow") — נפילה ליצירת Trade ישירה מותרת + לוג השוואה. Disabled (false) — יצירת Trade ישירה ללא Decision.
שדות חובה תלויי פעולה: validateDecision(data) בודק שדות נוספות לפי action. למשל BUY דורש quantity, reference_price, stop_loss, take_profit, max_entry_price, risk_reward_ratio, max_loss_usd, exit_rules. HOLD/WAIT/REJECT/REJECT_EXIT דורשים reasoning בלבד.
approval_required: נקבע לפי automation_mode + confidence. Risk Gate ו-Approval נשמרים כרשומות נפרדות (risk_gate_result_id, approval_id) המקושרות ל-Decision — הם נוצרים לאחר ההחלטה.
hard_semi (לא semi_hard): פרופיל משנה של semi (סף 9.5), לא מצב עבודה רביעי. מצבי העבודה: auto, semi, manual. hard_semi מופעל כש-automation_mode=semi_auto ו-confidence ≥ 9.5.
correlation_id זורם מ-Decision דרך Risk Gate → Approval → Trade → Audit — מאפשר שחזור מלא של מחזור החיים.
schema_version, policy_version, model_version, prompt_version, agent_profile_version נשמרים בעת יצירת ה-Decision — מאפשרים שחזור הקשר מלא.
Decision אינה משתנה לאחר פרסום (ACTIVE). risk_gate_passed, risk_gate_reason, risk_gate_result_id, approval_id אינם שדות ב-Decision — הם חיים ברשומות RiskVerdict ו-ApprovalRecord נפרדות המפנות ל-decision_id + decision_version.
supersedeDecision DRAFT-first אטומי: הגרסה החדשה נוצרת כ-DRAFT (לא ACTIVE). המעבר האטומי היחיד הוא updateMany שמסמן ישן כ-SUPERSEDED עם superseded_by→חדש (CAS). old.superseded_by הוא ה-Family Active Pointer — מקור אמת אחד. רק לאחר זכייה ב-CAS מקודם ה-DRAFT ל-ACTIVE. בשום שלב אין שתי גרסאות ACTIVE. אם קריסה אחרי CAS אך לפני קידום: 0 ACTIVE (לא 2), ו-getActiveDecision משחזר אוטומטית דרך superseded_by → מקדם DRAFT ל-ACTIVE (self-healing).
Trade חדש במצב Enforced חייב Decision תקף (ACTIVE, לא פג תוקף). Trade היסטורי שנוצר לפני מועד ההפעלה מסומן בדגל legacy או כלל Legacy מפורש — ממשיך לעבוד ללא Decision.
cancel_reason_code חל רק על מעבר ACTIVE→CANCELLED. TIMEOUT שייך למעבר ACTIVE→EXPIRED (לא CANCELLED). SUPERSEDED שייך למעבר ACTIVE→SUPERSEDED (לא CANCELLED). אין להשתמש בהם כ-cancel_reason_code.
שם קנוני: hard_semi. שינוי נתונים קיימים (semi_hard → hard_semi ברשומות Trade/Portfolio) אינו חלק מ-CH3-005 — משימת מיגרציה נפרדת לאחר בדיקת As-Is.
base44/shared/decisionObject.ts
1.ישות Decision נוצרת עם כל שדות החובה (base) — validateDecision מחזיר valid=true.
2.שדות חובה תלויי פעולה: BUY דורש quantity, reference_price, stop_loss, take_profit, max_entry_price, risk_reward_ratio, max_loss_usd, exit_rules. HOLD/WAIT/REJECT דורשים reasoning בלבד.
3.decision_context (ENTRY/POSITION/EXIT) מוגדר לכל Decision ותואם ל-action.
4.decision_family_id יציב: זהה לכל גרסאות אותה החלטה. decision_id ייחודי לכל גרסה.
5.מעבר DRAFT→ACTIVE מקפיא Snapshot: snapshot_frozen=true, snapshot_id, snapshot_hash, snapshot_schema_version, input_refs, snapshot_data מאוכלסים.
6.snapshot_data מסונן: אין Secrets, API keys, פרטי חשבון. "פלט LLM מלא" אינו נשמר ללא סינון.
7.snapshot_hash הוא SHA-256 של JSON קנוני (sorted keys, no whitespace) — ניתן להשוואה.
8.גרסה חדשה מעלה decision_version, מקשר parent_decision_id, ומסמן ישן כ-SUPERSEDED עם superseded_by.
9.החלפת גרסה אטומית DRAFT-first: הגרסה החדשה נוצרת כ-DRAFT, CAS אטומי מסמן ישן SUPERSEDED+superseded_by, רק אז קידום ל-ACTIVE. בשום שלב שתי גרסאות ACTIVE. אם קריסה — self-healing.
10.getActiveDecision מחזיר רק את הגרסה ה-ACTIVE האחרונה לפי family_id.
11.expires_at נאכף: Decision שפג תוקפו מסומן EXPIRED. TTL תלוי הקשר (ttl_context), נפרד מ-Approval/Proposal TTL.
12.EXPIRED/SUPERSEDED/CANCELLED אינם יכולים ליצור Trade.
13.Trade כולל decision_id, decision_version, decision_family_id המפנים ל-Decision.
14.assertDecisionActive נאכף בגבול הביצוע המשותף: tradeExecution.ts + executeTrade — לא רק refreshPortfolioPrices.
15.try/catch לפי מצב: Enforced — כשל חוסם עסקה. Shadow — נפילה למסלול ישן + לוג. Disabled — ישיר.
16.cancelDecision דורש cancel_reason_code קנוני. טקסט חופשי משלים בלבד.
17.approval_required נקבע לפי automation_mode + confidence. Risk Gate ו-Approval נשמרים כרשומות נפרדות.
18.שדות עקיבות: schema_version, policy_version, model_version, prompt_version, agent_profile_version, correlation_id נשמרים בעת יצירה.
19.created_at ו-published_at נפרדים — האחד ליצירת DRAFT, השני לפרסום ACTIVE.
20.hard_semi (לא semi_hard) בכל ה-entities: Trade, Portfolio, Decision. מודגש כפרופיל משנה.
21.correlation_id זורם מ-Decision דרך Risk Gate → Approval → Trade → Audit.
22.CH3-007: Risk Gate משווה מחיר ביצוע מול Decision.reference_price.
23.CH3-013: Decision.reasoning נפרד משדות מובנים. validateDecision אוכף Schema.
24.CH3-014: correlation_id + decision_id/version בנתיב ה-Audit המלא. Snapshot קפוא לשחזור.
25.decision_context מתואם לפרק 2: ENTRY=BUY,WAIT,REJECT · POSITION=HOLD,REDUCE,INCREASE,WAIT,EXIT_REVIEW · EXIT=SELL,WAIT,REJECT_EXIT.
26.DRAFT→ACTIVE אינו דורש Risk Gate. הסדר: Decision ACTIVE → Risk Gate → Approval → Execution.
27.Decision אינה מכילה risk_gate_passed, risk_gate_reason, risk_gate_result_id, approval_id — אלו חיים ב-RiskVerdict ו-ApprovalRecord נפרדות המפנות ל-decision_id + decision_version.
28.supersedeDecision DRAFT-first אטומי: גם בשתי קריאות מקבילות נשארת בדיוק גרסה ACTIVE אחת לכל משפחה. הגרסה החדשה נוצרת כ-DRAFT (לא ACTIVE), כך שקריסה אחרי CAS אינה משאירה ACTIVE יתומה. getActiveDecision משחזר אוטומטית.
29.Trade חדש ב-Enforced דורש Decision תקף. Trade היסטורי ממשיך באמצעות דגל legacy או כלל Legacy מפורש.
30.cancel_reason_code אינו כולל TIMEOUT או SUPERSEDED — אלו מעברי מצב נפרדים (EXPIRED ו-SUPERSEDED בהתאמה).
31.אין מיגרציית נתונים semi_hard→hard_semi ב-CH3-005. השם הקנוני hard_semi מופיע ב-enum, אך רשומות קיימות אינן משתנות.
base44/entities/Decision.jsonc — ישות חדשה/מעודכנת: כל השדות הנ״ל (ללא risk_gate_passed/reason/result_id, approval_id — אלו ברשומות נפרדות).
base44/entities/RiskVerdict.jsonc (חדש) — רשומת פסיקת סיכון: decision_id, decision_version, verdict, reason, evaluated_at.
base44/entities/ApprovalRecord.jsonc (חדש) — רשומת אישור: decision_id, decision_version, approver, approved_at, expires_at.
base44/entities/Trade.jsonc — הוספת decision_id, decision_version, decision_family_id, legacy (boolean). שם קנוני hard_semi ב-enum (ללא מיגרציית נתונים ב-CH3-005).
base44/entities/Portfolio.jsonc — שם קנוני hard_semi ב-enum (ללא מיגרציית נתונים ב-CH3-005).
base44/entities/SystemSettings.jsonc — הוספת decision_ttl_hours (24h — Paper בלבד).
base44/shared/decisionObject.ts (חדש/מעודכן) — כל הפונקציות הנ״ל + CAS + Idempotency + Snapshot CH5 + Secrets filtering.
base44/shared/tradeExecution.ts — הוספת assertDecisionActive בגבול הביצוע המשותף.
base44/functions/executeTrade/entry.ts — הוספת assertDecisionActive לפני ביצוע.
base44/functions/runAgentScan/entry.ts — יצירת Decision לפני Trade. try/catch לפי Feature Flag (Enforced/Shadow).
base44/functions/refreshPortfolioPrices/entry.ts — assertDecisionActive + checkDecisionExpiry + CH3-007.
src/pages/Ch3Spec005.jsx — עמוד מפרט זה.
1. עדכון Decision entity: הוספת כל השדות (base + action-dependent + traceability + versioning + snapshot + approval + TTL + cancel).
2. עדכון Trade entity: הוספת decision_id, decision_version, decision_family_id, legacy. שם קנוני hard_semi ב-enum (ללא מיגרציית נתונים).
3. עדכון Portfolio entity: שם קנוני hard_semi ב-enum (ללא מיגרציית נתונים).
3a. יצירת RiskVerdict entity: decision_id, decision_version, verdict, reason, evaluated_at.
3b. יצירת ApprovalRecord entity: decision_id, decision_version, approver, approved_at, expires_at.
4. עדכון SystemSettings: הוספת decision_ttl_hours (24h — Paper).
5. עדכון base44/shared/decisionObject.ts: כל הפונקציות + CAS + Idempotency + Snapshot CH5 + Secrets filtering + action-dependent validation.
6. עדכון base44/shared/tradeExecution.ts: assertDecisionActive בגבול הביצוע המשותף.
7. עדכון base44/functions/executeTrade/entry.ts: assertDecisionActive לפני ביצוע.
8. עדכון base44/functions/runAgentScan/entry.ts: יצירת Decision לפני Trade. try/catch לפי Feature Flag (Enforced=חסימה, Shadow=נפילה).
9. עדכון base44/functions/refreshPortfolioPrices/entry.ts: assertDecisionActive + checkDecisionExpiry + CH3-007.
10. בדיקות קבלה: יצירת גרסה, הקפאת Snapshot, Hash, תוקף, ביטול עם reason_code, החלפה אטומית (CAS), חסימת EXPIRED, Enforced vs Shadow.
11. תצלום לפני/אחרי והפקת דוח ביצוע CH3-005.
Decision היא ישות חדשה — אין נתונים קיימים שעלולים להיפגע.
Trade.decision_id הוא nullable — Trades היסטוריים ללא Decision ממשיכים לעבוד באמצעות דגל legacy. Trade חדש ב-Enforced חייב Decision תקף.
SystemSettings.decision_ttl_hours עם ברירת 24 — אין בעיית Migration.
decisionObject.ts הוא מודול חדש — הסרתו מחזירה ליצירת Trade ישירה.
Feature Flag: DECISION_OBJECT_ENABLED=false מכבה זמנית ללא שינוי קוד. Shadow מאפשר השוואה ללא חסימה.
שם קנוני hard_semi ב-enum. אין מיגרציית נתונים ב-CH3-005 — נתונים קיימים עם "semi_hard" נשארים, קוד מטפל בשני הערכים. מיגרציה במשימה נפרדת.