tasks
26c1e45 parent: 6ad5e0a added
specs/001-import-photos/tasks.md +186 -0 | new file mode 100644 | ||
| @@ -0,0 +1,186 @@ | ||
| 1 | +--- | |
| 2 | + | |
| 3 | +description: "Task list for feature implementation" | |
| 4 | +--- | |
| 5 | + | |
| 6 | +# Tasks: Importation de photos depuis une carte mémoire | |
| 7 | + | |
| 8 | +**Input**: Design documents from `/specs/001-import-photos/` | |
| 9 | +**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md | |
| 10 | + | |
| 11 | +**Tests**: Incluses — `plan.md` § Project Structure nomme explicitement les fichiers de test attendus, ce qui vaut demande explicite au sens des règles de génération. | |
| 12 | + | |
| 13 | +**Organisation**: Tasks groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune. | |
| 14 | + | |
| 15 | +## Dépendances inter-specs (à lire avant de commencer) | |
| 16 | + | |
| 17 | +Ce module est le premier point d'intégration réel de trois autres specs déjà planifiées. Aucun code n'existe encore dans ce dépôt à ce jour. | |
| 18 | + | |
| 19 | +| Dépendance | Fournit | État | | |
| 20 | +|---|---|---| | |
| 21 | +| `specs/004-categorisation-dossiers/tasks.md` | Squelette du monorepo (`packages/regine-core`, `packages/regine-cli`, `packages/regine-gui`, `packages/regine-agent`), `regine_core.dossier.root.determine_root`, `regine_core.config.categories`, `regine_core.config.db` | tasks.md existe (27 tâches) — **à exécuter avant la Phase 1 ci-dessous** si ce n'est pas déjà fait | | |
| 22 | +| `specs/002-profil-boitiers-optionnel` | `regine_core.camera_profile.resolve_collision`, `regine_core.metadata.exif` (lecture `Model`/`BodySerialNumber`) | **Pas encore de tasks.md** — à générer (`/speckit-tasks` sur 002) et implémenter avant la Phase 5 (US3, désambiguïsation de boîtiers) ; les Phases 1 à 4 de ce fichier n'en dépendent pas | | |
| 23 | +| `specs/005-checkout-reconciliation` | `regine_core.archive.checkout.checkout` | **Pas encore de tasks.md** — nécessaire uniquement pour T034 (fusion vers un dossier déjà archivé) ; tout le reste de ce fichier n'en dépend pas | | |
| 24 | + | |
| 25 | +## Phase 1: Setup | |
| 26 | + | |
| 27 | +**Purpose**: Vérifier les prérequis inter-specs et poser le squelette propre à ce module. | |
| 28 | + | |
| 29 | +- [ ] T001 Vérifier que `packages/regine-core/src/regine_core/dossier/root.py` et `packages/regine-core/src/regine_core/config/categories.py` existent déjà (créés par `specs/004-categorisation-dossiers/tasks.md`) ; si absents, exécuter d'abord ce fichier de tâches avant de continuer | |
| 30 | +- [ ] T002 [P] Créer `packages/regine-core/src/regine_core/import_carte/__init__.py` | |
| 31 | +- [ ] T003 [P] Créer `packages/regine-cli/src/regine_cli/import_cmd.py` (squelette de la commande `regine import`, sans logique) | |
| 32 | +- [ ] T004 [P] Créer les dossiers `packages/regine-core/tests/unit/` et `packages/regine-core/tests/integration/` s'ils n'existent pas déjà (normalement déjà créés par specs/004) | |
| 33 | + | |
| 34 | +--- | |
| 35 | + | |
| 36 | +## Phase 2: Foundational (Blocking Prerequisites) | |
| 37 | + | |
| 38 | +**Purpose**: Infrastructure partagée requise par les trois user stories de cette fonctionnalité. | |
| 39 | + | |
| 40 | +**⚠️ CRITICAL**: Aucune user story ne peut être implémentée avant la fin de cette phase. | |
| 41 | + | |
| 42 | +- [ ] T005 Créer `packages/regine-core/src/regine_core/import_carte/types.py` : dataclasses `FichierCandidat`, `GroupeImport`, `DestinationChoisie`, `Renommage` (cf. `data-model.md`) | |
| 43 | +- [ ] T006 Étendre `packages/regine-core/src/regine_core/metadata/exif.py` (existant, specs/002) avec `read_capture_date(chemin) -> datetime | None` (tag EXIF `DateTimeOriginal`, cf. `research.md` § 2) | |
| 44 | +- [ ] T007 Étendre `packages/regine-core/src/regine_core/metadata/exif.py` avec `write_document_id(chemin, identifiant) -> None` (écrit `xmpMM:DocumentID` via exiftool, idempotent, cf. `research.md` § 3) | |
| 45 | +- [ ] T008 [P] Test unitaire de `read_capture_date`/`write_document_id` dans `packages/regine-core/tests/unit/test_identifiant.py` (date valide, date absente ; écriture puis relecture de l'identifiant) | |
| 46 | + | |
| 47 | +**Checkpoint**: Fondations prêtes — les phases User Story peuvent commencer. | |
| 48 | + | |
| 49 | +--- | |
| 50 | + | |
| 51 | +## Phase 3: User Story 1 - Import simple d'une carte mémoire vers un nouveau dossier (Priority: P1) 🎯 MVP | |
| 52 | + | |
| 53 | +**Goal**: Copier une carte, proposer un groupe unique, choisir titre et placement par défaut (année), renommer, attribuer un identifiant pérenne, confirmer et archiver (FR-001/002/004/005/007 [nouveau_dossier]/010/012/013/014/017/018/019). | |
| 54 | + | |
| 55 | +**Independent Test**: Importer une carte d'une seule journée sans catégorie ; vérifier qu'un dossier correctement nommé et vérifié apparaît dans l'archive, sans dépendre d'aucune autre user story. | |
| 56 | + | |
| 57 | +### Tests for User Story 1 | |
| 58 | + | |
| 59 | +- [ ] T009 [P] [US1] Test unitaire copie vérifiée : une seule lecture de la carte, échec de vérification signalé sans marquer la carte sûre à effacer, dans `packages/regine-core/tests/unit/test_copie_checksum.py` | |
| 60 | +- [ ] T010 [P] [US1] Test unitaire découpage en groupe unique (cas simple, une seule journée) dans `packages/regine-core/tests/unit/test_groupage_dates.py` | |
| 61 | +- [ ] T011 [P] [US1] Test unitaire construction du nom de dossier et détection de collision dans `packages/regine-core/tests/unit/test_nommage.py` | |
| 62 | +- [ ] T012 [US1] Test d'intégration du pipeline complet (carte simple → dossier archivé, résumé confirmé) dans `packages/regine-core/tests/integration/test_pipeline_import_simple.py` | |
| 63 | + | |
| 64 | +### Implementation for User Story 1 | |
| 65 | + | |
| 66 | +- [ ] T013 [US1] Implémenter `copier_carte(carte, local_tmp) -> list[FichierCandidat]` dans `packages/regine-core/src/regine_core/import_carte/copie.py` (FR-001 : une seule lecture carte, FR-004 : filtrage des fichiers déjà importés) | |
| 67 | +- [ ] T014 [US1] Implémenter `decouper_en_groupes(fichiers) -> list[GroupeImport]` dans `packages/regine-core/src/regine_core/import_carte/groupage.py` (FR-002 lecture date, branche groupe unique par défaut de FR-005 ; le détachement multi-jours est complété en Phase 4) | |
| 68 | +- [ ] T015 [US1] Implémenter la branche `nouveau_dossier` de `resoudre_destination` dans `packages/regine-core/src/regine_core/import_carte/destination.py` (FR-007 pour ce cas, appelle `regine_core.dossier.root.determine_root`) | |
| 69 | +- [ ] T016 [US1] Implémenter `construire_nom_dossier`/`renommer_fichiers` dans `packages/regine-core/src/regine_core/import_carte/nommage.py` (FR-010/012/013/014) | |
| 70 | +- [ ] T017 [US1] Implémenter `attribuer_identifiants` dans `packages/regine-core/src/regine_core/import_carte/identifiant.py` (FR-017, appelle `write_document_id`) | |
| 71 | +- [ ] T018 [US1] Implémenter `preparer_resume`/`archiver` dans `packages/regine-core/src/regine_core/import_carte/push.py` (FR-018/019) | |
| 72 | +- [ ] T019 [US1] Orchestrer le pipeline dans `packages/regine-cli/src/regine_cli/import_cmd.py` pour `regine import <carte> --annee --titre TEXTE` (cas `nouveau_dossier` uniquement) | |
| 73 | + | |
| 74 | +**Checkpoint**: User Story 1 fonctionnelle et testable indépendamment (MVP). | |
| 75 | + | |
| 76 | +--- | |
| 77 | + | |
| 78 | +## Phase 4: User Story 2 - Découpage d'un import multi-jours en plusieurs groupes (Priority: P2) | |
| 79 | + | |
| 80 | +**Goal**: Détecter la plage de dates, exclure les dates aberrantes, mettre en avant un jour candidat au détachement, permettre à l'utilisateur de détacher et de traiter chaque groupe séparément (FR-003/005/006). | |
| 81 | + | |
| 82 | +**Independent Test**: Importer une carte couvrant une semaine avec un pic isolé ; vérifier que ce jour est proposé comme candidat, jamais détaché automatiquement, et que le détacher produit deux dossiers distincts. | |
| 83 | + | |
| 84 | +### Tests for User Story 2 | |
| 85 | + | |
| 86 | +- [ ] T020 [P] [US2] Test unitaire d'exclusion des dates aberrantes du calcul de plage dans `packages/regine-core/tests/unit/test_groupage_dates.py` | |
| 87 | +- [ ] T021 [P] [US2] Test unitaire de mise en avant d'un jour candidat au détachement (heuristique, cf. `research.md` § 5) et de détachement manuel dans `test_groupage_dates.py` | |
| 88 | +- [ ] T022 [US2] Test d'intégration du découpage en plusieurs groupes avec destination/titre distincts dans `packages/regine-core/tests/integration/test_pipeline_multi_jours.py` | |
| 89 | + | |
| 90 | +### Implementation for User Story 2 | |
| 91 | + | |
| 92 | +- [ ] T023 [US2] Étendre `decouper_en_groupes` : exclusion des dates aberrantes (FR-003) et heuristique de mise en avant d'un jour candidat (FR-006, cf. `research.md` § 5), dans `groupage.py` (dépend de T014) | |
| 93 | +- [ ] T024 [US2] Ajouter le détachement manuel d'un ou plusieurs jours par l'utilisateur, produisant des `GroupeImport` distincts, dans `groupage.py` | |
| 94 | +- [ ] T025 [US2] Étendre `packages/regine-cli/src/regine_cli/import_cmd.py` pour afficher la répartition jour par jour et gérer le détachement interactif, puis demander destination/titre pour chaque groupe résultant | |
| 95 | + | |
| 96 | +**Checkpoint**: User Stories 1 ET 2 fonctionnelles indépendamment. | |
| 97 | + | |
| 98 | +--- | |
| 99 | + | |
| 100 | +## Phase 5: User Story 3 - Import s'intégrant à un voyage en plusieurs étapes (Priority: P3) | |
| 101 | + | |
| 102 | +**Goal**: Structure à deux niveaux (parent/sous-dossier), fusion locale et vers l'archive, désambiguïsation de boîtiers en cas de collision (FR-007 [nouveau_sous_dossier/nouveau_parent/fusion]/008/009/015/016). | |
| 103 | + | |
| 104 | +**Independent Test**: Importer une première carte comme "nouveau dossier parent avec sa première étape", une seconde comme "nouveau sous-dossier", vérifier la structure à deux niveaux et l'héritage du répertoire racine. | |
| 105 | + | |
| 106 | +### Tests for User Story 3 | |
| 107 | + | |
| 108 | +- [ ] T026 [P] [US3] Test unitaire de recherche de dossiers candidats par titre/date proche (`difflib`, cf. `research.md` § 4) dans `packages/regine-core/tests/unit/test_destination_candidats.py` | |
| 109 | +- [ ] T027 [P] [US3] Test unitaire : la branche `nouveau_sous_dossier` réutilise le `RootLocation` du parent sans nouvel appel à `determine_root`, dans `test_destination_candidats.py` | |
| 110 | +- [ ] T028 [US3] Test d'intégration voyage complet (parent + sous-dossier + fusion locale + désambiguïsation de boîtiers) dans `packages/regine-core/tests/integration/test_pipeline_voyage.py` | |
| 111 | +- [ ] T029 [US3] Test d'intégration de la fusion vers un dossier **déjà archivé** dans `test_pipeline_voyage.py` — **dépend de l'implémentation de `specs/005-checkout-reconciliation`** (pas encore de tasks.md) ; à activer une fois celle-ci disponible, ne bloque pas le reste de cette phase | |
| 112 | + | |
| 113 | +### Implementation for User Story 3 | |
| 114 | + | |
| 115 | +- [ ] T030 [US3] Étendre `resoudre_destination` : branches `nouveau_sous_dossier` (hérite le `RootLocation` du parent, FR-006 de specs/004) et `nouveau_parent` (FR-007), dans `destination.py` (dépend de T015) | |
| 116 | +- [ ] T031 [US3] Implémenter `lister_dossiers_candidats(titre_partiel, date_proche)` dans `destination.py` (FR-008, cf. `research.md` § 4) | |
| 117 | +- [ ] T032 [US3] Implémenter la branche `fusion` locale de `resoudre_destination` (dossier cible présent dans l'espace de travail local) dans `destination.py` (FR-009, partie locale) | |
| 118 | +- [ ] T033 [US3] Implémenter la branche `fusion` vers un dossier déjà archivé : appelle `regine_core.archive.checkout.checkout` (`specs/005-checkout-reconciliation`), dans `destination.py` — **dépend de l'implémentation de specs/005** ; sans elle, cette branche lève une erreur explicite indiquant la dépendance manquante plutôt que d'échouer silencieusement | |
| 119 | +- [ ] T034 [US3] Intégrer la désambiguïsation de boîtiers dans `copie.py` : regrouper les fichiers en collision de nom d'origine, appeler `regine_core.camera_profile.resolve_collision` (specs/002), gérer les groupes nécessitant un étiquetage manuel (FR-015/016) — **dépend de l'implémentation de specs/002** | |
| 120 | +- [ ] T035 [US3] Étendre `packages/regine-cli/src/regine_cli/import_cmd.py` pour les 4 types de destination et la résolution interactive de collision de boîtiers | |
| 121 | + | |
| 122 | +**Checkpoint**: User Stories 1, 2 et 3 fonctionnelles (la fusion vers un dossier déjà archivé reste conditionnée à `specs/005`, tout le reste est indépendant). | |
| 123 | + | |
| 124 | +--- | |
| 125 | + | |
| 126 | +## Phase 6: Polish & Cross-Cutting Concerns | |
| 127 | + | |
| 128 | +- [ ] T036 [P] Exécuter manuellement les 3 scénarios de `specs/001-import-photos/quickstart.md` et consigner le résultat | |
| 129 | +- [ ] T037 [P] `ruff check --fix` sur `packages/regine-core` et `packages/regine-cli` | |
| 130 | +- [ ] T038 Mettre à jour `contracts/cli-import.md` et `contracts/regine-core-api.md` si l'implémentation révèle un écart avec les signatures documentées | |
| 131 | + | |
| 132 | +--- | |
| 133 | + | |
| 134 | +## Dependencies & Execution Order | |
| 135 | + | |
| 136 | +### Phase Dependencies | |
| 137 | + | |
| 138 | +- **Setup (Phase 1)** : dépend des tasks.md de `specs/004` (packages, dossier.root, config.categories) déjà exécutées — sinon, à exécuter en premier. | |
| 139 | +- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories. | |
| 140 | +- **User Stories (Phase 3–5)** : dépendent toutes de Foundational. | |
| 141 | + - US1 (P1) : aucune dépendance sur une autre user story de ce fichier ; dépend de `specs/004` (déjà disponible). | |
| 142 | + - US2 (P2) : étend directement les fonctions de US1 (T014, T023/T024) — livrable séparément mais techniquement postérieure à US1. | |
| 143 | + - US3 (P3) : étend `destination.py` de US1 (T015 → T030) ; sa branche de fusion vers l'archive (T033) et sa désambiguïsation de boîtiers (T034) dépendent respectivement de `specs/005` et `specs/002`, non encore implémentées — le reste de US3 (structure parent/sous-dossier, fusion locale, recherche de candidats) est indépendant de ces deux specs. | |
| 144 | +- **Polish (Phase 6)** : dépend des user stories livrées (au minimum US1). | |
| 145 | + | |
| 146 | +### Parallel Opportunities | |
| 147 | + | |
| 148 | +- T002–T004 (Phase 1) : fichiers/dossiers distincts. | |
| 149 | +- T008 (Phase 2) : peut suivre T006/T007 en parallèle d'autres tâches de Phase 2 déjà terminées. | |
| 150 | +- Tests marqués [P] au sein de chaque story (T009–T011, T020/T021, T026/T027) : fichiers distincts. | |
| 151 | + | |
| 152 | +--- | |
| 153 | + | |
| 154 | +## Parallel Example: User Story 1 | |
| 155 | + | |
| 156 | +```bash | |
| 157 | +# Lancer les tests de la User Story 1 ensemble : | |
| 158 | +Task: "Test unitaire copie vérifiée dans packages/regine-core/tests/unit/test_copie_checksum.py" | |
| 159 | +Task: "Test unitaire découpage groupe unique dans packages/regine-core/tests/unit/test_groupage_dates.py" | |
| 160 | +Task: "Test unitaire construction nom dossier dans packages/regine-core/tests/unit/test_nommage.py" | |
| 161 | +``` | |
| 162 | + | |
| 163 | +--- | |
| 164 | + | |
| 165 | +## Implementation Strategy | |
| 166 | + | |
| 167 | +### MVP First (User Story 1 uniquement) | |
| 168 | + | |
| 169 | +1. Vérifier/compléter les prérequis de `specs/004` (Phase 1). | |
| 170 | +2. Compléter Phase 2 (Foundational). | |
| 171 | +3. Compléter Phase 3 (US1) : import simple vers un nouveau dossier par défaut (année). | |
| 172 | +4. **Arrêt et validation** : exécuter le Scénario 1 de `quickstart.md`. | |
| 173 | + | |
| 174 | +### Livraison incrémentale | |
| 175 | + | |
| 176 | +1. Setup + Foundational → socle prêt. | |
| 177 | +2. US1 → import simple (MVP) → valider Scénario 1. | |
| 178 | +3. US2 → découpage multi-jours → valider Scénario 2. | |
| 179 | +4. US3 → voyage multi-étapes → valider Scénario 3, à l'exception de la fusion vers un dossier déjà archivé (T033) qui attend `specs/005-checkout-reconciliation`, et de la désambiguïsation de boîtiers (T034) qui attend `specs/002-profil-boitiers-optionnel`. | |
| 180 | + | |
| 181 | +## Notes | |
| 182 | + | |
| 183 | +- [P] = fichiers différents, sans dépendance non résolue. | |
| 184 | +- Chaque user story est livrable et testable indépendamment ; seules deux tâches précises de US3 (T033, T034) restent conditionnées à l'implémentation d'autres specs déjà planifiées. | |
| 185 | +- Committer après chaque tâche ou groupe logique de tâches. | |
| 186 | +- Ne pas recréer le squelette monorepo ni `regine_core.dossier.root`/`regine_core.config` : ils viennent de `specs/004-categorisation-dossiers/tasks.md`, à étendre si besoin, jamais dupliquer. | |
| new file mode 100644 | |||
| @@ -0,0 +1,186 @@ | |||
| 1 | +--- | ||
| 2 | + | ||
| 3 | +description: "Task list for feature implementation" | ||
| 4 | +--- | ||
| 5 | + | ||
| 6 | +# Tasks: Importation de photos depuis une carte mémoire | ||
| 7 | + | ||
| 8 | +**Input**: Design documents from `/specs/001-import-photos/` | ||
| 9 | +**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md | ||
| 10 | + | ||
| 11 | +**Tests**: Incluses — `plan.md` § Project Structure nomme explicitement les fichiers de test attendus, ce qui vaut demande explicite au sens des règles de génération. | ||
| 12 | + | ||
| 13 | +**Organisation**: Tasks groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune. | ||
| 14 | + | ||
| 15 | +## Dépendances inter-specs (à lire avant de commencer) | ||
| 16 | + | ||
| 17 | +Ce module est le premier point d'intégration réel de trois autres specs déjà planifiées. Aucun code n'existe encore dans ce dépôt à ce jour. | ||
| 18 | + | ||
| 19 | +| Dépendance | Fournit | État | | ||
| 20 | +|---|---|---| | ||
| 21 | +| `specs/004-categorisation-dossiers/tasks.md` | Squelette du monorepo (`packages/regine-core`, `packages/regine-cli`, `packages/regine-gui`, `packages/regine-agent`), `regine_core.dossier.root.determine_root`, `regine_core.config.categories`, `regine_core.config.db` | tasks.md existe (27 tâches) — **à exécuter avant la Phase 1 ci-dessous** si ce n'est pas déjà fait | | ||
| 22 | +| `specs/002-profil-boitiers-optionnel` | `regine_core.camera_profile.resolve_collision`, `regine_core.metadata.exif` (lecture `Model`/`BodySerialNumber`) | **Pas encore de tasks.md** — à générer (`/speckit-tasks` sur 002) et implémenter avant la Phase 5 (US3, désambiguïsation de boîtiers) ; les Phases 1 à 4 de ce fichier n'en dépendent pas | | ||
| 23 | +| `specs/005-checkout-reconciliation` | `regine_core.archive.checkout.checkout` | **Pas encore de tasks.md** — nécessaire uniquement pour T034 (fusion vers un dossier déjà archivé) ; tout le reste de ce fichier n'en dépend pas | | ||
| 24 | + | ||
| 25 | +## Phase 1: Setup | ||
| 26 | + | ||
| 27 | +**Purpose**: Vérifier les prérequis inter-specs et poser le squelette propre à ce module. | ||
| 28 | + | ||
| 29 | +- [ ] T001 Vérifier que `packages/regine-core/src/regine_core/dossier/root.py` et `packages/regine-core/src/regine_core/config/categories.py` existent déjà (créés par `specs/004-categorisation-dossiers/tasks.md`) ; si absents, exécuter d'abord ce fichier de tâches avant de continuer | ||
| 30 | +- [ ] T002 [P] Créer `packages/regine-core/src/regine_core/import_carte/__init__.py` | ||
| 31 | +- [ ] T003 [P] Créer `packages/regine-cli/src/regine_cli/import_cmd.py` (squelette de la commande `regine import`, sans logique) | ||
| 32 | +- [ ] T004 [P] Créer les dossiers `packages/regine-core/tests/unit/` et `packages/regine-core/tests/integration/` s'ils n'existent pas déjà (normalement déjà créés par specs/004) | ||
| 33 | + | ||
| 34 | +--- | ||
| 35 | + | ||
| 36 | +## Phase 2: Foundational (Blocking Prerequisites) | ||
| 37 | + | ||
| 38 | +**Purpose**: Infrastructure partagée requise par les trois user stories de cette fonctionnalité. | ||
| 39 | + | ||
| 40 | +**⚠️ CRITICAL**: Aucune user story ne peut être implémentée avant la fin de cette phase. | ||
| 41 | + | ||
| 42 | +- [ ] T005 Créer `packages/regine-core/src/regine_core/import_carte/types.py` : dataclasses `FichierCandidat`, `GroupeImport`, `DestinationChoisie`, `Renommage` (cf. `data-model.md`) | ||
| 43 | +- [ ] T006 Étendre `packages/regine-core/src/regine_core/metadata/exif.py` (existant, specs/002) avec `read_capture_date(chemin) -> datetime | None` (tag EXIF `DateTimeOriginal`, cf. `research.md` § 2) | ||
| 44 | +- [ ] T007 Étendre `packages/regine-core/src/regine_core/metadata/exif.py` avec `write_document_id(chemin, identifiant) -> None` (écrit `xmpMM:DocumentID` via exiftool, idempotent, cf. `research.md` § 3) | ||
| 45 | +- [ ] T008 [P] Test unitaire de `read_capture_date`/`write_document_id` dans `packages/regine-core/tests/unit/test_identifiant.py` (date valide, date absente ; écriture puis relecture de l'identifiant) | ||
| 46 | + | ||
| 47 | +**Checkpoint**: Fondations prêtes — les phases User Story peuvent commencer. | ||
| 48 | + | ||
| 49 | +--- | ||
| 50 | + | ||
| 51 | +## Phase 3: User Story 1 - Import simple d'une carte mémoire vers un nouveau dossier (Priority: P1) 🎯 MVP | ||
| 52 | + | ||
| 53 | +**Goal**: Copier une carte, proposer un groupe unique, choisir titre et placement par défaut (année), renommer, attribuer un identifiant pérenne, confirmer et archiver (FR-001/002/004/005/007 [nouveau_dossier]/010/012/013/014/017/018/019). | ||
| 54 | + | ||
| 55 | +**Independent Test**: Importer une carte d'une seule journée sans catégorie ; vérifier qu'un dossier correctement nommé et vérifié apparaît dans l'archive, sans dépendre d'aucune autre user story. | ||
| 56 | + | ||
| 57 | +### Tests for User Story 1 | ||
| 58 | + | ||
| 59 | +- [ ] T009 [P] [US1] Test unitaire copie vérifiée : une seule lecture de la carte, échec de vérification signalé sans marquer la carte sûre à effacer, dans `packages/regine-core/tests/unit/test_copie_checksum.py` | ||
| 60 | +- [ ] T010 [P] [US1] Test unitaire découpage en groupe unique (cas simple, une seule journée) dans `packages/regine-core/tests/unit/test_groupage_dates.py` | ||
| 61 | +- [ ] T011 [P] [US1] Test unitaire construction du nom de dossier et détection de collision dans `packages/regine-core/tests/unit/test_nommage.py` | ||
| 62 | +- [ ] T012 [US1] Test d'intégration du pipeline complet (carte simple → dossier archivé, résumé confirmé) dans `packages/regine-core/tests/integration/test_pipeline_import_simple.py` | ||
| 63 | + | ||
| 64 | +### Implementation for User Story 1 | ||
| 65 | + | ||
| 66 | +- [ ] T013 [US1] Implémenter `copier_carte(carte, local_tmp) -> list[FichierCandidat]` dans `packages/regine-core/src/regine_core/import_carte/copie.py` (FR-001 : une seule lecture carte, FR-004 : filtrage des fichiers déjà importés) | ||
| 67 | +- [ ] T014 [US1] Implémenter `decouper_en_groupes(fichiers) -> list[GroupeImport]` dans `packages/regine-core/src/regine_core/import_carte/groupage.py` (FR-002 lecture date, branche groupe unique par défaut de FR-005 ; le détachement multi-jours est complété en Phase 4) | ||
| 68 | +- [ ] T015 [US1] Implémenter la branche `nouveau_dossier` de `resoudre_destination` dans `packages/regine-core/src/regine_core/import_carte/destination.py` (FR-007 pour ce cas, appelle `regine_core.dossier.root.determine_root`) | ||
| 69 | +- [ ] T016 [US1] Implémenter `construire_nom_dossier`/`renommer_fichiers` dans `packages/regine-core/src/regine_core/import_carte/nommage.py` (FR-010/012/013/014) | ||
| 70 | +- [ ] T017 [US1] Implémenter `attribuer_identifiants` dans `packages/regine-core/src/regine_core/import_carte/identifiant.py` (FR-017, appelle `write_document_id`) | ||
| 71 | +- [ ] T018 [US1] Implémenter `preparer_resume`/`archiver` dans `packages/regine-core/src/regine_core/import_carte/push.py` (FR-018/019) | ||
| 72 | +- [ ] T019 [US1] Orchestrer le pipeline dans `packages/regine-cli/src/regine_cli/import_cmd.py` pour `regine import <carte> --annee --titre TEXTE` (cas `nouveau_dossier` uniquement) | ||
| 73 | + | ||
| 74 | +**Checkpoint**: User Story 1 fonctionnelle et testable indépendamment (MVP). | ||
| 75 | + | ||
| 76 | +--- | ||
| 77 | + | ||
| 78 | +## Phase 4: User Story 2 - Découpage d'un import multi-jours en plusieurs groupes (Priority: P2) | ||
| 79 | + | ||
| 80 | +**Goal**: Détecter la plage de dates, exclure les dates aberrantes, mettre en avant un jour candidat au détachement, permettre à l'utilisateur de détacher et de traiter chaque groupe séparément (FR-003/005/006). | ||
| 81 | + | ||
| 82 | +**Independent Test**: Importer une carte couvrant une semaine avec un pic isolé ; vérifier que ce jour est proposé comme candidat, jamais détaché automatiquement, et que le détacher produit deux dossiers distincts. | ||
| 83 | + | ||
| 84 | +### Tests for User Story 2 | ||
| 85 | + | ||
| 86 | +- [ ] T020 [P] [US2] Test unitaire d'exclusion des dates aberrantes du calcul de plage dans `packages/regine-core/tests/unit/test_groupage_dates.py` | ||
| 87 | +- [ ] T021 [P] [US2] Test unitaire de mise en avant d'un jour candidat au détachement (heuristique, cf. `research.md` § 5) et de détachement manuel dans `test_groupage_dates.py` | ||
| 88 | +- [ ] T022 [US2] Test d'intégration du découpage en plusieurs groupes avec destination/titre distincts dans `packages/regine-core/tests/integration/test_pipeline_multi_jours.py` | ||
| 89 | + | ||
| 90 | +### Implementation for User Story 2 | ||
| 91 | + | ||
| 92 | +- [ ] T023 [US2] Étendre `decouper_en_groupes` : exclusion des dates aberrantes (FR-003) et heuristique de mise en avant d'un jour candidat (FR-006, cf. `research.md` § 5), dans `groupage.py` (dépend de T014) | ||
| 93 | +- [ ] T024 [US2] Ajouter le détachement manuel d'un ou plusieurs jours par l'utilisateur, produisant des `GroupeImport` distincts, dans `groupage.py` | ||
| 94 | +- [ ] T025 [US2] Étendre `packages/regine-cli/src/regine_cli/import_cmd.py` pour afficher la répartition jour par jour et gérer le détachement interactif, puis demander destination/titre pour chaque groupe résultant | ||
| 95 | + | ||
| 96 | +**Checkpoint**: User Stories 1 ET 2 fonctionnelles indépendamment. | ||
| 97 | + | ||
| 98 | +--- | ||
| 99 | + | ||
| 100 | +## Phase 5: User Story 3 - Import s'intégrant à un voyage en plusieurs étapes (Priority: P3) | ||
| 101 | + | ||
| 102 | +**Goal**: Structure à deux niveaux (parent/sous-dossier), fusion locale et vers l'archive, désambiguïsation de boîtiers en cas de collision (FR-007 [nouveau_sous_dossier/nouveau_parent/fusion]/008/009/015/016). | ||
| 103 | + | ||
| 104 | +**Independent Test**: Importer une première carte comme "nouveau dossier parent avec sa première étape", une seconde comme "nouveau sous-dossier", vérifier la structure à deux niveaux et l'héritage du répertoire racine. | ||
| 105 | + | ||
| 106 | +### Tests for User Story 3 | ||
| 107 | + | ||
| 108 | +- [ ] T026 [P] [US3] Test unitaire de recherche de dossiers candidats par titre/date proche (`difflib`, cf. `research.md` § 4) dans `packages/regine-core/tests/unit/test_destination_candidats.py` | ||
| 109 | +- [ ] T027 [P] [US3] Test unitaire : la branche `nouveau_sous_dossier` réutilise le `RootLocation` du parent sans nouvel appel à `determine_root`, dans `test_destination_candidats.py` | ||
| 110 | +- [ ] T028 [US3] Test d'intégration voyage complet (parent + sous-dossier + fusion locale + désambiguïsation de boîtiers) dans `packages/regine-core/tests/integration/test_pipeline_voyage.py` | ||
| 111 | +- [ ] T029 [US3] Test d'intégration de la fusion vers un dossier **déjà archivé** dans `test_pipeline_voyage.py` — **dépend de l'implémentation de `specs/005-checkout-reconciliation`** (pas encore de tasks.md) ; à activer une fois celle-ci disponible, ne bloque pas le reste de cette phase | ||
| 112 | + | ||
| 113 | +### Implementation for User Story 3 | ||
| 114 | + | ||
| 115 | +- [ ] T030 [US3] Étendre `resoudre_destination` : branches `nouveau_sous_dossier` (hérite le `RootLocation` du parent, FR-006 de specs/004) et `nouveau_parent` (FR-007), dans `destination.py` (dépend de T015) | ||
| 116 | +- [ ] T031 [US3] Implémenter `lister_dossiers_candidats(titre_partiel, date_proche)` dans `destination.py` (FR-008, cf. `research.md` § 4) | ||
| 117 | +- [ ] T032 [US3] Implémenter la branche `fusion` locale de `resoudre_destination` (dossier cible présent dans l'espace de travail local) dans `destination.py` (FR-009, partie locale) | ||
| 118 | +- [ ] T033 [US3] Implémenter la branche `fusion` vers un dossier déjà archivé : appelle `regine_core.archive.checkout.checkout` (`specs/005-checkout-reconciliation`), dans `destination.py` — **dépend de l'implémentation de specs/005** ; sans elle, cette branche lève une erreur explicite indiquant la dépendance manquante plutôt que d'échouer silencieusement | ||
| 119 | +- [ ] T034 [US3] Intégrer la désambiguïsation de boîtiers dans `copie.py` : regrouper les fichiers en collision de nom d'origine, appeler `regine_core.camera_profile.resolve_collision` (specs/002), gérer les groupes nécessitant un étiquetage manuel (FR-015/016) — **dépend de l'implémentation de specs/002** | ||
| 120 | +- [ ] T035 [US3] Étendre `packages/regine-cli/src/regine_cli/import_cmd.py` pour les 4 types de destination et la résolution interactive de collision de boîtiers | ||
| 121 | + | ||
| 122 | +**Checkpoint**: User Stories 1, 2 et 3 fonctionnelles (la fusion vers un dossier déjà archivé reste conditionnée à `specs/005`, tout le reste est indépendant). | ||
| 123 | + | ||
| 124 | +--- | ||
| 125 | + | ||
| 126 | +## Phase 6: Polish & Cross-Cutting Concerns | ||
| 127 | + | ||
| 128 | +- [ ] T036 [P] Exécuter manuellement les 3 scénarios de `specs/001-import-photos/quickstart.md` et consigner le résultat | ||
| 129 | +- [ ] T037 [P] `ruff check --fix` sur `packages/regine-core` et `packages/regine-cli` | ||
| 130 | +- [ ] T038 Mettre à jour `contracts/cli-import.md` et `contracts/regine-core-api.md` si l'implémentation révèle un écart avec les signatures documentées | ||
| 131 | + | ||
| 132 | +--- | ||
| 133 | + | ||
| 134 | +## Dependencies & Execution Order | ||
| 135 | + | ||
| 136 | +### Phase Dependencies | ||
| 137 | + | ||
| 138 | +- **Setup (Phase 1)** : dépend des tasks.md de `specs/004` (packages, dossier.root, config.categories) déjà exécutées — sinon, à exécuter en premier. | ||
| 139 | +- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories. | ||
| 140 | +- **User Stories (Phase 3–5)** : dépendent toutes de Foundational. | ||
| 141 | + - US1 (P1) : aucune dépendance sur une autre user story de ce fichier ; dépend de `specs/004` (déjà disponible). | ||
| 142 | + - US2 (P2) : étend directement les fonctions de US1 (T014, T023/T024) — livrable séparément mais techniquement postérieure à US1. | ||
| 143 | + - US3 (P3) : étend `destination.py` de US1 (T015 → T030) ; sa branche de fusion vers l'archive (T033) et sa désambiguïsation de boîtiers (T034) dépendent respectivement de `specs/005` et `specs/002`, non encore implémentées — le reste de US3 (structure parent/sous-dossier, fusion locale, recherche de candidats) est indépendant de ces deux specs. | ||
| 144 | +- **Polish (Phase 6)** : dépend des user stories livrées (au minimum US1). | ||
| 145 | + | ||
| 146 | +### Parallel Opportunities | ||
| 147 | + | ||
| 148 | +- T002–T004 (Phase 1) : fichiers/dossiers distincts. | ||
| 149 | +- T008 (Phase 2) : peut suivre T006/T007 en parallèle d'autres tâches de Phase 2 déjà terminées. | ||
| 150 | +- Tests marqués [P] au sein de chaque story (T009–T011, T020/T021, T026/T027) : fichiers distincts. | ||
| 151 | + | ||
| 152 | +--- | ||
| 153 | + | ||
| 154 | +## Parallel Example: User Story 1 | ||
| 155 | + | ||
| 156 | +```bash | ||
| 157 | +# Lancer les tests de la User Story 1 ensemble : | ||
| 158 | +Task: "Test unitaire copie vérifiée dans packages/regine-core/tests/unit/test_copie_checksum.py" | ||
| 159 | +Task: "Test unitaire découpage groupe unique dans packages/regine-core/tests/unit/test_groupage_dates.py" | ||
| 160 | +Task: "Test unitaire construction nom dossier dans packages/regine-core/tests/unit/test_nommage.py" | ||
| 161 | +``` | ||
| 162 | + | ||
| 163 | +--- | ||
| 164 | + | ||
| 165 | +## Implementation Strategy | ||
| 166 | + | ||
| 167 | +### MVP First (User Story 1 uniquement) | ||
| 168 | + | ||
| 169 | +1. Vérifier/compléter les prérequis de `specs/004` (Phase 1). | ||
| 170 | +2. Compléter Phase 2 (Foundational). | ||
| 171 | +3. Compléter Phase 3 (US1) : import simple vers un nouveau dossier par défaut (année). | ||
| 172 | +4. **Arrêt et validation** : exécuter le Scénario 1 de `quickstart.md`. | ||
| 173 | + | ||
| 174 | +### Livraison incrémentale | ||
| 175 | + | ||
| 176 | +1. Setup + Foundational → socle prêt. | ||
| 177 | +2. US1 → import simple (MVP) → valider Scénario 1. | ||
| 178 | +3. US2 → découpage multi-jours → valider Scénario 2. | ||
| 179 | +4. US3 → voyage multi-étapes → valider Scénario 3, à l'exception de la fusion vers un dossier déjà archivé (T033) qui attend `specs/005-checkout-reconciliation`, et de la désambiguïsation de boîtiers (T034) qui attend `specs/002-profil-boitiers-optionnel`. | ||
| 180 | + | ||
| 181 | +## Notes | ||
| 182 | + | ||
| 183 | +- [P] = fichiers différents, sans dépendance non résolue. | ||
| 184 | +- Chaque user story est livrable et testable indépendamment ; seules deux tâches précises de US3 (T033, T034) restent conditionnées à l'implémentation d'autres specs déjà planifiées. | ||
| 185 | +- Committer après chaque tâche ou groupe logique de tâches. | ||
| 186 | +- Ne pas recréer le squelette monorepo ni `regine_core.dossier.root`/`regine_core.config` : ils viennent de `specs/004-categorisation-dossiers/tasks.md`, à étendre si besoin, jamais dupliquer. | ||
added
specs/002-profil-boitiers-optionnel/tasks.md +167 -0 | new file mode 100644 | ||
| @@ -0,0 +1,167 @@ | ||
| 1 | +--- | |
| 2 | + | |
| 3 | +description: "Task list for feature implementation" | |
| 4 | +--- | |
| 5 | + | |
| 6 | +# Tasks: Profil de boîtiers optionnel, déclaré à la demande | |
| 7 | + | |
| 8 | +**Input**: Design documents from `/specs/002-profil-boitiers-optionnel/` | |
| 9 | +**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md | |
| 10 | + | |
| 11 | +**Tests**: Incluses — `plan.md` § Project Structure nomme explicitement les fichiers de test attendus, ce qui vaut demande explicite au sens des règles de génération. | |
| 12 | + | |
| 13 | +**Organisation**: Tâches groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune. | |
| 14 | + | |
| 15 | +## Dépendances inter-specs | |
| 16 | + | |
| 17 | +| Dépendance | Fournit | État | | |
| 18 | +|---|---|---| | |
| 19 | +| `specs/004-categorisation-dossiers/tasks.md` | Squelette du monorepo (`packages/regine-core`), `regine_core.config.db.open_context_db` (base de contexte centralisée) | tasks.md existe — **à exécuter avant la Phase 1** si ce n'est pas déjà fait | | |
| 20 | + | |
| 21 | +Aucune autre dépendance externe : `exiftool` est un binaire système à installer sur le poste (pas une tâche de ce fichier). | |
| 22 | + | |
| 23 | +## Phase 1: Setup | |
| 24 | + | |
| 25 | +- [ ] T001 Vérifier que `packages/regine-core/src/regine_core/config/db.py` existe déjà (créé par `specs/004-categorisation-dossiers/tasks.md`, T009) ; si absent, exécuter d'abord ce fichier de tâches | |
| 26 | +- [ ] T002 [P] Créer `packages/regine-core/src/regine_core/metadata/__init__.py` et `packages/regine-core/src/regine_core/metadata/exif.py` (squelette) | |
| 27 | +- [ ] T003 [P] Créer `packages/regine-core/src/regine_core/camera_profile/__init__.py` | |
| 28 | +- [ ] T004 [P] Vérifier que `exiftool` est disponible sur le poste de développement (`exiftool -ver`) ; documenter cette dépendance externe dans le README du paquet `regine-core` si absent | |
| 29 | + | |
| 30 | +--- | |
| 31 | + | |
| 32 | +## Phase 2: Foundational (Blocking Prerequisites) | |
| 33 | + | |
| 34 | +**Purpose**: Infrastructure partagée requise par les trois user stories. | |
| 35 | + | |
| 36 | +**⚠️ CRITICAL**: Aucune user story ne peut être implémentée avant la fin de cette phase. | |
| 37 | + | |
| 38 | +- [ ] T005 Implémenter le wrapper de processus `exiftool -stay_open` (ouverture, envoi de commandes, fermeture propre) dans `packages/regine-core/src/regine_core/metadata/exif.py` (cf. `research.md` § 1) | |
| 39 | +- [ ] T006 Implémenter `read_camera_tags(chemin: Path) -> CameraTags` dans `metadata/exif.py` (lit `Model`/`BodySerialNumber`, retourne `None` si absent/inexploitable) | |
| 40 | +- [ ] T007 [P] Test unitaire de `read_camera_tags` dans `packages/regine-core/tests/unit/test_exif_reader.py` (modèle présent, numéro de série absent, métadonnées effacées) | |
| 41 | +- [ ] T008 Ajouter la table `boitiers` (`id`, `modele`, `numero_serie` nullable, `nom_lisible` nullable, `premiere_rencontre`, `source`) au schéma de `packages/regine-core/src/regine_core/config/db.py` (dépend de T001 ; unicité sur `(modele, numero_serie)` cf. `data-model.md`) | |
| 42 | +- [ ] T009 [P] Créer `packages/regine-core/src/regine_core/camera_profile/db.py` : `get_or_create_boitier(modele, numero_serie=None) -> int`, `list_boitiers() -> list[Boitier]`, `rename_boitier(id, nom) -> None` (dépend de T008) | |
| 43 | + | |
| 44 | +**Checkpoint**: Fondations prêtes — les phases User Story peuvent commencer. | |
| 45 | + | |
| 46 | +--- | |
| 47 | + | |
| 48 | +## Phase 3: User Story 1 - Premier import sans configuration préalable (Priority: P1) 🎯 MVP | |
| 49 | + | |
| 50 | +**Goal**: Garantir qu'aucun profil de boîtiers déclaré n'est requis avant un import, et que la désambiguïsation par défaut (sans collision) ne pose aucune question (FR-001/002/008). | |
| 51 | + | |
| 52 | +**Independent Test**: Lire les tags de fichiers de modèles différents sans jamais appeler `resolve_collision` (puisqu'aucune collision n'existe) ; vérifier qu'aucune entrée `boitiers` n'est créée tant qu'aucune collision réelle ne survient. | |
| 53 | + | |
| 54 | +### Tests for User Story 1 | |
| 55 | + | |
| 56 | +- [ ] T010 [P] [US1] Test unitaire : la détection d'un modèle jamais rencontré, sans collision, ne crée aucune entrée dans `boitiers` et ne lève aucune exception, dans `packages/regine-core/tests/unit/test_resolve_collision.py` | |
| 57 | + | |
| 58 | +### Implementation for User Story 1 | |
| 59 | + | |
| 60 | +- [ ] T011 [US1] Documenter (docstring) dans `packages/regine-core/src/regine_core/camera_profile/resolve.py` le contrat d'appel de `resolve_collision` : ne DOIT être invoquée par l'appelant (futur `specs/001-import-photos`) que pour des fichiers déjà identifiés en collision réelle par somme de contrôle (FR-003/FR-008) — aucune détection de collision n'est de la responsabilité de ce module | |
| 61 | + | |
| 62 | +**Checkpoint**: User Story 1 fonctionnelle et testable indépendamment (MVP — garantit l'absence de friction par défaut). | |
| 63 | + | |
| 64 | +--- | |
| 65 | + | |
| 66 | +## Phase 4: User Story 2 - Résolution ponctuelle d'une collision réelle entre boîtiers (Priority: P2) | |
| 67 | + | |
| 68 | +**Goal**: Distinguer automatiquement deux fichiers en collision par leur tag de modèle, et réutiliser une résolution déjà établie (FR-002/003/006). | |
| 69 | + | |
| 70 | +**Independent Test**: Deux fichiers de même nom d'origine, modèles EXIF différents → résolus automatiquement sans question ; un fichier ultérieur du même modèle/numéro de série réutilise le même `boitiers.id`. | |
| 71 | + | |
| 72 | +### Tests for User Story 2 | |
| 73 | + | |
| 74 | +- [ ] T012 [P] [US2] Test unitaire : `resolve_collision` avec deux fichiers de modèles différents → `a_etiqueter` vide, `id` distincts, dans `test_resolve_collision.py` | |
| 75 | +- [ ] T013 [P] [US2] Test unitaire : réutilisation d'un `boitiers.id` déjà créé pour un modèle/numéro de série déjà rencontré, dans `test_resolve_collision.py` | |
| 76 | +- [ ] T014 [US2] Test d'intégration : persistance d'une résolution entre deux appels successifs (simulant deux imports), dans `packages/regine-core/tests/integration/test_boitier_persistence.py` | |
| 77 | + | |
| 78 | +### Implementation for User Story 2 | |
| 79 | + | |
| 80 | +- [ ] T015 [US2] Implémenter `resolve_collision(fichiers: list[Path]) -> CollisionResolution` dans `packages/regine-core/src/regine_core/camera_profile/resolve.py` — regroupement par `modele`, résolution automatique quand les modèles diffèrent (dépend de T006, T009) | |
| 81 | +- [ ] T016 [US2] Implémenter la réutilisation automatique d'un `boitiers.id` déjà connu (même `modele`/`numero_serie`) sans nouvelle question, dans `resolve.py` (FR-006) | |
| 82 | + | |
| 83 | +**Checkpoint**: User Stories 1 ET 2 fonctionnelles indépendamment. | |
| 84 | + | |
| 85 | +--- | |
| 86 | + | |
| 87 | +## Phase 5: User Story 3 - Repli sur numéro de série pour deux boîtiers identiques (Priority: P3) | |
| 88 | + | |
| 89 | +**Goal**: Distinguer deux fichiers du même modèle par leur numéro de série ; à défaut, demander un étiquetage manuel (FR-004/005/007). | |
| 90 | + | |
| 91 | +**Independent Test**: Collision entre deux fichiers de même modèle, numéros de série différents et exploitables → résolution automatique ; numéros de série absents/identiques → groupe renvoyé dans `a_etiqueter`, résolu ensuite par `assign_manual_source`. | |
| 92 | + | |
| 93 | +### Tests for User Story 3 | |
| 94 | + | |
| 95 | +- [ ] T017 [P] [US3] Test unitaire : `resolve_collision` avec même modèle, numéros de série différents et exploitables → résolution automatique, dans `test_resolve_collision.py` | |
| 96 | +- [ ] T018 [P] [US3] Test unitaire : même modèle, numéro de série absent/identique/non exploitable → groupe dans `a_etiqueter`, jamais résolu arbitrairement, dans `test_resolve_collision.py` | |
| 97 | +- [ ] T019 [US3] Test unitaire de `assign_manual_source` (crée une nouvelle entrée si `boitier_id=None`, réutilise sinon) dans `test_resolve_collision.py` | |
| 98 | +- [ ] T020 [US3] Test unitaire de `list_boitiers`/`rename_boitier` (consultation et modification à tout moment, FR-007) dans `packages/regine-core/tests/unit/test_camera_profile_db.py` | |
| 99 | + | |
| 100 | +### Implementation for User Story 3 | |
| 101 | + | |
| 102 | +- [ ] T021 [US3] Étendre `resolve_collision` : repli sur `numero_serie` au sein d'un même `modele` (FR-004), dans `resolve.py` (dépend de T015) | |
| 103 | +- [ ] T022 [US3] Implémenter la construction du groupe `a_etiqueter` quand ni le modèle ni le numéro de série ne suffisent (FR-005), dans `resolve.py` | |
| 104 | +- [ ] T023 [US3] Implémenter `assign_manual_source(fichiers, boitier_id=None) -> int` dans `packages/regine-core/src/regine_core/camera_profile/resolve.py` (dépend de T009) | |
| 105 | + | |
| 106 | +**Checkpoint**: Les trois user stories fonctionnelles indépendamment. | |
| 107 | + | |
| 108 | +--- | |
| 109 | + | |
| 110 | +## Phase 6: Polish & Cross-Cutting Concerns | |
| 111 | + | |
| 112 | +- [ ] T024 [P] Exécuter manuellement les 5 scénarios de `specs/002-profil-boitiers-optionnel/quickstart.md` et consigner le résultat | |
| 113 | +- [ ] T025 [P] `ruff check --fix` sur `packages/regine-core` | |
| 114 | +- [ ] T026 Mettre à jour `contracts/regine-core-api.md` si l'implémentation révèle un écart avec les signatures documentées | |
| 115 | + | |
| 116 | +--- | |
| 117 | + | |
| 118 | +## Dependencies & Execution Order | |
| 119 | + | |
| 120 | +### Phase Dependencies | |
| 121 | + | |
| 122 | +- **Setup (Phase 1)** : dépend de `specs/004-categorisation-dossiers/tasks.md` (packages, `config.db`) déjà exécutées. | |
| 123 | +- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories. | |
| 124 | +- **User Stories (Phase 3–5)** : dépendent toutes de Foundational. | |
| 125 | + - US1 (P1) : aucune dépendance sur une autre user story. | |
| 126 | + - US2 (P2) : étend `resolve.py` initié en US1 (documentation du contrat) mais reste testable indépendamment. | |
| 127 | + - US3 (P3) : étend `resolve_collision` de US2 (T015 → T021/T022) ; livrable séparément. | |
| 128 | +- **Polish (Phase 6)** : dépend des user stories livrées (au minimum US1+US2). | |
| 129 | + | |
| 130 | +### Parallel Opportunities | |
| 131 | + | |
| 132 | +- T002–T004 (Phase 1) : fichiers/vérifications distincts. | |
| 133 | +- T007, T009 (Phase 2) : parallélisables une fois T006/T008 terminés respectivement. | |
| 134 | +- Tests marqués [P] au sein de chaque story. | |
| 135 | + | |
| 136 | +--- | |
| 137 | + | |
| 138 | +## Parallel Example: User Story 3 | |
| 139 | + | |
| 140 | +```bash | |
| 141 | +Task: "Test unitaire repli numéro de série dans packages/regine-core/tests/unit/test_resolve_collision.py" | |
| 142 | +Task: "Test unitaire groupe a_etiqueter dans packages/regine-core/tests/unit/test_resolve_collision.py" | |
| 143 | +Task: "Test unitaire list_boitiers/rename_boitier dans packages/regine-core/tests/unit/test_camera_profile_db.py" | |
| 144 | +``` | |
| 145 | + | |
| 146 | +--- | |
| 147 | + | |
| 148 | +## Implementation Strategy | |
| 149 | + | |
| 150 | +### MVP First (User Story 1 uniquement) | |
| 151 | + | |
| 152 | +1. Setup + Foundational. | |
| 153 | +2. US1 : garantir l'absence de friction par défaut (documentation de contrat + test de non-régression). | |
| 154 | +3. **Arrêt et validation** : Scénario 1 de `quickstart.md`. | |
| 155 | + | |
| 156 | +### Livraison incrémentale | |
| 157 | + | |
| 158 | +1. Setup + Foundational → socle prêt (lecture EXIF, table `boitiers`). | |
| 159 | +2. US1 → aucune question par défaut → valider Scénario 1. | |
| 160 | +3. US2 → résolution par modèle → valider Scénarios 2 et 5. | |
| 161 | +4. US3 → repli numéro de série + étiquetage manuel → valider Scénarios 3 et 4. | |
| 162 | + | |
| 163 | +## Notes | |
| 164 | + | |
| 165 | +- [P] = fichiers différents, sans dépendance non résolue. | |
| 166 | +- Ce module n'a pas de surface CLI propre (cf. `plan.md`) : son intégration réelle relève de `specs/001-import-photos` (T034 de son propre tasks.md) et de `specs/003-config-contexte-travail` (écran de nommage des boîtiers). | |
| 167 | +- Ne pas recréer le squelette monorepo ni `regine_core.config.db` : à étendre (T008), jamais dupliquer. | |
| new file mode 100644 | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | +--- | ||
| 2 | + | ||
| 3 | +description: "Task list for feature implementation" | ||
| 4 | +--- | ||
| 5 | + | ||
| 6 | +# Tasks: Profil de boîtiers optionnel, déclaré à la demande | ||
| 7 | + | ||
| 8 | +**Input**: Design documents from `/specs/002-profil-boitiers-optionnel/` | ||
| 9 | +**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md | ||
| 10 | + | ||
| 11 | +**Tests**: Incluses — `plan.md` § Project Structure nomme explicitement les fichiers de test attendus, ce qui vaut demande explicite au sens des règles de génération. | ||
| 12 | + | ||
| 13 | +**Organisation**: Tâches groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune. | ||
| 14 | + | ||
| 15 | +## Dépendances inter-specs | ||
| 16 | + | ||
| 17 | +| Dépendance | Fournit | État | | ||
| 18 | +|---|---|---| | ||
| 19 | +| `specs/004-categorisation-dossiers/tasks.md` | Squelette du monorepo (`packages/regine-core`), `regine_core.config.db.open_context_db` (base de contexte centralisée) | tasks.md existe — **à exécuter avant la Phase 1** si ce n'est pas déjà fait | | ||
| 20 | + | ||
| 21 | +Aucune autre dépendance externe : `exiftool` est un binaire système à installer sur le poste (pas une tâche de ce fichier). | ||
| 22 | + | ||
| 23 | +## Phase 1: Setup | ||
| 24 | + | ||
| 25 | +- [ ] T001 Vérifier que `packages/regine-core/src/regine_core/config/db.py` existe déjà (créé par `specs/004-categorisation-dossiers/tasks.md`, T009) ; si absent, exécuter d'abord ce fichier de tâches | ||
| 26 | +- [ ] T002 [P] Créer `packages/regine-core/src/regine_core/metadata/__init__.py` et `packages/regine-core/src/regine_core/metadata/exif.py` (squelette) | ||
| 27 | +- [ ] T003 [P] Créer `packages/regine-core/src/regine_core/camera_profile/__init__.py` | ||
| 28 | +- [ ] T004 [P] Vérifier que `exiftool` est disponible sur le poste de développement (`exiftool -ver`) ; documenter cette dépendance externe dans le README du paquet `regine-core` si absent | ||
| 29 | + | ||
| 30 | +--- | ||
| 31 | + | ||
| 32 | +## Phase 2: Foundational (Blocking Prerequisites) | ||
| 33 | + | ||
| 34 | +**Purpose**: Infrastructure partagée requise par les trois user stories. | ||
| 35 | + | ||
| 36 | +**⚠️ CRITICAL**: Aucune user story ne peut être implémentée avant la fin de cette phase. | ||
| 37 | + | ||
| 38 | +- [ ] T005 Implémenter le wrapper de processus `exiftool -stay_open` (ouverture, envoi de commandes, fermeture propre) dans `packages/regine-core/src/regine_core/metadata/exif.py` (cf. `research.md` § 1) | ||
| 39 | +- [ ] T006 Implémenter `read_camera_tags(chemin: Path) -> CameraTags` dans `metadata/exif.py` (lit `Model`/`BodySerialNumber`, retourne `None` si absent/inexploitable) | ||
| 40 | +- [ ] T007 [P] Test unitaire de `read_camera_tags` dans `packages/regine-core/tests/unit/test_exif_reader.py` (modèle présent, numéro de série absent, métadonnées effacées) | ||
| 41 | +- [ ] T008 Ajouter la table `boitiers` (`id`, `modele`, `numero_serie` nullable, `nom_lisible` nullable, `premiere_rencontre`, `source`) au schéma de `packages/regine-core/src/regine_core/config/db.py` (dépend de T001 ; unicité sur `(modele, numero_serie)` cf. `data-model.md`) | ||
| 42 | +- [ ] T009 [P] Créer `packages/regine-core/src/regine_core/camera_profile/db.py` : `get_or_create_boitier(modele, numero_serie=None) -> int`, `list_boitiers() -> list[Boitier]`, `rename_boitier(id, nom) -> None` (dépend de T008) | ||
| 43 | + | ||
| 44 | +**Checkpoint**: Fondations prêtes — les phases User Story peuvent commencer. | ||
| 45 | + | ||
| 46 | +--- | ||
| 47 | + | ||
| 48 | +## Phase 3: User Story 1 - Premier import sans configuration préalable (Priority: P1) 🎯 MVP | ||
| 49 | + | ||
| 50 | +**Goal**: Garantir qu'aucun profil de boîtiers déclaré n'est requis avant un import, et que la désambiguïsation par défaut (sans collision) ne pose aucune question (FR-001/002/008). | ||
| 51 | + | ||
| 52 | +**Independent Test**: Lire les tags de fichiers de modèles différents sans jamais appeler `resolve_collision` (puisqu'aucune collision n'existe) ; vérifier qu'aucune entrée `boitiers` n'est créée tant qu'aucune collision réelle ne survient. | ||
| 53 | + | ||
| 54 | +### Tests for User Story 1 | ||
| 55 | + | ||
| 56 | +- [ ] T010 [P] [US1] Test unitaire : la détection d'un modèle jamais rencontré, sans collision, ne crée aucune entrée dans `boitiers` et ne lève aucune exception, dans `packages/regine-core/tests/unit/test_resolve_collision.py` | ||
| 57 | + | ||
| 58 | +### Implementation for User Story 1 | ||
| 59 | + | ||
| 60 | +- [ ] T011 [US1] Documenter (docstring) dans `packages/regine-core/src/regine_core/camera_profile/resolve.py` le contrat d'appel de `resolve_collision` : ne DOIT être invoquée par l'appelant (futur `specs/001-import-photos`) que pour des fichiers déjà identifiés en collision réelle par somme de contrôle (FR-003/FR-008) — aucune détection de collision n'est de la responsabilité de ce module | ||
| 61 | + | ||
| 62 | +**Checkpoint**: User Story 1 fonctionnelle et testable indépendamment (MVP — garantit l'absence de friction par défaut). | ||
| 63 | + | ||
| 64 | +--- | ||
| 65 | + | ||
| 66 | +## Phase 4: User Story 2 - Résolution ponctuelle d'une collision réelle entre boîtiers (Priority: P2) | ||
| 67 | + | ||
| 68 | +**Goal**: Distinguer automatiquement deux fichiers en collision par leur tag de modèle, et réutiliser une résolution déjà établie (FR-002/003/006). | ||
| 69 | + | ||
| 70 | +**Independent Test**: Deux fichiers de même nom d'origine, modèles EXIF différents → résolus automatiquement sans question ; un fichier ultérieur du même modèle/numéro de série réutilise le même `boitiers.id`. | ||
| 71 | + | ||
| 72 | +### Tests for User Story 2 | ||
| 73 | + | ||
| 74 | +- [ ] T012 [P] [US2] Test unitaire : `resolve_collision` avec deux fichiers de modèles différents → `a_etiqueter` vide, `id` distincts, dans `test_resolve_collision.py` | ||
| 75 | +- [ ] T013 [P] [US2] Test unitaire : réutilisation d'un `boitiers.id` déjà créé pour un modèle/numéro de série déjà rencontré, dans `test_resolve_collision.py` | ||
| 76 | +- [ ] T014 [US2] Test d'intégration : persistance d'une résolution entre deux appels successifs (simulant deux imports), dans `packages/regine-core/tests/integration/test_boitier_persistence.py` | ||
| 77 | + | ||
| 78 | +### Implementation for User Story 2 | ||
| 79 | + | ||
| 80 | +- [ ] T015 [US2] Implémenter `resolve_collision(fichiers: list[Path]) -> CollisionResolution` dans `packages/regine-core/src/regine_core/camera_profile/resolve.py` — regroupement par `modele`, résolution automatique quand les modèles diffèrent (dépend de T006, T009) | ||
| 81 | +- [ ] T016 [US2] Implémenter la réutilisation automatique d'un `boitiers.id` déjà connu (même `modele`/`numero_serie`) sans nouvelle question, dans `resolve.py` (FR-006) | ||
| 82 | + | ||
| 83 | +**Checkpoint**: User Stories 1 ET 2 fonctionnelles indépendamment. | ||
| 84 | + | ||
| 85 | +--- | ||
| 86 | + | ||
| 87 | +## Phase 5: User Story 3 - Repli sur numéro de série pour deux boîtiers identiques (Priority: P3) | ||
| 88 | + | ||
| 89 | +**Goal**: Distinguer deux fichiers du même modèle par leur numéro de série ; à défaut, demander un étiquetage manuel (FR-004/005/007). | ||
| 90 | + | ||
| 91 | +**Independent Test**: Collision entre deux fichiers de même modèle, numéros de série différents et exploitables → résolution automatique ; numéros de série absents/identiques → groupe renvoyé dans `a_etiqueter`, résolu ensuite par `assign_manual_source`. | ||
| 92 | + | ||
| 93 | +### Tests for User Story 3 | ||
| 94 | + | ||
| 95 | +- [ ] T017 [P] [US3] Test unitaire : `resolve_collision` avec même modèle, numéros de série différents et exploitables → résolution automatique, dans `test_resolve_collision.py` | ||
| 96 | +- [ ] T018 [P] [US3] Test unitaire : même modèle, numéro de série absent/identique/non exploitable → groupe dans `a_etiqueter`, jamais résolu arbitrairement, dans `test_resolve_collision.py` | ||
| 97 | +- [ ] T019 [US3] Test unitaire de `assign_manual_source` (crée une nouvelle entrée si `boitier_id=None`, réutilise sinon) dans `test_resolve_collision.py` | ||
| 98 | +- [ ] T020 [US3] Test unitaire de `list_boitiers`/`rename_boitier` (consultation et modification à tout moment, FR-007) dans `packages/regine-core/tests/unit/test_camera_profile_db.py` | ||
| 99 | + | ||
| 100 | +### Implementation for User Story 3 | ||
| 101 | + | ||
| 102 | +- [ ] T021 [US3] Étendre `resolve_collision` : repli sur `numero_serie` au sein d'un même `modele` (FR-004), dans `resolve.py` (dépend de T015) | ||
| 103 | +- [ ] T022 [US3] Implémenter la construction du groupe `a_etiqueter` quand ni le modèle ni le numéro de série ne suffisent (FR-005), dans `resolve.py` | ||
| 104 | +- [ ] T023 [US3] Implémenter `assign_manual_source(fichiers, boitier_id=None) -> int` dans `packages/regine-core/src/regine_core/camera_profile/resolve.py` (dépend de T009) | ||
| 105 | + | ||
| 106 | +**Checkpoint**: Les trois user stories fonctionnelles indépendamment. | ||
| 107 | + | ||
| 108 | +--- | ||
| 109 | + | ||
| 110 | +## Phase 6: Polish & Cross-Cutting Concerns | ||
| 111 | + | ||
| 112 | +- [ ] T024 [P] Exécuter manuellement les 5 scénarios de `specs/002-profil-boitiers-optionnel/quickstart.md` et consigner le résultat | ||
| 113 | +- [ ] T025 [P] `ruff check --fix` sur `packages/regine-core` | ||
| 114 | +- [ ] T026 Mettre à jour `contracts/regine-core-api.md` si l'implémentation révèle un écart avec les signatures documentées | ||
| 115 | + | ||
| 116 | +--- | ||
| 117 | + | ||
| 118 | +## Dependencies & Execution Order | ||
| 119 | + | ||
| 120 | +### Phase Dependencies | ||
| 121 | + | ||
| 122 | +- **Setup (Phase 1)** : dépend de `specs/004-categorisation-dossiers/tasks.md` (packages, `config.db`) déjà exécutées. | ||
| 123 | +- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories. | ||
| 124 | +- **User Stories (Phase 3–5)** : dépendent toutes de Foundational. | ||
| 125 | + - US1 (P1) : aucune dépendance sur une autre user story. | ||
| 126 | + - US2 (P2) : étend `resolve.py` initié en US1 (documentation du contrat) mais reste testable indépendamment. | ||
| 127 | + - US3 (P3) : étend `resolve_collision` de US2 (T015 → T021/T022) ; livrable séparément. | ||
| 128 | +- **Polish (Phase 6)** : dépend des user stories livrées (au minimum US1+US2). | ||
| 129 | + | ||
| 130 | +### Parallel Opportunities | ||
| 131 | + | ||
| 132 | +- T002–T004 (Phase 1) : fichiers/vérifications distincts. | ||
| 133 | +- T007, T009 (Phase 2) : parallélisables une fois T006/T008 terminés respectivement. | ||
| 134 | +- Tests marqués [P] au sein de chaque story. | ||
| 135 | + | ||
| 136 | +--- | ||
| 137 | + | ||
| 138 | +## Parallel Example: User Story 3 | ||
| 139 | + | ||
| 140 | +```bash | ||
| 141 | +Task: "Test unitaire repli numéro de série dans packages/regine-core/tests/unit/test_resolve_collision.py" | ||
| 142 | +Task: "Test unitaire groupe a_etiqueter dans packages/regine-core/tests/unit/test_resolve_collision.py" | ||
| 143 | +Task: "Test unitaire list_boitiers/rename_boitier dans packages/regine-core/tests/unit/test_camera_profile_db.py" | ||
| 144 | +``` | ||
| 145 | + | ||
| 146 | +--- | ||
| 147 | + | ||
| 148 | +## Implementation Strategy | ||
| 149 | + | ||
| 150 | +### MVP First (User Story 1 uniquement) | ||
| 151 | + | ||
| 152 | +1. Setup + Foundational. | ||
| 153 | +2. US1 : garantir l'absence de friction par défaut (documentation de contrat + test de non-régression). | ||
| 154 | +3. **Arrêt et validation** : Scénario 1 de `quickstart.md`. | ||
| 155 | + | ||
| 156 | +### Livraison incrémentale | ||
| 157 | + | ||
| 158 | +1. Setup + Foundational → socle prêt (lecture EXIF, table `boitiers`). | ||
| 159 | +2. US1 → aucune question par défaut → valider Scénario 1. | ||
| 160 | +3. US2 → résolution par modèle → valider Scénarios 2 et 5. | ||
| 161 | +4. US3 → repli numéro de série + étiquetage manuel → valider Scénarios 3 et 4. | ||
| 162 | + | ||
| 163 | +## Notes | ||
| 164 | + | ||
| 165 | +- [P] = fichiers différents, sans dépendance non résolue. | ||
| 166 | +- Ce module n'a pas de surface CLI propre (cf. `plan.md`) : son intégration réelle relève de `specs/001-import-photos` (T034 de son propre tasks.md) et de `specs/003-config-contexte-travail` (écran de nommage des boîtiers). | ||
| 167 | +- Ne pas recréer le squelette monorepo ni `regine_core.config.db` : à étendre (T008), jamais dupliquer. | ||
added
specs/005-checkout-reconciliation/tasks.md +238 -0 | new file mode 100644 | ||
| @@ -0,0 +1,238 @@ | ||
| 1 | +--- | |
| 2 | + | |
| 3 | +description: "Task list for feature implementation" | |
| 4 | +--- | |
| 5 | + | |
| 6 | +# Tasks: Checkout et réconciliation d'un dossier de l'archive | |
| 7 | + | |
| 8 | +**Input**: Design documents from `/specs/005-checkout-reconciliation/` | |
| 9 | +**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md | |
| 10 | + | |
| 11 | +**Tests**: Incluses — `plan.md` § Project Structure nomme explicitement les fichiers de test attendus, ce qui vaut demande explicite au sens des règles de génération. | |
| 12 | + | |
| 13 | +**Organisation**: Tâches groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune. | |
| 14 | + | |
| 15 | +## Dépendances inter-specs | |
| 16 | + | |
| 17 | +| Dépendance | Fournit | État | | |
| 18 | +|---|---|---| | |
| 19 | +| `specs/004-categorisation-dossiers/tasks.md` | Squelette du monorepo (`packages/regine-core`, `packages/regine-cli`) | tasks.md existe — **à exécuter avant la Phase 1** si ce n'est pas déjà fait | | |
| 20 | +| `specs/002-profil-boitiers-optionnel/tasks.md` | `packages/regine-core/src/regine_core/metadata/exif.py` (wrapper `exiftool -stay_open`, à étendre ici) | tasks.md existe — **à exécuter avant la Phase 2** | | |
| 21 | + | |
| 22 | +Ce que ce module débloque une fois implémenté : `specs/001-import-photos` (T033, fusion vers dossier déjà archivé) et `specs/004-categorisation-dossiers` (T024b, recatégorisation a posteriori) — cf. Phase 9. | |
| 23 | + | |
| 24 | +## Phase 1: Setup | |
| 25 | + | |
| 26 | +- [ ] T001 Vérifier que `packages/regine-core` (specs/004) et `packages/regine-core/src/regine_core/metadata/exif.py` (specs/002) existent déjà ; si absents, exécuter d'abord ces deux fichiers de tâches | |
| 27 | +- [ ] T002 [P] Créer `packages/regine-core/src/regine_core/integrity/__init__.py` | |
| 28 | +- [ ] T003 [P] Créer `packages/regine-core/src/regine_core/archive/__init__.py` | |
| 29 | +- [ ] T004 [P] Créer `packages/regine-cli/src/regine_cli/archive_cmd.py` (squelette des commandes `regine checkout`/`regine reconcile`, sans logique) | |
| 30 | + | |
| 31 | +--- | |
| 32 | + | |
| 33 | +## Phase 2: Foundational (Blocking Prerequisites) | |
| 34 | + | |
| 35 | +**Purpose**: Infrastructure partagée requise par les six user stories. | |
| 36 | + | |
| 37 | +**⚠️ CRITICAL**: Aucune user story ne peut être implémentée avant la fin de cette phase. | |
| 38 | + | |
| 39 | +- [ ] T005 Étendre `packages/regine-core/src/regine_core/metadata/exif.py` avec `read_image_data_hash(chemin) -> str | None` (`exiftool -api ImageHashType=SHA256 -ImageDataHash`, cf. `research.md` § 5) | |
| 40 | +- [ ] T006 [P] Implémenter `hash_fichier_entier(chemin) -> str` (SHA-256 stdlib) dans `packages/regine-core/src/regine_core/integrity/hash.py` | |
| 41 | +- [ ] T007 Implémenter `hash_image_only(chemin) -> str | None` dans `integrity/hash.py` (délègue à `read_image_data_hash` pour DNG/TIFF/JPEG uniquement, dépend de T005) | |
| 42 | +- [ ] T008 [P] Test unitaire du hash à deux niveaux (RAW propriétaire → un seul hash ; DNG/TIFF/JPEG → deux hash) dans `packages/regine-core/tests/unit/test_hash_deux_niveaux.py` | |
| 43 | +- [ ] T009 Implémenter `est_maitre_modifie(format, ref, actuel) -> bool` dans `packages/regine-core/src/regine_core/integrity/anomalie.py` (FR-007/008, Principe I — bascule fichier entier / image-only selon le format, dépend de T006/T007) | |
| 44 | +- [ ] T010 Implémenter `manifest.ouvrir_ou_creer(dossier) -> ManifestHandle` dans `packages/regine-core/src/regine_core/archive/manifest.py` : table `fichiers`, table `verrou`, `PRAGMA user_version` (`structurel*1000+additif`, cf. `research.md` § 2), `PRAGMA application_id`, refus explicite si version structurelle non supportée | |
| 45 | +- [ ] T011 [P] Test unitaire du versionnement de schéma (tolérance additive, refus structurel, réouverture idempotente sans perte) dans `packages/regine-core/tests/unit/test_manifest_versioning.py` | |
| 46 | +- [ ] T012 Implémenter `verrou.poser/verifier/lever(manifest)` dans `packages/regine-core/src/regine_core/archive/verrou.py` (FR-003/004/016, dépend de T010) | |
| 47 | +- [ ] T013 [P] Test unitaire du verrou (pose, refus de double pose, levée après réconciliation) dans `packages/regine-core/tests/unit/test_verrou.py` | |
| 48 | + | |
| 49 | +**Checkpoint**: Fondations prêtes — les phases User Story peuvent commencer. | |
| 50 | + | |
| 51 | +--- | |
| 52 | + | |
| 53 | +## Phase 3: User Story 1 - Checkout d'un dossier pour édition (Priority: P1) 🎯 MVP | |
| 54 | + | |
| 55 | +**Goal**: Copier un dossier complet (parent + sous-dossiers) vers un espace de travail local avec manifeste de référence et verrouillage (FR-001/002/003/004/005/015). | |
| 56 | + | |
| 57 | +**Independent Test**: Checkout d'un dossier archivé existant → copie locale identique, manifeste complet enregistré, dossier marqué verrouillé. | |
| 58 | + | |
| 59 | +### Tests for User Story 1 | |
| 60 | + | |
| 61 | +- [ ] T014 [P] [US1] Test unitaire : checkout copie un dossier parent avec tous ses sous-dossiers ensemble (jamais un sous-dossier isolé), chaque fichier vérifié par empreinte, dans `packages/regine-core/tests/unit/test_checkout.py` | |
| 62 | +- [ ] T015 [US1] Test d'intégration (partiel) : checkout crée un manifeste complet et pose le verrou, dans `packages/regine-core/tests/integration/test_cycle_checkout_reconciliation.py` | |
| 63 | + | |
| 64 | +### Implementation for User Story 1 | |
| 65 | + | |
| 66 | +- [ ] T016 [US1] Implémenter `checkout(dossier_archive, dest_locale, formats=None) -> ManifestSnapshot` dans `packages/regine-core/src/regine_core/archive/checkout.py` (FR-001/002/015 ; le paramètre `formats` est géré en Phase 8, ignoré ici) (dépend de T010) | |
| 67 | +- [ ] T017 [US1] Intégrer la pose du verrou avant la copie dans `checkout()`, lever une exception dédiée si déjà verrouillé (FR-003/004, dépend de T012) | |
| 68 | +- [ ] T018 [US1] Orchestrer `regine checkout <dossier>` dans `packages/regine-cli/src/regine_cli/archive_cmd.py` | |
| 69 | + | |
| 70 | +**Checkpoint**: User Story 1 fonctionnelle et testable indépendamment (MVP). | |
| 71 | + | |
| 72 | +--- | |
| 73 | + | |
| 74 | +## Phase 4: User Story 2 - Réconciliation normale et point avant archive (Priority: P2) | |
| 75 | + | |
| 76 | +**Goal**: Classifier les changements normaux, présenter un récapitulatif, écrire uniquement après confirmation, vérifier les transferts, lever le verrou (FR-006/011/012/013/015/016). | |
| 77 | + | |
| 78 | +**Independent Test**: Checkout, modification d'un sidecar seul, réconciliation → classé normal, résumé affiché, rien écrit sans confirmation, verrou levé après confirmation. | |
| 79 | + | |
| 80 | +### Tests for User Story 2 | |
| 81 | + | |
| 82 | +- [ ] T019 [P] [US2] Test unitaire de classification "normal" (fichier maître inchangé + sidecar nouveau/modifié) dans `packages/regine-core/tests/unit/test_reconciliation_classification.py` | |
| 83 | +- [ ] T020 [US2] Test d'intégration complétant `test_cycle_checkout_reconciliation.py` : édition sidecar → réconciliation → résumé → confirmation → réarchivage → verrou levé | |
| 84 | + | |
| 85 | +### Implementation for User Story 2 | |
| 86 | + | |
| 87 | +- [ ] T021 [US2] Implémenter `comparer(manifest, copie_locale) -> RapportReconciliation` dans `packages/regine-core/src/regine_core/archive/reconciliation.py` : construit les index hash→chemin, classe "normal" (dépend de T007/T009) | |
| 88 | +- [ ] T022 [US2] Implémenter `archiver(dossier_archive, rapport, decisions) -> None` dans `reconciliation.py` : écrit les changements confirmés, vérifie chaque transfert (FR-015), met à jour le manifeste, lève le verrou une fois toutes les écritures terminées (FR-016, dépend de T012/T021) | |
| 89 | +- [ ] T023 [US2] Orchestrer `regine reconcile <dossier>` dans `archive_cmd.py` : affichage du point avant archive et confirmation globale avant écriture (FR-013) | |
| 90 | + | |
| 91 | +**Checkpoint**: User Stories 1 ET 2 fonctionnelles indépendamment. | |
| 92 | + | |
| 93 | +--- | |
| 94 | + | |
| 95 | +## Phase 5: User Story 3 - Détection d'une anomalie sur un fichier maître (Priority: P3) | |
| 96 | + | |
| 97 | +**Goal**: Signaler un fichier maître modifié comme anomalie (jamais silencieusement réarchivé), avec le hash pertinent selon le format (FR-007/008). | |
| 98 | + | |
| 99 | +**Independent Test**: RAW modifié dans la copie de travail → anomalie signalée ; DNG modifié en métadonnées seules → pas d'anomalie ; DNG modifié en pixels → anomalie. | |
| 100 | + | |
| 101 | +### Tests for User Story 3 | |
| 102 | + | |
| 103 | +- [ ] T024 [P] [US3] Test unitaire anomalie sur RAW propriétaire (hash fichier entier changé) dans `test_reconciliation_classification.py` | |
| 104 | +- [ ] T025 [P] [US3] Test unitaire DNG/TIFF/JPEG : édition de métadonnées seule → pas d'anomalie (hash image-only inchangé) ; édition des pixels → anomalie, dans `test_reconciliation_classification.py` | |
| 105 | +- [ ] T026 [US3] Test d'intégration : décision explicite (confirmer/restaurer) par anomalie, sans bloquer l'archivage des changements normaux déjà confirmés (FR-014), complétant `test_cycle_checkout_reconciliation.py` | |
| 106 | + | |
| 107 | +### Implementation for User Story 3 | |
| 108 | + | |
| 109 | +- [ ] T027 [US3] Étendre `comparer()` : classification "anomalie" via `est_maitre_modifie` (dépend de T009/T021) | |
| 110 | +- [ ] T028 [US3] Implémenter la résolution par anomalie (confirmer malgré tout / restaurer depuis l'archive) dans `reconciliation.py` et `archive_cmd.py`, sans bloquer les autres changements déjà confirmés (FR-014) | |
| 111 | + | |
| 112 | +**Checkpoint**: User Stories 1, 2 et 3 fonctionnelles. | |
| 113 | + | |
| 114 | +--- | |
| 115 | + | |
| 116 | +## Phase 6: User Story 4 - Détection de renommage/déplacement par le contenu (Priority: P4) | |
| 117 | + | |
| 118 | +**Goal**: Reconnaître un renommage, une promotion à la racine, ou un déplacement de dossier entier comme un déplacement à contenu inchangé (FR-009/010). | |
| 119 | + | |
| 120 | +**Independent Test**: Fichier renommé/déplacé vers la racine → classé déplacement ; dossier entier déplacé d'une année vers une catégorie → classé déplacement. | |
| 121 | + | |
| 122 | +### Tests for User Story 4 | |
| 123 | + | |
| 124 | +- [ ] T029 [P] [US4] Test unitaire renommage/promotion détecté par contenu dans `test_reconciliation_classification.py` | |
| 125 | +- [ ] T030 [US4] Test unitaire déplacement d'un dossier entier entre répertoires racine (année→catégorie) détecté par contenu, dans `test_reconciliation_classification.py` — **débloque `specs/004-categorisation-dossiers` T024b** | |
| 126 | + | |
| 127 | +### Implementation for User Story 4 | |
| 128 | + | |
| 129 | +- [ ] T031 [US4] Étendre `comparer()` : classification "deplacement" via l'index hash→chemin (manifeste vs copie locale), couvrant indifféremment un fichier ou un dossier entier (FR-009/010, dépend de T021) | |
| 130 | + | |
| 131 | +**Checkpoint**: User Stories 1 à 4 fonctionnelles. | |
| 132 | + | |
| 133 | +--- | |
| 134 | + | |
| 135 | +## Phase 7: User Story 5 - Refus d'un double-checkout concurrent (Priority: P5) | |
| 136 | + | |
| 137 | +**Goal**: Un second checkout sur un dossier déjà verrouillé est refusé explicitement (FR-004). | |
| 138 | + | |
| 139 | +**Independent Test**: Checkout, puis second checkout du même dossier avant réconciliation → refusé avec message clair ; accepté après réconciliation. | |
| 140 | + | |
| 141 | +### Tests for User Story 5 | |
| 142 | + | |
| 143 | +- [ ] T032 [US5] Test d'intégration bout-en-bout : second `regine checkout` refusé pendant qu'un premier est en cours (code de sortie non-zéro, message explicite), accepté après réconciliation du premier, dans `packages/regine-core/tests/integration/test_double_checkout.py` | |
| 144 | + | |
| 145 | +### Implementation for User Story 5 | |
| 146 | + | |
| 147 | +- [ ] T033 [US5] Vérifier/compléter le message d'erreur explicite de `archive_cmd.py` pour le cas de verrou déjà posé (comportement déjà couvert par T017 ; cette tâche ne fait qu'exposer un message clair côté CLI) | |
| 148 | + | |
| 149 | +**Checkpoint**: User Stories 1 à 5 fonctionnelles. | |
| 150 | + | |
| 151 | +--- | |
| 152 | + | |
| 153 | +## Phase 8: User Story 6 - Checkout partiel par format (Priority: P6) | |
| 154 | + | |
| 155 | +**Goal**: Restreindre un checkout à certains formats sans que les fichiers exclus ne soient jamais signalés comme anormaux (FR-019/020). | |
| 156 | + | |
| 157 | +**Independent Test**: Checkout `--formats jpeg` → seuls JPEG+racine copiés ; réconciliation ultérieure ne signale aucun RAW comme supprimé. | |
| 158 | + | |
| 159 | +### Tests for User Story 6 | |
| 160 | + | |
| 161 | +- [ ] T034 [P] [US6] Test d'intégration checkout partiel (`formats=["jpeg"]` → seuls JPEG et racine copiés) dans `packages/regine-core/tests/integration/test_checkout_partiel.py` | |
| 162 | +- [ ] T035 [US6] Test d'intégration : réconciliation après checkout partiel ne signale pas les fichiers exclus comme supprimés (FR-020), complétant `test_checkout_partiel.py` | |
| 163 | + | |
| 164 | +### Implementation for User Story 6 | |
| 165 | + | |
| 166 | +- [ ] T036 [US6] Étendre `checkout()` : paramètre `formats`, copie restreinte aux sous-répertoires demandés plus la racine (FR-019, dépend de T016) | |
| 167 | +- [ ] T037 [US6] Étendre `comparer()`/le manifeste pour exclure les fichiers hors du périmètre d'un checkout partiel de la détection de suppression (FR-020, dépend de T021) | |
| 168 | + | |
| 169 | +**Checkpoint**: Les six user stories fonctionnelles. | |
| 170 | + | |
| 171 | +--- | |
| 172 | + | |
| 173 | +## Phase 9: Polish & Cross-Cutting Concerns | |
| 174 | + | |
| 175 | +- [ ] T038 [P] Exécuter manuellement les 5 scénarios de `specs/005-checkout-reconciliation/quickstart.md` et consigner le résultat | |
| 176 | +- [ ] T039 [P] `ruff check --fix` sur `packages/regine-core` et `packages/regine-cli` | |
| 177 | +- [ ] T040 Mettre à jour `contracts/regine-core-api.md` si écart d'implémentation ; puis lever les dépendances documentées dans `specs/001-import-photos/tasks.md` (T033) et `specs/004-categorisation-dossiers/tasks.md` (T024b), désormais implémentables | |
| 178 | + | |
| 179 | +--- | |
| 180 | + | |
| 181 | +## Dependencies & Execution Order | |
| 182 | + | |
| 183 | +### Phase Dependencies | |
| 184 | + | |
| 185 | +- **Setup (Phase 1)** : dépend de `specs/004` et `specs/002` (tasks.md déjà exécutées). | |
| 186 | +- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories. | |
| 187 | +- **User Stories (Phase 3–8)** : dépendent toutes de Foundational. | |
| 188 | + - US1 (P1) : aucune dépendance sur une autre user story de ce fichier. | |
| 189 | + - US2 (P2) : dépend de `checkout()` (US1) pour ses tests d'intégration, mais `comparer()`/`archiver()` sont des fonctions distinctes. | |
| 190 | + - US3 (P3) : étend `comparer()` de US2 (T021 → T027). | |
| 191 | + - US4 (P4) : étend `comparer()` de US2 (T021 → T031), indépendante de US3. | |
| 192 | + - US5 (P5) : vérifie un comportement déjà posé en US1 (T012/T017) — pas de nouvelle logique métier. | |
| 193 | + - US6 (P6) : étend `checkout()` (US1) et `comparer()` (US2). | |
| 194 | +- **Polish (Phase 9)** : dépend des user stories livrées (au minimum US1+US2). | |
| 195 | + | |
| 196 | +### Parallel Opportunities | |
| 197 | + | |
| 198 | +- T002–T004 (Phase 1) : fichiers distincts. | |
| 199 | +- T006, T008 (Phase 2) : parallélisables une fois T005 terminé. | |
| 200 | +- T011, T013 (Phase 2) : parallélisables une fois T010/T012 terminés respectivement. | |
| 201 | +- Tests marqués [P] au sein de chaque story. | |
| 202 | + | |
| 203 | +--- | |
| 204 | + | |
| 205 | +## Parallel Example: User Story 3 | |
| 206 | + | |
| 207 | +```bash | |
| 208 | +Task: "Test unitaire anomalie RAW propriétaire dans packages/regine-core/tests/unit/test_reconciliation_classification.py" | |
| 209 | +Task: "Test unitaire anomalie DNG/TIFF/JPEG (métadonnées vs pixels) dans packages/regine-core/tests/unit/test_reconciliation_classification.py" | |
| 210 | +``` | |
| 211 | + | |
| 212 | +--- | |
| 213 | + | |
| 214 | +## Implementation Strategy | |
| 215 | + | |
| 216 | +### MVP First (User Story 1 uniquement) | |
| 217 | + | |
| 218 | +1. Setup + Foundational (hash à deux niveaux, manifeste versionné, verrou). | |
| 219 | +2. US1 : checkout complet avec manifeste et verrouillage. | |
| 220 | +3. **Arrêt et validation** : Scénario 1 de `quickstart.md`. | |
| 221 | + | |
| 222 | +### Livraison incrémentale | |
| 223 | + | |
| 224 | +1. Setup + Foundational → socle prêt. | |
| 225 | +2. US1 → checkout (MVP) → valider Scénario 1. | |
| 226 | +3. US2 → réconciliation normale → valider Scénario 2. | |
| 227 | +4. US3 → détection d'anomalie → valider Scénario 3. | |
| 228 | +5. US4 → détection de déplacement → valider Scénario 4 → débloque `specs/004` T024b. | |
| 229 | +6. US5 → refus de double-checkout → couvert par les fondations de US1, tests dédiés. | |
| 230 | +7. US6 → checkout partiel → valider Scénario 5. | |
| 231 | +8. Une fois Phase 9 (T040) terminée → débloquer `specs/001-import-photos` T033. | |
| 232 | + | |
| 233 | +## Notes | |
| 234 | + | |
| 235 | +- [P] = fichiers différents, sans dépendance non résolue. | |
| 236 | +- Ce module complète les deux derniers modules de la bibliothèque centrale nommés dans l'architecture du projet (`integrity`, `archive`). | |
| 237 | +- Ne pas recréer le squelette monorepo ni `regine_core.metadata.exif` : à étendre (T005), jamais dupliquer. | |
| 238 | +- Le verrouillage reste une protection de bonne foi entre instances de Régine (cf. `research.md` § 3) — aucune tâche de ce fichier ne doit tenter de construire une garantie distribuée plus forte, hors périmètre assumé. | |
| new file mode 100644 | |||
| @@ -0,0 +1,238 @@ | |||
| 1 | +--- | ||
| 2 | + | ||
| 3 | +description: "Task list for feature implementation" | ||
| 4 | +--- | ||
| 5 | + | ||
| 6 | +# Tasks: Checkout et réconciliation d'un dossier de l'archive | ||
| 7 | + | ||
| 8 | +**Input**: Design documents from `/specs/005-checkout-reconciliation/` | ||
| 9 | +**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md | ||
| 10 | + | ||
| 11 | +**Tests**: Incluses — `plan.md` § Project Structure nomme explicitement les fichiers de test attendus, ce qui vaut demande explicite au sens des règles de génération. | ||
| 12 | + | ||
| 13 | +**Organisation**: Tâches groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune. | ||
| 14 | + | ||
| 15 | +## Dépendances inter-specs | ||
| 16 | + | ||
| 17 | +| Dépendance | Fournit | État | | ||
| 18 | +|---|---|---| | ||
| 19 | +| `specs/004-categorisation-dossiers/tasks.md` | Squelette du monorepo (`packages/regine-core`, `packages/regine-cli`) | tasks.md existe — **à exécuter avant la Phase 1** si ce n'est pas déjà fait | | ||
| 20 | +| `specs/002-profil-boitiers-optionnel/tasks.md` | `packages/regine-core/src/regine_core/metadata/exif.py` (wrapper `exiftool -stay_open`, à étendre ici) | tasks.md existe — **à exécuter avant la Phase 2** | | ||
| 21 | + | ||
| 22 | +Ce que ce module débloque une fois implémenté : `specs/001-import-photos` (T033, fusion vers dossier déjà archivé) et `specs/004-categorisation-dossiers` (T024b, recatégorisation a posteriori) — cf. Phase 9. | ||
| 23 | + | ||
| 24 | +## Phase 1: Setup | ||
| 25 | + | ||
| 26 | +- [ ] T001 Vérifier que `packages/regine-core` (specs/004) et `packages/regine-core/src/regine_core/metadata/exif.py` (specs/002) existent déjà ; si absents, exécuter d'abord ces deux fichiers de tâches | ||
| 27 | +- [ ] T002 [P] Créer `packages/regine-core/src/regine_core/integrity/__init__.py` | ||
| 28 | +- [ ] T003 [P] Créer `packages/regine-core/src/regine_core/archive/__init__.py` | ||
| 29 | +- [ ] T004 [P] Créer `packages/regine-cli/src/regine_cli/archive_cmd.py` (squelette des commandes `regine checkout`/`regine reconcile`, sans logique) | ||
| 30 | + | ||
| 31 | +--- | ||
| 32 | + | ||
| 33 | +## Phase 2: Foundational (Blocking Prerequisites) | ||
| 34 | + | ||
| 35 | +**Purpose**: Infrastructure partagée requise par les six user stories. | ||
| 36 | + | ||
| 37 | +**⚠️ CRITICAL**: Aucune user story ne peut être implémentée avant la fin de cette phase. | ||
| 38 | + | ||
| 39 | +- [ ] T005 Étendre `packages/regine-core/src/regine_core/metadata/exif.py` avec `read_image_data_hash(chemin) -> str | None` (`exiftool -api ImageHashType=SHA256 -ImageDataHash`, cf. `research.md` § 5) | ||
| 40 | +- [ ] T006 [P] Implémenter `hash_fichier_entier(chemin) -> str` (SHA-256 stdlib) dans `packages/regine-core/src/regine_core/integrity/hash.py` | ||
| 41 | +- [ ] T007 Implémenter `hash_image_only(chemin) -> str | None` dans `integrity/hash.py` (délègue à `read_image_data_hash` pour DNG/TIFF/JPEG uniquement, dépend de T005) | ||
| 42 | +- [ ] T008 [P] Test unitaire du hash à deux niveaux (RAW propriétaire → un seul hash ; DNG/TIFF/JPEG → deux hash) dans `packages/regine-core/tests/unit/test_hash_deux_niveaux.py` | ||
| 43 | +- [ ] T009 Implémenter `est_maitre_modifie(format, ref, actuel) -> bool` dans `packages/regine-core/src/regine_core/integrity/anomalie.py` (FR-007/008, Principe I — bascule fichier entier / image-only selon le format, dépend de T006/T007) | ||
| 44 | +- [ ] T010 Implémenter `manifest.ouvrir_ou_creer(dossier) -> ManifestHandle` dans `packages/regine-core/src/regine_core/archive/manifest.py` : table `fichiers`, table `verrou`, `PRAGMA user_version` (`structurel*1000+additif`, cf. `research.md` § 2), `PRAGMA application_id`, refus explicite si version structurelle non supportée | ||
| 45 | +- [ ] T011 [P] Test unitaire du versionnement de schéma (tolérance additive, refus structurel, réouverture idempotente sans perte) dans `packages/regine-core/tests/unit/test_manifest_versioning.py` | ||
| 46 | +- [ ] T012 Implémenter `verrou.poser/verifier/lever(manifest)` dans `packages/regine-core/src/regine_core/archive/verrou.py` (FR-003/004/016, dépend de T010) | ||
| 47 | +- [ ] T013 [P] Test unitaire du verrou (pose, refus de double pose, levée après réconciliation) dans `packages/regine-core/tests/unit/test_verrou.py` | ||
| 48 | + | ||
| 49 | +**Checkpoint**: Fondations prêtes — les phases User Story peuvent commencer. | ||
| 50 | + | ||
| 51 | +--- | ||
| 52 | + | ||
| 53 | +## Phase 3: User Story 1 - Checkout d'un dossier pour édition (Priority: P1) 🎯 MVP | ||
| 54 | + | ||
| 55 | +**Goal**: Copier un dossier complet (parent + sous-dossiers) vers un espace de travail local avec manifeste de référence et verrouillage (FR-001/002/003/004/005/015). | ||
| 56 | + | ||
| 57 | +**Independent Test**: Checkout d'un dossier archivé existant → copie locale identique, manifeste complet enregistré, dossier marqué verrouillé. | ||
| 58 | + | ||
| 59 | +### Tests for User Story 1 | ||
| 60 | + | ||
| 61 | +- [ ] T014 [P] [US1] Test unitaire : checkout copie un dossier parent avec tous ses sous-dossiers ensemble (jamais un sous-dossier isolé), chaque fichier vérifié par empreinte, dans `packages/regine-core/tests/unit/test_checkout.py` | ||
| 62 | +- [ ] T015 [US1] Test d'intégration (partiel) : checkout crée un manifeste complet et pose le verrou, dans `packages/regine-core/tests/integration/test_cycle_checkout_reconciliation.py` | ||
| 63 | + | ||
| 64 | +### Implementation for User Story 1 | ||
| 65 | + | ||
| 66 | +- [ ] T016 [US1] Implémenter `checkout(dossier_archive, dest_locale, formats=None) -> ManifestSnapshot` dans `packages/regine-core/src/regine_core/archive/checkout.py` (FR-001/002/015 ; le paramètre `formats` est géré en Phase 8, ignoré ici) (dépend de T010) | ||
| 67 | +- [ ] T017 [US1] Intégrer la pose du verrou avant la copie dans `checkout()`, lever une exception dédiée si déjà verrouillé (FR-003/004, dépend de T012) | ||
| 68 | +- [ ] T018 [US1] Orchestrer `regine checkout <dossier>` dans `packages/regine-cli/src/regine_cli/archive_cmd.py` | ||
| 69 | + | ||
| 70 | +**Checkpoint**: User Story 1 fonctionnelle et testable indépendamment (MVP). | ||
| 71 | + | ||
| 72 | +--- | ||
| 73 | + | ||
| 74 | +## Phase 4: User Story 2 - Réconciliation normale et point avant archive (Priority: P2) | ||
| 75 | + | ||
| 76 | +**Goal**: Classifier les changements normaux, présenter un récapitulatif, écrire uniquement après confirmation, vérifier les transferts, lever le verrou (FR-006/011/012/013/015/016). | ||
| 77 | + | ||
| 78 | +**Independent Test**: Checkout, modification d'un sidecar seul, réconciliation → classé normal, résumé affiché, rien écrit sans confirmation, verrou levé après confirmation. | ||
| 79 | + | ||
| 80 | +### Tests for User Story 2 | ||
| 81 | + | ||
| 82 | +- [ ] T019 [P] [US2] Test unitaire de classification "normal" (fichier maître inchangé + sidecar nouveau/modifié) dans `packages/regine-core/tests/unit/test_reconciliation_classification.py` | ||
| 83 | +- [ ] T020 [US2] Test d'intégration complétant `test_cycle_checkout_reconciliation.py` : édition sidecar → réconciliation → résumé → confirmation → réarchivage → verrou levé | ||
| 84 | + | ||
| 85 | +### Implementation for User Story 2 | ||
| 86 | + | ||
| 87 | +- [ ] T021 [US2] Implémenter `comparer(manifest, copie_locale) -> RapportReconciliation` dans `packages/regine-core/src/regine_core/archive/reconciliation.py` : construit les index hash→chemin, classe "normal" (dépend de T007/T009) | ||
| 88 | +- [ ] T022 [US2] Implémenter `archiver(dossier_archive, rapport, decisions) -> None` dans `reconciliation.py` : écrit les changements confirmés, vérifie chaque transfert (FR-015), met à jour le manifeste, lève le verrou une fois toutes les écritures terminées (FR-016, dépend de T012/T021) | ||
| 89 | +- [ ] T023 [US2] Orchestrer `regine reconcile <dossier>` dans `archive_cmd.py` : affichage du point avant archive et confirmation globale avant écriture (FR-013) | ||
| 90 | + | ||
| 91 | +**Checkpoint**: User Stories 1 ET 2 fonctionnelles indépendamment. | ||
| 92 | + | ||
| 93 | +--- | ||
| 94 | + | ||
| 95 | +## Phase 5: User Story 3 - Détection d'une anomalie sur un fichier maître (Priority: P3) | ||
| 96 | + | ||
| 97 | +**Goal**: Signaler un fichier maître modifié comme anomalie (jamais silencieusement réarchivé), avec le hash pertinent selon le format (FR-007/008). | ||
| 98 | + | ||
| 99 | +**Independent Test**: RAW modifié dans la copie de travail → anomalie signalée ; DNG modifié en métadonnées seules → pas d'anomalie ; DNG modifié en pixels → anomalie. | ||
| 100 | + | ||
| 101 | +### Tests for User Story 3 | ||
| 102 | + | ||
| 103 | +- [ ] T024 [P] [US3] Test unitaire anomalie sur RAW propriétaire (hash fichier entier changé) dans `test_reconciliation_classification.py` | ||
| 104 | +- [ ] T025 [P] [US3] Test unitaire DNG/TIFF/JPEG : édition de métadonnées seule → pas d'anomalie (hash image-only inchangé) ; édition des pixels → anomalie, dans `test_reconciliation_classification.py` | ||
| 105 | +- [ ] T026 [US3] Test d'intégration : décision explicite (confirmer/restaurer) par anomalie, sans bloquer l'archivage des changements normaux déjà confirmés (FR-014), complétant `test_cycle_checkout_reconciliation.py` | ||
| 106 | + | ||
| 107 | +### Implementation for User Story 3 | ||
| 108 | + | ||
| 109 | +- [ ] T027 [US3] Étendre `comparer()` : classification "anomalie" via `est_maitre_modifie` (dépend de T009/T021) | ||
| 110 | +- [ ] T028 [US3] Implémenter la résolution par anomalie (confirmer malgré tout / restaurer depuis l'archive) dans `reconciliation.py` et `archive_cmd.py`, sans bloquer les autres changements déjà confirmés (FR-014) | ||
| 111 | + | ||
| 112 | +**Checkpoint**: User Stories 1, 2 et 3 fonctionnelles. | ||
| 113 | + | ||
| 114 | +--- | ||
| 115 | + | ||
| 116 | +## Phase 6: User Story 4 - Détection de renommage/déplacement par le contenu (Priority: P4) | ||
| 117 | + | ||
| 118 | +**Goal**: Reconnaître un renommage, une promotion à la racine, ou un déplacement de dossier entier comme un déplacement à contenu inchangé (FR-009/010). | ||
| 119 | + | ||
| 120 | +**Independent Test**: Fichier renommé/déplacé vers la racine → classé déplacement ; dossier entier déplacé d'une année vers une catégorie → classé déplacement. | ||
| 121 | + | ||
| 122 | +### Tests for User Story 4 | ||
| 123 | + | ||
| 124 | +- [ ] T029 [P] [US4] Test unitaire renommage/promotion détecté par contenu dans `test_reconciliation_classification.py` | ||
| 125 | +- [ ] T030 [US4] Test unitaire déplacement d'un dossier entier entre répertoires racine (année→catégorie) détecté par contenu, dans `test_reconciliation_classification.py` — **débloque `specs/004-categorisation-dossiers` T024b** | ||
| 126 | + | ||
| 127 | +### Implementation for User Story 4 | ||
| 128 | + | ||
| 129 | +- [ ] T031 [US4] Étendre `comparer()` : classification "deplacement" via l'index hash→chemin (manifeste vs copie locale), couvrant indifféremment un fichier ou un dossier entier (FR-009/010, dépend de T021) | ||
| 130 | + | ||
| 131 | +**Checkpoint**: User Stories 1 à 4 fonctionnelles. | ||
| 132 | + | ||
| 133 | +--- | ||
| 134 | + | ||
| 135 | +## Phase 7: User Story 5 - Refus d'un double-checkout concurrent (Priority: P5) | ||
| 136 | + | ||
| 137 | +**Goal**: Un second checkout sur un dossier déjà verrouillé est refusé explicitement (FR-004). | ||
| 138 | + | ||
| 139 | +**Independent Test**: Checkout, puis second checkout du même dossier avant réconciliation → refusé avec message clair ; accepté après réconciliation. | ||
| 140 | + | ||
| 141 | +### Tests for User Story 5 | ||
| 142 | + | ||
| 143 | +- [ ] T032 [US5] Test d'intégration bout-en-bout : second `regine checkout` refusé pendant qu'un premier est en cours (code de sortie non-zéro, message explicite), accepté après réconciliation du premier, dans `packages/regine-core/tests/integration/test_double_checkout.py` | ||
| 144 | + | ||
| 145 | +### Implementation for User Story 5 | ||
| 146 | + | ||
| 147 | +- [ ] T033 [US5] Vérifier/compléter le message d'erreur explicite de `archive_cmd.py` pour le cas de verrou déjà posé (comportement déjà couvert par T017 ; cette tâche ne fait qu'exposer un message clair côté CLI) | ||
| 148 | + | ||
| 149 | +**Checkpoint**: User Stories 1 à 5 fonctionnelles. | ||
| 150 | + | ||
| 151 | +--- | ||
| 152 | + | ||
| 153 | +## Phase 8: User Story 6 - Checkout partiel par format (Priority: P6) | ||
| 154 | + | ||
| 155 | +**Goal**: Restreindre un checkout à certains formats sans que les fichiers exclus ne soient jamais signalés comme anormaux (FR-019/020). | ||
| 156 | + | ||
| 157 | +**Independent Test**: Checkout `--formats jpeg` → seuls JPEG+racine copiés ; réconciliation ultérieure ne signale aucun RAW comme supprimé. | ||
| 158 | + | ||
| 159 | +### Tests for User Story 6 | ||
| 160 | + | ||
| 161 | +- [ ] T034 [P] [US6] Test d'intégration checkout partiel (`formats=["jpeg"]` → seuls JPEG et racine copiés) dans `packages/regine-core/tests/integration/test_checkout_partiel.py` | ||
| 162 | +- [ ] T035 [US6] Test d'intégration : réconciliation après checkout partiel ne signale pas les fichiers exclus comme supprimés (FR-020), complétant `test_checkout_partiel.py` | ||
| 163 | + | ||
| 164 | +### Implementation for User Story 6 | ||
| 165 | + | ||
| 166 | +- [ ] T036 [US6] Étendre `checkout()` : paramètre `formats`, copie restreinte aux sous-répertoires demandés plus la racine (FR-019, dépend de T016) | ||
| 167 | +- [ ] T037 [US6] Étendre `comparer()`/le manifeste pour exclure les fichiers hors du périmètre d'un checkout partiel de la détection de suppression (FR-020, dépend de T021) | ||
| 168 | + | ||
| 169 | +**Checkpoint**: Les six user stories fonctionnelles. | ||
| 170 | + | ||
| 171 | +--- | ||
| 172 | + | ||
| 173 | +## Phase 9: Polish & Cross-Cutting Concerns | ||
| 174 | + | ||
| 175 | +- [ ] T038 [P] Exécuter manuellement les 5 scénarios de `specs/005-checkout-reconciliation/quickstart.md` et consigner le résultat | ||
| 176 | +- [ ] T039 [P] `ruff check --fix` sur `packages/regine-core` et `packages/regine-cli` | ||
| 177 | +- [ ] T040 Mettre à jour `contracts/regine-core-api.md` si écart d'implémentation ; puis lever les dépendances documentées dans `specs/001-import-photos/tasks.md` (T033) et `specs/004-categorisation-dossiers/tasks.md` (T024b), désormais implémentables | ||
| 178 | + | ||
| 179 | +--- | ||
| 180 | + | ||
| 181 | +## Dependencies & Execution Order | ||
| 182 | + | ||
| 183 | +### Phase Dependencies | ||
| 184 | + | ||
| 185 | +- **Setup (Phase 1)** : dépend de `specs/004` et `specs/002` (tasks.md déjà exécutées). | ||
| 186 | +- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories. | ||
| 187 | +- **User Stories (Phase 3–8)** : dépendent toutes de Foundational. | ||
| 188 | + - US1 (P1) : aucune dépendance sur une autre user story de ce fichier. | ||
| 189 | + - US2 (P2) : dépend de `checkout()` (US1) pour ses tests d'intégration, mais `comparer()`/`archiver()` sont des fonctions distinctes. | ||
| 190 | + - US3 (P3) : étend `comparer()` de US2 (T021 → T027). | ||
| 191 | + - US4 (P4) : étend `comparer()` de US2 (T021 → T031), indépendante de US3. | ||
| 192 | + - US5 (P5) : vérifie un comportement déjà posé en US1 (T012/T017) — pas de nouvelle logique métier. | ||
| 193 | + - US6 (P6) : étend `checkout()` (US1) et `comparer()` (US2). | ||
| 194 | +- **Polish (Phase 9)** : dépend des user stories livrées (au minimum US1+US2). | ||
| 195 | + | ||
| 196 | +### Parallel Opportunities | ||
| 197 | + | ||
| 198 | +- T002–T004 (Phase 1) : fichiers distincts. | ||
| 199 | +- T006, T008 (Phase 2) : parallélisables une fois T005 terminé. | ||
| 200 | +- T011, T013 (Phase 2) : parallélisables une fois T010/T012 terminés respectivement. | ||
| 201 | +- Tests marqués [P] au sein de chaque story. | ||
| 202 | + | ||
| 203 | +--- | ||
| 204 | + | ||
| 205 | +## Parallel Example: User Story 3 | ||
| 206 | + | ||
| 207 | +```bash | ||
| 208 | +Task: "Test unitaire anomalie RAW propriétaire dans packages/regine-core/tests/unit/test_reconciliation_classification.py" | ||
| 209 | +Task: "Test unitaire anomalie DNG/TIFF/JPEG (métadonnées vs pixels) dans packages/regine-core/tests/unit/test_reconciliation_classification.py" | ||
| 210 | +``` | ||
| 211 | + | ||
| 212 | +--- | ||
| 213 | + | ||
| 214 | +## Implementation Strategy | ||
| 215 | + | ||
| 216 | +### MVP First (User Story 1 uniquement) | ||
| 217 | + | ||
| 218 | +1. Setup + Foundational (hash à deux niveaux, manifeste versionné, verrou). | ||
| 219 | +2. US1 : checkout complet avec manifeste et verrouillage. | ||
| 220 | +3. **Arrêt et validation** : Scénario 1 de `quickstart.md`. | ||
| 221 | + | ||
| 222 | +### Livraison incrémentale | ||
| 223 | + | ||
| 224 | +1. Setup + Foundational → socle prêt. | ||
| 225 | +2. US1 → checkout (MVP) → valider Scénario 1. | ||
| 226 | +3. US2 → réconciliation normale → valider Scénario 2. | ||
| 227 | +4. US3 → détection d'anomalie → valider Scénario 3. | ||
| 228 | +5. US4 → détection de déplacement → valider Scénario 4 → débloque `specs/004` T024b. | ||
| 229 | +6. US5 → refus de double-checkout → couvert par les fondations de US1, tests dédiés. | ||
| 230 | +7. US6 → checkout partiel → valider Scénario 5. | ||
| 231 | +8. Une fois Phase 9 (T040) terminée → débloquer `specs/001-import-photos` T033. | ||
| 232 | + | ||
| 233 | +## Notes | ||
| 234 | + | ||
| 235 | +- [P] = fichiers différents, sans dépendance non résolue. | ||
| 236 | +- Ce module complète les deux derniers modules de la bibliothèque centrale nommés dans l'architecture du projet (`integrity`, `archive`). | ||
| 237 | +- Ne pas recréer le squelette monorepo ni `regine_core.metadata.exif` : à étendre (T005), jamais dupliquer. | ||
| 238 | +- Le verrouillage reste une protection de bonne foi entre instances de Régine (cf. `research.md` § 3) — aucune tâche de ce fichier ne doit tenter de construire une garantie distribuée plus forte, hors périmètre assumé. | ||