# SUDANNA AI OS v23 — Architecture Guide
# New System Patterns, Rules & Anti-Patterns

> الوثيقة المرجعية للنظام الجديد.
> أي endpoint يُكتب أو يُعدَّل يجب أن يلتزم بهذه القواعد.

---

## 1. الفلسفة الأساسية

```
«لا تنفيذ بدون فهم.
 لا خصم بدون نجاح.
 لا AI بدون محاسبة.»
```

النظام القديم كان يُنفِّذ ثم يحاسب. النظام الجديد يحاسب أولاً.

---

## 2. Core Contract — الإجباري في كل endpoint

كل endpoint في النظام الجديد يلتزم بهذا التسلسل بالترتيب:

```php
// ① GOVERNANCE — أول سطر دائماً
$ctx  = Kernel::handle(['auth' => true, 'rate' => 'ai']);
$user = $ctx['user'];
$uid  = (int)$user['id'];
$plan = $user['plan_key'] ?? $user['subscription_tier'] ?? 'free';

// ② PRICE — من PricingEngine فقط (SSOT)
$priceSdg = PricingEngine::getDocumentPrice($type, $plan);
// أو:
$threshold = (int)PricingEngine::getSetting('estimation_threshold_sdg', 3000);

// ③ BUDGET — قبل أي عملية مالية أو AI
$budget = BudgetGate::canAffordEstimate($uid, $plan);
// أو:
$limit = BillingEngine::checkAILimit($uid, $plan); // for AI daily quota

// ④ EXECUTION DECISION
if ($priceSdg >= $threshold) {
    // → Type 1: TaskRunner OR Type 2: Direct Executor via Reservation
} else {
    // → Type 2 Direct: generate + debit AFTER success
}

// ⑤ RECONCILIATION (if Reservation was used)
ReservationEngine::complete($reservationId, $actualSdg, $executionLog);

// ⑥ AUDIT (all paths)
Logger::info('action_name', ['key' => 'value']);
Logger::aiUsage($uid, 'event_type', ['cost_sdg' => $cost]);

// ⑦ RESPOND (auto-injects trace_id)
Kernel::respond(['ok' => true, 'result' => $result]);
// OR on error:
Kernel::fail('error_key', 'human message', $httpCode);
```

---

## 3. نوعا التنفيذ

### Type 1 — TaskRunner Flow

**متى نستخدمه:**
- المهمة تحتوي على أجزاء متعددة (document + images + video)
- النوع غير محدد مسبقاً (free-form prompt)
- Confidence < 1.0 يستدعي TaskEstimator::analyze

**مثال:** `api/tools/run.php`

```
User prompt
    ↓
TaskEstimator::analyze (RuleParser → [AIAnalyzer if conf < 0.70])
    ↓
BudgetGate::canAffordEstimate (soft accounting)
    ↓
ReservationEngine::hold (price_snapshot frozen, TTL 30min)
    ↓ { token }
User confirms
    ↓
Kernel::executeWithReservation(token)
    ├─ ReservationEngine::activate (atomic, debits)
    ├─ KernelExecutionToken::generate() → ReservationContext
    └─ TaskRunner::run()
           ├─ text → document → diagram → table → image → video
           ├─ prices from price_snapshot (NOT live PricingEngine)
           └─ skip_debit=true (debit done in activate())
                    ↓
             ReservationEngine::complete (refund difference)
```

**الـ skip_debit ضروري هنا:** activate() خصم pessimistic, TaskRunner لا يخصم مجدداً.

---

### Type 2 — Direct Executor Flow

**متى نستخدمه:**
- النوع معروف مسبقاً (doc_id أو service_id في DB)
- Confidence = 1.0 (يقين كامل)
- سعر ثابت من PricingEngine مباشرة

**مثال:** `api/bde/generate.php`

```
{ doc_id }
    ↓
PricingEngine::getDocumentPrice(docType, plan)   ← SSOT
    ↓
price ≥ threshold?
    │
    ├─ YES: Estimation (بدون TaskEstimator::analyze)
    │         Build plan { confidence=1.0, method='direct_pricing' }
    │         Store built_prompt in plan.bde_context (FROZEN)
    │         BudgetGate::canAffordEstimate
    │         ReservationEngine::hold
    │         → { token }
    │         ┆
    │         User confirms → { token }
    │         ReservationEngine::activate (debits)
    │         Read frozen prompt from bde_context
    │         Execute AI generation
    │         IF FAIL: cancel → full refund
    │         IF SUCCESS: complete (actual = reserved)
    │
    └─ NO: Direct
              BillingEngine::checkAILimit → 429
              UsageMeter::checkFunds → 402
              Generate AI
              IF FAIL: 500, NO debit
              IF SUCCESS: UsageMeter::debit → respond
```

