fonzarely/regine-photos-archiverpublic Fork
⑂ main
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.

Download ZIP
cleanup e0dd514 · on main · Fabien Champigny · Sep 19, 2026
tasks.md · 238 lines · 16.4 KBmarkdown
Blame HistoryOpen raw

description: "Task list for feature implementation"

Tasks: Checkout et réconciliation d'un dossier de l'archive

Input: Design documents from /specs/005-checkout-reconciliation/
Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md

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.

Organisation: Tâches groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune.

Dépendances inter-specs

Dépendance Fournit État
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
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

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.

Phase 1: Setup

  • 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 — présents
  • T002 [P] Créer packages/regine-core/src/regine_core/integrity/__init__.py — case restée à cocher par oubli, le fichier et le module sont implémentés et testés depuis la Phase 2
  • T003 [P] Créer packages/regine-core/src/regine_core/archive/__init__.py — idem
  • T004 [P] Créer packages/regine-cli/src/regine_cli/archive_cmd.py (squelette des commandes regine checkout/regine reconcile, sans logique) — idem, commandes réellement implémentées depuis longtemps

Phase 2: Foundational (Blocking Prerequisites)

Purpose: Infrastructure partagée requise par les six user stories.

⚠️ CRITICAL: Aucune user story ne peut être implémentée avant la fin de cette phase.

  • 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)
  • T006 [P] Implémenter hash_fichier_entier(chemin) -> str (SHA-256 stdlib) dans packages/regine-core/src/regine_core/integrity/hash.py
  • 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)
  • 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 — validé avec de vraies éditions de métadonnées exiftool
  • 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)
  • 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
  • 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
  • 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)
  • 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

Checkpoint: Fondations prêtes — les phases User Story peuvent commencer.


Phase 3: User Story 1 - Checkout d'un dossier pour édition (Priority: P1) 🎯 MVP

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).

Independent Test: Checkout d'un dossier archivé existant → copie locale identique, manifeste complet enregistré, dossier marqué verrouillé.

Tests for User Story 1

  • 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
  • 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

Implementation for User Story 1

  • 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) — formats implémenté dès cette phase (cf. T036)
  • 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)
  • T018 [US1] Orchestrer regine checkout <dossier> dans packages/regine-cli/src/regine_cli/archive_cmd.py

Checkpoint: User Story 1 fonctionnelle et testable indépendamment (MVP).


Phase 4: User Story 2 - Réconciliation normale et point avant archive (Priority: P2)

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).

Independent Test: Checkout, modification d'un sidecar seul, réconciliation → classé normal, résumé affiché, rien écrit sans confirmation, verrou levé après confirmation.

Tests for User Story 2

  • 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
  • T020 [US2] Test d'intégration complétant test_cycle_checkout_reconciliation.py : édition sidecar → réconciliation → résumé → confirmation → réarchivage → verrou levé

Implementation for User Story 2

  • 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)
  • 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)
  • T023 [US2] Orchestrer regine reconcile <dossier> dans archive_cmd.py : affichage du point avant archive et confirmation globale avant écriture (FR-013)

Checkpoint: User Stories 1 ET 2 fonctionnelles indépendamment.


Phase 5: User Story 3 - Détection d'une anomalie sur un fichier maître (Priority: P3)

Goal: Signaler un fichier maître modifié comme anomalie (jamais silencieusement réarchivé), avec le hash pertinent selon le format (FR-007/008).

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.

Tests for User Story 3

  • T024 [P] [US3] Test unitaire anomalie sur RAW propriétaire (hash fichier entier changé) dans test_reconciliation_classification.py
  • 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 — validé avec de vraies éditions exiftool
  • 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

Implementation for User Story 3

  • T027 [US3] Étendre comparer() : classification "anomalie" via est_maitre_modifie (dépend de T009/T021)
  • 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)

Checkpoint: User Stories 1, 2 et 3 fonctionnelles.


