Research: Checkout et réconciliation d'un dossier de l'archive
1. Schéma du manifeste persistant
Decision: Un fichier SQLite unique par dossier principal (dossier simple, ou dossier parent avec tous ses sous-dossiers), à sa racine sur l'archive — conventions déjà actées par la constitution du projet (PRAGMA user_version pour le numéro de schéma, PRAGMA application_id pour marquer le format Régine). Table fichiers : chemin_relatif, taille, hash_fichier_entier, hash_image_only (nullable, DNG/TIFF/JPEG uniquement), identifiant_perenne. Table verrou : verrouille (bool), identifiant_session, horodatage (cf. research.md § 3).
Rationale: La constitution du projet (§ Workflow d'archivage) et les notes de conception (section 12) ont déjà tranché ce choix pour ce besoin précis — pas une nouvelle décision à ce stade, une application directe. Regrouper manifeste et verrou dans le même fichier évite d'inventer un second mécanisme de persistance pour un état étroitement lié (le verrou n'a de sens que par rapport à un manifeste existant).
Alternatives considered:
- Fichier JSON pour le manifeste — déjà écarté par la constitution (pas d'écriture transactionnelle, risque de corruption en cas de coupure, cf. § Workflow d'archivage).
- Verrou dans un fichier séparé (ex.
.regine-lock) — rejeté : duplique un mécanisme de persistance pour un état qui vit naturellement à côté du manifeste, dans le même fichier.
2. Versionnement du schéma du manifeste (FR-018)
Decision: Encoder PRAGMA user_version comme un entier structurel * 1000 + additif. À l'ouverture, le code refuse explicitement (erreur claire, pas de tentative d'interprétation) si structurel lu est supérieur au structurel que la version courante du logiciel sait traiter ; il tolère un additif supérieur (ignore les champs qu'il ne connaît pas encore).
Rationale: Répond à FR-018 (distinguer évolution additive tolérée / évolution structurelle refusée) avec un mécanisme simple, entièrement dans la bibliothèque standard (sqlite3 expose PRAGMA user_version nativement), sans dépendance à un outil de migration externe. Cohérent avec la référence de la constitution à core.repositoryformatversion de Git comme précédent de conception pour ce genre de garde-fou.
Alternatives considered:
- Outil de migration de schéma tiers (ex.
alembic) — rejeté : complexité disproportionnée pour un schéma aussi simple et un besoin de version déjà entièrement couvert par les pragmas SQLite natifs.
3. Verrouillage de dossier sur un partage réseau ordinaire
Decision: Le verrou est une ligne dans la table verrou du manifeste (posée au checkout, vérifiée avant tout nouveau checkout, levée à la réconciliation réussie). C'est une protection applicative de bonne foi : toute instance de Régine la respecte, mais rien n'empêche techniquement une écriture concurrente hors de Régine (édition directe du fichier NAS, autre logiciel) — limitation assumée et déjà annoncée par la constitution elle-même (« protections filesystem... en filet de sécurité complémentaire, plus comme mécanisme principal »).
Rationale: Un partage SMB ordinaire n'offre pas de primitive de verrouillage distribué fiable et portable entre systèmes d'exploitation ; construire un mécanisme de verrouillage réseau robuste (ex. bail avec expiration, élection) serait une complexité disproportionnée pour un usage mono-utilisateur avec, au pire, deux instances de la même personne (cf. Assumptions de la spec : le travail à plusieurs avec verrouillage fin est explicitement hors périmètre).
Alternatives considered:
- Verrouillage au niveau du système de fichiers (ex. fichier
.lockavecflock) — rejeté :flockn'est pas fiable sur tous les montages SMB selon la configuration du serveur et du client, alors qu'une ligne dans une base déjà lue/écrite de façon transactionnelle par Régine est un mécanisme plus prévisible. - Service de verrouillage distribué (ex. base centrale accessible par API réseau) — rejeté : introduirait un démon/service, contraire à la contrainte « exécution sans démon » déjà actée par la constitution.
4. Détection de renommage/déplacement par contenu
Decision: Construire, pour la comparaison, un index hash → chemin(s) à la fois pour le manifeste et pour la copie de travail (hash_image_only quand disponible pour DNG/TIFF/JPEG, sinon hash_fichier_entier). Une entrée présente dans les deux index mais sous un chemin différent est un renommage/déplacement ; ce même mécanisme couvre indifféremment un renommage de fichier, une promotion vers la racine de sélection, et un déplacement de dossier entier vers un autre répertoire racine (specs/004-categorisation-dossiers FR-010).
Rationale: Un seul algorithme couvre tous les cas de FR-009/FR-010 sans traitement spécial, cohérent avec la spec elle-même qui insiste sur ce point (« pas un cas à part »).
Alternatives considered:
- Traiter la promotion à la racine ou le déplacement de dossier comme des opérations dédiées, détectées séparément — rejeté : complexité et risque de divergence de comportement pour des cas fondamentalement identiques (contenu inchangé, chemin différent).
5. Calcul de ImageDataHash pour DNG/TIFF/JPEG
Decision: Étendre regine_core.metadata.exif (créé par specs/002-profil-boitiers-optionnel) avec read_image_data_hash(chemin) -> str, invoquant exiftool -api ImageHashType=SHA256 -ImageDataHash sur le même processus persistant déjà utilisé pour Model/BodySerialNumber/DateTimeOriginal.
Rationale: Réutilise le mécanisme déjà en place plutôt que d'en créer un second ; les notes de conception du projet (section 11) documentent déjà ce choix précis (ImageDataHash d'ExifTool) comme la solution retenue.
Alternatives considered: aucune — la décision est déjà actée par les notes de conception du projet, pas un nouveau choix à faire ici.
Résumé
Tous les points du Technical Context sont résolus. Aucune dépendance tierce Python nouvelle. Une limitation est explicitement documentée plutôt que masquée : le verrouillage reste une protection de bonne foi entre instances de Régine, pas une garantie absolue contre toute écriture concurrente externe.
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 |
|