Implementation Plan: Profil de boîtiers optionnel, déclaré à la demande
Branch: 002-profil-boitiers-optionnel | Date: 2026-09-18 | Spec: spec.md
Input: Feature specification from /specs/002-profil-boitiers-optionnel/spec.md
Summary
Désambiguïse par défaut deux fichiers en collision de nom d'origine à partir du seul tag EXIF Model, sans exiger de profil de boîtiers préalable ; ne sollicite le profil (numéro de série, puis étiquetage manuel en dernier recours) que lorsque le modèle seul ne suffit pas à distinguer les sources d'une collision réelle. Approche technique : un nouveau module regine_core.camera_profile (bibliothèque centrale, cf. docs/interface-cli-gui-architecture.md) exposant un algorithme de résolution pur (modèle → numéro de série → étiquetage manuel), consommé par le futur flux d'import (specs/001-import-photos) et par l'écran de configuration des boîtiers (specs/003-config-contexte-travail User Story 3). Les tags EXIF sont lus via exiftool (dépendance externe déjà implicite dans les notes de conception du projet pour ce type de lecture) ; la table boitiers réutilise la base de contexte centralisée déjà actée par specs/003-config-contexte-travail.
Technical Context
Language/Version: Python 3.11+ (cohérent avec regine-core, cf. specs/003-config-contexte-travail/plan.md et specs/004-categorisation-dossiers/plan.md)
Primary Dependencies: exiftool (binaire externe, Phil Harvey ExifTool — déjà une dépendance implicite du projet pour la lecture EXIF/XMP avancée, cf. docs/archivage-photo-elements-cles.md sections 11 et 13), invoqué en processus persistant (-stay_open) via un wrapper léger regine_core/metadata/exif.py ; aucune autre dépendance tierce Python
Storage: réutilise la base de contexte centralisée SQLite déjà définie par specs/003-config-contexte-travail (même fichier, regine_core/config/db.py créé par specs/004-categorisation-dossiers) — table boitiers, possédée par ce module (camera_profile) plutôt que par config (cf. research.md § 2, correction de placement par rapport au plan initial de specs/003)
Testing: pytest ; tests unitaires sur l'algorithme de résolution pur (modèle → numéro de série → manuel), tests d'intégration sur la persistance et la réutilisation d'une résolution déjà établie
Target Platform: identique aux autres modules de regine-core (poste de bureau) ; nécessite exiftool installé sur le poste — dépendance externe à documenter à l'installation
Project Type: Monorepo existant (specs/003, specs/004) — nouveau module regine_core/camera_profile/ dans le paquet regine-core ; aucune nouvelle façade
Performance Goals: résolution quasi instantanée pour un import typique (dizaines à centaines de fichiers) ; le coût dominant est la lecture EXIF elle-même, minimisé par un processus exiftool persistant plutôt qu'un lancement par fichier
Constraints: ne jamais interrompre un import ni poser de question en l'absence de collision réelle (FR-008) ; jamais plus d'une question par groupe de fichiers en collision non résolu automatiquement (FR-005)
Scale/Scope: profil mono-utilisateur ; nombre de boîtiers attendu faible (unités à dizaines) sur la durée de vie d'une archive personnelle
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
| Principe / contrainte | Évaluation | Justification |
|---|---|---|
| I. Fichier maître intouchable | N/A | Ce module ne modifie aucun fichier maître, il ne fait que lire des métadonnées EXIF déjà embarquées. |
| II. Confirmation explicite avant toute action à risque | PASS | Aucune écriture sur l'archive n'est effectuée par ce module ; la demande d'étiquetage manuel (FR-005) n'est posée qu'en cas d'ambiguïté réelle, jamais par anticipation (FR-003, FR-008), cohérent avec l'esprit du principe (ne pas sursolliciter l'utilisateur). |
| III. Identité par contenu, jamais par nom de fichier seul | PASS | La collision elle-même reste détectée par somme de contrôle de contenu (hors périmètre de ce module, fourni par l'appelant) ; ce module ajoute une désambiguïsation de la source par métadonnées EXIF, complémentaire et non contradictoire. |
| IV. Métadonnées ouvertes et embarquées | PASS | Le modèle et le numéro de série sont des métadonnées EXIF déjà embarquées, lues en lecture seule. Le nom lisible attribué par l'utilisateur reste une convenance locale (base de contexte), pas une métadonnée descriptive/de droits au sens du Principe IV — même justification que specs/003-config-contexte-travail. |
| V. L'utilisateur décide, Régine suggère | PASS | FR-005 : l'étiquetage manuel est toujours demandé, jamais deviné ; la résolution automatique ne s'appuie que sur des faits (tags EXIF identiques ou différents), jamais une heuristique probabiliste. |
| VI. Bibliothèque centrale, façades minces | PASS | Nouveau module camera_profile dans regine-core ; consommé par les futures façades CLI de l'import (specs/001) et de la configuration (specs/003) sans logique dupliquée entre elles. |
| CLI-first | PASS | Aucune commande dédiée introduite par ce plan ; l'algorithme est consommé par les futures façades CLI d'autres specs. |
| Exécution sans démon | PASS | Le processus exiftool -stay_open est démarré et arrêté par le processus d'import lui-même, pas un service permanent. |
| Formats ouverts et documentés | PASS | Réutilise la base SQLite déjà actée ; EXIF est un standard ouvert documenté. |
Aucune violation identifiée ; la section Complexity Tracking reste vide.
Re-check post Phase 1 (après génération de data-model.md, contracts/regine-core-api.md, quickstart.md) : le modèle de données (une seule table boitiers, aucune donnée sensible) et le contrat d'API (fonctions pures + accès base de contexte, jamais d'écriture archive) confirment chaque évaluation PASS ci-dessus. Aucune violation nouvelle.
Project Structure
Documentation (this feature)
specs/002-profil-boitiers-optionnel/
├── plan.md # This file (/speckit-plan command output)
├── research.md # Phase 0 output (/speckit-plan command)
├── data-model.md # Phase 1 output (/speckit-plan command)
├── quickstart.md # Phase 1 output (/speckit-plan command)
├── contracts/ # Phase 1 output (/speckit-plan command)
│ └── regine-core-api.md
└── tasks.md # Phase 2 output (/speckit-tasks command - NOT created by /speckit-plan)
Source Code (repository root) — extension du monorepo posé par specs/003/004
packages/regine-core/
├── src/regine_core/
│ ├── metadata/
│ │ ├── __init__.py
│ │ └── exif.py # NOUVEAU : read_camera_tags(path) -> CameraTags, process exiftool persistant
│ └── camera_profile/ # NOUVEAU module (cf. docs/interface-cli-gui-architecture.md)
│ ├── __init__.py
│ ├── resolve.py # resolve_collision(files) -> CollisionResolution (algorithme pur)
│ └── db.py # table `boitiers` dans la base de contexte centralisée (regine_core.config.db)
│
└── tests/
├── unit/
│ ├── test_exif_reader.py # lecture Model/BodySerialNumber, tags absents
│ └── test_resolve_collision.py # modèle différent, même modèle + série, même modèle sans série (manuel)
└── integration/
└── test_boitier_persistence.py # résolution réutilisée sans nouvelle question à l'import suivant
Structure Decision: Extension du monorepo existant — nouveau module regine_core/camera_profile/ (absent jusqu'ici), plus un petit module regine_core/metadata/ pour l'accès EXIF (également absent, réutilisable par de futurs modules integrity/import). Correction de placement par rapport à specs/003-config-contexte-travail/plan.md : ce dernier avait provisoirement prévu regine_core/config/cameras.py pour la logique boîtiers ; ce plan établit que la table boitiers et sa logique de résolution appartiennent à camera_profile (module de premier niveau déjà nommé dans l'architecture du projet), pas à config. specs/003-config-contexte-travail/plan.md doit être corrigé en conséquence pour que son écran de configuration des boîtiers délègue à regine_core.camera_profile — correction appliquée séparément après ce plan (cf. rapport de fin de commande).
Complexity Tracking
Aucune violation de gate à justifier — section laissée vide intentionnellement.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
|