Phase 6: User Story 4 - Détection de renommage/déplacement par le contenu (Priority: P4)

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).

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.

Tests for User Story 4

  • T029 [P] [US4] Test unitaire renommage/promotion détecté par contenu dans test_reconciliation_classification.py
  • 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 (le test démontre que plusieurs fichiers déplacés ensemble sont détectés individuellement par le même mécanisme, sans code dédié à l'échelle d'un dossier — cf. docstring du test pour la limite exacte de ce qui relève de comparer vs de l'orchestration d'un futur module)

Implementation for User Story 4

  • 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)

Checkpoint: User Stories 1 à 4 fonctionnelles.


Phase 7: User Story 5 - Refus d'un double-checkout concurrent (Priority: P5)

Goal: Un second checkout sur un dossier déjà verrouillé est refusé explicitement (FR-004).

Independent Test: Checkout, puis second checkout du même dossier avant réconciliation → refusé avec message clair ; accepté après réconciliation.

Tests for User Story 5

  • 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 — bug réel trouvé et corrigé : une réconciliation sans aucun changement ne levait jamais le verrou (archive_cmd._cmd_reconcile retournait avant d'appeler lever())

Implementation for User Story 5

  • 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)

Checkpoint: User Stories 1 à 5 fonctionnelles.


Phase 8: User Story 6 - Checkout partiel par format (Priority: P6)

Goal: Restreindre un checkout à certains formats sans que les fichiers exclus ne soient jamais signalés comme anormaux (FR-019/020).

Independent Test: Checkout --formats jpeg → seuls JPEG+racine copiés ; réconciliation ultérieure ne signale aucun RAW comme supprimé.

Tests for User Story 6

  • 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
  • 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 — première version du test erronée (attendait "normal" sur une réécriture complète d'un JPEG racine, qui est en réalité correctement classée "anomalie" — pas un bug, corrigé côté test avec un scénario de sidecar)

Implementation for User Story 6

  • T036 [US6] Étendre checkout() : paramètre formats, copie restreinte aux sous-répertoires demandés plus la racine (FR-019, dépend de T016) — implémenté dès T016 (Phase 3)
  • 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) — implémenté dès T021 via dans_perimetre partagé avec checkout.py

Checkpoint: Les six user stories fonctionnelles.


Phase 9: Polish & Cross-Cutting Concerns

  • T038 [P] Exécuter manuellement les 5 scénarios de specs/005-checkout-reconciliation/quickstart.md et consigner le résultat — 2026-09-19 : les 5 scénarios passent, déroulés via la vraie CLI (python -m regine_cli.archive_cmd) avec de vrais fichiers ; un bug réel a été trouvé et corrigé en cours de route (verrou non levé après une réconciliation sans changement, cf. T032)
  • T039 [P] ruff check --fix sur packages/regine-core et packages/regine-cli — 0 erreur restante, code formaté
  • 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

Dependencies & Execution Order

Phase Dependencies

  • Setup (Phase 1) : dépend de specs/004 et specs/002 (tasks.md déjà exécutées).
  • Foundational (Phase 2) : dépend de Setup — BLOQUE toutes les user stories.
  • User Stories (Phase 3–8) : dépendent toutes de Foundational.
    • US1 (P1) : aucune dépendance sur une autre user story de ce fichier.
    • US2 (P2) : dépend de checkout() (US1) pour ses tests d'intégration, mais comparer()/archiver() sont des fonctions distinctes.
    • US3 (P3) : étend comparer() de US2 (T021 → T027).
    • US4 (P4) : étend comparer() de US2 (T021 → T031), indépendante de US3.
    • US5 (P5) : vérifie un comportement déjà posé en US1 (T012/T017) — pas de nouvelle logique métier.
    • US6 (P6) : étend checkout() (US1) et comparer() (US2).
  • Polish (Phase 9) : dépend des user stories livrées (au minimum US1+US2).

