Motorul NU trimite o notificare per eveniment. Decide daca merita notificat,
cui, ce severitate, daca se agrega, ce canal si cand.
- notifications / notification_preferences / notification_processed_events.
Ultimul da idempotency: aceeasi regula nu proceseaza acelasi eveniment de
doua ori (unique rule_id + event_id).
- Reguli in COD, nu in DB cu condition_expression evaluat dinamic. Un
evaluator de expresii e o suprafata de atac si un limbaj in plus, fara ca
nimeni sa administreze inca reguli per tenant. Contractul ramane acelasi
cand vor migra in DB.
- Destinatarii se rezolva server-side din memberships, niciodata din payload.
Implicit nu notificam actorul despre propria actiune.
- Deduplicare pe cheia tenant+recipient+categorie+entitate+versiune regula.
- Agregare: 47 de taskuri intr-o ora devin o notificare cu aggregated_count,
nu 47 de notificari. Testul verifica invariantul ca fereastra de agregare
<= fereastra de dedup, altfel s-ar crea una noua inainte sa se agrege.
- Quiet hours cu fereastra care poate traversa miezul noptii. CRITICAL le
depaseste DOAR daca politica userului permite, nu implicit.
- Inbox: filtre (unread/action_required/critical/intelligence/system),
mark-read, acknowledge (opreste escaladarea), dismiss, snooze.
- Preferinte per user+tenant cu UPSERT (NULLS NOT DISTINCT pe category, ca
randul implicit "toate categoriile" sa fie unic).