# CLAUDE.md — Focale Appareil photo manuel + photothèque interrogeable. SwiftUI + AVFoundation + PhotoKit + Vision + Foundation Models. Ce fichier est la source de vérité du projet. Le lire avant toute modification. --- ## 1. Thèse produit **Deux produits qui se sauvent mutuellement.** Un appareil photo pro seul, c'est un marché saturé (Halide, Kino, Obscura, Moment). Une photothèque IA seule, c'est 40 000 photos à rattraper — des dizaines d'heures d'inférence avant la première valeur. Ensemble, ils règlent le problème de l'autre : **une photo prise dans Focale arrive déjà comprise.** Le contexte est capturé au moment du déclenchement — intention, sujet, projet, réglages, lieu — gratuitement et sans ambiguïté. Pas de devinette, pas d'inférence coûteuse. La vieille bibliothèque se rattrape tranquillement en arrière-plan pendant que le produit est déjà utile. La promesse : *tout ce que tu photographies à partir d'aujourd'hui est parfaitement retrouvable.* Trois règles non négociables : 1. **Rien ne quitte l'appareil. Jamais.** La photothèque de quelqu'un est ce qu'il possède de plus intime. Aucune escalade cloud, même optionnelle, même sur les métadonnées. C'est l'argument de vente central — le renier une fois tue le produit. 2. **Aucune photo n'est dupliquée, déplacée ou modifiée.** Focale indexe la photothèque système par `localIdentifier`. L'utilisateur peut désinstaller demain sans rien perdre. 3. **Utile avant que l'index soit complet.** Les résultats arrivent progressivement, du plus récent au plus ancien. ### Positionnement de l'appareil photo **Contrôle et intention, jamais « meilleures photos ».** Le mode Nuit n'est pas accessible aux apps tierces (voir §2) — en basse lumière, l'app Camera d'Apple gagnera toujours. Ne jamais promettre le contraire. Focale s'adresse à qui veut décider, pas à qui veut que ça marche tout seul. --- ## 2. Contraintes — lire avant de promettre quoi que ce soit ### Ce qui est accessible | Capacité | API | Note | |---|---|---| | ISO, obturation, focus, WB manuels | `AVCaptureDevice` | verrouillage complet, contrôle continu | | Choix d'objectif, zoom | `AVCaptureDevice.DiscoverySession` | incl. ultra grand-angle, télé | | ProRAW | `isAppleProRAWSupported` → `isAppleProRAWEnabled` | iPhone 12 Pro+, DNG linéaire | | RAW Bayer | `availableRawPhotoPixelFormatTypes` | vérifier le format, pas garanti partout | | Apple Log, ProRes | `AVCaptureDevice.activeFormat` | Pro seulement | | Bouton Camera Control | iPhone 16+ | lancement + réglage assignable | | Bouton Action | iPhone 15 Pro+ | lancement direct | | Interface de capture | 100 % personnalisable | aucune contrainte | ### Ce qui n'est PAS accessible - **Mode Nuit.** Jamais exposé aux apps tierces. Aucun contournement. Assumer ce déficit dans le positionnement. - **Mode Portrait complet** d'Apple. La profondeur est disponible (`AVCaptureDepthDataOutput`), le rendu d'Apple ne l'est pas. - **Deep Fusion / Smart HDR** s'appliquent dans le pipeline standard, mais sans contrôle fin. Le contourner est un *choix* de positionnement, comme Halide — pas un défaut à cacher. - **Remplacer Photos comme app par défaut.** Impossible. ### Foundation Models - **A17 Pro minimum.** Sur appareil non compatible, Focale reste un excellent appareil photo manuel avec une recherche Vision-only. **Ce mode dégradé doit être bon, pas frustrant.** - Contexte 4096 / 8192 tokens. On ne passe jamais de longues listes de photos au modèle. Voir §5. - **Inférence sérialisée sur le Neural Engine.** Les sessions parallèles s'exécutent en série. C'est LA contrainte du projet. - Vision on-device (iOS 27) : image en entrée directe dans le prompt. ### PhotoKit — le risque produit le plus sous-estimé Focale exige `.authorized` (**Accès complet**). En accès limité, le produit ne fonctionne pas du tout. iOS relance périodiquement l'utilisateur pour l'inciter à réduire l'accès. Conséquences obligatoires : - L'onboarding explique **pourquoi** avant d'afficher le dialogue système. Un dialogue nu = permission refusée = installation perdue. - Détecter `.limited` et afficher un écran explicatif dédié, pas un message d'erreur. - Le fait que rien ne sorte de l'appareil est l'argument qui débloque cette permission. Le dire à cet endroit précis. --- ## 3. Architecture ``` Focale/ ├─ App/ ├─ Capture/ │ ├─ Session/ AVCaptureSession, configuration, cycle de vie │ ├─ Controls/ ISO, obturation, focus, WB — modèle + gestes │ ├─ Output/ ProRAW, HEIC, écriture PhotoKit │ ├─ Recipes/ presets utilisateur ⚠️ voir §6 │ └─ Layout/ dispositions d'interface personnalisables ⚠️ voir §6 ├─ Intent/ contexte capturé au déclenchement ⚠️ cœur du projet ├─ Index/ │ ├─ Vision/ étage 1 — OCR, visages, feature prints │ ├─ Semantic/ étage 2 — Foundation Models, sur candidats seulement │ ├─ Store/ SwiftData + vecteurs │ └─ Scheduler/ BGProcessingTask, budget d'énergie ├─ Search/ requête naturelle → filtre → classement ├─ Library/ navigation, albums vivants, timeline └─ Design/ tokens, typographie, thèmes ``` --- ## 4. L'appareil photo ### Contexte de capture — la pièce qui différencie tout le projet Au déclenchement, Focale enregistre bien plus que les EXIF, et **sans coût d'inférence** : ```swift struct CaptureContext: Codable { let recipe: Recipe.ID? // preset actif let project: Project.ID? // projet déclaré par l'utilisateur let subjectHint: String? // dicté ou tapé avant/après la prise let settings: ManualSettings // ISO, vitesse, objectif, focus let scene: SceneSignal // luminosité, mouvement, distance (LiDAR si dispo) let burstRole: BurstRole? // sélection dans une rafale let place: PlaceRef? // lieu, pas coordonnées brutes } ``` Règles : - **Le contexte est écrit dans les métadonnées de l'actif** en plus de la base locale. Si l'utilisateur désinstalle, l'information reste dans sa photo. - **Le mode Projet** : l'utilisateur déclare « chantier cuisine », « voyage Gaspésie », « inventaire assurance ». Tout ce qui est pris ensuite est marqué. Un geste, une fois — puis zéro friction. - **Jamais de saisie obligatoire avant une photo.** Le déclencheur doit toujours répondre instantanément. Le contexte s'ajoute autour, jamais devant. ### Règles de fer de la capture - **Le déclencheur est sacré.** Aucune inférence, aucun disque, aucune allocation dans le chemin du déclenchement. Tout est différé. - `AVCaptureSession` configurée hors du main thread, une seule fois. Reconfiguration en `beginConfiguration`/`commitConfiguration`. - Toujours interroger les capacités avant d'exposer un contrôle : `isAppleProRAWSupported`, `activeFormat.maxISO`, plages d'exposition. **Aucun contrôle affiché s'il n'est pas supporté par l'appareil** — pas de bouton mort. - Écriture PhotoKit via `PHAssetCreationRequest`, dans la photothèque système. Pas de bibliothèque parallèle, pas de sandbox privé. --- ## 5. L'indexeur — deux étages, obligatoirement **Le calcul à ne jamais oublier : 40 000 photos × 1 à 3 s d'inférence = 11 à 33 heures, en série.** Une passe Foundation Models sur toute la bibliothèque est impossible. L'architecture entière découle de ça. ### Étage 1 — Vision framework, sur tout Rapide, mature, faible coût énergétique. Couvre environ 80 % des besoins de recherche réels. - `VNRecognizeTextRequest` — OCR. **Le gagnant silencieux** : reçus, factures, tableaux blancs, captures d'écran, panneaux, cartes d'affaires. C'est ce que les gens cherchent le plus et que Photos trouve le plus mal. - `VNGenerateImageFeaturePrintRequest` — empreinte visuelle, gratuite. Donne la similarité (« des photos comme celle-ci ») et le regroupement de quasi-doublons sans aucun LLM. - `VNClassifyImageRequest` — classification grossière. - Détection de visages — **regroupement local uniquement, jamais d'identification nommée sans action explicite de l'utilisateur.** ### Étage 2 — Foundation Models, sur candidats seulement Déclenché uniquement quand : - la photo est prise dans Focale (contexte déjà présent → coût minime, valeur maximale) ; - l'utilisateur ouvre une photo et demande explicitement ; - une recherche ne trouve rien à l'étage 1 et il faut approfondir un sous-ensemble ; - la photo est marquée importante (favori, partagée, souvent ouverte). ```swift @Generable struct PhotoSemantics { @Guide(description: "Ce que montre l'image, une phrase, factuelle") let gist: String @Guide(description: "Type de contenu") let kind: PhotoKind // document, reçu, personne, lieu, objet, écran, nourriture, animal, autre @Guide(description: "Éléments cherchables présents dans l'image", .count(0...8)) let entities: [String] @Guide(description: "Vrai si l'image contient une information à conserver (montant, date, code, adresse)") let hasActionableInfo: Bool } ``` ### Ordonnancement - **Du plus récent au plus ancien.** Toujours. Les gens cherchent surtout dans les 2 dernières années. - `BGProcessingTask` : en charge, Wi-Fi, écran éteint. Jamais sur batterie. - Reprise incrémentale après interruption, sans jamais repartir de zéro. - **Honnêteté dans l'interface** : « 12 400 / 41 000 photos indexées — les plus récentes sont déjà cherchables. Prêt dans environ 2 nuits. » Avec des résultats utilisables tout de suite. Un écran de chargement opaque de 20 heures est une désinstallation garantie. --- ## 6. Personnalisation — la promesse « ultra personnalisable » C'est la raison d'être de l'appareil photo. Elle doit aller plus loin que des sliders. ### Recettes Un preset complet, nommé, rappelable en un geste : réglages manuels, format (ProRAW/HEIC), objectif, look, comportement du déclencheur, contexte à appliquer. Exemples : « Documents » (correction de perspective, OCR prioritaire), « Nuit longue pose », « Chantier » (projet auto, grand-angle, horodatage visible). - Recettes exportables et importables en fichier. Elles se partagent — c'est un canal d'acquisition gratuit. - Une recette peut se déclencher automatiquement sur signal de scène (basse lumière, document détecté), **avec une proposition, jamais un basculement silencieux.** ### Disposition d'interface L'utilisateur place ses propres contrôles : quels réglages sont visibles, où, à quelle taille, quels gestes les pilotent. Trois profils fournis (Simple / Photographe / Expert), tous modifiables. - **Mapping des gestes** : glissement vertical gauche = ISO, horizontal = exposition, molette = focus… entièrement réassignable. - **Bouton Camera Control et bouton Action** assignables à une recette précise. - Retour haptique paramétrable par contrôle (crans à chaque tiers d'IL, par exemple). ### Looks Rendus appliqués **en non-destructif**, jamais cuits dans l'original. Courbes, teinte, grain, virage. Toujours réversible, toujours avec l'original intact. ### Règle qui encadre tout ça **Les valeurs par défaut doivent être excellentes.** La personnalisation est une profondeur pour ceux qui la cherchent, pas un devoir à l'installation. Le premier lancement doit donner une bonne photo en trois secondes, sans réglage. Une app qui exige d'être configurée avant d'être bonne perd la majorité de ses utilisateurs le premier jour. --- ## 7. Recherche - Entrée en langage naturel. Le modèle **analyse la requête** (petit prompt, coût dérisoire), il ne parcourt pas les photos. - La requête est convertie en filtre structuré : période, lieu, type, entités, texte OCR, similarité visuelle. Le filtre s'exécute sur la base locale — instantané, sur 40 000 photos comme sur 400. - Le classement combine : correspondance OCR, distance de feature print, contexte de capture, récence. - **Foundation Models n'intervient qu'au bout**, pour départager un petit ensemble de candidats si nécessaire. - Requêtes cibles à faire fonctionner parfaitement (à tester en continu) : « le reçu du garage l'automne passé », « la photo du tableau blanc de la réunion budget », « où j'ai stationné la semaine passée », « les photos comme celle-ci », « le numéro de série de la laveuse ». ### Albums vivants Un album défini par une requête, pas par une sélection. « Tous mes reçus 2026 », « chantier cuisine », « documents avec une date d'expiration ». Il se remplit tout seul à mesure que tu photographies. --- ## 8. Honnêteté du modèle - **Une description générée est visuellement distincte d'une donnée réelle** (EXIF, OCR, lieu). Toujours. - **L'OCR prime sur le modèle** pour tout ce qui est chiffre, date, montant, code. Le modèle 3B peut halluciner un montant ; l'OCR non. Ne jamais afficher un chiffre venu du LLM comme un fait. - Sur incertitude, ne rien dire. Une photo mal comprise vaut mieux qu'une photo faussement étiquetée — l'utilisateur perdrait confiance dans tout l'index. - Aucune identification de personne sans action explicite. Aucun regroupement de visages nommé automatiquement. - Les garde-fous du modèle peuvent refuser des images légitimes. Traiter le refus comme un état normal : la photo reste indexée par l'étage 1, sans message d'erreur. --- ## 9. Performance & énergie - **Le déclencheur répond en moins de 50 ms, toujours.** C'est la métrique numéro un du produit. Mesurée à chaque build. - Aperçu à 60 fps sans chute pendant l'indexation. Si l'indexeur tourne, il est suspendu dès l'ouverture de la caméra. - File d'inférence à priorité : geste utilisateur > photo qu'on regarde > arrière-plan. Annulation immédiate au changement d'écran. - Budget thermique : surveiller `ProcessInfo.thermalState`, suspendre l'étage 2 dès `.fair`. - Taille de l'index : viser moins de 500 Mo pour 50 000 photos. Vignettes via PhotoKit, jamais stockées en double. --- ## 10. Conventions de code - SwiftUI, iOS 27+, `@Observable`. Swift 6, concurrence stricte. - `AVCaptureSession` sur sa propre file série, jamais sur le main actor. L'UI de capture est `@MainActor`. - **Jamais de `String` libre en sortie de modèle.** Toujours `@Generable`. - Toute capacité matérielle est vérifiée avant d'être exposée. Aucune supposition sur le modèle d'iPhone. - Chaque fonctionnalité IA a un chemin de repli Vision-only testé. La suite de tests tourne avec Foundation Models désactivé. - Nommage utilisateur en français ; code, commentaires et commits en anglais. --- ## 11. Ordre de construction 1. **Appareil photo manuel excellent.** Contrôles, ProRAW, recettes, écriture PhotoKit. Zéro IA. Vendable seul dès cette étape — et c'est le test : si personne ne le veut ici, l'IA ne le sauvera pas. 2. **Contexte de capture + projets.** Invisible, mais c'est la fondation de la recherche. 3. **Étage 1 Vision sur les nouvelles photos**, puis rétroactif. L'OCR d'abord : c'est le gain le plus spectaculaire pour l'effort le plus faible. 4. **Recherche naturelle + albums vivants.** Le moment « je ne peux plus revenir à Photos ». 5. **Étage 2 Foundation Models** sur candidats. 6. **Personnalisation avancée** : dispositions, mapping de gestes, looks, partage de recettes. --- ## 12. Risques connus | Risque | Réalité | |---|---| | Permission Accès complet refusée | Le risque numéro un. L'onboarding est un chantier produit à part entière, pas un écran | | Mode Nuit inaccessible | Ne jamais se positionner sur la basse lumière. Contrôle et intention | | 20-30 h d'indexation initiale | Transparence + valeur immédiate sur les photos récentes. Jamais d'attente opaque | | Marché des apps photo saturé | La photothèque est la différence. L'appareil photo seul ne suffit pas à se démarquer | | A17 Pro requis | Le mode Vision-only doit être un bon produit à part entière | | Batterie et chaleur | Une app photo qui chauffe pendant qu'on shoote est désinstallée le jour même | | Attentes sur la reconnaissance de personnes | Terrain sensible. Rester en retrait volontairement, et le dire |