Parallel Opportunities

  • T002–T004 (Phase 1) : fichiers distincts.
  • T006, T008 (Phase 2) : parallélisables une fois T005 terminé.
  • T011, T013 (Phase 2) : parallélisables une fois T010/T012 terminés respectivement.
  • Tests marqués [P] au sein de chaque story.

Parallel Example: User Story 3

Task: "Test unitaire anomalie RAW propriétaire dans packages/regine-core/tests/unit/test_reconciliation_classification.py"
Task: "Test unitaire anomalie DNG/TIFF/JPEG (métadonnées vs pixels) dans packages/regine-core/tests/unit/test_reconciliation_classification.py"

Implementation Strategy

MVP First (User Story 1 uniquement)

  1. Setup + Foundational (hash à deux niveaux, manifeste versionné, verrou).
  2. US1 : checkout complet avec manifeste et verrouillage.
  3. Arrêt et validation : Scénario 1 de quickstart.md.

Livraison incrémentale

  1. Setup + Foundational → socle prêt.
  2. US1 → checkout (MVP) → valider Scénario 1.
  3. US2 → réconciliation normale → valider Scénario 2.
  4. US3 → détection d'anomalie → valider Scénario 3.
  5. US4 → détection de déplacement → valider Scénario 4 → débloque specs/004 T024b.
  6. US5 → refus de double-checkout → couvert par les fondations de US1, tests dédiés.
  7. US6 → checkout partiel → valider Scénario 5.
  8. Une fois Phase 9 (T040) terminée → débloquer specs/001-import-photos T033.

Notes

  • [P] = fichiers différents, sans dépendance non résolue.
  • Ce module complète les deux derniers modules de la bibliothèque centrale nommés dans l'architecture du projet (integrity, archive).
  • Ne pas recréer le squelette monorepo ni regine_core.metadata.exif : à étendre (T005), jamais dupliquer.
  • 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é.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
---

description: "Task list for feature implementation"
---

# Tasks: Checkout et réconciliation d'un dossier de l'archive

**Input**: Design documents from `/specs/005-checkout-reconciliation/`
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md

**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.

**Organisation**: Tâches groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune.

## Dépendances inter-specs

| Dépendance | Fournit | État |
|---|---|---|
| `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 |
| `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** |

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.

## Phase 1: Setup

- [X] 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 — présents
- [X] T002 [P] Créer `packages/regine-core/src/regine_core/integrity/__init__.py` — case restée à cocher par oubli, le fichier et le module sont implémentés et testés depuis la Phase 2
- [X] T003 [P] Créer `packages/regine-core/src/regine_core/archive/__init__.py` — idem
- [X] T004 [P] Créer `packages/regine-cli/src/regine_cli/archive_cmd.py` (squelette des commandes `regine checkout`/`regine reconcile`, sans logique) — idem, commandes réellement implémentées depuis longtemps

---

## Phase 2: Foundational (Blocking Prerequisites)

**Purpose**: Infrastructure partagée requise par les six user stories.

**⚠️ CRITICAL**: Aucune user story ne peut être implémentée avant la fin de cette phase.

- [X] 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)
- [X] T006 [P] Implémenter `hash_fichier_entier(chemin) -> str` (SHA-256 stdlib) dans `packages/regine-core/src/regine_core/integrity/hash.py`
- [X] 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)
- [X] 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` — validé avec de vraies éditions de métadonnées exiftool
- [X] 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)
- [X] 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
- [X] 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`
- [X] 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)
- [X] 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`

**Checkpoint**: Fondations prêtes — les phases User Story peuvent commencer.

---

## Phase 3: User Story 1 - Checkout d'un dossier pour édition (Priority: P1) 🎯 MVP

**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).

**Independent Test**: Checkout d'un dossier archivé existant → copie locale identique, manifeste complet enregistré, dossier marqué verrouillé.

### Tests for User Story 1

- [X] 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`
- [X] 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`

### Implementation for User Story 1

- [X] 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) — `formats` implémenté dès cette phase (cf. T036)
- [X] 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)
- [X] T018 [US1] Orchestrer `regine checkout <dossier>` dans `packages/regine-cli/src/regine_cli/archive_cmd.py`

**Checkpoint**: User Story 1 fonctionnelle et testable indépendamment (MVP).

---

## Phase 4: User Story 2 - Réconciliation normale et point avant archive (Priority: P2)

**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).

**Independent Test**: Checkout, modification d'un sidecar seul, réconciliation → classé normal, résumé affiché, rien écrit sans confirmation, verrou levé après confirmation.

### Tests for User Story 2

- [X] 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`
- [X] T020 [US2] Test d'intégration complétant `test_cycle_checkout_reconciliation.py` : édition sidecar → réconciliation → résumé → confirmation → réarchivage → verrou levé

