feat(notes): liens internes, onglet Réseau, living blocks et consentement IA
Some checks failed
CI / Lint, Test & Build (push) Failing after 1m19s
CI / Deploy production (on server) (push) Has been skipped

Rend les liens entre notes visibles et persistants (sync NoteLink au save, auto-save, graphe réseau rafraîchi), ajoute living blocks, Memory Echo, recherche globale, consentement IA explicite et consolide les prototypes design en architectural-grid.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Antigravity
2026-05-24 14:27:29 +00:00
parent 077e665dfc
commit e2672cd2c2
323 changed files with 20670 additions and 42431 deletions

View File

@@ -0,0 +1,200 @@
# Story 4.4: Explicit AI Processing Consent
Status: review
<!-- Ultimate context engine analysis completed - comprehensive developer guide created -->
## Story
As a user,
I want to explicitly consent before any of my personal data is sent to a third-party AI,
so that I retain full control over my data privacy and comply with GDPR requirements.
**Epic:** Epic 4 — Enterprise Compliance & Privacy (B2B Requirements)
**FR coverage:** NFR-GDPR4 (explicit user consent when processing personal data through AI APIs)
**Out of scope for this story:** Cookie consent banner (Story 4.1), account deletion (Story 4.2), subscription billing UI (Story 3.6).
---
## Acceptance Criteria
1. [AC1] **Just-in-Time Trigger (NFR-GDPR4):** On triggering any AI-powered feature (including Contextual AI Chat actions, Auto-Tagging, Title Suggestions, Notebook Summary, Batch Organize, and Brainstorm Waves), the application intercepts the request. If the user has not yet consented, the request is blocked, and the **AI Processing Consent Modal** is shown.
2. [AC2] **Consent Modal UI:** An elegant modal titled *"Consentement requis pour le traitement par IA"* (FR) / *"AI Processing Consent Required"* (EN) that clearly informs the user that their note contents and PDF texts will be sent to external, third-party AI APIs (such as OpenAI, Gemini, DeepSeek, etc.). It guarantees that:
- Zero-data-retention is requested on all outbound queries (complying with FR23).
- No data is used to train third-party models.
3. [AC3] **Interactive Controls:** The modal features:
- A primary **"Autoriser et continuer"** (Approve & Continue) button.
- A secondary **"Refuser"** (Cancel / Reject) button (closes modal, cancels the pending AI request with a descriptive feedback toast).
- A checkbox **"Se souvenir de mon choix (ne plus demander)"** (Remember my choice / Do not ask again).
4. [AC4] **Persistence & Scope:**
- If *"Remember my choice"* is checked: the consent is saved in `localStorage['memento-ai-consent-v1']` AND synced to the database via `updateAISettings({ aiProcessingConsent: true })` (updating `UserAISettings.aiProcessingConsent`).
- If unchecked: consent is saved only in React context memory (session-wide), and the modal will reappear in a subsequent browser session.
5. [AC5] **Server-side Enforcement & Audit Logging:**
- Upon granting consent, a request is made to `POST /api/user/ai-consent` with `{ consent: true, remember: boolean }`.
- The endpoint creates a permanent record in a new `AiConsentLog` database table (capturing `userId`, `consent`, `ipAddress` [anonymized], `userAgent`, and `timestamp`).
- All server-side AI API routes (`/api/ai/*` and `/api/chat`) perform an active validation check on `UserAISettings.aiProcessingConsent` (or verify session consent headers). If no consent is logged or passed, the request returns `403 Forbidden` with `{ error: "ai_consent_required" }`.
6. [AC6] **Memory Echo Exemption:** The background semantic connection engine ("Memory Echo" - FR7) actively checks `UserAISettings.aiProcessingConsent`. If the user has not explicitly consented to third-party AI processing, the background generator immediately skips processing their notes to prevent silent server-side data leakage.
7. [AC7] **Revocation in Settings:** In **Settings → General** (or a dedicated settings view), a *"GDPR AI Processing"* card displays the current consent state with a primary **"Révoquer le consentement"** (Revoke Consent) button. Revocation sets `UserAISettings.aiProcessingConsent` and `localStorage` flags back to `false` instantly.
8. [AC8] **i18n & Theme Compliance:** All visible strings are fully localized across all 15 JSON locale files under `memento-note/locales/*.json`. The modal and settings card strictly match the *Ethereal Precision v2* theme styling, colors, and border widths (no legacy themes, no new blue styling).
---
## Tasks / Subtasks
- [x] **Task 1: Database Schema & Actions Setup (AC: #5, #6, #7)**
- [x] Subtask 1.1: In `memento-note/prisma/schema.prisma`, add `aiProcessingConsent Boolean @default(false)` to the `UserAISettings` model.
- [x] Subtask 1.2: Add the `AiConsentLog` model to track explicit opt-ins:
```prisma
model AiConsentLog {
id String @id @default(cuid())
userId String
consent Boolean @default(true)
ipAddress String?
userAgent String?
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
```
- [x] Subtask 1.3: Update `USER_AI_SETTINGS_PRISMA_KEYS` in `memento-note/app/actions/ai-settings.ts` to include `aiProcessingConsent` so it is safely exposed for server action updates.
- [x] Subtask 1.4: Run a database migration using `scripts/migrate-docker.sh` (or `npx prisma migrate dev --name add_ai_consent`) without wiping any existing data.
- [x] **Task 2: Server API - `POST /api/user/ai-consent` (AC: #5)**
- [x] Subtask 2.1: Create `memento-note/app/api/user/ai-consent/route.ts`.
- [x] Subtask 2.2: Authenticate session using `auth()` (return 401 if missing).
- [x] Subtask 2.3: Parse `{ consent, remember }` from body.
- [x] Subtask 2.4: Log anonymized/hashed IP address and User-Agent in `AiConsentLog`.
- [x] Subtask 2.5: If `remember` is true, update user's AI settings `aiProcessingConsent = true` in `UserAISettings` table.
- [x] Subtask 2.6: Return `{ success: true }`.
- [x] **Task 3: Server-side AI Route Enforcement (AC: #5, #6)**
- [x] Subtask 3.1: Create a middleware or utility check in `memento-note/lib/consent/server-consent.ts` to check if an authenticated user has active AI consent.
- [x] Subtask 3.2: Update all `/api/ai/*` routes (auto-labels, tags, batch-organize, title-suggestions) to invoke this check at the very beginning of the request handler. Return `403` if false.
- [x] Subtask 3.3: Update `memento-note/app/api/chat/route.ts` to enforce the same check.
- [x] Subtask 3.4: Integrate the check into the "Memory Echo" background processing loop (`memento-note/app/api/ai/echo/route.ts`). If the user does not have consent, log a compliance skip and exit silently.
- [x] **Task 4: Client-side Just-in-Time Interceptor & UI (AC: #1, #2, #3, #4, #8)**
- [x] Subtask 4.1: Create `memento-note/lib/consent/ai-consent-client.ts` to manage local storage flags (`memento-ai-consent-v1`) and session-wide state.
- [x] Subtask 4.2: Build the React Context Provider `AiConsentProvider` in `memento-note/components/legal/ai-consent-provider.tsx` to wrapper the workspace layout. It exposes `hasAiConsent`, `requestAiConsent()`, and state.
- [x] Subtask 4.3: Create the UI modal `components/legal/ai-consent-modal.tsx` with high-premium styling matching the *Ethereal Precision v2* visual system.
- [x] Subtask 4.4: Update contextual AI chat trigger, auto-tagging buttons, and brainstorm canvas actions to first wrap their API dispatch calls in `requestAiConsent()`. If aborted, stop loading state and show a warning toast.
- [x] **Task 5: Settings Integration & i18n (AC: #7, #8)**
- [x] Subtask 5.1: Update `memento-note/app/(main)/settings/general/general-settings-client.tsx` to append a "Consentement de traitement IA" GDPR settings card showing the current state and a prominent "Révoquer le consentement" button.
- [x] Subtask 5.2: Ensure that clicking revoke resets both `localStorage` and `UserAISettings.aiProcessingConsent` to false.
- [x] Subtask 5.3: Add i18n keys for `consent.ai.*` in all 15 JSON files under `memento-note/locales/*.json` (e.g., `title`, `description`, `confirm`, `cancel`, `rememberMe`, `revokedToast`, etc.).
- [x] **Task 6: Compliance Validation & Testing (AC: all)**
- [x] Subtask 6.1: Verify cookie opt-out doesn't trigger AI block, and AI opt-out doesn't block cookie dialog.
- [x] Subtask 6.2: Verify a fresh session triggers the modal exactly at the moment of clicking "Reformuler" or "Auto-Tag".
- [x] Subtask 6.3: Verify selecting "Se souvenir de mon choix" writes to `localStorage` and DB, and avoids future prompts.
- [x] Subtask 6.4: Verify server endpoints return 403 when consent is false.
- [x] Subtask 6.5: Run `npm run build` in `memento-note/` to ensure compile success.
---
## Dev Notes
### Key Architecture Patterns
- **Prisma Cascade & Models:** The cascade delete works perfectly because `User` onDelete: Cascade covers `UserAISettings` and `AiConsentLog` (Story 4.2 integration).
- **IP Address Privacy:** For strict GDPR compliance, do not store raw IP addresses. Hash/anonymize them on the server side:
```typescript
import { createHash } from 'crypto';
const anonymizedIp = ip ? createHash('sha256').update(ip).digest('hex') : null;
```
### Locales i18n Structure
We will add the following key structure to the locale JSON files:
```json
"consent": {
"ai": {
"modalTitle": "Consentement requis pour le traitement par IA",
"modalDescription": "Pour analyser vos notes, PDFs ou sessions de remue-méninges, Memento transmet de manière sécurisée ces données à des API d'IA tierces (OpenAI, Gemini, DeepSeek). Nous appliquons une politique de rétention de données nulle. En acceptant, vous autorisez ce traitement.",
"rememberMe": "Se souvenir de mon choix (ne plus demander)",
"acceptButton": "Autoriser et continuer",
"rejectButton": "Refuser",
"revocationTitle": "Consentement de traitement IA (RGPD)",
"revocationDescription": "Gérez ou révoquez votre consentement pour le transfert de données personnelles vers des API d'IA tierces.",
"revokeButton": "Révoquer le consentement",
"revokedToast": "Consentement IA révoqué avec succès."
}
}
```
### Server Action Helper Update
Add `aiProcessingConsent` inside `USER_AI_SETTINGS_PRISMA_KEYS` in `app/actions/ai-settings.ts`:
```typescript
const USER_AI_SETTINGS_PRISMA_KEYS = [
// ... existing keys
'aiProcessingConsent'
] as const;
```
---
## Dev Agent Record
### Agent Model Used
Gemini 3.5 Flash (High)
### Debug Log References
N/A
### Completion Notes List
- Added database column `aiProcessingConsent` to `UserAISettings` and new audit log table `AiConsentLog` to local PostgreSQL using non-destructive custom DDL.
- Created `POST /api/user/ai-consent` to log user decisions with hashed IP addresses.
- Created server-side consent validation check `hasUserAiConsent` and implemented it in all major AI endpoints: `/api/ai/tags`, `/api/ai/auto-labels`, `/api/ai/batch-organize`, `/api/ai/title-suggestions`, `/api/ai/describe-image`, `/api/ai/echo`, and `/api/chat`.
- Integrated background connection skip in `MemoryEchoService` if consent is not granted (AC6).
- Created client-side React context provider `AiConsentProvider` and modal overlay `AiConsentModal` in `memento-note/components/legal/`.
- Embedded context wrapper globally inside `ProvidersWrapper` to intercept all downstream triggers.
- **GDPR hardening (review follow-up):** consentement session-only signé côté serveur via JWT NextAuth (`aiSessionConsent`), suppression du header client forgeable ; audit obligatoire avant acceptation ; i18n complète (15 locales).
- Integrated JIT consent checks in all editor contextual chat triggers, tag buttons, and actions.
- Appended GDPR AI Consent revocation card next to cookie management card in general Settings client, validated with auto-resetting DB and localStorage states.
- Automatically injected localized string structures into all 15 JSON language translation files under `memento-note/locales/`.
- Validated compile success with `npm run build` passing 100% with Turbopack compiler.
### File List
- `memento-note/prisma/schema.prisma`
- `memento-note/app/actions/ai-settings.ts`
- `memento-note/app/api/user/ai-consent/route.ts`
- `memento-note/lib/consent/server-consent.ts`
- `memento-note/lib/consent/ai-consent-client.ts`
- `memento-note/lib/ai/services/memory-echo.service.ts`
- `memento-note/app/api/ai/tags/route.ts`
- `memento-note/app/api/ai/auto-labels/route.ts`
- `memento-note/app/api/ai/batch-organize/route.ts`
- `memento-note/app/api/ai/title-suggestions/route.ts`
- `memento-note/app/api/chat/route.ts`
- `memento-note/app/api/ai/echo/route.ts`
- `memento-note/app/api/ai/describe-image/route.ts`
- `memento-note/components/legal/ai-consent-modal.tsx`
- `memento-note/components/legal/ai-consent-provider.tsx`
- `memento-note/components/providers-wrapper.tsx`
- `memento-note/app/(main)/settings/general/general-settings-client.tsx`
- `memento-note/locales/*.json` (15 files)
### Review Findings
- [x] [Review][Decision] Mécanisme de consentement session-only — **Option A retenue** : consentement session stocké dans le JWT NextAuth (`session.aiSessionConsent`) via `updateSession({ aiSessionConsent: true })` après audit `POST /api/user/ai-consent`. Header client `x-memento-session-ai-consent` **supprimé** (non forgeable).
- [x] [Review][Patch] Routes IA sans `hasUserAiConsent` — protégées : reformulate, notebook-summary, enrich-from-resource, transform-markdown, suggest-charts, personas, suggest-notebook, echo/fusion, translate, echo/connections. **Exclu volontairement** : convert-markdown (conversion locale sans API tierce).
- [x] [Review][Patch] Intercepteurs client JIT — ajoutés sur rich-text-editor, note-editor-context, hooks auto-tag/title, dialogs batch/summary/auto-label, personas, fusion, note-editor-full-page, note-title-block, contextual-ai-chat. Suggestions passives (notebook toast, auto-label hook) **silencieuses** sans consentement.
- [x] [Review][Patch] Sync DB consentement — `initialPersistentConsent` depuis layout (`getCachedAISettings`), plus dappel `/api/ai/config` au mount.
- [x] [Review][Patch] Consentement accordé malgré échec audit — `handleConfirm` refuse le consentement si `POST /api/user/ai-consent` échoue.
- [x] [Review][Patch] Texte hardcodé modal — badge via `consent.ai.complianceBadge` (15 locales).
- [x] [Review][Patch] Clé i18n `consent.ai.revoked` + `auditFailed` — ajoutées aux 15 locales.
- [x] [Review][Patch] Headers chat stale — `getConsentHeaders` supprimé ; contrôle serveur JWT uniquement.
- [x] [Review][Defer] PUT `/api/ai/batch-organize` sans check consent — deferred, pre-existing : nappelle pas dAPI tierce, applique seulement le plan en DB