**الفرق من Type 1:**
- لا KernelExecutionToken (لا TaskRunner)
- Prompt مُجمَّد في reservation وليس في TaskRunner
- Execution مباشر بعد activate(), لا خطوة وسيطة

---

## 4. قاعدة الاختيار بين النوعين

```
IF confidence < 1.0 (نوع مجهول من prompt حر):
    → Use TaskEstimator::analyze + TaskRunner (Type 1)

IF confidence = 1.0 (نوع معروف من DB/context):
    → Use direct_pricing + Direct Executor (Type 2)

BOTH must use:
    Kernel::handle + PricingEngine + BudgetGate + ReservationEngine (if ≥ threshold) + Logger
```

---

## 5. Kernel API Reference

جميع هذه الدوال موجودة في `engine/Kernel.php`:

| الدالة | الدور | متى تُستخدم |
|---|---|---|
| `Kernel::handle(['auth'=>true, 'rate'=>'ai'])` | governance gate | أول سطر دائماً |
| `Kernel::jsonBody()` | parse request body (cached) | بعد handle() |
| `Kernel::require(['field1', 'field2'])` | validate required fields | للـ endpoints المباشرة |
| `Kernel::respond(array $payload, int $code=200)` | output JSON + trace_id + exit | كل success response |
| `Kernel::fail(string $error, string $msg, int $code, array $extra=[])` | output error + exit | كل error response |
| `Kernel::meteredAI(string $event, string $service, callable $fn, int $uid)` | AI call + automatic debit | للـ inline AI calls |
| `Kernel::estimateAndHold(array $user, string $prompt)` | Type 1 estimation + hold | tools/run scenario 2 |
| `Kernel::executeWithReservation(string $token)` | activate + TaskRunner | tools/run scenario 1 |
| `Kernel::traceId()` | get current trace | للـ external engines |
| `Kernel::context()` | get full request context | للـ middleware |

---

## 6. PricingEngine — Single Source of Truth

```php
// Document prices
PricingEngine::getDocumentPrice('contract', 'free');    // → int SDG
PricingEngine::getDocumentPrice('feasibility', 'plus'); // → int SDG

// AI limits
PricingEngine::getAILimit('free');          // → int (requests/day)
PricingEngine::getBookingLimit('plus');     // → int (bookings/day)

// Image/Video prices
PricingEngine::getImagePrices();            // → ['512x512' => [...], ...]
PricingEngine::getVideoPrices();            // → ['15s' => [...], ...]

// Settings (all numeric config from platform_settings)
PricingEngine::getSetting('estimation_threshold_sdg', 3000); // → int/string
PricingEngine::getSetting('estimation_ttl_minutes', 30);     // → int

// Exchange rate
PricingEngine::getRate();                   // → float (USD/SDG)
PricingEngine::toSDG(float $usd);          // → int SDG
```

**القاعدة:** أي رقم SDG في الكود = خطأ. كلها من PricingEngine.

---

## 7. HTTP Response Codes — الاستخدام الموحَّد

| الكود | المعنى | متى |
|---|---|---|
| `200` | نجاح | كل success |
| `400` | خطأ في الطلب (client error) | missing fields, invalid token, wrong format |
| `402` | رصيد غير كافٍ | insufficient_funds, insufficient_balance |
| `403` | غير مصرح | ownership failure, admin-only |
| `404` | غير موجود | document/booking/user not found |
| `422` | خطأ في البيانات (semantic) | invalid type, invalid amount |
| `429` | تجاوز الحد | rate_limit, daily_limit_exceeded |
| `500` | خطأ في الخادم | generation_failed, server_error |
| `502` | خطأ في الـ AI provider | ai_network_error, ai_api_error |

---

## 8. BudgetGate — Budget Enforcement

