Entropyk/_bmad-output/implementation-artifacts/coherence-audit-remediation-plan.md

551 lines
16 KiB
Markdown

# Plan de Correction Post-Audit de Cohérence
**Date:** 2026-02-22
**Auteur:** Audit Architecture Rust & BMAD
**Priorité:** CRITIQUE
**Statut:** À implémenter
---
## Résumé Exécutif
Cet audit a identifié **12 écarts de cohérence** répartis en 3 catégories de priorité. Les problèmes les plus critiques concernent l'Epic 7 (Validation) où l'implémentation incomplète des méthodes `energy_transfers()` et `port_enthalpies()` empêche une validation thermodynamique correcte.
**Impact métier:** Sans ces corrections, un système thermodynamique contenant un détendeur, des jonctions ou des conditions aux limites NE sera PAS correctement validé, ce qui peut masquer des erreurs physiques graves.
---
## Catalogue des User Stories de Correction
### Epic 9: Correction de Cohérence (Nouvel Epic)
#### Story 9.1: Unification des Types Duplicats - CircuitId
**Priorité:** P1-CRITIQUE
**Estimation:** 2h
**Dépendances:** Aucune
**Story:**
> En tant que développeur Rust,
> Je veux un type `CircuitId` unique et cohérent,
> Afin d'éviter les erreurs de compilation lors de l'utilisation conjointe des modules solver et components.
**Problème actuel:**
```rust
// crates/solver/src/system.rs:31
pub struct CircuitId(pub u8); // Représentation numérique
// crates/components/src/state_machine.rs:332
pub struct CircuitId(String); // Représentation textuelle
```
**Solution proposée:**
1. Garder `CircuitId(u8)` dans `crates/core/src/types.rs` pour performance
2. Ajouter `impl From<&str> for CircuitId` avec hash interne
3. Supprimer `CircuitId` de `state_machine.rs`
4. Ré-exporter depuis `entropyk_core`
**Fichiers à modifier:**
- `crates/core/src/types.rs` (ajout)
- `crates/solver/src/system.rs` (suppression, ré-import)
- `crates/components/src/state_machine.rs` (suppression, ré-import)
- `crates/entropyk/src/lib.rs` (mise à jour exports)
**Critères d'acceptation:**
- [ ] Un seul `CircuitId` dans la codebase
- [ ] Conversion `From<&str>` et `From<u8>` disponibles
- [ ] `cargo test --workspace` passe
- [ ] `cargo clippy -- -D warnings` passe
---
#### Story 9.2: Unification des Types Duplicats - FluidId
**Priorité:** P1-CRITIQUE
**Estimation:** 2h
**Dépendances:** Aucune
**Story:**
> En tant que développeur Rust,
> Je veux un type `FluidId` unique avec API cohérente,
> Afin d'éviter la confusion entre `fluids::FluidId` et `components::port::FluidId`.
**Problème actuel:**
```rust
// crates/fluids/src/types.rs:35
pub struct FluidId(pub String); // Champ public
// crates/components/src/port.rs:137
pub struct FluidId(String); // Champ privé, méthode as_str()
```
**Solution proposée:**
1. Garder `FluidId` dans `crates/fluids/src/types.rs` comme source unique
2. Exposer `as_str()` et garder champ public pour compatibilité
3. Supprimer `FluidId` de `port.rs`
4. Ré-importer depuis `entropyk_fluids`
**Fichiers à modifier:**
- `crates/fluids/src/types.rs` (ajout méthode `as_str()`)
- `crates/components/src/port.rs` (suppression, ré-import)
- `crates/components/src/lib.rs` (mise à jour exports)
**Critères d'acceptation:**
- [ ] Un seul `FluidId` dans la codebase
- [ ] Méthode `as_str()` disponible
- [ ] Champ `0` accessible pour compatibilité
- [ ] `cargo test --workspace` passe
---
#### Story 9.3: Complétion Epic 7 - ExpansionValve Energy Methods
**Priorité:** P1-CRITIQUE
**Estimation:** 3h
**Dépendances:** Story 9.2
**Story:**
> En tant que moteur de simulation thermodynamique,
> Je veux que `ExpansionValve` implémente `energy_transfers()` et `port_enthalpies()`,
> Afin que le bilan énergétique soit correctement validé pour les cycles frigorifiques.
**Problème actuel:**
- `ExpansionValve` implémente seulement `port_mass_flows()`
- Le détendeur est **ignoré** dans `check_energy_balance()`
- Or, c'est un composant critique de tout cycle frigorifique
**Solution proposée:**
```rust
// Dans crates/components/src/expansion_valve.rs
impl Component for ExpansionValve<Connected> {
// ... existing code ...
fn port_enthalpies(
&self,
_state: &SystemState,
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
// Retourne les enthalpies des ports (ordre: inlet, outlet)
Ok(vec![
self.port_inlet.enthalpy(),
self.port_outlet.enthalpy(),
])
}
fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> {
// Détendeur isenthalpique: Q=0, W=0
// (Pas de transfert thermique, pas de travail)
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
}
}
```
**Fichiers à modifier:**
- `crates/components/src/expansion_valve.rs`
**Critères d'acceptation:**
- [ ] `energy_transfers()` retourne `(Power(0), Power(0))`
- [ ] `port_enthalpies()` retourne `[h_in, h_out]`
- [ ] Test unitaire `test_expansion_valve_energy_balance` passe
- [ ] `check_energy_balance()` ne skip plus `ExpansionValve`
---
#### Story 9.4: Complétion Epic 7 - FlowSource/FlowSink Energy Methods
**Priorité:** P1-CRITIQUE
**Estimation:** 3h
**Dépendances:** Story 9.2
**Story:**
> En tant que moteur de simulation thermodynamique,
> Je veux que `FlowSource` et `FlowSink` implémentent `energy_transfers()` et `port_enthalpies()`,
> Afin que les conditions aux limites soient correctement prises en compte dans le bilan énergétique.
**Problème actuel:**
- `FlowSource` et `FlowSink` implémentent seulement `port_mass_flows()`
- Ces composants sont ignorés dans la validation
**Solution proposée:**
```rust
// Dans crates/components/src/flow_boundary.rs
impl Component for FlowSource {
// ... existing code ...
fn port_enthalpies(
&self,
_state: &SystemState,
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
Ok(vec![self.port.enthalpy()])
}
fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> {
// Source: pas de transfert actif, le fluide "apparaît" avec son enthalpie
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
}
}
impl Component for FlowSink {
// ... existing code ...
fn port_enthalpies(
&self,
_state: &SystemState,
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
Ok(vec![self.port.enthalpy()])
}
fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> {
// Sink: pas de transfert actif
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
}
}
```
**Fichiers à modifier:**
- `crates/components/src/flow_boundary.rs`
**Critères d'acceptation:**
- [ ] `FlowSource` et `FlowSink` implémentent les 3 méthodes
- [ ] Tests unitaires associés passent
- [ ] `check_energy_balance()` ne skip plus ces composants
---
#### Story 9.5: Complétion Epic 7 - FlowSplitter/FlowMerger Energy Methods
**Priorité:** P1-CRITIQUE
**Estimation:** 4h
**Dépendances:** Story 9.2
**Story:**
> En tant que moteur de simulation thermodynamique,
> Je veux que `FlowSplitter` et `FlowMerger` implémentent `energy_transfers()` et `port_enthalpies()`,
> Afin que les jonctions soient correctement prises en compte dans le bilan énergétique.
**Problème actuel:**
- `FlowSplitter` et `FlowMerger` implémentent seulement `port_mass_flows()`
- Les jonctions sont ignorées dans la validation
**Solution proposée:**
```rust
// Dans crates/components/src/flow_junction.rs
impl Component for FlowSplitter {
// ... existing code ...
fn port_enthalpies(
&self,
_state: &SystemState,
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
// Ordre: inlet, puis outlets
let mut enthalpies = vec![self.port_inlet.enthalpy()];
for outlet in &self.ports_outlet {
enthalpies.push(outlet.enthalpy());
}
Ok(enthalpies)
}
fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> {
// Jonction adiabatique: Q=0, W=0
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
}
}
impl Component for FlowMerger {
// ... existing code ...
fn port_enthalpies(
&self,
_state: &SystemState,
) -> Result<Vec<entropyk_core::Enthalpy>, ComponentError> {
// Ordre: inlets, puis outlet
let mut enthalpies = Vec::new();
for inlet in &self.ports_inlet {
enthalpies.push(inlet.enthalpy());
}
enthalpies.push(self.port_outlet.enthalpy());
Ok(enthalpies)
}
fn energy_transfers(&self, _state: &SystemState) -> Option<(Power, Power)> {
// Jonction adiabatique: Q=0, W=0
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
}
}
```
**Fichiers à modifier:**
- `crates/components/src/flow_junction.rs`
**Critères d'acceptation:**
- [ ] `FlowSplitter` et `FlowMerger` implémentent les 3 méthodes
- [ ] Tests unitaires associés passent
- [ ] `check_energy_balance()` ne skip plus ces composants
---
#### Story 9.6: Amélioration Logging Validation Énergie
**Priorité:** P2-IMPORTANTE
**Estimation:** 1h
**Dépendances:** Stories 9.3, 9.4, 9.5
**Story:**
> En tant que développeur debuggant une simulation,
> Je veux un avertissement explicite quand des composants sont ignorés dans la validation énergétique,
> Afin d'identifier rapidement les implémentations manquantes.
**Problème actuel:**
```rust
// crates/solver/src/system.rs:1873-1879
_ => {
components_skipped += 1;
tracing::debug!( // ← Niveau DEBUG, pas WARNING
node_index = node_idx.index(),
"Component lacks full energy transfer or enthalpy data - skipping energy balance check"
);
}
```
**Solution proposée:**
```rust
_ => {
components_skipped += 1;
tracing::warn!(
node_index = node_idx.index(),
component_type = std::any::type_name_of_val(component),
"Component lacks energy_transfers() or port_enthalpies() - SKIPPED in energy balance validation"
);
}
```
**Fichiers à modifier:**
- `crates/solver/src/system.rs`
**Critères d'acceptation:**
- [ ] Logging au niveau WARN (pas DEBUG)
- [ ] Inclut le type du composant dans le message
- [ ] Test que le warning est bien émis
---
#### Story 9.7: Refactoring Solver - Scinder solver.rs
**Priorité:** P3-AMÉLIORATION
**Estimation:** 4h
**Dépendances:** Aucune
**Story:**
> En tant que développeur maintenant le code,
> Je veux que les stratégies de solver soient dans des fichiers séparés,
> Afin d'améliorer la maintenabilité du code.
**Problème actuel:**
- `solver.rs` fait ~2800 lignes
- Architecture.md spécifie `strategies/newton_raphson.rs`, `strategies/sequential_substitution.rs`, `strategies/fallback.rs`
**Solution proposée:**
```
crates/solver/src/
├── lib.rs
├── solver.rs # Trait Solver, SolverStrategy enum
├── strategies/
│ ├── mod.rs
│ ├── newton_raphson.rs
│ ├── sequential_substitution.rs
│ └── fallback.rs
├── system.rs
├── jacobian.rs
└── ...
```
**Fichiers à créer/modifier:**
- `crates/solver/src/strategies/mod.rs` (nouveau)
- `crates/solver/src/strategies/newton_raphson.rs` (nouveau)
- `crates/solver/src/strategies/sequential_substitution.rs` (nouveau)
- `crates/solver/src/strategies/fallback.rs` (nouveau)
- `crates/solver/src/solver.rs` (réduit)
- `crates/solver/src/lib.rs` (mise à jour exports)
**Critères d'acceptation:**
- [ ] Chaque fichier < 500 lignes
- [ ] `cargo test --workspace` passe
- [ ] API publique inchangée
---
#### Story 9.8: Création SystemState Struct Dédié
**Priorité:** P3-AMÉLIORATION
**Estimation:** 6h
**Dépendances:** Stories 9.1, 9.2
**Story:**
> En tant que développeur Rust,
> Je veux un struct `SystemState` dédié au lieu d'un type alias,
> Afin d'avoir une validation du layout et une meilleure sémantique.
**Problème actuel:**
```rust
// crates/components/src/lib.rs:180
pub type SystemState = Vec<f64>;
```
**Solution proposée:**
```rust
// crates/core/src/state.rs (nouveau fichier)
/// État du système thermodynamique.
///
/// Layout: [P_edge0, h_edge0, P_edge1, h_edge1, ...]
/// - P: Pression en Pascals
/// - h: Enthalpie en J/kg
#[derive(Debug, Clone)]
pub struct SystemState {
data: Vec<f64>,
edge_count: usize,
}
impl SystemState {
pub fn new(edge_count: usize) -> Self {
Self {
data: vec![0.0; edge_count * 2],
edge_count,
}
}
pub fn pressure(&self, edge_idx: usize) -> Option<Pressure> {
self.data.get(edge_idx * 2).map(|&p| Pressure::from_pascals(p))
}
pub fn enthalpy(&self, edge_idx: usize) -> Option<Enthalpy> {
self.data.get(edge_idx * 2 + 1).map(|&h| Enthalpy::from_joules_per_kg(h))
}
pub fn set_pressure(&mut self, edge_idx: usize, p: Pressure) {
if let Some(slot) = self.data.get_mut(edge_idx * 2) {
*slot = p.to_pascals();
}
}
pub fn set_enthalpy(&mut self, edge_idx: usize, h: Enthalpy) {
if let Some(slot) = self.data.get_mut(edge_idx * 2 + 1) {
*slot = h.to_joules_per_kg();
}
}
pub fn as_slice(&self) -> &[f64] {
&self.data
}
pub fn as_mut_slice(&mut self) -> &mut [f64] {
&mut self.data
}
}
```
**Critères d'acceptation:**
- [ ] Struct `SystemState` avec méthodes d'accès typées
- [ ] Migration progressive (compatibilité avec `AsRef<[f64]>`)
- [ ] Tests unitaires pour accès par edge
---
## Plan d'Exécution Recommandé
### Sprint 1: Corrections Critiques (Semaine 1)
| Jour | Story | Durée |
|------|-------|-------|
| Lundi AM | 9.1 CircuitId Unification | 2h |
| Lundi PM | 9.2 FluidId Unification | 2h |
| Mardi AM | 9.3 ExpansionValve Energy | 3h |
| Mardi PM | 9.4 FlowSource/FlowSink Energy | 3h |
| Mercredi AM | 9.5 FlowSplitter/FlowMerger Energy | 4h |
| Mercredi PM | 9.6 Logging Improvement | 1h |
| Jeudi | Tests d'intégration complets | 4h |
| Vendredi | Code review & documentation | 4h |
### Sprint 2: Améliorations (Semaine 2)
| Jour | Story | Durée |
|------|-------|-------|
| Lundi-Mardi | 9.7 Solver Refactoring | 4h |
| Mercredi-Vendredi | 9.8 SystemState Struct | 6h |
---
## Tests de Validation Finale
Après implémentation de toutes les stories, exécuter:
```bash
# 1. Tests unitaires complets
cargo test --workspace
# 2. Tests d'intégration thermodynamique
cargo test --test refrigeration_cycle_integration
cargo test --test mass_balance_integration
# 3. Validation clippy stricte
cargo clippy -- -D warnings
# 4. Benchmark performance
cargo bench
# 5. Test de simulation complète
cargo run --example simple_cycle
```
---
## Métriques de Succès
| Métrique | Avant | Après |
|----------|-------|-------|
| Types duplicats | 2 (`CircuitId`, `FluidId`) | 0 |
| Composants sans `energy_transfers()` | 5 | 0 |
| Composants sans `port_enthalpies()` | 5 | 0 |
| Lignes dans `solver.rs` | ~2800 | ~500 |
| Couverture validation énergie | Partielle | Complète |
---
## Annexes
### A. Liste Complète des Composants et Méthodes
| Composant | `port_mass_flows()` | `port_enthalpies()` | `energy_transfers()` |
|-----------|---------------------|---------------------|----------------------|
| Compressor | | | |
| ExpansionValve | | | |
| Pipe | | | |
| Pump | | | |
| Fan | | | |
| FlowSource | | | |
| FlowSink | | | |
| FlowSplitter | | | |
| FlowMerger | | | |
| HeatExchanger | | | |
| Evaporator | | | |
| Condenser | | | |
| Economizer | | | |
| EvaporatorCoil | | | |
| CondenserCoil | | | |
### B. Références Architecture
- [Architecture.md - Component Model](../planning-artifacts/architecture.md#Component-Model)
- [Architecture.md - Error Handling](../planning-artifacts/architecture.md#Error-Handling-Strategy)
- [Architecture.md - Project Structure](../planning-artifacts/architecture.md#Project-Structure)
- [PRD - FR35-FR39 Validation Requirements](../planning-artifacts/prd.md#Validation)
---
*Document généré par audit automatique - 2026-02-22*