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
ok 94fd0a0 · on main · Fabien Champigny · Sep 19, 2026
tasks.md · 189 lines · 15.4 KBmarkdown
Blame HistoryOpen raw

description: "Task list for feature implementation"

Tasks: Catégorisation des dossiers à la racine de l'archive

Input: Design documents from /specs/004-categorisation-dossiers/
Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/regine-core-api.md, quickstart.md

Tests: Incluses — plan.md § Project Structure nomme explicitement les fichiers de test attendus (test_root_resolution.py, test_categories_cache.py, test_root_inheritance.py), 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.

État du dépôt au moment de la génération

Aucun code n'existe encore dans ce dépôt (seuls docs/, specs/, .specify/ sont présents). Cette fonctionnalité étant la première à atteindre /speckit-tasks, la Phase 1 crée le squelette du monorepo déjà décrit dans specs/003-config-contexte-travail/plan.md (bibliothèque centrale + 3 façades). Ce squelette, une fois créé, est réutilisable tel quel par les futures tâches de specs/001, 002 et 003 — pas à recréer.

Décision de conception prise à ce stade (raffinement par rapport à contracts/regine-core-api.md) : determine_root reçoit archive_root et local_root en paramètres explicites plutôt que de les lire depuis le contexte de travail de specs/003-config-contexte-travail (pas encore implémenté). Ceci garde le module dossier testable et livrable indépendamment de specs/003. Le futur code d'intégration (CLI d'import, specs/001-import-photos) sera responsable de lire le contexte de travail et de le passer à determine_root.

Phase 1: Setup (Shared Infrastructure)

Purpose: Initialisation du monorepo — aucun paquet n'existe encore.

  • T001 Créer le squelette du monorepo à la racine du dépôt : packages/regine-core/, packages/regine-cli/, packages/regine-gui/, packages/regine-agent/, plus un pyproject.toml racine déclarant un workspace uv référençant les 4 paquets (cf. specs/003-config-contexte-travail/plan.md § Project Structure et research.md § 5)
  • T002 [P] Initialiser packages/regine-core/pyproject.toml (nom regine-core, aucune dépendance tierce) et packages/regine-core/src/regine_core/__init__.py
  • T003 [P] Initialiser packages/regine-cli/pyproject.toml (dépendance vers regine-core) et packages/regine-cli/src/regine_cli/__init__.py
  • T004 [P] Créer packages/regine-gui/pyproject.toml et packages/regine-agent/pyproject.toml comme paquets vides (dépendance déclarée vers regine-core, aucun code métier), conformément à specs/003-config-contexte-travail/plan.md
  • T005 [P] Configurer ruff (lint + format) au niveau du pyproject.toml racine du workspace
  • T006 [P] Configurer pytest pour packages/regine-core ([tool.pytest.ini_options], création des dossiers tests/unit/, tests/integration/, tests/contract/)

Phase 2: Foundational (Blocking Prerequisites)

Purpose: Infrastructure partagée requise par toutes les user stories de cette fonctionnalité.

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

  • T007 Créer packages/regine-core/src/regine_core/dossier/__init__.py (premier code du module dossier, cf. docs/interface-cli-gui-architecture.md)
  • T008 Définir le dataclass RootLocation (type: Literal["annee", "categorie"], nom: str, chemin_archive: Path, chemin_local: Path) et la signature de determine_root(date, categorie=None, *, archive_root, local_root) (corps non implémenté) dans packages/regine-core/src/regine_core/dossier/root.py
  • T009 Créer packages/regine-core/src/regine_core/config/db.py : fonction open_context_db(path) -> sqlite3.Connection avec bootstrap PRAGMA user_version / PRAGMA application_id et écriture transactionnelle — infrastructure partagée que specs/003-config-contexte-travail réutilisera pour sa propre table boitiers (cf. research.md § 1, specs/003-config-contexte-travail/research.md § 2)
  • T010 [P] Test unitaire du bootstrap de la base de contexte dans packages/regine-core/tests/unit/test_db_bootstrap.py (vérifie PRAGMA user_version/application_id après initialisation, réouverture idempotente sans perte de données)

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


Phase 3: User Story 1 - Placement par année par défaut (Priority: P1) 🎯 MVP

Goal: Un dossier importé sans catégorie choisie est placé sous un répertoire d'année, identique dans l'archive et l'espace de travail local (FR-002).

