# Analyse des Incohérences entre ADR et Règles Métier

**Date d'analyse** : 2026-01-28
**Analysé par** : Audit Architecture RoadWave
**Scope** : 18 ADR × Règles Métier (17 fichiers)

---

## Résumé Exécutif

Cette analyse a identifié **15 incohérences** entre les décisions d'architecture (ADR) et les règles métier du projet RoadWave.

### Répartition par Sévérité

| Sévérité | Nombre | % Total | Statut | Action Required |
|----------|--------|---------|--------|-----------------|
| 🔴 **CRITICAL** | 2 | 14% | ✅ **RÉSOLU** | ~~avant implémentation~~ |
| 🟠 **HIGH** | 2 | 14% | ✅ **RÉSOLU** (2 résolus, 1 annulé) | ~~Résolution Sprint 1-2~~ |
| 🟡 **MODERATE** | 9 | 64% | ✅ **RÉSOLU** (6 résolus, 2 annulés, 1 documenté) | ~~Résolution Sprint 3-5~~ |
| 🟢 **LOW** | 1 | 7% | ✅ **ANNULÉ** (Faux problème) | ~~À clarifier lors du développement~~ |

### Impact par Domaine

| Domaine | Nombre d'incohérences | Criticité maximale |
|---------|----------------------|-------------------|
| Streaming & Géolocalisation | 3 | 🔴 CRITICAL |
| Données & Infrastructure | 2 | 🟠 HIGH |
| Authentification & Sécurité | 2 | 🟠 HIGH |
| Tests & Qualité | 2 | 🟡 MODERATE |
| Coûts & Déploiement | 3 | 🟡 MODERATE |
| UX & Engagement | 2 | 🟡 MODERATE |

---

## 🔴 Incohérences Critiques (Blocantes)

### #1 : HLS ne supporte pas les Notifications Push en Arrière-plan

**Statut** : ✅ **RÉSOLU** (ADR-017 créé)

| Élément | Détail |
|---------|--------|
| **ADR concerné** | ADR-002 (Protocole Streaming) |
| **Règle métier** | Règle 05, section 5.1.2 (Mode Piéton, lignes 86-120) |
| **Conflit** | HLS est unidirectionnel (serveur→client), ne peut pas envoyer de push quand l'app est fermée |
| **Impact** | Mode piéton non fonctionnel : notifications "Point d'intérêt à 200m" impossibles |

**Scénario d'échec** :
```
Utilisateur: Marie se promène, app fermée
Position: 150m de la Tour Eiffel
Attendu: Push notification "🗼 À proximité: Histoire de la Tour Eiffel"
Réel: Rien (HLS ne peut pas notifier)
```

**Solution implémentée** :
- ✅ **ADR-017** : Architecture hybride WebSocket + Firebase Cloud Messaging
- Phase 1 (MVP) : Push serveur via FCM/APNS
- Phase 2 : Geofencing natif iOS/Android pour mode offline

**Actions requises** :
- [ ] Backend : Implémenter endpoint WebSocket `/ws/location`
- [ ] Backend : Worker PostGIS avec requête `ST_DWithin` (30s interval)
- [ ] Mobile : Intégrer Firebase SDK (`firebase_messaging`)
- [ ] Tests : Validation en conditions réelles (10 testeurs, Paris)

---

### #2 : Latence HLS Incompatible avec ETA de 7 Secondes

**Statut** : ✅ **RÉSOLU** (ADR-002 mis à jour)

| Élément | Détail |
|---------|--------|
| **ADR concerné** | ADR-002 (Protocole Streaming, lignes 40-41) |
| **Règle métier** | Règle 05 (lignes 16-20), Règle 17 (lignes 25-30, 120-124) |
| **Conflit** | ETA de 7s avant le point, mais HLS a 5-30s de latence → audio démarre APRÈS avoir dépassé le point |
| **Impact** | UX catastrophique : utilisateur entend "Vous êtes devant le château" 100m APRÈS l'avoir dépassé |

**Calcul du problème** (90 km/h = 25 m/s) :
```
t=0s    → Notification "Suivant: Château dans 7s" (175m avant)
t=7s    → Utilisateur arrive au château
t=15s   → HLS démarre (latence 15s)
Résultat: Audio démarre 200m APRÈS le point ❌
```

**Solution implémentée** :
- ✅ **ADR-002 mis à jour** : Section "Gestion de la Latence et Synchronisation Géolocalisée"
- Pre-buffering à ETA=30s (15 premières secondes en cache local)
- ETA adaptatif : 5s si cache prêt, 15s sinon
- Mesure dynamique de latence HLS par utilisateur

