# FEEDBACK TO ARCHITECT — v19

> **Spec technique** : `001-histometeo-mvp.tech.v19.md`
> **Spec fonctionnelle** : `001-histometeo-mvp.md`
> **Date** : 2026-03-13

---

## 1) Functional Compliance

| AC       | Statut    | Justification                                                                                                                                                                                |
| -------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AC1–AC23 | ✅ OK     | Non concernés par cette itération — les 93 tests hérités passent tous                                                                                                                        |
| AC24     | ❌ **KO** | **L'erreur ne s'affiche pas** — `app.js` contient une erreur de syntaxe (R58) qui empêche l'exécution de tout JavaScript dans le navigateur. Le bloc d'erreur ne peut donc jamais apparaître |
| AC25     | ❌ **KO** | Même cause (R58)                                                                                                                                                                             |
| AC26     | ❌ **KO** | Le bouton « Réessayer » est correctement implémenté dans le code, mais le JavaScript ne s'exécute pas                                                                                        |
| AC27     | ❌ **KO** | Le spinner et le `setLoadingState` sont implémentés mais ne fonctionnent pas (R58)                                                                                                           |
| AC28     | ✅ OK     | Les routes SEO injectent le `<script id="prefetched-data" type="application/json">` côté backend — vérifié par tests T12–T15                                                                 |
| AC29     | ❌ **KO** | Le frontend est censé lire les données pré-chargées, mais le JavaScript ne s'exécute pas (R58)                                                                                               |
| AC30     | ✅ OK     | Le cache fichier `FileCache` persiste les données — vérifié par tests T1–T5 et le flux prefetch T6–T11                                                                                       |

**Résumé** : le backend est **correct et complet**. Le frontend est correctement conçu mais **totalement cassé** par une erreur de syntaxe JavaScript (R58 ci-dessous).

---

## 2) Contract Compliance

### Scope respecté ?

✅ Oui — seuls les fichiers autorisés sont modifiés :

- `public/index.html`, `public/style.css`, `public/app.js`
- `src/main.py`, `src/cache.py`, `src/config.py`, `src/weather_service.py`
- Nouveau : `src/prefetch_service.py`, `tests/test_prefetch_service.py`
- `tests/test_api.py` (ajout de tests uniquement)
- `.gitignore` (ajout `data/weather_cache/`)

### Forbidden changes respectés ?

✅ Oui — aucune modification détectée sur :

- `src/normals_service.py`, `src/commune_service.py`, `src/og_service.py`
- `requirements.txt`, `Dockerfile`, `pyproject.toml`
- `README.md`, `.github/`, `public/assets/`
- Tests existants ≤ v18 non modifiés (93 tests passent)

### Invariants préservés ?

| INV    | Statut | Note                                                                                                               |
| ------ | ------ | ------------------------------------------------------------------------------------------------------------------ |
| INV-7  | ✅ OK  | Pas de `innerHTML` dans `app.js` (`replaceChildren`, `textContent`, `createElement` utilisés)                      |
| INV-12 | ✅ OK  | Pas de `matchMedia` ni `window.innerWidth`                                                                         |
| INV-14 | ✅ OK  | OG tags injectés côté serveur uniquement                                                                           |
| INV-20 | ✅ OK  | Un seul `<h1>` visible par page                                                                                    |
| INV-25 | ✅ OK  | Formulaire de recherche non dupliqué                                                                               |
| INV-26 | ✅ OK  | Bloc compact peuplé via `textContent`                                                                              |
| INV-27 | ✅ OK  | CSS animations pour compact ↔ ouvert                                                                               |
| INV-28 | ✅ OK  | `<script type="application/json">` — données JSON inertes, échappement `</` via `<\/`                              |
| INV-29 | ✅ OK  | Écriture atomique via `.tmp` + `replace()` avec gestion d'erreur et `threading.Lock`                               |
| INV-30 | ⚠️ N/A | `hideAllDataSections()` couvre 18 sections — implémentation correcte mais non vérifiable en l'état (R58)           |
| INV-31 | ✅ OK  | Le `retryCallback` utilise une closure capturant les paramètres existants                                          |
| INV-32 | ✅ OK  | Clés de cache basées sur slug + dates (`period_`, `month_`, `town_` + sanitisation regex)                          |
| INV-33 | ✅ OK  | `_key_to_path` sanitise via `re.sub(r"[^a-zA-Z0-9_\-]", "_", key)` — aucune entrée brute dans les noms de fichiers |