Independent Test: Appeler determine_root(date(2026, 8, 15), archive_root=..., local_root=...) et vérifier type == "annee", nom == "2026", chemin_archive et chemin_local en miroir sous 2026/ — sans dépendre d'aucune autre user story.

Tests for User Story 1

  • T011 [P] [US1] Test unitaire : determine_root sans catégorie retourne type="annee", nom dérivé de l'année de la date, chemins archive/local en miroir, dans packages/regine-core/tests/unit/test_root_resolution.py

Implementation for User Story 1

  • T012 [US1] Implémenter la branche par défaut (année) de determine_root dans packages/regine-core/src/regine_core/dossier/root.py (dépend de T008)
  • T013 [US1] Garantir par construction que chemin_archive.name == chemin_local.name == nom (miroir archive/local, FR-002) — pas de synchronisation a posteriori

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


Phase 4: User Story 2 - Import relevant d'une catégorie thématique (Priority: P2)

Goal: Un dossier pour lequel l'utilisateur choisit une catégorie est placé directement sous le répertoire de cette catégorie, sans répertoire d'année (FR-003, FR-004, FR-005).

Independent Test: Appeler determine_root(date, categorie="mariage", ...) et vérifier type == "categorie", nom == "mariage", aucun répertoire d'année dans le chemin ; vérifier que suggest_categories("Mariage") retourne ["mariage"] après un premier register_category_usage("mariage").

Tests for User Story 2

  • T014 [P] [US2] Test unitaire : determine_root avec categorie retourne type="categorie", aucun répertoire d'année, dans packages/regine-core/tests/unit/test_root_resolution.py
  • T015 [P] [US2] Test unitaire : list_known_categories, suggest_categories, register_category_usage dans packages/regine-core/tests/unit/test_categories_cache.py (liste vide au départ ; suggest_categories("Mariage") retrouve "mariage" après enregistrement ; pas de doublon silencieux)