```php
// للـ Estimation flows (soft reservation accounting)
$budget = BudgetGate::canAffordEstimate($uid, $estimatedPlan);
// available = wallet + credit - SUM(active_reservations)
// Returns: { ok, required_sdg, available_sdg, shortfall_sdg, breakdown, suggestions }

// للـ AI calls المباشرة (real-time check)
BudgetGate::enforce($uid, 'document', 'contract'); // throws on fail
// or:
$check = BudgetGate::check($uid, 'document', 'contract'); // returns status

// للـ AI calls مع execution
$result = BudgetGate::metered($uid, 'document', 'contract', function() {
    return AIClient::call($prompt, $system);
});
```

---

## 9. ReservationEngine — Lifecycle

```
hold()          → status: pending     (no debit, row lock on wallet)
activate()      → status: active      (debits, nulls token = anti-replay)
complete()      → status: completed   (refund difference, logs accuracy)
cancel()        → status: cancelled   (refunds if was active)
expireOld()     → status: expired     (cron every 5 min)
```

**Atomicity guarantees:**
- `hold()`: `BEGIN TRANSACTION + SELECT wallet FOR UPDATE + INSERT`
- `activate()`: `UPDATE WHERE status='pending'` (fails silently if already active)
- Both prevent race conditions at DB level

---

## 10. Logger — Audit Trail

```php
// Establish trace (call once per request, auto-inherited by all engines)
Logger::traceId();                          // generates or returns existing

// Context (auto-included in all subsequent logs)
Logger::setContext('user_id', $uid);

// Structured logs
Logger::info('booking_created',  ['booking_id' => $id]);
Logger::warn('debit_failed',     ['amount' => $sdg]);
Logger::error('generation_failed', ['error' => $msg]);
Logger::critical('kernel_bypass_attempt', ['caller' => 'unknown']); // SECURITY

// AI-specific (writes to ai_usage table with trace_id)
Logger::aiUsage($uid, 'bde_document', [
    'service'        => 'bde:contract',
    'cost_sdg'       => $charged,
    'reservation_id' => $resId,    // optional, for reservation flows
]);
```

---

## 11. Anti-Patterns ❌

هذه الأنماط **محظورة** في أي كود جديد:

### ❌ 1. AI Call بدون UsageMeter

```php
// ❌ WRONG
$response = AIClient::call($prompt, $system);

// ✅ CORRECT
$result = UsageMeter::wrap($uid, 'document', 'contract',
    fn() => AIClient::call($prompt, $system)
);
// OR via Kernel:
$result = Kernel::meteredAI('document', 'contract', fn() => AIClient::call(...), $uid);
```

### ❌ 2. Pricing خارج PricingEngine

```php
// ❌ WRONG
$price = 15000; // hardcoded

// ✅ CORRECT
$price = PricingEngine::getDocumentPrice('feasibility', $plan);
```

### ❌ 3. Execution بدون Reservation (للمهام > threshold)

```php
// ❌ WRONG (high-cost task without reservation)
if ($price >= 3000) {
    $result = AIClient::call($prompt, $system); // direct execution
}

// ✅ CORRECT
if ($price >= $threshold) {
    Kernel::estimateAndHold($user, $prompt); // exits with token
}
```

### ❌ 4. Debit قبل Success (في non-reservation flows)

```php
// ❌ WRONG
UsageMeter::debit($uid, $price, 'document', 'contract');
$result = ask_claude($prompt, $system);
if (!$result) { /* rصيد ضاع! */ }

// ✅ CORRECT
$result = ask_claude($prompt, $system);
if (!$result) { Kernel::fail('generation_failed', '...', 500); } // exits, no debit
UsageMeter::debit($uid, $price, 'document', 'contract'); // only reaches here on success
```

### ❌ 5. تعديل Prompt بعد Estimation

```php
// ❌ WRONG (price locked on old prompt, new prompt may cost more)
$token = Reservation::hold($uid, $estimatedPlan);
$prompt = modifyPrompt($prompt); // NOT OK

// ✅ CORRECT: Freeze prompt at estimation time
$estimatedPlan['bde_context']['prompt'] = $builtPrompt; // frozen
ReservationEngine::hold($uid, $estimatedPlan);
// Token path reads $plan['bde_context']['prompt'] — unchanged
```

### ❌ 6. json_response() في New Endpoints

```php
// ❌ WRONG (legacy — no trace_id, no request_end logging)
json_response(['success' => true, 'data' => $result]);

// ✅ CORRECT
Kernel::respond(['ok' => true, 'data' => $result]); // auto-adds trace_id
```

### ❌ 7. require_auth() في New Endpoints