---

## 3) Technical Quality

### R58 — BLOQUANT — Erreur de syntaxe JavaScript dans `app.js` (ligne ~3420)

**Sévérité** : 🔴 CRITIQUE — empêche l'exécution de TOUT le JavaScript

```
$ node -c public/app.js
SyntaxError: Unexpected token 'catch'
    at public/app.js:3420
```

**Cause** : Dans `loadFromURL()`, le bloc `if (seoState.mode === "comparison") {` (ligne ~3388) n'est jamais fermé. L'ancien code avait une structure `if (comparison) { ... } else { ... }` avec du code commun après. Lors du refactoring v19 (extraction du mode "simple" dans son propre bloc), le `} else {` a été supprimé mais le `}` fermant le bloc comparison a été perdu avec.

**Code actuel (cassé)** :

```javascript
      if (seoState.mode === "comparison") {
        // ... setup comparison ...
        commune2Input.value = formatCommuneLabel(selectedCommune2);
      // ← CLOSING BRACE MISSING HERE
      dateStart.value = seoState.start;
      dateEnd.value = seoState.end;
      // ...
      return;
    } catch (error) {  // ← closes try, but comparison block is still open
```

**Conséquence** : le navigateur ne peut parser `app.js` → aucune fonctionnalité JavaScript ne fonctionne (formulaire, recherche, prefetch, erreurs, retry, tout). La page affiche le HTML statique uniquement.

**Impact** : AC24, AC25, AC26, AC27, AC29 sont tous KO. D20–D23, D27, D30, D32 sont également non fonctionnels.

**Correction** : ajouter `}` pour fermer le bloc `if (comparison)` avant les lignes communes. Deux options :