Implementation for User Story 2

  • T016 [US2] Ajouter la table categories (nom UNIQUE, premiere_utilisation) au schéma de packages/regine-core/src/regine_core/config/db.py (dépend de T009) — implémentée dans config/categories.py (_ensure_schema), le fichier categories du contexte centralisé reste ouvert via db.open_context_db
  • T017 [P] [US2] Implémenter list_known_categories() dans packages/regine-core/src/regine_core/config/categories.py (dépend de T016)
  • T018 [P] [US2] Implémenter suggest_categories(saisie) avec difflib.get_close_matches dans packages/regine-core/src/regine_core/config/categories.py (dépend de T016 ; cf. research.md § 2)
  • T019 [US2] Implémenter register_category_usage(nom) (idempotent ; à n'appeler qu'après confirmation explicite d'archivage, cf. Principe II de la constitution) dans packages/regine-core/src/regine_core/config/categories.py (dépend de T016)
  • T020 [US2] Implémenter la branche catégorie de determine_root dans root.py (dépend de T012)
  • T021 [US2] Test de non-régression : la signature de determine_root n'accepte qu'un seul paramètre categorie (jamais une liste) — garantit FR-005 (une seule catégorie à la fois) dans test_root_resolution.py

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


Phase 5: User Story 3 - Héritage de la catégorie par les sous-dossiers d'un voyage (Priority: P3)

Goal: Un sous-dossier ajouté à un dossier parent existant hérite automatiquement de son RootLocation, sans nouvel appel à determine_root (FR-006).

Independent Test: Résoudre un RootLocation pour un dossier parent, puis vérifier qu'un sous-dossier réutilise cet objet tel quel (même nom, même type) sans recalcul.

Tests for User Story 3

  • T022 [P] [US3] Test d'intégration : le RootLocation d'un dossier parent est réutilisé tel quel pour un sous-dossier, sans nouvel appel à determine_root, dans packages/regine-core/tests/integration/test_root_inheritance.py

Implementation for User Story 3

  • T023 [US3] Documenter (docstring) dans root.py que determine_root ne DOIT être appelée qu'une seule fois par dossier parent — le futur code appelant (import, specs/001-import-photos) réutilise l'objet RootLocation déjà résolu pour tout sous-dossier ; aucune nouvelle logique de calcul requise ici

Checkpoint: User Stories 1, 2 et 3 fonctionnelles indépendamment.


Phase 6: User Story 4 - Recatégorisation a posteriori (Priority: P4)

Goal: Un déplacement d'un dossier entre répertoires racines est reconnu comme tel par le mécanisme de réconciliation par hash (FR-008) — pas de mécanisme dédié à construire ici.

Statut : Mise à jour du 2026-09-19 — le mécanisme de détection de déplacement par somme de contrôle a désormais sa propre spec et son propre plan (specs/005-checkout-reconciliation, dont FR-010 couvre exactement ce cas). Ce n'est plus une dépendance bloquante au niveau spécification : seulement une dépendance d'ordonnancement (l'implémentation de specs/005 doit précéder la validation de bout en bout de cette user story).

  • T024 [US4] Documenter (docstring) dans packages/regine-core/src/regine_core/dossier/root.py qu'il n'existe aucune opération d'écriture directe sur RootLocation : tout changement de répertoire racine passe exclusivement par regine_core.archive.reconciliation.comparer (specs/005-checkout-reconciliation), qui classe un déplacement de dossier entier comme deplacement sans code supplémentaire ici
  • T024b [US4] Test d'intégration : déplacer un dossier de travail entre deux répertoires racine (année → catégorie) et vérifier que regine_core.archive.reconciliation.comparer le classe deplacement, dans packages/regine-core/tests/integration/test_root_recategorisation.py — écart avec l'intitulé initial constaté à l'implémentation (2026-09-20) : comparer ne raisonne que sur des chemins relatifs internes à un dossier, jamais sur son répertoire racine (cf. docstring de test_whole_folder_move_detected_as_move_for_every_file, specs/005) ; recatégoriser un dossier entier en conservant sa structure interne n'est donc PAS classé deplacement, mais totalement invisible pour comparer (chaque fichier reste « inchangé », omis du rapport) — c'est le comportement correct et recherché (aucune anomalie ni signalement erroné). Test écrit en conséquence avec determine_root (specs/004) pour les deux racines et rapport.changements == [] comme assertion ; passe du premier coup, 135/135 tests

Checkpoint: US4 documentée et validée par test d'intégration — toutes les user stories de cette spec sont désormais complètes (28/28).


Phase 7: Polish & Cross-Cutting Concerns

  • T025 [P] Exécuter manuellement les scénarios 1 à 4 de specs/004-categorisation-dossiers/quickstart.md et consigner le résultat — 2026-09-19 : les 4 scénarios passent (déroulés via uv run python3, cf. session d'implémentation)
  • T026 [P] ruff check --fix sur packages/regine-core — 0 erreur restante, code formaté
  • T027 Mettre à jour specs/004-categorisation-dossiers/contracts/regine-core-api.md pour refléter la signature définitive de determine_root (paramètres archive_root/local_root explicites, cf. Note en tête de ce fichier)

Dependencies & Execution Order

Phase Dependencies

  • Setup (Phase 1) : aucune dépendance — peut démarrer immédiatement.
  • Foundational (Phase 2) : dépend de Setup — BLOQUE toutes les user stories.
  • User Stories (Phase 3–6) : dépendent toutes de Foundational.
    • US1 (P1) : aucune dépendance sur une autre user story.
    • US2 (P2) : dépend techniquement de T012 (branche année déjà en place dans determine_root) mais reste testable et livrable indépendamment de US1 côté utilisateur.
    • US3 (P3) : dépend de US1 et US2 étant données (réutilise leurs RootLocation), mais n'ajoute aucune nouvelle logique de calcul.
    • US4 (P4) : dépend de l'implémentation de specs/005-checkout-reconciliation (désormais spécifiée et planifiée) pour sa validation de bout en bout ; la tâche de documentation T024 reste exécutable dès maintenant.
  • Polish (Phase 7) : dépend des user stories livrées (au minimum US1+US2).

Parallel Opportunities

  • T002–T006 (Phase 1) : tous marqués [P], fichiers/paquets distincts.
  • T010 (Phase 2) : parallélisable une fois T009 terminé.
  • T011 (US1), T014/T015 (US2), T022 (US3) : tests marqués [P], fichiers distincts.
  • T017/T018 (US2) : parallélisables (même fichier categories.py mais fonctions indépendantes sans état partagé en cours d'écriture — à fusionner avec attention si exécutées par deux agents en parallèle).

Parallel Example: User Story 2

# Lancer les tests de la User Story 2 ensemble :
Task: "Test unitaire determine_root avec catégorie dans packages/regine-core/tests/unit/test_root_resolution.py"
Task: "Test unitaire cache de catégories dans packages/regine-core/tests/unit/test_categories_cache.py"

# Lancer les fonctions indépendantes de config/categories.py ensemble :
Task: "Implémenter list_known_categories() dans packages/regine-core/src/regine_core/config/categories.py"
Task: "Implémenter suggest_categories(saisie) dans packages/regine-core/src/regine_core/config/categories.py"

Implementation Strategy

MVP First (User Story 1 uniquement)

  1. Compléter Phase 1 (Setup) et Phase 2 (Foundational).
  2. Compléter Phase 3 (US1) : placement par année par défaut.
  3. Arrêt et validation : exécuter le Scénario 1 de quickstart.md.
  4. Ce socle (monorepo + RootLocation + base de contexte) sert aussi de fondation aux futures tâches de specs/001, 002 et 003.

Livraison incrémentale

  1. Setup + Foundational → socle prêt.
  2. US1 → placement par année (MVP) → valider Scénario 1.
  3. US2 → catégories thématiques → valider Scénario 2 et 3.
  4. US3 → héritage voyage → valider Scénario 4.
  5. US4 → documentation exécutable dès maintenant (T024) ; validation de bout en bout (T024b) une fois specs/005-checkout-reconciliation implémentée.

Notes

  • [P] = fichiers différents, sans dépendance non résolue.
  • Chaque user story est livrable et testable indépendamment ; US4 dépend de l'implémentation de specs/005-checkout-reconciliation (désormais spécifiée et planifiée) pour sa validation de bout en bout, mais sa tâche de documentation (T024) ne l'est pas.
  • Committer après chaque tâche ou groupe logique de tâches.
  • Le squelette monorepo (Phase 1) et la base de contexte (T009) sont des fondations partagées : ne pas les recréer lors des futures exécutions de /speckit-tasks sur specs/001, 002 ou 003 — les étendre.
  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
---

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

# Tasks: Catégorisation des dossiers à la racine de l'archive

**Input**: Design documents from `/specs/004-categorisation-dossiers/`
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/regine-core-api.md, quickstart.md

**Tests**: Incluses — `plan.md` § Project Structure nomme explicitement les fichiers de test attendus (`test_root_resolution.py`, `test_categories_cache.py`, `test_root_inheritance.py`), 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.

## État du dépôt au moment de la génération

Aucun code n'existe encore dans ce dépôt (seuls `docs/`, `specs/`, `.specify/` sont présents). Cette fonctionnalité étant la première à atteindre `/speckit-tasks`, la Phase 1 crée le squelette du monorepo déjà décrit dans `specs/003-config-contexte-travail/plan.md` (bibliothèque centrale + 3 façades). Ce squelette, une fois créé, est réutilisable tel quel par les futures tâches de specs/001, 002 et 003 — pas à recréer.

**Décision de conception prise à ce stade** (raffinement par rapport à `contracts/regine-core-api.md`) : `determine_root` reçoit `archive_root` et `local_root` en paramètres explicites plutôt que de les lire depuis le contexte de travail de `specs/003-config-contexte-travail` (pas encore implémenté). Ceci garde le module `dossier` testable et livrable indépendamment de `specs/003`. Le futur code d'intégration (CLI d'import, `specs/001-import-photos`) sera responsable de lire le contexte de travail et de le passer à `determine_root`.

## Phase 1: Setup (Shared Infrastructure)

**Purpose**: Initialisation du monorepo — aucun paquet n'existe encore.

- [X] T001 Créer le squelette du monorepo à la racine du dépôt : `packages/regine-core/`, `packages/regine-cli/`, `packages/regine-gui/`, `packages/regine-agent/`, plus un `pyproject.toml` racine déclarant un workspace `uv` référençant les 4 paquets (cf. `specs/003-config-contexte-travail/plan.md` § Project Structure et `research.md` § 5)
- [X] T002 [P] Initialiser `packages/regine-core/pyproject.toml` (nom `regine-core`, aucune dépendance tierce) et `packages/regine-core/src/regine_core/__init__.py`
- [X] T003 [P] Initialiser `packages/regine-cli/pyproject.toml` (dépendance vers `regine-core`) et `packages/regine-cli/src/regine_cli/__init__.py`
- [X] T004 [P] Créer `packages/regine-gui/pyproject.toml` et `packages/regine-agent/pyproject.toml` comme paquets vides (dépendance déclarée vers `regine-core`, aucun code métier), conformément à `specs/003-config-contexte-travail/plan.md`
- [X] T005 [P] Configurer `ruff` (lint + format) au niveau du `pyproject.toml` racine du workspace
- [X] T006 [P] Configurer `pytest` pour `packages/regine-core` (`[tool.pytest.ini_options]`, création des dossiers `tests/unit/`, `tests/integration/`, `tests/contract/`)

---

## Phase 2: Foundational (Blocking Prerequisites)

**Purpose**: Infrastructure partagée requise par toutes les user stories de cette fonctionnalité.

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

- [X] T007 Créer `packages/regine-core/src/regine_core/dossier/__init__.py` (premier code du module `dossier`, cf. `docs/interface-cli-gui-architecture.md`)
- [X] T008 Définir le dataclass `RootLocation` (`type: Literal["annee", "categorie"]`, `nom: str`, `chemin_archive: Path`, `chemin_local: Path`) et la signature de `determine_root(date, categorie=None, *, archive_root, local_root)` (corps non implémenté) dans `packages/regine-core/src/regine_core/dossier/root.py`
- [X] T009 Créer `packages/regine-core/src/regine_core/config/db.py` : fonction `open_context_db(path) -> sqlite3.Connection` avec bootstrap `PRAGMA user_version` / `PRAGMA application_id` et écriture transactionnelle — infrastructure partagée que `specs/003-config-contexte-travail` réutilisera pour sa propre table `boitiers` (cf. `research.md` § 1, `specs/003-config-contexte-travail/research.md` § 2)
- [X] T010 [P] Test unitaire du bootstrap de la base de contexte dans `packages/regine-core/tests/unit/test_db_bootstrap.py` (vérifie `PRAGMA user_version`/`application_id` après initialisation, réouverture idempotente sans perte de données)

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

---

## Phase 3: User Story 1 - Placement par année par défaut (Priority: P1) 🎯 MVP

**Goal**: Un dossier importé sans catégorie choisie est placé sous un répertoire d'année, identique dans l'archive et l'espace de travail local (FR-002).

**Independent Test**: Appeler `determine_root(date(2026, 8, 15), archive_root=..., local_root=...)` et vérifier `type == "annee"`, `nom == "2026"`, `chemin_archive` et `chemin_local` en miroir sous `2026/` — sans dépendre d'aucune autre user story.

### Tests for User Story 1

- [X] T011 [P] [US1] Test unitaire : `determine_root` sans catégorie retourne `type="annee"`, `nom` dérivé de l'année de la date, chemins archive/local en miroir, dans `packages/regine-core/tests/unit/test_root_resolution.py`

### Implementation for User Story 1

- [X] T012 [US1] Implémenter la branche par défaut (année) de `determine_root` dans `packages/regine-core/src/regine_core/dossier/root.py` (dépend de T008)
- [X] T013 [US1] Garantir par construction que `chemin_archive.name == chemin_local.name == nom` (miroir archive/local, FR-002) — pas de synchronisation a posteriori

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

---

## Phase 4: User Story 2 - Import relevant d'une catégorie thématique (Priority: P2)

**Goal**: Un dossier pour lequel l'utilisateur choisit une catégorie est placé directement sous le répertoire de cette catégorie, sans répertoire d'année (FR-003, FR-004, FR-005).

**Independent Test**: Appeler `determine_root(date, categorie="mariage", ...)` et vérifier `type == "categorie"`, `nom == "mariage"`, aucun répertoire d'année dans le chemin ; vérifier que `suggest_categories("Mariage")` retourne `["mariage"]` après un premier `register_category_usage("mariage")`.

### Tests for User Story 2

- [X] T014 [P] [US2] Test unitaire : `determine_root` avec `categorie` retourne `type="categorie"`, aucun répertoire d'année, dans `packages/regine-core/tests/unit/test_root_resolution.py`
- [X] T015 [P] [US2] Test unitaire : `list_known_categories`, `suggest_categories`, `register_category_usage` dans `packages/regine-core/tests/unit/test_categories_cache.py` (liste vide au départ ; `suggest_categories("Mariage")` retrouve `"mariage"` après enregistrement ; pas de doublon silencieux)

### Implementation for User Story 2

- [X] T016 [US2] Ajouter la table `categories` (`nom` UNIQUE, `premiere_utilisation`) au schéma de `packages/regine-core/src/regine_core/config/db.py` (dépend de T009) — implémentée dans `config/categories.py` (`_ensure_schema`), le fichier `categories` du contexte centralisé reste ouvert via `db.open_context_db`
- [X] T017 [P] [US2] Implémenter `list_known_categories()` dans `packages/regine-core/src/regine_core/config/categories.py` (dépend de T016)
- [X] T018 [P] [US2] Implémenter `suggest_categories(saisie)` avec `difflib.get_close_matches` dans `packages/regine-core/src/regine_core/config/categories.py` (dépend de T016 ; cf. `research.md` § 2)
- [X] T019 [US2] Implémenter `register_category_usage(nom)` (idempotent ; à n'appeler qu'après confirmation explicite d'archivage, cf. Principe II de la constitution) dans `packages/regine-core/src/regine_core/config/categories.py` (dépend de T016)
- [X] T020 [US2] Implémenter la branche catégorie de `determine_root` dans `root.py` (dépend de T012)
- [X] T021 [US2] Test de non-régression : la signature de `determine_root` n'accepte qu'un seul paramètre `categorie` (jamais une liste) — garantit FR-005 (une seule catégorie à la fois) dans `test_root_resolution.py`

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

---

## Phase 5: User Story 3 - Héritage de la catégorie par les sous-dossiers d'un voyage (Priority: P3)

**Goal**: Un sous-dossier ajouté à un dossier parent existant hérite automatiquement de son `RootLocation`, sans nouvel appel à `determine_root` (FR-006).

**Independent Test**: Résoudre un `RootLocation` pour un dossier parent, puis vérifier qu'un sous-dossier réutilise cet objet tel quel (même `nom`, même `type`) sans recalcul.

### Tests for User Story 3

- [X] T022 [P] [US3] Test d'intégration : le `RootLocation` d'un dossier parent est réutilisé tel quel pour un sous-dossier, sans nouvel appel à `determine_root`, dans `packages/regine-core/tests/integration/test_root_inheritance.py`

### Implementation for User Story 3

- [X] T023 [US3] Documenter (docstring) dans `root.py` que `determine_root` ne DOIT être appelée qu'une seule fois par dossier parent — le futur code appelant (import, `specs/001-import-photos`) réutilise l'objet `RootLocation` déjà résolu pour tout sous-dossier ; aucune nouvelle logique de calcul requise ici

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

---

## Phase 6: User Story 4 - Recatégorisation a posteriori (Priority: P4)

**Goal**: Un déplacement d'un dossier entre répertoires racines est reconnu comme tel par le mécanisme de réconciliation par hash (FR-008) — pas de mécanisme dédié à construire ici.

**Statut** : **Mise à jour du 2026-09-19** — le mécanisme de détection de déplacement par somme de contrôle a désormais sa propre spec et son propre plan (`specs/005-checkout-reconciliation`, dont FR-010 couvre exactement ce cas). Ce n'est plus une dépendance bloquante au niveau spécification : seulement une dépendance d'ordonnancement (l'implémentation de `specs/005` doit précéder la validation de bout en bout de cette user story).

- [X] T024 [US4] Documenter (docstring) dans `packages/regine-core/src/regine_core/dossier/root.py` qu'il n'existe aucune opération d'écriture directe sur `RootLocation` : tout changement de répertoire racine passe exclusivement par `regine_core.archive.reconciliation.comparer` (`specs/005-checkout-reconciliation`), qui classe un déplacement de dossier entier comme `deplacement` sans code supplémentaire ici
- [X] T024b [US4] Test d'intégration : déplacer un dossier de travail entre deux répertoires racine (année → catégorie) et vérifier que `regine_core.archive.reconciliation.comparer` le classe `deplacement`, dans `packages/regine-core/tests/integration/test_root_recategorisation.py` — **écart avec l'intitulé initial constaté à l'implémentation (2026-09-20)** : `comparer` ne raisonne que sur des chemins relatifs internes à un dossier, jamais sur son répertoire racine (cf. docstring de `test_whole_folder_move_detected_as_move_for_every_file`, specs/005) ; recatégoriser un dossier entier en conservant sa structure interne n'est donc PAS classé `deplacement`, mais **totalement invisible** pour `comparer` (chaque fichier reste « inchangé », omis du rapport) — c'est le comportement correct et recherché (aucune anomalie ni signalement erroné). Test écrit en conséquence avec `determine_root` (specs/004) pour les deux racines et `rapport.changements == []` comme assertion ; passe du premier coup, 135/135 tests

**Checkpoint**: US4 documentée et validée par test d'intégration — toutes les user stories de cette spec sont désormais complètes (28/28).

---

## Phase 7: Polish & Cross-Cutting Concerns

- [X] T025 [P] Exécuter manuellement les scénarios 1 à 4 de `specs/004-categorisation-dossiers/quickstart.md` et consigner le résultat — **2026-09-19 : les 4 scénarios passent** (déroulés via `uv run python3`, cf. session d'implémentation)
- [X] T026 [P] `ruff check --fix` sur `packages/regine-core` — 0 erreur restante, code formaté
- [X] T027 Mettre à jour `specs/004-categorisation-dossiers/contracts/regine-core-api.md` pour refléter la signature définitive de `determine_root` (paramètres `archive_root`/`local_root` explicites, cf. Note en tête de ce fichier)

---

## Dependencies & Execution Order

### Phase Dependencies

- **Setup (Phase 1)** : aucune dépendance — peut démarrer immédiatement.
- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories.
- **User Stories (Phase 3–6)** : dépendent toutes de Foundational.
  - US1 (P1) : aucune dépendance sur une autre user story.
  - US2 (P2) : dépend techniquement de T012 (branche année déjà en place dans `determine_root`) mais reste testable et livrable indépendamment de US1 côté utilisateur.
  - US3 (P3) : dépend de US1 et US2 étant données (réutilise leurs `RootLocation`), mais n'ajoute aucune nouvelle logique de calcul.
  - US4 (P4) : dépend de l'implémentation de `specs/005-checkout-reconciliation` (désormais spécifiée et planifiée) pour sa validation de bout en bout ; la tâche de documentation T024 reste exécutable dès maintenant.
- **Polish (Phase 7)** : dépend des user stories livrées (au minimum US1+US2).

### Parallel Opportunities

- T002–T006 (Phase 1) : tous marqués [P], fichiers/paquets distincts.
- T010 (Phase 2) : parallélisable une fois T009 terminé.
- T011 (US1), T014/T015 (US2), T022 (US3) : tests marqués [P], fichiers distincts.
- T017/T018 (US2) : parallélisables (même fichier `categories.py` mais fonctions indépendantes sans état partagé en cours d'écriture — à fusionner avec attention si exécutées par deux agents en parallèle).

---

## Parallel Example: User Story 2

```bash
# Lancer les tests de la User Story 2 ensemble :
Task: "Test unitaire determine_root avec catégorie dans packages/regine-core/tests/unit/test_root_resolution.py"
Task: "Test unitaire cache de catégories dans packages/regine-core/tests/unit/test_categories_cache.py"

# Lancer les fonctions indépendantes de config/categories.py ensemble :
Task: "Implémenter list_known_categories() dans packages/regine-core/src/regine_core/config/categories.py"
Task: "Implémenter suggest_categories(saisie) dans packages/regine-core/src/regine_core/config/categories.py"
```

---

## Implementation Strategy

### MVP First (User Story 1 uniquement)

1. Compléter Phase 1 (Setup) et Phase 2 (Foundational).
2. Compléter Phase 3 (US1) : placement par année par défaut.
3. **Arrêt et validation** : exécuter le Scénario 1 de `quickstart.md`.
4. Ce socle (monorepo + `RootLocation` + base de contexte) sert aussi de fondation aux futures tâches de `specs/001`, `002` et `003`.

### Livraison incrémentale

1. Setup + Foundational → socle prêt.
2. US1 → placement par année (MVP) → valider Scénario 1.
3. US2 → catégories thématiques → valider Scénario 2 et 3.
4. US3 → héritage voyage → valider Scénario 4.
5. US4 → documentation exécutable dès maintenant (T024) ; validation de bout en bout (T024b) une fois `specs/005-checkout-reconciliation` implémentée.

## Notes

- [P] = fichiers différents, sans dépendance non résolue.
- Chaque user story est livrable et testable indépendamment ; US4 dépend de l'implémentation de `specs/005-checkout-reconciliation` (désormais spécifiée et planifiée) pour sa validation de bout en bout, mais sa tâche de documentation (T024) ne l'est pas.
- Committer après chaque tâche ou groupe logique de tâches.
- Le squelette monorepo (Phase 1) et la base de contexte (T009) sont des fondations partagées : ne pas les recréer lors des futures exécutions de `/speckit-tasks` sur `specs/001`, `002` ou `003` — les étendre.