fonzarely/regine-photos-archiverpublic⑂ Fork 0
⑂ 26c1e45
Commits
⬇ Clone ▾
git clone https://git.rickub.com/fonzarely/regine-photos-archiver.git
git clone ssh://git@rickub.com/fonzarely/regine-photos-archiver.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

tasks

Fabien Champigny committed 2026-09-19T14:18:52+02:00 Browse files
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é.