Option A (préfère la cohérence avec l'ancienne structure) :

```javascript
        commune2Input.value = formatCommuneLabel(selectedCommune2);
      }

      dateStart.value = seoState.start;
```

Option B (regrouper tout dans le if puisque c'est le seul chemin restant) :

```javascript
        commune2Input.value = formatCommuneLabel(selectedCommune2);
        dateStart.value = seoState.start;
        dateEnd.value = seoState.end;
        // ... rest at 8-space indent ...
        return;
      }
    } catch (error) {
```

**Routage** : retour Développeur

---

### R59 — Indentation incohérente dans le bloc comparison de `loadFromURL()`

Lié à R58. Les lignes ~ 3408–3419 sont indentées à 6 espaces alors qu'elles devraient être à 8 espaces (à l'intérieur du bloc `if (comparison)`) ou rester à 6 espaces si elles sont après le `}` fermant. Normaliser l'indentation lors de la correction R58.

---

### R60 — `retry-button` sans classe CSS

**Spec v19 §3.5.1** prescrit `class="btn-primary"` sur le bouton :

```html
<button type="button" id="retry-button" class="btn-primary hidden"></button>
```

**Implémentation** :

```html
<button type="button" id="retry-button" class="hidden"></button>
```

Le style est directement appliqué via `#retry-button` en CSS, ce qui fonctionne. Cependant, ceci diverge de la spec. En pratique, l'effet visuel est le même car le CSS cible `#retry-button` directement avec `background: var(--accent)`. Non bloquant.

---

### Pas de complexité inutile

✅ Les abstractions sont minimales :

- `FileCache` est une classe simple sans dépendances exotiques
- `prefetch_service.py` ne duplique pas la logique métier — il orchestre les services existants
- Le mécanisme `render*FromData()` appelle les fonctions de rendu existantes
- `get_weather_for_prefetch()` est un alias transparent de `get_weather()` — acceptable comme couche d'indirection pour lisibilité

### Pas de dette technique introduite

✅ Le cache fichier est simple et approprié au volume. Les constantes sont dans `config.py`. Les clés de cache sont préfixées par type (`period_`, `month_`, `town_`).

### Pas de duplication

✅ R57 (`formatCompactCommune` fusionné dans `formatCommuneLabel`) est correctement implémenté — `formatCompactCommune` n'existe plus dans le code.

---

## 4) Test Coverage

### Tests existants

✅ 93 tests hérités passent tous sans modification.

### Nouveaux tests

✅ 16 nouveaux tests (total = 109) couvrant :

| Test  | Couverture                                                             |
| ----- | ---------------------------------------------------------------------- |
| T1–T5 | `FileCache` : set/get, miss, TTL, atomicité, sanitisation clé          |
| T6    | `prefetch_period` cache hit — pas d'appel service                      |
| T7    | `prefetch_period` cache miss — appel services + stockage               |
| T8    | `prefetch_period` service failure → `None`                             |
| T9    | `prefetch_period` normals failure → bundle avec `normals: None`        |
| T10   | `prefetch_town` OK                                                     |
| T11   | `prefetch_month` OK                                                    |
| T12   | Route `/meteo/{slug}/{start}/{end}` → HTML contient `prefetched-data`  |
| T13   | Prefetch fail → HTML sans `prefetched-data`                            |
| T14   | Route `/ville/{slug}` → HTML contient `prefetched-data`                |
| T15   | Route `/meteo/{slug}/{year}/{month}` → HTML contient `prefetched-data` |
| T16   | `_inject_prefetched_data` échappe `</script>`                          |

### R61 — Edge case manquant (non bloquant)

Aucun test ne vérifie le comportement quand `_key_to_path` produit des collisions de clés (deux slugs différents donnant la même clé après sanitisation). En pratique, avec les préfixes `period_`/`month_`/`town_` + la structure des slugs, le risque est quasi nul. Suggestion : un test pourrait vérifier l'injectivité sur quelques cas représentatifs.

---

## 5) UX Consistency Check

### ⚠️ Issue critique (liée à R58)

Puisque le JavaScript ne s'exécute pas, l'UX est totalement brisée :

- Le formulaire de recherche ne fonctionne pas
- L'auto-complétion ne fonctionne pas
- Les données pré-chargées ne sont pas affichées
- La page est un squelette HTML vide

### Design des erreurs (si R58 corrigé)

✅ Le design d'erreur est cohérent :

- Le message « service indisponible » est clair et actionnable (bouton Réessayer)
- Le message « aucune donnée » est distinct et sans bouton Réessayer
- `hideAllDataSections()` masque exhaustivement 18 sections
- Le spinner est CSS pur et léger

### Prefetch UX

✅ Le flux prefetch est transparent pour l'utilisateur — les pages SEO s'affichent instantanément sans flash de chargement (si R58 corrigé).

---

## 6) Required Corrections

| #       | Correction                                                                                                                                         | Sévérité    | Routage       |
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------- |
| **R58** | **Ajouter le `}` fermant le bloc `if (seoState.mode === "comparison")` dans `loadFromURL()` — erreur de syntaxe empêchant l'exécution de tout JS** | 🔴 CRITIQUE | → Développeur |
| **R59** | Normaliser l'indentation du bloc comparison (lignes ~3408–3419) à 8 espaces                                                                        | 🟡 Mineure  | → Développeur |

---

## 7) Recommended Improvements (non bloquantes)

| #   | Suggestion                                                                                                                                     |
| --- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| R60 | Ajouter `class="btn-primary"` au `#retry-button` dans `index.html` pour conformité avec la spec, même si l'effet visuel est identique          |
| R61 | Ajouter un test vérifiant la non-collision des clés de cache pour quelques slugs représentatifs                                                |
| R62 | Considérer un test frontend minimal (ex: `node -c public/app.js` dans le CI) pour détecter les erreurs de syntaxe JavaScript avant déploiement |

---

## 🧭 Décision finale

### ❌ Rejeté (correction obligatoire)

**Justification** : R58 est une erreur de syntaxe JavaScript qui empêche l'exécution de l'intégralité du code frontend. Aucune fonctionnalité v19 (gestion d'erreur, prefetch côté client, retry) ni aucune fonctionnalité existante (formulaire, recherche, affichage des résultats) ne fonctionne dans le navigateur.

Le backend est correct et complet (routes SEO, prefetch service, cache fichier, tests). La correction est localisée : ajouter un seul `}` dans `loadFromURL()`.

**Routage** : retour → Développeur pour corriger R58 + R59.