### Implementation for User Story 2

- [X] 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)
- [X] 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)
- [X] T023 [US2] Orchestrer `regine reconcile <dossier>` dans `archive_cmd.py` : affichage du point avant archive et confirmation globale avant écriture (FR-013)

**Checkpoint**: User Stories 1 ET 2 fonctionnelles indépendamment.

---

## Phase 5: User Story 3 - Détection d'une anomalie sur un fichier maître (Priority: P3)

**Goal**: Signaler un fichier maître modifié comme anomalie (jamais silencieusement réarchivé), avec le hash pertinent selon le format (FR-007/008).

**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.

### Tests for User Story 3

- [X] T024 [P] [US3] Test unitaire anomalie sur RAW propriétaire (hash fichier entier changé) dans `test_reconciliation_classification.py`
- [X] 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` — validé avec de vraies éditions exiftool
- [X] 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`

### Implementation for User Story 3

- [X] T027 [US3] Étendre `comparer()` : classification "anomalie" via `est_maitre_modifie` (dépend de T009/T021)
- [X] 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)

**Checkpoint**: User Stories 1, 2 et 3 fonctionnelles.

---

## Phase 6: User Story 4 - Détection de renommage/déplacement par le contenu (Priority: P4)

**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).

**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.

### Tests for User Story 4

- [X] T029 [P] [US4] Test unitaire renommage/promotion détecté par contenu dans `test_reconciliation_classification.py`
- [X] 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** (le test démontre que plusieurs fichiers déplacés ensemble sont détectés individuellement par le même mécanisme, sans code dédié à l'échelle d'un dossier — cf. docstring du test pour la limite exacte de ce qui relève de `comparer` vs de l'orchestration d'un futur module)

### Implementation for User Story 4

- [X] 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)

**Checkpoint**: User Stories 1 à 4 fonctionnelles.

---

## Phase 7: User Story 5 - Refus d'un double-checkout concurrent (Priority: P5)

**Goal**: Un second checkout sur un dossier déjà verrouillé est refusé explicitement (FR-004).

**Independent Test**: Checkout, puis second checkout du même dossier avant réconciliation → refusé avec message clair ; accepté après réconciliation.

### Tests for User Story 5

