fonzarely/regine-photos-archiverpublic⑂ Fork 0
⑂ 23745dd
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-18T23:13:28+02:00 Browse files
23745dd parent: 40fcb39
added specs/004-categorisation-dossiers/tasks.md +188 -0
new file mode 100644
@@ -0,0 +1,188 @@
1+---
2+
3+description: "Task list for feature implementation"
4+---
5+
6+# Tasks: Catégorisation des dossiers à la racine de l'archive
7+
8+**Input**: Design documents from `/specs/004-categorisation-dossiers/`
9+**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/regine-core-api.md, quickstart.md
10+
11+**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.
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+## État du dépôt au moment de la génération
16+
17+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.
18+
19+**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`.
20+
21+## Phase 1: Setup (Shared Infrastructure)
22+
23+**Purpose**: Initialisation du monorepo — aucun paquet n'existe encore.
24+
25+- [ ] 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)
26+- [ ] T002 [P] Initialiser `packages/regine-core/pyproject.toml` (nom `regine-core`, aucune dépendance tierce) et `packages/regine-core/src/regine_core/__init__.py`
27+- [ ] T003 [P] Initialiser `packages/regine-cli/pyproject.toml` (dépendance vers `regine-core`) et `packages/regine-cli/src/regine_cli/__init__.py`
28+- [ ] 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`
29+- [ ] T005 [P] Configurer `ruff` (lint + format) au niveau du `pyproject.toml` racine du workspace
30+- [ ] T006 [P] Configurer `pytest` pour `packages/regine-core` (`[tool.pytest.ini_options]`, création des dossiers `tests/unit/`, `tests/integration/`, `tests/contract/`)
31+
32+---
33+
34+## Phase 2: Foundational (Blocking Prerequisites)
35+
36+**Purpose**: Infrastructure partagée requise par toutes les user stories de cette fonctionnalité.
37+
38+**⚠️ CRITICAL**: Aucune user story ne peut être implémentée avant la fin de cette phase.
39+
40+- [ ] T007 Créer `packages/regine-core/src/regine_core/dossier/__init__.py` (premier code du module `dossier`, cf. `docs/interface-cli-gui-architecture.md`)
41+- [ ] 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`
42+- [ ] 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)
43+- [ ] 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)
44+
45+**Checkpoint**: Fondations prêtes — les phases User Story peuvent commencer.
46+
47+---
48+
49+## Phase 3: User Story 1 - Placement par année par défaut (Priority: P1) 🎯 MVP
50+
51+**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).
52+
53+**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.
54+
55+### Tests for User Story 1
56+
57+- [ ] 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`
58+
59+### Implementation for User Story 1
60+
61+- [ ] 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)
62+- [ ] T013 [US1] Garantir par construction que `chemin_archive.name == chemin_local.name == nom` (miroir archive/local, FR-002) — pas de synchronisation a posteriori
63+
64+**Checkpoint**: User Story 1 fonctionnelle et testable indépendamment (MVP).
65+
66+---
67+
68+## Phase 4: User Story 2 - Import relevant d'une catégorie thématique (Priority: P2)
69+
70+**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).
71+
72+**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")`.
73+
74+### Tests for User Story 2
75+
76+- [ ] 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`
77+- [ ] 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)
78+
79+### Implementation for User Story 2
80+
81+- [ ] 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)
82+- [ ] T017 [P] [US2] Implémenter `list_known_categories()` dans `packages/regine-core/src/regine_core/config/categories.py` (dépend de T016)
83+- [ ] 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)
84+- [ ] 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)
85+- [ ] T020 [US2] Implémenter la branche catégorie de `determine_root` dans `root.py` (dépend de T012)
86+- [ ] 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`
87+
88+**Checkpoint**: User Stories 1 ET 2 fonctionnelles indépendamment.
89+
90+---
91+
92+## Phase 5: User Story 3 - Héritage de la catégorie par les sous-dossiers d'un voyage (Priority: P3)
93+
94+**Goal**: Un sous-dossier ajouté à un dossier parent existant hérite automatiquement de son `RootLocation`, sans nouvel appel à `determine_root` (FR-006).
95+
96+**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.
97+
98+### Tests for User Story 3
99+
100+- [ ] 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`
101+
102+### Implementation for User Story 3
103+
104+- [ ] 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
105+
106+**Checkpoint**: User Stories 1, 2 et 3 fonctionnelles indépendamment.
107+
108+---
109+
110+## Phase 6: User Story 4 - Recatégorisation a posteriori (Priority: P4)
111+
112+**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.
113+
114+**Statut** : **Bloqué**. Le mécanisme de détection de déplacement par somme de contrôle (notes de conception, section 8) n'a pas encore de spécification ni d'implémentation dans ce dépôt. Aucune tâche de code n'est exécutable pour cette user story à ce stade.
115+
116+- [ ] 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 un déplacement physique détecté à la réconciliation (mécanisme externe, hors périmètre de ce module)
117+
118+**Checkpoint**: US4 documentée comme dépendance externe ; à reprendre quand le mécanisme de réconciliation aura sa propre spec/plan/tasks.
119+
120+---
121+
122+## Phase 7: Polish & Cross-Cutting Concerns
123+
124+- [ ] T025 [P] Exécuter manuellement les scénarios 1 à 4 de `specs/004-categorisation-dossiers/quickstart.md` et consigner le résultat
125+- [ ] T026 [P] `ruff check --fix` sur `packages/regine-core`
126+- [ ] 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)
127+
128+---
129+
130+## Dependencies & Execution Order
131+
132+### Phase Dependencies
133+
134+- **Setup (Phase 1)** : aucune dépendance — peut démarrer immédiatement.
135+- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories.
136+- **User Stories (Phase 3–6)** : dépendent toutes de Foundational.
137+ - US1 (P1) : aucune dépendance sur une autre user story.
138+ - 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.
139+ - US3 (P3) : dépend de US1 et US2 étant données (réutilise leurs `RootLocation`), mais n'ajoute aucune nouvelle logique de calcul.
140+ - US4 (P4) : bloquée par une dépendance externe (mécanisme de réconciliation non spécifié) — aucune tâche de code à ce stade.
141+- **Polish (Phase 7)** : dépend des user stories livrées (au minimum US1+US2).
142+
143+### Parallel Opportunities
144+
145+- T002–T006 (Phase 1) : tous marqués [P], fichiers/paquets distincts.
146+- T010 (Phase 2) : parallélisable une fois T009 terminé.
147+- T011 (US1), T014/T015 (US2), T022 (US3) : tests marqués [P], fichiers distincts.
148+- 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).
149+
150+---
151+
152+## Parallel Example: User Story 2
153+
154+```bash
155+# Lancer les tests de la User Story 2 ensemble :
156+Task: "Test unitaire determine_root avec catégorie dans packages/regine-core/tests/unit/test_root_resolution.py"
157+Task: "Test unitaire cache de catégories dans packages/regine-core/tests/unit/test_categories_cache.py"
158+
159+# Lancer les fonctions indépendantes de config/categories.py ensemble :
160+Task: "Implémenter list_known_categories() dans packages/regine-core/src/regine_core/config/categories.py"
161+Task: "Implémenter suggest_categories(saisie) dans packages/regine-core/src/regine_core/config/categories.py"
162+```
163+
164+---
165+
166+## Implementation Strategy
167+
168+### MVP First (User Story 1 uniquement)
169+
170+1. Compléter Phase 1 (Setup) et Phase 2 (Foundational).
171+2. Compléter Phase 3 (US1) : placement par année par défaut.
172+3. **Arrêt et validation** : exécuter le Scénario 1 de `quickstart.md`.
173+4. Ce socle (monorepo + `RootLocation` + base de contexte) sert aussi de fondation aux futures tâches de `specs/001`, `002` et `003`.
174+
175+### Livraison incrémentale
176+
177+1. Setup + Foundational → socle prêt.
178+2. US1 → placement par année (MVP) → valider Scénario 1.
179+3. US2 → catégories thématiques → valider Scénario 2 et 3.
180+4. US3 → héritage voyage → valider Scénario 4.
181+5. US4 → reste bloquée en documentation jusqu'à ce que le mécanisme de réconciliation par hash ait sa propre spec.
182+
183+## Notes
184+
185+- [P] = fichiers différents, sans dépendance non résolue.
186+- Chaque user story est livrable et testable indépendamment, à l'exception de US4 (dépendance externe non encore spécifiée).
187+- Committer après chaque tâche ou groupe logique de tâches.
188+- 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.
new file mode 100644
@@ -0,0 +1,188 @@
1+---
2+
3+description: "Task list for feature implementation"
4+---
5+
6+# Tasks: Catégorisation des dossiers à la racine de l'archive
7+
8+**Input**: Design documents from `/specs/004-categorisation-dossiers/`
9+**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/regine-core-api.md, quickstart.md
10+
11+**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.
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+## État du dépôt au moment de la génération
16+
17+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.
18+
19+**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`.
20+
21+## Phase 1: Setup (Shared Infrastructure)
22+
23+**Purpose**: Initialisation du monorepo — aucun paquet n'existe encore.
24+
25+- [ ] 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)
26+- [ ] T002 [P] Initialiser `packages/regine-core/pyproject.toml` (nom `regine-core`, aucune dépendance tierce) et `packages/regine-core/src/regine_core/__init__.py`
27+- [ ] T003 [P] Initialiser `packages/regine-cli/pyproject.toml` (dépendance vers `regine-core`) et `packages/regine-cli/src/regine_cli/__init__.py`
28+- [ ] 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`
29+- [ ] T005 [P] Configurer `ruff` (lint + format) au niveau du `pyproject.toml` racine du workspace
30+- [ ] T006 [P] Configurer `pytest` pour `packages/regine-core` (`[tool.pytest.ini_options]`, création des dossiers `tests/unit/`, `tests/integration/`, `tests/contract/`)
31+
32+---
33+
34+## Phase 2: Foundational (Blocking Prerequisites)
35+
36+**Purpose**: Infrastructure partagée requise par toutes les user stories de cette fonctionnalité.
37+
38+**⚠️ CRITICAL**: Aucune user story ne peut être implémentée avant la fin de cette phase.
39+
40+- [ ] T007 Créer `packages/regine-core/src/regine_core/dossier/__init__.py` (premier code du module `dossier`, cf. `docs/interface-cli-gui-architecture.md`)
41+- [ ] 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`
42+- [ ] 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)
43+- [ ] 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)
44+
45+**Checkpoint**: Fondations prêtes — les phases User Story peuvent commencer.
46+
47+---
48+
49+## Phase 3: User Story 1 - Placement par année par défaut (Priority: P1) 🎯 MVP
50+
51+**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).
52+
53+**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.
54+
55+### Tests for User Story 1
56+
57+- [ ] 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`
58+
59+### Implementation for User Story 1
60+
61+- [ ] 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)
62+- [ ] T013 [US1] Garantir par construction que `chemin_archive.name == chemin_local.name == nom` (miroir archive/local, FR-002) — pas de synchronisation a posteriori
63+
64+**Checkpoint**: User Story 1 fonctionnelle et testable indépendamment (MVP).
65+
66+---
67+
68+## Phase 4: User Story 2 - Import relevant d'une catégorie thématique (Priority: P2)
69+
70+**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).
71+
72+**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")`.
73+
74+### Tests for User Story 2
75+
76+- [ ] 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`
77+- [ ] 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)
78+
79+### Implementation for User Story 2
80+
81+- [ ] 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)
82+- [ ] T017 [P] [US2] Implémenter `list_known_categories()` dans `packages/regine-core/src/regine_core/config/categories.py` (dépend de T016)
83+- [ ] 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)
84+- [ ] 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)
85+- [ ] T020 [US2] Implémenter la branche catégorie de `determine_root` dans `root.py` (dépend de T012)
86+- [ ] 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`
87+
88+**Checkpoint**: User Stories 1 ET 2 fonctionnelles indépendamment.
89+
90+---
91+
92+## Phase 5: User Story 3 - Héritage de la catégorie par les sous-dossiers d'un voyage (Priority: P3)
93+
94+**Goal**: Un sous-dossier ajouté à un dossier parent existant hérite automatiquement de son `RootLocation`, sans nouvel appel à `determine_root` (FR-006).
95+
96+**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.
97+
98+### Tests for User Story 3
99+
100+- [ ] 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`
101+
102+### Implementation for User Story 3
103+
104+- [ ] 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
105+
106+**Checkpoint**: User Stories 1, 2 et 3 fonctionnelles indépendamment.
107+
108+---
109+
110+## Phase 6: User Story 4 - Recatégorisation a posteriori (Priority: P4)
111+
112+**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.
113+
114+**Statut** : **Bloqué**. Le mécanisme de détection de déplacement par somme de contrôle (notes de conception, section 8) n'a pas encore de spécification ni d'implémentation dans ce dépôt. Aucune tâche de code n'est exécutable pour cette user story à ce stade.
115+
116+- [ ] 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 un déplacement physique détecté à la réconciliation (mécanisme externe, hors périmètre de ce module)
117+
118+**Checkpoint**: US4 documentée comme dépendance externe ; à reprendre quand le mécanisme de réconciliation aura sa propre spec/plan/tasks.
119+
120+---
121+
122+## Phase 7: Polish & Cross-Cutting Concerns
123+
124+- [ ] T025 [P] Exécuter manuellement les scénarios 1 à 4 de `specs/004-categorisation-dossiers/quickstart.md` et consigner le résultat
125+- [ ] T026 [P] `ruff check --fix` sur `packages/regine-core`
126+- [ ] 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)
127+
128+---
129+
130+## Dependencies & Execution Order
131+
132+### Phase Dependencies
133+
134+- **Setup (Phase 1)** : aucune dépendance — peut démarrer immédiatement.
135+- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories.
136+- **User Stories (Phase 3–6)** : dépendent toutes de Foundational.
137+ - US1 (P1) : aucune dépendance sur une autre user story.
138+ - 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.
139+ - US3 (P3) : dépend de US1 et US2 étant données (réutilise leurs `RootLocation`), mais n'ajoute aucune nouvelle logique de calcul.
140+ - US4 (P4) : bloquée par une dépendance externe (mécanisme de réconciliation non spécifié) — aucune tâche de code à ce stade.
141+- **Polish (Phase 7)** : dépend des user stories livrées (au minimum US1+US2).
142+
143+### Parallel Opportunities
144+
145+- T002–T006 (Phase 1) : tous marqués [P], fichiers/paquets distincts.
146+- T010 (Phase 2) : parallélisable une fois T009 terminé.
147+- T011 (US1), T014/T015 (US2), T022 (US3) : tests marqués [P], fichiers distincts.
148+- 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).
149+
150+---
151+
152+## Parallel Example: User Story 2
153+
154+```bash
155+# Lancer les tests de la User Story 2 ensemble :
156+Task: "Test unitaire determine_root avec catégorie dans packages/regine-core/tests/unit/test_root_resolution.py"
157+Task: "Test unitaire cache de catégories dans packages/regine-core/tests/unit/test_categories_cache.py"
158+
159+# Lancer les fonctions indépendantes de config/categories.py ensemble :
160+Task: "Implémenter list_known_categories() dans packages/regine-core/src/regine_core/config/categories.py"
161+Task: "Implémenter suggest_categories(saisie) dans packages/regine-core/src/regine_core/config/categories.py"
162+```
163+
164+---
165+
166+## Implementation Strategy
167+
168+### MVP First (User Story 1 uniquement)
169+
170+1. Compléter Phase 1 (Setup) et Phase 2 (Foundational).
171+2. Compléter Phase 3 (US1) : placement par année par défaut.
172+3. **Arrêt et validation** : exécuter le Scénario 1 de `quickstart.md`.
173+4. Ce socle (monorepo + `RootLocation` + base de contexte) sert aussi de fondation aux futures tâches de `specs/001`, `002` et `003`.
174+
175+### Livraison incrémentale
176+
177+1. Setup + Foundational → socle prêt.
178+2. US1 → placement par année (MVP) → valider Scénario 1.
179+3. US2 → catégories thématiques → valider Scénario 2 et 3.
180+4. US3 → héritage voyage → valider Scénario 4.
181+5. US4 → reste bloquée en documentation jusqu'à ce que le mécanisme de réconciliation par hash ait sa propre spec.
182+
183+## Notes
184+
185+- [P] = fichiers différents, sans dépendance non résolue.
186+- Chaque user story est livrable et testable indépendamment, à l'exception de US4 (dépendance externe non encore spécifiée).
187+- Committer après chaque tâche ou groupe logique de tâches.
188+- 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.