|
| 1 | +# M1 — Data Layer |
| 2 | + |
| 3 | +> Objectif : aligner le schema DB et les Pydantic sur l'architecture KB / work_order |
| 4 | +> décidée dans `technical.md` §2.4. |
| 5 | +> Bloquant pour M2, M3, M4. |
| 6 | +
|
| 7 | +--- |
| 8 | + |
| 9 | +## Issue M1.1 — Migration 007 : `equipment_kb` colonnes manquantes |
| 10 | + |
| 11 | +**Scope.** Étendre la table `equipment_kb` (créée en migration 005) avec les colonnes que |
| 12 | +les agents et le frontend Onboarding attendent. |
| 13 | + |
| 14 | +**Fichier.** `backend/infrastructure/database/migrations/versions/007_aria_kb_workorder_extension.up.sql` |
| 15 | + |
| 16 | +**Colonnes à ajouter.** |
| 17 | + |
| 18 | +| Colonne | Type | Default | Rôle | |
| 19 | +|-----------------------|---------------|---------------|-----------------------------------------| |
| 20 | +| `structured_data` | `jsonb` | `'{}'::jsonb` | Document principal validé Pydantic | |
| 21 | +| `raw_markdown` | `text` | `null` | Sortie brute Opus avant parsing (audit) | |
| 22 | +| `confidence_score` | `real` | `0.0` | Complétude 0.0–1.0 | |
| 23 | +| `last_enriched_at` | `timestamptz` | `null` | Trace dernier enrichissement | |
| 24 | +| `onboarding_complete` | `boolean` | `false` | Flag UI Onboarding | |
| 25 | + |
| 26 | +**Décision — migration destructive.** |
| 27 | +✅ **DROP** des 3 colonnes héritées (`nominal_specs`, `common_failure_modes`, |
| 28 | +`maintenance_recommendations`). On est en dev, on drop la DB et on rejoue les |
| 29 | +migrations from scratch. `structured_data` est la seule source de vérité KB. |
| 30 | + |
| 31 | +À faire dans la même migration 007 : |
| 32 | +```sql |
| 33 | +ALTER TABLE equipment_kb |
| 34 | + DROP COLUMN nominal_specs, |
| 35 | + DROP COLUMN common_failure_modes, |
| 36 | + DROP COLUMN maintenance_recommendations; |
| 37 | +``` |
| 38 | + |
| 39 | +Mettre à jour `006_aria_seed_p02.up.sql` pour seeder directement `structured_data` |
| 40 | +avec un blob `EquipmentKB` minimal valide (Grundfos CR 32-2 réaliste pour P-02). |
| 41 | + |
| 42 | +**Acceptance.** |
| 43 | +- [ ] `make reset-db` (drop + recreate + apply 001→007) passe sans erreur |
| 44 | +- [ ] `\d equipment_kb` ne montre QUE les nouvelles colonnes (plus les 3 héritées) |
| 45 | +- [ ] Seed 006 produit un `structured_data` parsable par `EquipmentKB.model_validate()` |
| 46 | +- [ ] Pas de fichier `.down.sql` |
| 47 | + |
| 48 | +--- |
| 49 | + |
| 50 | +## Issue M1.2 — Migration 007 : `work_order` colonnes manquantes |
| 51 | + |
| 52 | +**Scope.** Étendre `work_order` pour porter les sorties d'agents. |
| 53 | + |
| 54 | +**Colonnes à ajouter.** |
| 55 | + |
| 56 | +| Colonne | Type | Default | Rôle | |
| 57 | +|------------------------|---------------|---------|------------------------------| |
| 58 | +| `rca_summary` | `text` | `null` | RCA produit par Investigator | |
| 59 | +| `recommended_actions` | `jsonb` | `null` | Actions structurées (steps) | |
| 60 | +| `generated_by_agent` | `boolean` | `false` | Distinguer manuels vs agents | |
| 61 | +| `trigger_anomaly_time` | `timestamptz` | `null` | Timestamp anomalie source | |
| 62 | + |
| 63 | +**Note sur `status`.** La contrainte actuelle est |
| 64 | +`CHECK (status IN ('open','in_progress','completed','cancelled'))`. Le flow agent |
| 65 | +introduit `'detected'` (Sentinel) et `'analyzed'` (post-Investigator). |
| 66 | + |
| 67 | +✅ **DÉCIDÉ — étendre le CHECK.** Migration 007 : |
| 68 | +```sql |
| 69 | +ALTER TABLE work_order DROP CONSTRAINT work_order_status_check; |
| 70 | +ALTER TABLE work_order ADD CONSTRAINT work_order_status_check |
| 71 | + CHECK (status IN ('detected','analyzed','open','in_progress','completed','cancelled')); |
| 72 | +``` |
| 73 | +Flow : `detected` (Sentinel) → `analyzed` (Investigator a posé `rca_summary`) → |
| 74 | +`open` (Work Order Generator a posé `recommended_actions`) → `in_progress` → |
| 75 | +`completed`. Frontend filtre/colore par statut. |
| 76 | + |
| 77 | +**Acceptance.** |
| 78 | +- [ ] Migration applique proprement |
| 79 | +- [ ] INSERT avec `status='detected'` accepté |
| 80 | +- [ ] `WorkOrderCreate` Pydantic mis à jour pour accepter ces champs (issue M1.6) |
| 81 | + |
| 82 | +--- |
| 83 | + |
| 84 | +## Issue M1.3 — Migration 007 : `failure_history.signal_patterns` |
| 85 | + |
| 86 | +**Scope.** Ajouter `signal_patterns jsonb` à `failure_history` pour que l'Investigator |
| 87 | +puisse stocker la signature signaux de la panne (utile pour le pattern matching dans |
| 88 | +les futures investigations). |
| 89 | + |
| 90 | +**Colonne.** `signal_patterns jsonb DEFAULT NULL` |
| 91 | + |
| 92 | +**Acceptance.** |
| 93 | +- [ ] Colonne présente |
| 94 | +- [ ] `FailureHistoryOut` Pydantic accepte le champ |
| 95 | + |
| 96 | +--- |
| 97 | + |
| 98 | +## Issue M1.4 — Pydantic `EquipmentKB` complet |
| 99 | + |
| 100 | +**Scope.** Créer `backend/modules/kb/kb_schema.py` avec les 4 classes définies dans |
| 101 | +`technical.md` §2.4 (`ThresholdValue`, `FailurePattern`, `MaintenanceProcedure`, |
| 102 | +`EquipmentKB`). |
| 103 | + |
| 104 | +**Critères de design.** |
| 105 | +- Tous les sous-champs ont des defaults sains pour permettre une KB partielle |
| 106 | + (pendant l'onboarding, beaucoup de champs sont vides) |
| 107 | +- `EquipmentKB.kb_meta` doit contenir au minimum : |
| 108 | + `{version, completeness_score, onboarding_complete, last_calibrated_by}` |
| 109 | +- Une méthode utilitaire `EquipmentKB.compute_completeness() -> float` qui retourne |
| 110 | + un score 0.0–1.0 |
| 111 | + |
| 112 | +✅ **DÉCIDÉ — `completeness_score` pondéré.** Algorithme : |
| 113 | +``` |
| 114 | +weights = { |
| 115 | + "thresholds": 0.50, # cœur de la valeur (Sentinel les utilise) |
| 116 | + "failure_patterns": 0.20, # base du pattern matching Investigator |
| 117 | + "maintenance_procedures": 0.20, # nourrit le Work Order Generator |
| 118 | + "equipment": 0.10, # métadonnées identifiantes |
| 119 | +} |
| 120 | +score = Σ weight_i × (champs_remplis_i / champs_attendus_i) |
| 121 | +``` |
| 122 | +Retourne float ∈ [0.0, 1.0]. Un threshold compte comme "rempli" si |
| 123 | +`alert IS NOT NULL`. Test seuil démo : Onboarding doit faire passer P-02 de ~0.40 |
| 124 | +(PDF only) à ~0.85 (après calibration opérateur) — c'est le moment "aha". |
| 125 | + |
| 126 | +**Acceptance.** |
| 127 | +- [ ] `from modules.kb.kb_schema import EquipmentKB; EquipmentKB(equipment={}, thresholds={}, ...).model_dump()` passe |
| 128 | +- [ ] `EquipmentKB.model_validate(json.loads(structured_data))` fonctionne sur seed P-02 |
| 129 | + |
| 130 | +--- |
| 131 | + |
| 132 | +## Issue M1.5 — Adapter `KbRepository.upsert()` pour `structured_data` |
| 133 | + |
| 134 | +**Scope.** Modifier `backend/modules/kb/repository.py` : |
| 135 | +- Étendre `JSON_FIELDS` pour inclure `structured_data` (et `signal_patterns` côté failures) |
| 136 | +- Réécrire `EquipmentKbUpsert` Pydantic dans `schemas.py` : |
| 137 | + `structured_data: EquipmentKB`, `raw_markdown: str | None`, |
| 138 | + `confidence_score: float`, `last_enriched_at: datetime | None`, |
| 139 | + `onboarding_complete: bool` |
| 140 | +- **Supprimer** toute référence aux 3 anciens champs (`nominal_specs`, |
| 141 | + `common_failure_modes`, `maintenance_recommendations`) dans `schemas.py`, |
| 142 | + `repository.py`, `router.py`. Pas de retro-compat (cf. M1.1, on drop la DB). |
| 143 | + |
| 144 | +✅ **DÉCIDÉ — pas de retro-compat.** Suppression complète des 3 anciens champs |
| 145 | +dans le code. Une seule source : `structured_data`. |
| 146 | + |
| 147 | +**Acceptance.** |
| 148 | +- [ ] `PUT /api/v1/kb/equipment` avec body contenant `structured_data` persiste correctement |
| 149 | +- [ ] `GET /api/v1/kb/equipment/2` retourne le `structured_data` parsé |
| 150 | +- [ ] Test : roundtrip Pydantic → JSON → DB → JSON → Pydantic identique |
| 151 | + |
| 152 | +--- |
| 153 | + |
| 154 | +## Issue M1.6 — Mettre à jour `WorkOrderCreate` / `WorkOrderUpdate` Pydantic |
| 155 | + |
| 156 | +**Scope.** Ajouter dans `backend/modules/work_order/schemas.py` les nouveaux champs |
| 157 | +de l'issue M1.2 : |
| 158 | +- `rca_summary: str | None` |
| 159 | +- `recommended_actions: Any | None` (JSONB) |
| 160 | +- `generated_by_agent: bool = False` |
| 161 | +- `trigger_anomaly_time: datetime | None` |
| 162 | + |
| 163 | +Étendre `JSON_FIELDS` du `WorkOrderRepository` pour inclure `recommended_actions`. |
| 164 | + |
| 165 | +**Acceptance.** |
| 166 | +- [ ] Création d'un work_order avec tous les nouveaux champs via API → 201 |
| 167 | +- [ ] Lecture renvoie les champs correctement décodés |
| 168 | + |
| 169 | +--- |
| 170 | + |
| 171 | +## Bloque |
| 172 | + |
| 173 | +- M2 (les tools `get_equipment_kb` et `update_equipment_kb` ont besoin du schema) |
| 174 | +- M3 (KB Builder produit du `structured_data`) |
| 175 | +- M4 (Sentinel lit `thresholds` depuis `structured_data`, Investigator écrit |
| 176 | + `rca_summary` + `failure_history.signal_patterns`) |
0 commit comments