```php
// ❌ WRONG (legacy — no rate limit, no trace, no SecurityGate)
$u = require_auth();

// ✅ CORRECT
$ctx  = Kernel::handle(['auth' => true, 'rate' => 'ai']);
$user = $ctx['user'];
```

### ❌ 8. Double Debit في Reservation Flows

```php
// ❌ WRONG (activate() already debited, skip_debit MUST be true in TaskRunner)
$metered = UsageMeter::wrap($uid, 'document', $type,
    fn() => AIClient::call($prompt, $system)
    // missing: ['skip_debit' => true]
);

// ✅ CORRECT (in TaskRunner::runDocument)
$metered = UsageMeter::wrap($uid, 'document', $docType,
    fn() => ToolsEngine::generateDocument($query, $sector, $uid, $docType),
    ['skip_debit' => true]  // reservation already paid in activate()
);
```

---

## 12. Endpoint Classification

### New System Endpoints (Phase 3 Replace — Full Compliance)
| Endpoint | Type | Pattern |
|---|---|---|
| `api/tools/run.php` | Type 1 (TaskRunner) | Kernel + Estimator + Reservation + TaskRunner |
| `api/bde/generate.php` | Type 2 (Direct) | Kernel + direct_pricing + Reservation + BDE Executor |
| `api/tasks/estimate.php` | New | Kernel + TaskEstimator + hold |
| `api/tasks/execute.php` | New | Kernel + activate + TaskRunner |
| `api/tasks/status.php` | New | Kernel + ReservationEngine::getById |

### Bridge Endpoints (Wave 0 + Wave 1 — Partial Compliance)
| Endpoint | Added | Remaining Gap |
|---|---|---|
| `api/visitor_ask.php` | Logger + trace_id | No Reservation |
| `api/booking/confirm.php` | Logger + ownership | No Kernel::handle |
| `api/booking/create.php` | Logger + limit check | No Kernel::handle |
| `api/bde/generate.php` | **Replaced** ← | — |
| `api/ai/answer.php` | Logger + trace_id | — (already complete) |
| `api/vision/describe.php` | Logger + limit | _gemini_call outside AIClient |
| `api/subscription/activate.php` | Logger + session fix | No Kernel::handle |
| `api/payment/create_invoice.php` | Logger + trace_id | No Kernel::handle |

### Legacy Endpoints (146 — Backlog)
Prioritized by risk: P0 → P1 → P2. Full map in Consolidation Audit.

---

## 13. Checklist لأي Endpoint جديد أو Replace

قبل أي PR أو deploy:

```
□ Kernel::handle() هو أول سطر
□ لا require_auth() في الكود
□ لا json_response() في الكود
□ لا hardcoded SDG numbers
□ كل AI call يمر بـ UsageMeter أو Kernel::meteredAI
□ Debit يحدث بعد success فقط (في non-reservation flows)
□ skip_debit=true في TaskRunner::runDocument
□ Logger::info عند كل success
□ Logger::aiUsage عند كل AI call
□ trace_id موجود في response (Kernel::respond يضيفه تلقائياً)
□ HTTP codes مطابقة للجدول (§7)
□ PricingEngine هو المصدر الوحيد للأسعار
□ لو price ≥ threshold → Reservation إجباري
□ لو Reservation → complete() أو cancel() لا يُترك معلقاً
```

---

## 14. اختيار الـ Execution Type

```
سؤال 1: هل النوع معروف مسبقاً من DB؟
  نعم → Type 2 (Direct Executor)
  لا  → Type 1 (TaskRunner)

سؤال 2: هل المهمة أجزاء متعددة (text + images + video)؟
  نعم → Type 1 (TaskRunner: decompose + execute in order)
  لا  → Type 2 (Direct: single execution unit)

سؤال 3: هل السعر ثابت وغير متغير؟
  نعم → Type 2 (direct_pricing, confidence=1.0)
  لا  → Type 1 (TaskEstimator, confidence < 1.0)
```

---

## 15. تقييم api/consultation/start.php (Next Replace Candidate)