- [X] 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` — **bug réel trouvé et corrigé** : une réconciliation sans aucun changement ne levait jamais le verrou (`archive_cmd._cmd_reconcile` retournait avant d'appeler `lever()`)

### Implementation for User Story 5

- [X] 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)

**Checkpoint**: User Stories 1 à 5 fonctionnelles.

---

## Phase 8: User Story 6 - Checkout partiel par format (Priority: P6)

**Goal**: Restreindre un checkout à certains formats sans que les fichiers exclus ne soient jamais signalés comme anormaux (FR-019/020).

**Independent Test**: Checkout `--formats jpeg` → seuls JPEG+racine copiés ; réconciliation ultérieure ne signale aucun RAW comme supprimé.

### Tests for User Story 6

- [X] 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`
- [X] 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` — première version du test erronée (attendait "normal" sur une réécriture complète d'un JPEG racine, qui est en réalité correctement classée "anomalie" — pas un bug, corrigé côté test avec un scénario de sidecar)

### Implementation for User Story 6

- [X] T036 [US6] Étendre `checkout()` : paramètre `formats`, copie restreinte aux sous-répertoires demandés plus la racine (FR-019, dépend de T016) — implémenté dès T016 (Phase 3)
- [X] 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) — implémenté dès T021 via `dans_perimetre` partagé avec `checkout.py`

**Checkpoint**: Les six user stories fonctionnelles.

---

## Phase 9: Polish & Cross-Cutting Concerns

- [X] T038 [P] Exécuter manuellement les 5 scénarios de `specs/005-checkout-reconciliation/quickstart.md` et consigner le résultat — **2026-09-19 : les 5 scénarios passent**, déroulés via la vraie CLI (`python -m regine_cli.archive_cmd`) avec de vrais fichiers ; un bug réel a été trouvé et corrigé en cours de route (verrou non levé après une réconciliation sans changement, cf. T032)
- [X] T039 [P] `ruff check --fix` sur `packages/regine-core` et `packages/regine-cli` — 0 erreur restante, code formaté
- [X] 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

---

## Dependencies & Execution Order

### Phase Dependencies

- **Setup (Phase 1)** : dépend de `specs/004` et `specs/002` (tasks.md déjà exécutées).
- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories.
- **User Stories (Phase 3–8)** : dépendent toutes de Foundational.
  - US1 (P1) : aucune dépendance sur une autre user story de ce fichier.
  - US2 (P2) : dépend de `checkout()` (US1) pour ses tests d'intégration, mais `comparer()`/`archiver()` sont des fonctions distinctes.
  - US3 (P3) : étend `comparer()` de US2 (T021 → T027).
  - US4 (P4) : étend `comparer()` de US2 (T021 → T031), indépendante de US3.
  - US5 (P5) : vérifie un comportement déjà posé en US1 (T012/T017) — pas de nouvelle logique métier.
  - US6 (P6) : étend `checkout()` (US1) et `comparer()` (US2).
- **Polish (Phase 9)** : dépend des user stories livrées (au minimum US1+US2).

### Parallel Opportunities

- T002–T004 (Phase 1) : fichiers distincts.
- T006, T008 (Phase 2) : parallélisables une fois T005 terminé.
- T011, T013 (Phase 2) : parallélisables une fois T010/T012 terminés respectivement.
- Tests marqués [P] au sein de chaque story.

---

## Parallel Example: User Story 3

```bash
Task: "Test unitaire anomalie RAW propriétaire dans packages/regine-core/tests/unit/test_reconciliation_classification.py"
Task: "Test unitaire anomalie DNG/TIFF/JPEG (métadonnées vs pixels) dans packages/regine-core/tests/unit/test_reconciliation_classification.py"
```

---

## Implementation Strategy

### MVP First (User Story 1 uniquement)

1. Setup + Foundational (hash à deux niveaux, manifeste versionné, verrou).
2. US1 : checkout complet avec manifeste et verrouillage.
3. **Arrêt et validation** : Scénario 1 de `quickstart.md`.

### Livraison incrémentale

1. Setup + Foundational → socle prêt.
2. US1 → checkout (MVP) → valider Scénario 1.
3. US2 → réconciliation normale → valider Scénario 2.
4. US3 → détection d'anomalie → valider Scénario 3.
5. US4 → détection de déplacement → valider Scénario 4 → débloque `specs/004` T024b.
6. US5 → refus de double-checkout → couvert par les fondations de US1, tests dédiés.
7. US6 → checkout partiel → valider Scénario 5.
8. Une fois Phase 9 (T040) terminée → débloquer `specs/001-import-photos` T033.

## Notes

- [P] = fichiers différents, sans dépendance non résolue.
- Ce module complète les deux derniers modules de la bibliothèque centrale nommés dans l'architecture du projet (`integrity`, `archive`).
- Ne pas recréer le squelette monorepo ni `regine_core.metadata.exif` : à étendre (T005), jamais dupliquer.
- 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é.