# WP2 — Wire the capability digest into the chat (dynamic system prompt + cache versioning)

## Goal
Make the AI chat assistant's system prompt **dynamic**: built at request time from the WP1 `CapabilityDigestService` (framed by a wrapper template), aware of the user's company, locale, permissions, and current page. Fix the response cache so different contexts don't collide on the same message. This is the heart of the feature — after this WP, the assistant actually knows the program's capabilities.

## Context
- BE `/home/moonui/moon-erp-be`, branch `hazemdev`, Pest on sqlite. Bilingual AR/EN via `Accept-Language`.
- Target file: `Modules/Core/app/Services/AiChatService.php`. Today:
  - `getSystemPrompt()` (~:509) = `return config('ai.system_prompt', '...')` — static, no args.
  - `buildMessages($message, $history)` (~:491) = `[['role'=>'system','content'=>getSystemPrompt()], ...history, ['role'=>'user',...]]`.
  - `chat(User $user, string $message, array $history=[])` (~:21) — loads AiSetting by company, guards, **cache short-circuit** via `getCachedResponse($companyId,$message)` (~:42), resolves provider, `buildMessages`, `callProvider`, logs, `cacheResponse`.
  - Cache: `AiResponseCache` keyed `sha256(normalizeQuery($message)) + company_id` — **prompt-agnostic** (the bug: a page/permission-aware prompt would serve stale/cross-context answers).
  - `complete(...)` (~:228) already takes a dynamic `$systemPrompt` — precedent, don't break its callers (CostAiService).

## Interfaces consumed (from WP1)
- `CapabilityDigestService::cached(string $locale, ?array $userPermissions): string` and `::version(): string`.

## Deliverables
1. **Dynamic system prompt.** Change `buildMessages()` (or `getSystemPrompt()`) to assemble: the `config('ai.system_prompt')` **wrapper/instructions** (persona, guidelines — KEEP as the framing template) + the WP1 capability digest (`cached($locale, $userPerms)`) + optional page-context line (WP3 will supply the page; design the signature to accept an optional `?string $pageContext` now so WP3 only fills it). Resolve `$locale` from the request/`Accept-Language`; resolve `$userPermissions` from the authenticated `User` (Spatie `getAllPermissions()->pluck('name')`; super-admin/owner ⇒ null ⇒ unfiltered).
   - Signature suggestion: `buildMessages(string $message, array $history, User $user, ?string $pageContext = null): array` and a `getSystemPrompt(User $user, string $locale, ?string $pageContext = null): string`. Update `chat()` to pass `$user`.
2. **Version the cache key (the critical fix).** `getCachedResponse()`/`cacheResponse()`/`normalizeQuery()` must incorporate a **prompt fingerprint** = `CapabilityDigestService::version()` + a permission-scope tag (e.g. hash of the user's permission set OR a coarse role bucket) + (when present) a page-context tag. So two users on different screens / with different capabilities don't collide, and after a digest refresh the old cache naturally misses. Prefer adding a `prompt_version`/`context_hash` component to the hash rather than a schema change — the cache is keyed by a single `query_hash` string, so fold the extra context into that hash. Confirm no migration needed (hash string only).
3. Keep `complete()` and its callers untouched (out of scope).

## Acceptance criteria
- [ ] A chat request now sends a system prompt containing the capability digest (screens from all modules). A Pest test doubles/asserts the assembled system message contains a known new-feature screen (e.g. clinic lab-results or loyalty) — proving the assistant is fed current capabilities.
- [ ] Locale respected: `Accept-Language: ar` vs `en` yields the digest in that language.
- [ ] Permission filtering: a limited user's prompt omits screens they can't access; super-admin gets all.
- [ ] Cache no longer collides across contexts: same `message` with different page-context / permission-scope / digest-version produces distinct cache keys (test asserts different `query_hash`). Same everything ⇒ cache hit.
- [ ] Existing 27 AI tests still pass (no regression); `complete()` path unchanged.

## Tests
- Extend `Modules/Core/tests/Unit/AiChatServiceTest.php` + `Modules/Core/tests/Feature/AiChatApiTest.php`: dynamic-prompt content, locale, permission filter, cache-key differentiation. Run the full AI suite (baseline = 27 passed).

## Flags
- No [FIN]. No migration (cache-key change is a hash string, not schema). Pivotal (shared core chat flow) → careful Codex/native review of the cache-key logic (regression risk: a wrong key = permanent cache miss or cross-user leak of an answer). NOT a security leak of data (digest has no PHI), but a wrong permission-scope tag could show a user a screen name they can't open — keep the filter correct.

## Out of scope
- FE changes / page-context plumbing (WP3 — but leave the `?string $pageContext` param ready). Owner knowledge field (WP4).