```
النوع الحالي: Legacy (require_auth + json_response)
is_paid field: موجود لكن بدون enforcement
السعر: من provider_services.price_sdg (معروف عند start)

التصنيف: Type 2 (Direct Executor)
  - consultation_id معروف بعد INSERT
  - السعر من DB (ثابت)
  - is_paid=false: Kernel only (لا Reservation)
  - is_paid=true:  Kernel + Reservation + deferred debit (عند complete)

تحدي خاص: Deferred Payment
  - الخصم يحدث عند اكتمال الاستشارة، لا عند بدئها
  - يحتاج: consultation_reservation مستقل (مختلف عن task_reservations)
  - أو: استخدام task_reservations بـ status='pending' حتى complete

قرار: مناسب لـ Phase 3 Replace التالي
التعقيد: متوسط (أعلى من BDE بسبب deferred payment)
```

---

## 16. النظام الصوتي: "Dual Architecture" المؤقت

حتى اكتمال Phase 3، يعمل النظام بمسارين موازيين:

```
New System (6.4% endpoints, 2 core flows):
  Kernel → PricingEngine → BudgetGate → [Reservation] → Logger → Kernel::respond

Legacy System (93.6% endpoints):
  require_auth → ... → json_response
  (Bridge endpoints: لديها Logger + trace_id لكن بقية Legacy)
```

هذا مقبول مرحلياً بشرط:
1. كل كود جديد يتبع النظام الجديد
2. Legacy لا يُضاف إليه منطق جديد
3. كل Replace يستبدل كاملاً (لا partial)

---

## 17. خلاصة

| القاعدة | الجزء المسؤول |
|---|---|
| Auth + Rate + Trace | Kernel::handle |
| Pricing | PricingEngine (SSOT) |
| Budget | BudgetGate::canAffordEstimate |
| Hold | ReservationEngine::hold (atomic) |
| Execute | TaskRunner (Type 1) أو Direct (Type 2) |
| Debit | activate() (Type 1) أو after success (Type 2) |
| Refund | complete() — تلقائي |
| Audit | Logger::info + Logger::aiUsage |
| Response | Kernel::respond (trace_id تلقائي) |

---

## 18. Wave 2 — Roadmap

الـ endpoints التالية هي الأولوية لـ Replace في Wave 2 (بعد Closed Beta):

### Group A — Financial (أعلى أولوية)
| Endpoint | النوع | التحدي |
|---|---|---|
| `api/wallet/topup.php` | Type 2 Direct | تم Bridge ← Replace مستقبلاً |
| `api/payments/subscribe.php` | Type 2 Direct | تم Bridge ← Replace مستقبلاً |
| `api/payment/webhook.php` | Webhook special | HMAC auth، لا Kernel |
| `api/subscription/activate.php` | Type 2 Direct | تم Bridge ✅ |

### Group B — AI Flows
| Endpoint | النوع | التحدي |
|---|---|---|
| `api/vision/describe.php` | Type 2 Direct | _gemini_call خارج AIClient |
| `api/engine/chat.php` | Type 1 Gateway | يمر بـ Gateway، يحتاج Kernel wrapper |

### Group C — Booking
| Endpoint | النوع | التحدي |
|---|---|---|
| `api/booking/create.php` | Type 2 Direct | تم Bridge ✅ ← Replace |
| `api/consultation/start.php` | Type 2 Deferred | مُعطَّل مؤقتاً، Deferred Payment pattern |

### قاعدة Wave 2
> كل Replace في Wave 2 يستخدم نفس نمط v23.FINAL:
> `Kernel::handle → PricingEngine → BudgetGate → [hold|direct] → Logger → Kernel::respond`
> بدون استثناءات.

---

## 19. Closed Beta — Monitoring Checklist

تشغيل هذه الاستعلامات يومياً أثناء Closed Beta:

```sql
-- 1. Reservation health
SELECT status, COUNT(*), SUM(reserved_sdg) FROM task_reservations
WHERE created_at > NOW() - INTERVAL 24 HOUR GROUP BY status;

-- 2. Financial blind spots
SELECT COUNT(*) FROM wallet_transactions
WHERE reservation_id IS NULL AND type='debit' AND created_at > NOW() - INTERVAL 24 HOUR;

-- 3. Trace coverage
SELECT COUNT(*) total, SUM(trace_id IS NOT NULL) traced,
       ROUND(100*SUM(trace_id IS NOT NULL)/COUNT(*),1) pct
FROM ai_usage WHERE created_at > NOW() - INTERVAL 24 HOUR;

-- 4. No negative wallets
SELECT id, user_id, balance FROM wallet WHERE balance < 0;

-- 5. Cron health (last run)
SELECT * FROM system_logs WHERE event='cron_expire_run' ORDER BY created_at DESC LIMIT 1;
```