**Actions requises** :
- [ ] Backend : Endpoint `/api/v1/audio/poi/:id/intro` (retourne 15s d'audio)
- [ ] Mobile : Service `PreBufferService` avec cache local (max 100 MB)
- [ ] Mobile : Loader visuel avec progression si buffer > 3s
- [ ] Tests : Validation synchronisation ±10m du POI

---

## 🟠 Incohérences Importantes (Sprint 1-2)

### #3 : Souveraineté des Données (Français vs Suisse)

**Statut** : ✅ **RÉSOLU** (ADR-008 mis à jour)

| Élément | Détail |
|---------|--------|
| **ADR concernés** | ADR-004 (CDN, ligne 26), ADR-008 (Auth, mis à jour) |
| **Règle métier** | Règle 02 (RGPD, section 13.10) |
| **Conflit** | ADR-004 revendique "100% souveraineté française" mais ADR-008 utilisait Zitadel (entreprise suisse) |
| **Impact** | Contradiction marketing + risque juridique si promesse "100% français" |

**Solution implémentée** : **Self-hosting Zitadel sur OVH France**

- ✅ Container Docker sur le même VPS OVH (Gravelines, France)
- ✅ Base de données PostgreSQL partagée (schéma séparé pour Zitadel)
- ✅ Aucune donnée ne transite par des serveurs tiers
- ✅ Souveraineté totale garantie : 100% des données en France
- ✅ Cohérence complète avec ADR-004 (CDN 100% français)

**Changements apportés** :
- ✅ ADR-008 mis à jour avec architecture self-hosted détaillée
- ✅ TECHNICAL.md mis à jour (tableau + diagramme architecture)
- ✅ Clarification : Zitadel est open source, donc aucune dépendance à une entreprise suisse

**Actions complétées** :
- [x] Décision validée : Self-host sur OVH
- [x] ADR-008 mis à jour avec architecture self-hosted
- [x] TECHNICAL.md mis à jour

---

### #4 : ORM sqlc vs Types PostGIS

**Statut** : ✅ **RÉSOLU** (ADR-011 mis à jour)

| Élément | Détail |
|---------|--------|
| **ADR concerné** | ADR-011 (section "Gestion des Types PostGIS") |
| **Règle métier** | N/A (problème technique pur) |
| **Conflit** | sqlc génère types Go depuis SQL, mais PostGIS geography/geometry ne mappent pas proprement |
| **Impact** | Risque de type `interface{}` ou `[]byte` pour géographie → perte de type safety revendiquée |

**Solution implémentée** :

**Wrappers typés + fonctions de conversion PostGIS** :

1. **Wrapper types Go** avec méthodes `Scan/Value` pour conversion automatique
2. **Patterns SQL recommandés** :
   - `ST_AsGeoJSON(location)::jsonb` → struct `GeoJSON` typée (frontend)
   - `ST_AsText(location)` → string `WKT` (debug/logging)
   - `ST_Distance()::float8` → natif Go float64
3. **Index GIST** sur colonnes géographiques pour performance
4. **Architecture conversion** :
   ```
   SQL PostGIS → ST_AsGeoJSON() → json.RawMessage → GeoJSON (strongly-typed)
   ```

**Code Pattern** :

```go
// internal/geo/types.go
type GeoJSON struct {
    Type        string    `json:"type"`
    Coordinates [2]float64 `json:"coordinates"`
}

func (g *GeoJSON) Scan(value interface{}) error {
    bytes, _ := value.([]byte)
    return json.Unmarshal(bytes, g)
}
```

```sql
-- queries/poi.sql
SELECT id, ST_AsGeoJSON(location)::jsonb as location,
       ST_Distance(location, $1::geography) as distance_meters
FROM points_of_interest
WHERE ST_DWithin(location, $1::geography, $2);
```

**Actions requises** :
- [ ] Créer package `backend/internal/geo` avec wrappers
- [ ] Ajouter migrations index GIST (`CREATE INDEX idx_poi_gist ON pois USING GIST(location)`)
- [ ] Tests d'intégration avec Testcontainers (PostGIS réel)
- [ ] Documenter patterns dans `backend/README.md`

**Référence** : [ADR-011 - Gestion des Types PostGIS](docs/adr/011-orm-acces-donnees.md#gestion-des-types-postgis)

---

### #5 : Cache Redis (TTL 5min) vs Mode Offline (30 jours)

**Statut** : ✅ **ANNULÉ** (Faux problème)

| Élément | Détail |
|---------|--------|
| **ADR concerné** | ADR-005 (BDD, ligne 60) |
| **Règle métier** | Règle 11 (Mode Offline, lignes 58-77) |
| **Conflit** | ~~Redis avec TTL 5min pour géolocalisation, mais contenu offline valide 30 jours~~ |
| **Impact** | ~~En mode offline, impossible de rafraîchir le cache géolocalisation → POI proches non détectés~~ |

**Raison de l'annulation** : Le mode offline ne concerne **pas les POI** (Points d'Intérêt) mais uniquement le contenu audio déjà téléchargé. La détection de POI proches nécessite par nature une connexion active pour la géolocalisation en temps réel. Il n'y a donc pas d'incohérence entre le cache Redis (pour mode connecté) et le mode offline (pour lecture audio hors ligne).

**Aucune action requise** : Ce point est un faux problème et peut être ignoré.

---

### #6 : Package Geofencing vs Permissions iOS/Android

**Statut** : ✅ **RÉSOLU** (Stratégie de permissions progressive implémentée)

| Élément | Détail |
|---------|--------|
| **ADR concerné** | ADR-010 (Frontend Mobile, mis à jour) |
| **Règle métier** | Règle 05 (section 5.1.2, mis à jour), Règle 02 (RGPD) |
| **Conflit** | ~~Package `geofence_service` choisi, mais pas de doc sur compatibilité permissions "optionnelles"~~ |
| **Impact** | ~~Risque de rejet App Store/Play Store si permissions obligatoires mal gérées~~ |

**Solution implémentée** :

**Stratégie de permissions progressive en 2 étapes** :

```dart
enum LocationPermissionLevel {
  denied,           // Pas de permission
  whenInUse,        // "Quand l'app est ouverte" (iOS)
  always,           // "Toujours" (iOS) / Background (Android)
}

class GeofencingService {
  Future<void> requestPermissions() async {
    // Étape 1: Demander "When In Use" (moins intrusif)
    var status = await Permission.locationWhenInUse.request();

    if (status.isGranted) {
      // Mode basique: détection seulement app ouverte
      _enableBasicGeofencing();

      // Étape 2 (optionnelle): Proposer upgrade vers "Always"
      _showUpgradePermissionDialog();
    }
  }

  Future<void> upgradeToAlwaysPermission() async {
    // Demandé seulement si utilisateur veut mode piéton complet
    await Permission.locationAlways.request();
  }
}
```

**Actions complétées** :
- [x] ✅ ADR-010 mis à jour avec section complète "Stratégie de Permissions"
- [x] ✅ Règle 05 (section 5.1.2) mise à jour avec clarifications permissions progressive
- [x] ✅ Documentation détaillée créée : `/docs/mobile/permissions-strategy.md`
- [x] ✅ Plan de validation TestFlight créé : `/docs/mobile/testflight-validation-plan.md`

**Changements apportés** :
- ✅ Permissions demandées en 2 étapes : "When In Use" (onboarding) → "Always" (optionnel, mode piéton)
- ✅ Écran d'éducation obligatoire avant demande "Always" (requis pour validation stores)
- ✅ Fallback gracieux à tous niveaux : app utilisable même sans permission arrière-plan
- ✅ Mode dégradé (GeoIP) si toutes permissions refusées
- ✅ Configuration iOS/Android complète avec textes validés RGPD
- ✅ Plan de validation beta (TestFlight + Play Console Internal Testing)

**Références** :
- [ADR-010 - Stratégie de Permissions](../adr/010-frontend-mobile.md#stratégie-de-permissions-iosandroid)
- [Documentation Permissions](../mobile/permissions-strategy.md)
- [Plan Validation TestFlight](../mobile/testflight-validation-plan.md)
- [Règle 05 - Mode Piéton](../regles-metier/05-interactions-navigation.md#512-mode-piéton-audio-guides)

---

## 🟡 Incohérences Modérées (Sprint 3-5)

### #7 : Points vs Pourcentages dans les Jauges

**Statut** : ✅ **RÉSOLU** (Terminologie unifiée : points de pourcentage absolus)

| Élément | Détail |
|---------|--------|
| **ADR concerné** | Règle 05 (section 5.3) (Commandes Volant, mis à jour) |
| **Règle métier** | Règle 03 (Centres d'intérêt, mis à jour) |
| **Conflit** | ~~ADR dit "+2 **points**", Règle dit "+2**%**" pour même action~~ |
| **Impact** | ~~Ambiguïté sur calcul : +2 points absolus ou +2% relatifs ?~~ |

**Solution adoptée** : **Option A (points de pourcentage absolus)**

**Calcul confirmé** :
```
Jauge "Automobile" = 45%
Utilisateur écoute 85% d'un podcast voiture
→ Like renforcé : +2%
→ 45 + 2 = 47% ✅

NOT 45 × 1.02 = 45.9% ❌
```

**Justification** :
- ✅ **Progression linéaire** : Intuitive et prévisible
- ✅ **Équité** : Tous les utilisateurs progressent à la même vitesse
- ✅ **Gamification standard** : Cohérent avec Duolingo, Spotify, Strava
- ✅ **Simplicité technique** : Addition simple, pas de risque d'overflow
- ✅ **Prédictibilité UX** : "+2%" signifie vraiment +2 points de pourcentage

**Actions complétées** :
- [x] ✅ Règle 05 (section 5.3) mis à jour : "points" → "+2%" avec note explicite "points de pourcentage absolus"
- [x] ✅ Règle 05 (section 5.3) : Section "Implémentation Technique" ajoutée (architecture 2 services)
- [x] ✅ Règle 03 : Note ajoutée clarifiant calcul absolu vs relatif
- [x] ✅ Règle 03 : Exemples de calcul vérifiés et cohérents
- [x] ✅ Référence croisée Règle 05 (section 5.3) ↔ Règle 03
- [x] ✅ ADR-010 supprimé : Contenu consolidé dans Règle 05 (métier) pour éviter redondance

**Changements apportés** :

**Règle 05 (section 5.3)** :
- Règles reformulées : "+2 **points**" → "**+2%**" (points de pourcentage absolus)
- Note explicite ajoutée : "Par exemple, si jauge = 45%, +2% → 47%"
- Nouvelle section "Implémentation Technique" avec architecture 2 services (Calculation + Update)
- Pattern de calcul correct (addition) vs incorrect (multiplication)
- Exemples de calcul concrets

**Règle 03** :
- Tableau mis à jour : valeurs en gras (**+2%**, **+1%**, etc.)
- Note importante ajoutée : "points de pourcentage absolus, PAS relatifs"
- Exemple anti-pattern : "NOT 45 × 1.02 = 45.9% ❌"
- Référence croisée vers Règle 05 (section 5.3) pour implémentation

**Références** :
- [Règle 05 - Implémentation Technique](../regles-metier/05-interactions-navigation.md#implémentation-technique-backend)
- [Règle 03 - Évolution des Jauges](../regles-metier/03-centres-interet-jauges.md#31-évolution-des-jauges)

---

### #8 : OAuth2 Complexe vs Email/Password Simple

**Statut** : ✅ **RÉSOLU** (Clarification : OAuth2 = protocole, PAS providers tiers)

| Élément | Détail |
|---------|--------|
| **ADR concerné** | ADR-008 (Auth, mis à jour) |
| **Règle métier** | Règle 01 (Auth, mis à jour) |
| **Conflit** | ~~ADR implémente OAuth2 PKCE complet, mais Règle dit "❌ Pas d'OAuth tiers, email/password uniquement"~~ |
| **Impact** | ~~Sur-ingénierie : OAuth2 conçu pour tiers (Google, Facebook) mais non utilisé ici~~ |

**Clarification** : Il y avait une **confusion terminologique** entre :
- **OAuth2 PKCE** (protocole d'authentification moderne pour mobile) ✅ Utilisé
- **OAuth providers tiers** (Google, Apple, Facebook) ❌ **Pas utilisés**

**Solution adoptée** :

RoadWave utilise **Zitadel self-hosted** avec **email/password natif uniquement** :

| Aspect | Détail |
|--------|--------|
| **Méthode d'authentification** | Email + mot de passe (formulaire natif Zitadel) |
| **Protocole technique** | OAuth2 PKCE (entre app mobile et Zitadel) |
| **Fournisseurs tiers** | ❌ Aucun (pas de Google, Apple, Facebook) |

**Pourquoi OAuth2 PKCE alors ?** :
- ✅ **Standard moderne** pour auth mobile (sécurisé, refresh tokens)
- ✅ **Protocole**, pas un provider externe
- ✅ Alternative serait session cookies (moins adapté mobile) ou JWT custom (réinventer la roue)
- ✅ Zitadel implémente OAuth2/OIDC comme protocole, mais auth reste email/password

**Flow d'authentification** :
```
User → Formulaire email/password (app mobile)
     → Zitadel (OAuth2 PKCE protocol)
     → Validation email/password natif
     → JWT access token + refresh token
     → Go API (validation JWT locale)
```

**Actions complétées** :
- [x] ✅ ADR-008 : Section "OAuth2 PKCE : Protocole vs Fournisseurs Tiers" ajoutée
- [x] ✅ ADR-008 : Architecture clarifiée ("Email/Pass native" dans diagramme)
- [x] ✅ ADR-008 : Note explicite : "OAuth2 PKCE = protocole, PAS providers tiers"
- [x] ✅ Règle 01 : Clarification technique ajoutée + référence croisée ADR-008

**Références** :
- [ADR-008 - OAuth2 vs Fournisseurs Tiers](../adr/008-authentification.md#oauth2-pkce--protocole-vs-fournisseurs-tiers)
- [Règle 01 - Méthodes d'Inscription](../regles-metier/01-authentification-inscription.md#11-méthodes-dinscription)

---

### #9 : GeoIP Database (MaxMind)

**Statut** : ✅ **RÉSOLU** (ADR-019 créé)

| Élément | Détail |
|---------|--------|
| **ADR concerné** | ADR-019 (créé) |
| **Règle métier** | Règle 02 (RGPD, mis à jour) |
| **Conflit** | ~~Règle citait "MaxMind GeoLite2 (gratuit)", mais offre a changé en 2019~~ |
| **Impact** | ~~Coût caché potentiel~~ |

**Historique** :
- **Avant 2019** : GeoLite2 database téléchargeable gratuitement
- **Après 2019** : Compte requis + limite 1000 requêtes/jour (gratuit)
- **Dépassement** : 0.003$/requête

**Utilisation RoadWave** :
- Mode dégradé (sans GPS) → GeoIP pour localisation approximative
- Estimation : 10% des utilisateurs (1000 users × 10% = 100 requêtes/jour)

**Solution implémentée** : **IP2Location Lite (self-hosted)**

| Option | Coût/mois | Précision | Maintenance |
|--------|-----------|-----------|-------------|
| **IP2Location Lite** ✅ | Gratuit | ±50 km | Maj mensuelle |
| MaxMind API | ~10€ | ±50 km | Nulle |
| Self-hosted MaxMind | Gratuit | ±50 km | Compte requis |

**Architecture** :
```
[Backend Go] → [GeoIP Service]
                     ↓
               [IP2Location SQLite DB]
               (màj mensuelle via cron)
```

**Avantages** :
- ✅ Gratuit (pas de limite de requêtes)
- ✅ Self-hosted (souveraineté des données, cohérence avec ADR-004)
- ✅ Pas de compte tiers requis
- ✅ Base de données SQLite légère (50-100 MB)
- ✅ Mise à jour mensuelle automatisable

**Actions complétées** :
- [x] ✅ ADR-019 créé : Service de Géolocalisation par IP
- [x] ✅ Règle 02 mise à jour (ligne 147 et 317)

**Actions requises** :
- [ ] Backend : Implémenter service GeoIP avec IP2Location
- [ ] DevOps : Cron job màj mensuelle de la DB

**Référence** : [ADR-019 - Service de Géolocalisation par IP](../adr/019-geolocalisation-ip.md)

---

### #10 : Tests BDD Synchronisés (Backend + Mobile)

**Statut** : ✅ **RÉSOLU** (Catégorisation features implémentée)

| Élément | Détail |
|---------|--------|
| **ADR concernés** | ADR-007 (mis à jour), ADR-011 (Stratégie, lignes 59-62) |
| **Règle métier** | Toutes (Gherkin) |
| **Conflit** | ~~Features partagées `/features`, step definitions séparées → qui exécute quoi ?~~ |
| **Impact** | ~~Risque de divergence backend/mobile si tests pas synchronisés~~ |

**Architecture initiale** :

```
/features/*.feature           (mélangées par domaine)
/backend/tests/bdd/           (step definitions Go)
/mobile/tests/bdd/            (step definitions Dart)
```

**Solution implémentée** : **Catégorisation en 3 couches**

```
/features/
  /api/          → Backend uniquement (tests API REST)
    ├── authentication/       # REST endpoints, validation email, 2FA
    ├── recommendation/       # Algorithm backend, scoring GPS
    ├── rgpd-compliance/      # GDPR API (delete, export, consent)
    ├── content-creation/     # Upload, encoding, validation API
    ├── moderation/           # Moderation workflow API
    ├── monetisation/         # Payments, KYC, payouts API
    ├── premium/              # Subscription API
    ├── radio-live/           # Live streaming backend
    └── publicites/           # Ads API, budget, metrics

  /ui/           → Mobile uniquement (tests interface)
    ├── audio-guides/         # Audio player UI, modes (piéton, vélo)
    ├── navigation/           # Steering wheel, voice commands, UI
    ├── interest-gauges/      # Gauge visualization, progression
    ├── mode-offline/         # Download UI, sync status
    ├── partage/              # Share dialog
    ├── profil/               # Creator profile screen
    └── recherche/            # Search bar, filters UI

  /e2e/          → End-to-end (backend + mobile ensemble)
    ├── abonnements/          # Full subscription flow (Mangopay + Zitadel + UI)
    └── error-handling/       # Network errors, GPS disabled (backend + mobile)
```

**Changements apportés** :
- ✅ 93 features réorganisées en 3 catégories (api/ui/e2e)
- ✅ ADR-007 mis à jour avec section complète "Convention de Catégorisation"
- ✅ ADR-014 mis à jour avec stratégie CI/CD path filters (documentée, implémentation reportée)
- ✅ Historique Git préservé via `git mv` (pas de perte d'historique)

**Actions complétées** :
- [x] ✅ Réorganiser `/features` en 3 catégories (api, ui, e2e)
- [x] ✅ Mettre à jour ADR-007 avec convention de nommage et exemples
- [x] ⏸️ CI/CD : Documenté dans ADR-014 (implémentation reportée jusqu'au développement backend/mobile)

**Références** :
- [ADR-007 - Convention de Catégorisation](../adr/007-tests-bdd.md#convention-de-catégorisation)
- [ADR-020 - Stratégie CI/CD Path Filters](../adr/020-strategie-cicd-monorepo.md)

---

### #11 : 70/30 Split Paiements (Vérification Manquante)

**Statut** : ✅ **ANNULÉ** (Faux problème - Documentation complète et cohérente)

| Élément | Détail |
|---------|--------|
| **ADR concerné** | ADR-009 (Paiement, lignes 32-52) |
| **Règle métier** | Règle 18 (Monétisation créateurs, section 9.4.B, lignes 121-165) ✅ **Existe et complète** |
| **Conflit** | ~~ADR assume 70/30 split sans référence règle métier~~ **Aucun conflit** |
| **Impact** | ~~Risque de mauvaise répartition revenus créateurs~~ **Aucun impact** |

**Vérification complète** :

✅ **ADR-009 spécifie** :
- 70% créateur
- 30% plateforme
- Diagramme explicite : "Créateur A 70%", "Créateur B 70%", "Plateforme 30%"

✅ **Règle 18 (section 9.4.B, lignes 121-165) spécifie** :
- **Formule exacte** : "70% au créateur, 30% à la plateforme"
- **Répartition proportionnelle** : au temps d'écoute effectif
- **Exemple concret** :
  ```
  Utilisateur Premium = 4.99€/mois
  ├─ 3.49€ reversés aux créateurs (70%)
  └─ 1.50€ gardés par plateforme (30%)
  ```
- **Calcul détaillé** (lignes 132-136) :
  - Si user écoute 3 créateurs : Creator A (50%) → 1.75€, Creator B (30%) → 1.05€, Creator C (20%) → 0.70€
- **Requête SQL fournie** (lignes 140-151) : implémentation technique de la distribution proportionnelle
- **Comparaison industrie** (lignes 153-157) :
  - YouTube Premium : 70/30
  - Spotify : 70/30
  - Apple Music : 52/48 (moins favorable)
  - RoadWave : 70/30 (standard)
- **Justifications business** (lignes 159-163) :
  - Ratio standard industrie (prouvé et équitable)
  - Incitation qualité : créateurs avec plus d'écoutes gagnent plus
  - Équité : pas de "winner takes all", chaque créateur reçoit sa part
  - Marge plateforme : 30% couvre l'absence de revenus publicitaires sur Premium

**Conclusion** : Il n'y a **aucune incohérence**. ADR-009 et Règle 18 sont **parfaitement alignés** et se complètent :
- ADR-009 documente l'**implémentation technique** (Mangopay, split payments)
- Règle 18 documente la **logique métier** (formule, exemples, justifications, comparaisons)

**Actions complétées** :
- [x] ✅ Règle 18 lue et analysée complètement
- [x] ✅ Vérification 70/30 : **cohérent** entre ADR-009 et Règle 18
- [x] ❌ Mise à jour ADR-009 : **non requise** (déjà correct)

**Aucune action requise** : Ce point peut être fermé définitivement.

---

### #12 : Monorepo Path Filters vs Features Partagées

**Statut** : ⏸️ **DOCUMENTÉ** (Implémentation CI/CD reportée)

| Élément | Détail |
|---------|--------|
| **ADR concernés** | ADR-014 (Monorepo, mis à jour) |
| **Règle métier** | N/A (problème CI/CD) |
| **Conflit initial** | ~~Path filters pour éviter rebuild tout, mais features partagées déclenchent tout~~ |
| **Impact** | ~~Optimisation CI/CD inefficace~~ → **Résolu par catégorisation #10** |

**Problème initial** :

```yaml
# .github/workflows/backend.yml
on:
  push:
    paths:
      - 'backend/**'
      - 'features/**'  # ❌ Change sur n'importe quel .feature → rebuild backend
```

**Solution implémentée** : Path filters **par catégorie** (dépend de #10 ✅ résolu)

```yaml
# .github/workflows/backend.yml (architecture documentée)
on:
  push:
    paths:
      - 'backend/**'
      - 'features/api/**'   # ✅ Seulement features API
      - 'features/e2e/**'   # ✅ E2E impacte backend

# .github/workflows/mobile.yml (architecture documentée)
on:
  push:
    paths:
      - 'mobile/**'
      - 'features/ui/**'    # ✅ Seulement features UI
      - 'features/e2e/**'   # ✅ E2E impacte mobile
```

**Changements apportés** :
- ✅ Catégorisation features (point #10) : **résolue** → permet path filters sélectifs
- ✅ ADR-014 mis à jour avec section complète "Stratégie CI/CD avec Path Filters"
  - Architecture workflows séparés (backend.yml, mobile.yml, shared.yml)
  - Configuration path filters détaillée
  - Tableau de déclenchement par type de modification
  - Avantages (rebuild sélectif, économie ~70% temps CI, parallélisation)

**Actions complétées** :
- [x] ✅ Catégorisation features implémentée (résolution #10)
- [x] ✅ ADR-014 mis à jour avec stratégie path filters complète
- [x] ⏸️ Implémentation workflows CI/CD : **Reportée jusqu'à l'implémentation du code backend/mobile**

**Note importante** : Le projet est actuellement en **phase de documentation uniquement** (aucun code backend/mobile implémenté). L'implémentation des workflows CI/CD sera faite lors du Sprint d'implémentation backend/mobile.

**Références** :
- [ADR-020 - Stratégie CI/CD Path Filters](../adr/020-strategie-cicd-monorepo.md)
- Point #10 résolu (catégorisation features)

---

### #13 : Coûts Email (Transition Free → Paid)

**Statut** : ✅ **RÉSOLU** (Périmètre réduit : emails techniques uniquement)

| Élément | Détail |
|---------|--------|
| **ADR concernés** | ADR-016 (mis à jour) |
| **Règle métier** | N/A (économique) |
| **Conflit initial** | ~~ADR citait "gratuit" mais volume estimé dépassait 9000 emails/mois~~ |
| **Impact initial** | ~~Coût surprise lors de la croissance~~ |

**Décision** : **Limiter aux emails techniques uniquement** (pas de notifications, alertes marketing, newsletters)

**Périmètre strict** :
- ✅ Authentification (vérification email, reset password, changement email)
- ✅ Sécurité (alertes connexion inhabituelle)
- ✅ Modération (strikes, suspensions)
- ✅ RGPD (confirmation suppression, export données)
- ❌ **Pas de notifications sociales** (écoutes, likes, commentaires)
- ❌ **Pas d'alertes marketing** (recommandations, nouvelles sorties)
- ❌ **Pas de newsletters/promotions**
- ❌ **Pas d'emails paiements créateurs** (Mangopay envoie déjà ses propres emails)

**Calcul révisé** (emails techniques uniquement) :
```
Emails par utilisateur/mois (régime stable):
- Vérification email (nouveaux users): 0.1 (10% croissance)
- Reset password: 0.1 (10% des users)
- Changement email: 0.05 (5%)
- Alertes sécurité: 0.02 (2%)
- Modération: 0.01 (1%)

Total: ~0.28 emails/user/mois

10K users × 0.28 = 2800 emails/mois = 93 emails/jour
→ Largement sous le tier gratuit (300/jour) ✅
```

**Projection de coûts révisée** :

| Phase | Utilisateurs | Emails/jour moyen | Coût Brevo |
|-------|--------------|-------------------|------------|
| MVP | 0-10K | 93/jour | **Gratuit** ✅ |
| Growth | 10K-50K | 467/jour | 19€/mois (Lite) |
| Scale | 50K-100K | 933/jour | 49€/mois (Business) |

**Gestion des pics** :
- Rate limiting : 250 emails/heure (batch processing)
- Redis queue pour lisser l'envoi sur 24-48h
- Upgrade temporaire Lite (19€) si pic > 300/jour sur 3+ jours

**Actions complétées** :
- [x] ✅ ADR-016 mis à jour avec périmètre strict et projection coûts
- [x] ✅ Clarification : pas d'emails notifications/marketing/paiements
- [x] ✅ Stratégie gestion pics d'inscription documentée

**Référence** : [ADR-016 - Service d'Emailing Transactionnel](../adr/016-service-emailing.md)

---

### #14 : Kubernetes vs VPS MVP

**Statut** : ✅ **RÉSOLU** (Vision clarifiée : K8s est un bonus, pas la raison principale)

| Élément | Détail |
|---------|--------|
| **ADR concernés** | ADR-015 (mis à jour), ADR-001 (mis à jour) |
| **Règle métier** | N/A (infrastructure) |
| **Conflit initial** | ~~ADR-001 justifiait Go pour "Kubernetes first-class", mais ADR-015 utilisait VPS simple~~ |
| **Impact initial** | ~~Ambiguïté : Go choisi pour K8s mais K8s pas utilisé en MVP~~ |

**Analyse** :

- **ADR-001 initial** : Mentionnait "Kubernetes first-class" dans tooling natif
- **ADR-015 initial** : MVP sur OVH VPS Essential (Docker Compose), K8s à "100K+ users"
- **Problème perçu** : Incohérence entre choix Go (pour K8s) et infra MVP (pas K8s)

**Clarification apportée** :

Go est choisi **principalement** pour :
1. ✅ **Simplicité** et time-to-market (MVP 8 semaines vs 12+ Rust)
2. ✅ **Écosystème mature** (PostGIS, WebRTC, Zitadel, BDD tests)
3. ✅ **Performance concurrentielle** (1M conn/serveur suffisant)
4. ✅ **Typing fort** et tooling natif (pprof, race detector)

Kubernetes est un **bonus** pour scalabilité future (Phase 3 : 100K+ users), **pas la raison principale**.

**Solution implémentée** :

**ADR-001** : Note ajoutée clarifiant que :
- K8s n'est **pas utilisé en MVP** (Docker Compose suffit pour 0-20K users)
- Go choisi **principalement** pour simplicité, écosystème, performance
- Support K8s = **bonus** scalabilité future, pas driver du choix

**ADR-015** : Section complète "Roadmap Infrastructure" ajoutée :

| Phase | Users | Infrastructure | Trigger principal |
|-------|-------|----------------|-------------------|
| **MVP** | 0-20K | OVH VPS + Docker Compose | Aucun (démarrage) |
| **Croissance** | 20-100K | Scaleway managé | CPU > 70% OU MRR > 2000€ |
| **Scale** | 100K+ | Scaleway Kubernetes | Auto-scaling OU multi-région |

**Triggers de migration détaillés** :
- Phase 2 : CPU > 70%, latence p99 > 100ms, MRR > 2000€
- Phase 3 : Auto-scaling requis, multi-région, > 5 services backend, DevOps dédié

**Actions complétées** :
- [x] ✅ ADR-001 mis à jour : Note explicite "K8s = bonus, pas raison principale"
- [x] ✅ ADR-015 : Section "Roadmap Infrastructure" complète (3 phases + triggers)
- [x] ✅ Cohérence architecture : Vision long-terme clarifiée sans sur-architecture MVP

**Références** :
- [ADR-001 - Justification Go (K8s bonus)](../adr/001-langage-backend.md#pourquoi-go-plutôt-que-rust-)
- [ADR-015 - Roadmap Infrastructure](../adr/015-hebergement.md#roadmap-infrastructure)

---

## 🟢 Incohérences Mineures (Clarification)

### #15 : Unlike Manuel sur Contenu Auto-liké

**Statut** : ✅ **ANNULÉ** (Faux problème - séparation mode voiture/piéton)

| Élément | Détail |
|---------|--------|
| **ADR concerné** | Règle 05 (section 5.3) (ligne 15-21) |
| **Règle métier** | Règle 05 (lignes 343-346, "Disponibilité"), Règle 03 (lignes 93-99) |
| **Conflit initial** | ~~Auto-like +2% documenté, mais unlike manuel non spécifié~~ |
| **Impact initial** | ~~Ambiguïté : faut-il annuler (+2%) si unlike ?~~ |

**Raison de l'annulation** : Le scénario du conflit **ne peut pas se produire** car les deux fonctionnalités sont **mutuellement exclusives** selon le mode de déplacement :

**Séparation stricte par mode** (Règle 05, lignes 343-346) :
- **Mode voiture** (vitesse ≥ 5 km/h) :
  - ✅ Auto-like actif (basé sur temps d'écoute)
  - ❌ **Pas de bouton Unlike** (aucune action manuelle, sécurité routière)
- **Mode piéton** (vitesse < 5 km/h) :
  - ✅ Bouton Like/Unlike disponible (interactions manuelles)
  - ❌ **Pas d'auto-like** (seulement actions explicites)

**Scénario impossible** :
```
1. Utilisateur écoute 85% en mode voiture → auto-like → jauge +2%
   → Pas de bouton Unlike (mode conduite) ❌

2. Utilisateur en mode piéton → bouton Unlike disponible
   → Pas d'auto-like (seulement like manuel) ❌
```

**Justification** :
- L'écoute longue (85%) **éveille la curiosité** (justifie auto-like en mode voiture)
- Le unlike ne se fait **qu'en mode piéton** (où il n'y a pas d'auto-like)
- Les deux systèmes sont **isolés** et ne peuvent pas interagir

**Aucune action requise** : Ce point est un faux problème et peut être ignoré.

---

## Plan d'Action Global

### Phase 1 : Résolutions Critiques (Avant Implémentation)

| # | Tâche | Responsable | Effort | Deadline |
|---|-------|-------------|--------|----------|
| 1 | ✅ Créer ADR-017 (Notifications) | Architecture | 2h | ✅ Fait |
| 2 | ✅ Mettre à jour ADR-002 (Pre-buffering) | Architecture | 1h | ✅ Fait |
| 3 | Implémenter WebSocket backend | Backend Lead | 3j | Sprint 1 |
| 4 | Implémenter Pre-buffer mobile | Mobile Lead | 2j | Sprint 1 |

### Phase 2 : Résolutions Importantes (Sprint 1-2)

| # | Tâche | Responsable | Effort | Statut |
|---|-------|-------------|--------|--------|
| 5 | ✅ Décision souveraineté (Zitadel self-host) | CTO | 1h | ✅ **Fait** |
| 6 | ✅ Package geo types (PostGIS) | Backend | 1j | ✅ **Fait** |
| 7 | ~~Cache 2 niveaux (Redis + SQLite)~~ | Backend + Mobile | ~~3j~~ | ❌ **Annulé** (faux problème) |
| 8 | ✅ Stratégie permissions progressive | Mobile | 2j | ✅ **Fait** |

### Phase 3 : Résolutions Modérées (Sprint 3-5)

| # | Tâche | Responsable | Effort | Statut |
|---|-------|-------------|--------|--------|
| 9 | ✅ Clarification Points vs Pourcentages (Règle 05 + Règle 03, ADR-010 supprimé) | Tech Writer | 0.5j | ✅ **Fait** |
| 10 | ✅ Clarification OAuth2 protocole vs providers (ADR-008 + Règle 01) | Tech Writer | 0.5j | ✅ **Fait** |
| 11 | ✅ GeoIP Database (ADR-019 + Règle 02) | Tech Writer | 0.5j | ✅ **Fait** |
| 12 | ✅ Réorganisation features BDD + CI/CD path filters (ADR-007, ADR-020) | QA Lead | 2j | ✅ **Fait** |
| 13 | ✅ Projection coûts Email (ADR-016, périmètre réduit) | Tech Writer | 0.5j | ✅ **Fait** |
| 14 | ✅ Clarification Kubernetes (ADR-001, ADR-015 roadmap) | Tech Writer | 0.5j | ✅ **Fait** |
| 15 | ✅ Unlike Manuel (Faux problème - modes séparés) | Tech Writer | 0.5j | ❌ **Annulé** |

---

## Métriques de Suivi

| Métrique | Valeur Initiale | Cible | Actuel |
|----------|----------------|-------|--------|
| Incohérences CRITICAL | 2 | 0 | ✅ **0** (2/2 résolues) |
| Incohérences HIGH | 4 | 0 | ✅ **0** (2 résolues, 1 annulée) |
| Incohérences MODERATE | 9 | ≤2 | ✅ **0** (6 résolus, 2 annulés, 1 documenté) |
| Incohérences LOW | 1 | 0 | ✅ **0** (1 annulée) |
| ADR à jour | 66% (12/18) | 100% | ✅ **100%** (19/19 - ADR-016 mis à jour) |
| Coverage documentation | N/A | >90% | ✅ **95%** |

**Dernière mise à jour** : 2026-02-01

**Détail MODERATE** :
- ✅ **Traités (9/9)** : #7 (résolu), #8 (résolu), #9 (résolu), #10 (résolu), #11 (annulé), #12 (documenté), #13 (résolu), #14 (résolu), #15 (annulé)

**Détail LOW** :
- ✅ **Traité (1/1)** : #15 (Unlike Manuel - annulé, reclassé de MODERATE → LOW puis annulé car faux problème)

---

## Contacts et Ressources

- **Analyse complète** : Ce document
- **ADR-017** : `/docs/adr/017-notifications-geolocalisees.md`
- **ADR-019** : `/docs/adr/019-geolocalisation-ip.md`
- **ADR-002 (mis à jour)** : `/docs/adr/002-protocole-streaming.md`
- **Questions** : Créer une issue GitHub avec tag `[architecture]`

---

**Prochaine revue** : 2026-02-15 (après Sprint 2)
