# Lot 1 Devtools — Implémentation détaillée

> Archive historique conservée côté `TTM_API`.
>
> Source de vérité active : `C:\Users\VT\PhpstormProjects\ttm-dev-tools\docs\DEV_TOOLS_DEPLOYMENT_AUDIT.md` et `C:\Users\VT\PhpstormProjects\ttm-dev-tools\docs\README_DEV_TOOLS_UI.md`.
>
> Ce document décrit un lot d’implémentation antérieur à l’extraction du `dev-tools` vers `ttm-dev-tools`. À utiliser seulement comme contexte d’archive.

_Date : 2026-04-06_

## 1. Objectif du lot

### Ce que le lot 1 doit rendre visible dans l’UI

Le lot 1 ne refond pas encore les pages métier une par une.
Il installe le **socle transverse visible partout** dans le Devtools actuel.

Résultat attendu à la fin du lot :

1. un **bandeau global de lecture** visible sur toutes les pages ;
2. un **pipeline transverse sticky** visible sur toutes les pages ;
3. un **bloc `Prochain pas recommandé`** rendu par le même moteur sur toutes les pages ;
4. un **view-model transverse unique** dérivé de `app.state.overview` + état temps réel ;
5. une **normalisation unique des états UI** (`normal`, `warning`, `critique` + `Prêt`, `Bloqué`, `Obsolète`, `En cours`, `Non requis`, `À faire`) ;
6. une gestion transverse de la **fraîcheur**, de la **reconnexion** et du **fallback HTTP** ;
7. un **socle CSS transverse** pour ces nouveaux blocs.

### Ce que le lot 1 ne traite pas encore

Le lot 1 **ne traite pas encore** :

- la refonte détaillée de `Build` ;
- la refonte détaillée de `Déploiement` ;
- l’extraction de la page `Base de données` dans un fichier dédié ;
- la refonte détaillée de `Releases` ;
- la refonte détaillée de `Runtime` ;
- la refonte détaillée de `profiles` en UI `Env` ;
- le renommage technique `profiles` → `env` ;
- les changements de DTO backend ;
- le durcissement final de toutes les règles de désactivation métier page par page.

Décision de mise en œuvre pour le lot 1 :

- **on garde l’overview backend tel quel** ;
- **on dérive le transverse côté frontend** ;
- **on ne casse pas les ids DOM existants** ;
- **on ajoute des roots stables** avant de refactorer les pages.

---

## 2. Fichiers à créer

### 2.1 `shared/dev-tools-ui/dev-tools-view-model.js`

- **Chemin exact** : `C:\Users\VT\PhpstormProjects\TTM_API\shared\dev-tools-ui\dev-tools-view-model.js`
- **Rôle** : dériver un view-model transverse unique à partir de `app.state.overview`, `app.state.realtimeConnected`, `app.state.lastRealtimeEventAt` et de l’état de fallback.
- **Responsabilités** :
  - construire le view-model du bandeau global ;
  - construire le view-model du pipeline transverse ;
  - construire le view-model du bloc `Prochain pas recommandé` ;
  - normaliser les badges d’état ;
  - calculer fraîcheur, reconnexion et fallback ;
  - exposer les blocages transverses minimaux du lot 1 ;
  - fournir un contrat stable aux renderers.
- **Fonctions à créer** :
  - `app.getTransverseViewModel()`
  - `app.buildGlobalBannerViewModel()`
  - `app.buildPromotionPipelineViewModel()`
  - `app.buildNextStepViewModel()`
  - `app.buildStatusBadgeViewModel()`
  - `app.buildFreshnessViewModel()`
  - `app.buildRealtimeViewModel()`
  - `app.getGlobalBlockingReason()`
  - `app.getSelectedTargetContext()`
  - `app.invalidateTransverseViewModelCache()`
- **Ce que le fichier ne doit pas contenir** :
  - aucun accès DOM ;
  - aucun `innerHTML` ;
  - aucun `fetch` ;
  - aucun binding d’événements ;
  - aucun lancement d’action ;
  - aucune logique spécifique de layout CSS.

### 2.2 `shared/dev-tools-ui/dev-tools-layout.js`

- **Chemin exact** : `C:\Users\VT\PhpstormProjects\TTM_API\shared\dev-tools-ui\dev-tools-layout.js`
- **Rôle** : rendre les blocs UI transverses du lot 1 à partir du view-model calculé ailleurs.
- **Responsabilités** :
  - rendre le bandeau global ;
  - rendre le pipeline transverse sticky ;
  - rendre le bloc `Prochain pas recommandé` ;
  - rendre un badge d’état unifié ;
  - injecter ces blocs dans leurs roots dédiés.
- **Fonctions à créer** :
  - `app.renderTransverseLayout()`
  - `app.renderDevToolsGlobalBanner(viewModel)`
  - `app.renderDevToolsPromotionPipeline(viewModel)`
  - `app.renderDevToolsNextStepCard(viewModel)`
  - `app.renderDevToolsStatusBadge(badge)`
  - `app.renderBannerItems(items)`
  - `app.renderPipelineStep(step)`
- **Ce que le fichier ne doit pas contenir** :
  - aucune dérivation depuis `overview` brut ;
  - aucune logique de transport temps réel ;
  - aucune règle métier de désactivation ;
  - aucune connaissance de détail sur `Build`, `Database`, `Runtime`, etc.

### 2.3 Décision tranchée

Pour le lot 1, **aucun autre nouveau fichier** n’est nécessaire.

- pas de nouveau fichier backend ;
- pas de nouveau DTO partagé ;
- pas de nouveau module CSS séparé.

Le CSS transverse reste dans `shared/dev-tools-ui/dev-tools.css`.

---

## 3. Fichiers existants à modifier

### 3.1 `src/modules/devTools/dev-tools.page.parts.ts`

- **Chemin exact** : `C:\Users\VT\PhpstormProjects\TTM_API\src\modules\devTools\dev-tools.page.parts.ts`
- **Rôle actuel** : construit la shell HTML serveur : navigation, toolbars globales, topbar, panneaux, modales, ordre de chargement des scripts.
- **Modifications à apporter** :
  1. garder `globalContextBar` comme **barre interactive** de sélection `release / profil` ;
  2. ajouter un root `globalBannerRoot` **sous** `globalContextBar` ;
  3. ajouter un root `promotionPipelineRoot` **sous** `globalBannerRoot` ;
  4. ajouter un root `nextStepRoot` dans `renderDevToolsPanels()` ;
  5. garder les roots existants inchangés ;
  6. charger `dev-tools-view-model.js` et `dev-tools-layout.js` avant `dev-tools-renderers.js` et `dev-tools-app.js`.
- **Fonctions à ajouter / modifier / déplacer** :
  - modifier `renderDevToolsNavigation()` ;
  - modifier `renderDevToolsPanels()` ;
  - modifier `renderDevToolsScripts()` ;
  - ne pas toucher aux modales du lot 1.
- **Dépendances avec les autres fichiers** :
  - dépend de `shared/dev-tools-ui/dev-tools-layout.js` pour le rendu réel des roots ;
  - dépend de `shared/dev-tools-ui/dev-tools-view-model.js` pour alimenter `dev-tools-layout.js` ;
  - dépend de `shared/dev-tools-ui/dev-tools-renderers.js` pour brancher le rendu transverse au cycle global.

### 3.2 `shared/dev-tools-ui/dev-tools-renderers.js`

- **Chemin exact** : `C:\Users\VT\PhpstormProjects\TTM_API\shared\dev-tools-ui\dev-tools-renderers.js`
- **Rôle actuel** : point d’orchestration global via `app.renderOverview()`.
- **Modifications à apporter** :
  1. appeler `app.renderTransverseLayout()` juste après `app.configurePageChrome()` ;
  2. conserver l’ordre actuel des renderers métier ;
  3. ne pas déplacer encore les renderers page par page ;
  4. faire du rendu transverse un passage systématique du cycle de rendu.
- **Fonctions à ajouter / modifier / déplacer** :
  - modifier `app.renderOverview()` ;
  - ajouter éventuellement un helper interne `renderTransverseLayoutIfAvailable()` si on veut garder le fichier robuste pendant la transition.
- **Dépendances avec les autres fichiers** :
  - appelle `app.renderTransverseLayout()` défini dans `dev-tools-layout.js` ;
  - s’appuie sur `app.configurePageChrome()` défini dans `dev-tools-overview-page.js` ;
  - s’appuie sur le view-model transverse exposé par `dev-tools-view-model.js`.

### 3.3 `shared/dev-tools-ui/dev-tools-shared.js`

- **Chemin exact** : `C:\Users\VT\PhpstormProjects\TTM_API\shared\dev-tools-ui\dev-tools-shared.js`
- **Rôle actuel** : état global `app.state`, helpers de statuts, page configs, tâches, lecture des options, merge de patchs overview.
- **Modifications à apporter** :
  1. ajouter un sous-état transverse minimal dans `app.state` ;
  2. centraliser les drapeaux de fraîcheur / source de synchronisation / fallback ;
  3. compléter la normalisation de statuts existante sans casser `statusClass()` ;
  4. exposer des helpers bas niveau consommés par `dev-tools-view-model.js`.
- **Fonctions à ajouter / modifier / déplacer** :
  - ajouter `app.state.uiSync` ;
  - ajouter `app.getLastOverviewTimestamp()` ;
  - ajouter `app.setOverviewSyncState(patch)` ;
  - ajouter `app.getOverviewSyncState()` ;
  - ajouter `app.invalidateTransverseViewModelCache()` si le cache vit ici ;
  - garder `app.statusClass()` pour compatibilité, mais ne plus l’utiliser comme seule source de vérité transverse ;
  - garder `app.taskStatusLabel()` et `app.activityLabel()` ;
  - ne pas déplacer les helpers runtime existants au lot 1.
- **Dépendances avec les autres fichiers** :
  - utilisé par `dev-tools-view-model.js` pour lire `overview` et `uiSync` ;
  - mis à jour par `dev-tools-realtime.js` ;
  - consommé par `dev-tools-layout.js` via `app.getTransverseViewModel()`.

### 3.4 `shared/dev-tools-ui/dev-tools-realtime.js`

- **Chemin exact** : `C:\Users\VT\PhpstormProjects\TTM_API\shared\dev-tools-ui\dev-tools-realtime.js`
- **Rôle actuel** : hydrate `app.state.overview` via HTTP/WS, maintient `lastRealtimeEventAt`, gère reconnect/fallback et applique les snapshots.
- **Modifications à apporter** :
  1. mettre à jour explicitement la source de synchro (`ws`, `http`, `boot`) ;
  2. mettre à jour explicitement l’état `fallbackActive` ;
  3. enregistrer l’instant du dernier overview complet ;
  4. invalider le cache du view-model transverse à chaque changement significatif ;
  5. laisser la logique de transport en place ;
  6. ne pas embarquer de logique de rendu HTML ici.
- **Fonctions à ajouter / modifier / déplacer** :
  - modifier `app.applyOverviewData()` ;
  - modifier `app.fetchOverview()` ;
  - modifier `app.scheduleOverviewFallback()` ;
  - modifier `app.cancelOverviewFallback()` ;
  - modifier `app.connectDevToolsRealtime()` ;
  - ajouter si besoin un helper `markOverviewRefreshSource(source)` local au fichier.
- **Dépendances avec les autres fichiers** :
  - écrit dans `app.state` défini par `dev-tools-shared.js` ;
  - invalide le cache exposé par `dev-tools-view-model.js` ;
  - déclenche `app.renderOverview()` qui appellera le rendu transverse via `dev-tools-renderers.js`.

### 3.5 `shared/dev-tools-ui/dev-tools.css`

- **Chemin exact** : `C:\Users\VT\PhpstormProjects\TTM_API\shared\dev-tools-ui\dev-tools.css`
- **Rôle actuel** : styles globaux de la shell et des cartes.
- **Modifications à apporter** :
  1. ajouter les styles du bandeau global ;
  2. ajouter les styles du pipeline sticky ;
  3. ajouter les styles du bloc `Prochain pas recommandé` ;
  4. ajouter les variantes `normal / warning / critique` ;
  5. ne pas casser les styles existants des cartes métier.
- **Fonctions à ajouter / modifier / déplacer** :
  - pas de fonctions ;
  - ajouter uniquement des classes nouvelles et ciblées.
- **Dépendances avec les autres fichiers** :
  - consommé par les nouveaux renderers de `dev-tools-layout.js` ;
  - doit coexister avec les classes existantes `.status`, `.panel`, `.pill`, `.topbar`, `.shell-header`.

### 3.6 `shared/dev-tools-ui/dev-tools-overview-page.js`

- **Décision** : **pas de refactor structurel dans le lot 1**.
- **Intervention minimale autorisée seulement si nécessaire** :
  - masquer `pageContextSummary` si la duplication visuelle devient trop forte ;
  - ne pas casser les quick actions existantes ;
  - ne pas déplacer les renderers page par page.

Décision tranchée :

- **si le nouveau bandeau est lisible sans masquer `pageContextSummary`, on ne touche pas le fichier au lot 1** ;
- **si la duplication est trop forte, on cache `pageContextSummary` par CSS ou par `configurePageChrome()` sans modifier les renderers métier**.

---

## 4. Squelette concret des fonctions

## 4.1 `shared/dev-tools-ui/dev-tools-view-model.js`

### `app.getTransverseViewModel()`
- **Responsabilité exacte** : retourner l’objet racine consommé par `dev-tools-layout.js`.
- **Entrée** : aucune ; lit `app.state.overview`, `app.state.uiSync`, `app.currentPage`.
- **Sortie** :
  - `globalBanner`
  - `pipeline`
  - `nextStep`
  - `freshness`
  - `realtime`
  - `blocking`
- **Règle** : peut utiliser un cache interne invalidé à chaque changement overview/realtime.

### `app.buildGlobalBannerViewModel()`
- **Responsabilité exacte** : produire les deux lignes du bandeau global avec tonalités par champ.
- **Entrée** : état courant global.
- **Sortie** : objet `globalBanner` prêt à rendre.
- **Règle** : aucune logique DOM.

### `app.buildPromotionPipelineViewModel()`
- **Responsabilité exacte** : construire les 7 étapes transverses du pipeline du lot 1.
- **Étapes figées** :
  1. `build`
  2. `local-test`
  3. `push`
  4. `remote-check`
  5. `database`
  6. `preview`
  7. `activation`
- **Règle** : dériver depuis l’overview existant, sans nouveau champ backend.

### `app.buildNextStepViewModel()`
- **Responsabilité exacte** : déterminer le prochain pas recommandé global, page courante incluse.
- **Règle** :
  - si une étape pipeline est `Bloqué`, le prochain pas doit pointer vers le déblocage ;
  - sinon il pointe vers la première étape non `Prêt` ;
  - si tout est prêt, il peut afficher une action de validation finale ou un message de stabilisation.

### `app.buildStatusBadgeViewModel(input)`
- **Responsabilité exacte** : convertir un état brut en badge UI transverse.
- **Cas gérés** :
  - `ready`
  - `running`
  - `blocked`
  - `stale`
  - `warning`
  - `neutral`
  - `not-required`
- **Règle** : retourner toujours `tone`, `label`, `className`.

### `app.buildFreshnessViewModel()`
- **Responsabilité exacte** : produire le statut de fraîcheur lisible global.
- **Règle** : utiliser en priorité :
  - `app.state.uiSync.lastOverviewAt`
  - `app.state.lastRealtimeEventAt`
  - les timestamps métier disponibles (`checkedAt`, `generatedAt`, `preparedAt`).

### `app.buildRealtimeViewModel()`
- **Responsabilité exacte** : produire l’état `connecté / reconnexion… / fallback HTTP`.
- **Règle** :
  - socket absent → `fallback HTTP`
  - socket présent mais déconnecté → `reconnexion…`
  - socket connecté → `connecté`

### `app.getGlobalBlockingReason()`
- **Responsabilité exacte** : exposer le blocage transverse principal du lot 1.
- **Blocages minimum à gérer** :
  - release visée absente ;
  - release ciblée absente côté distant au moment où la page a besoin du distant ;
  - DB cible non qualifiée si la chaîne DB/activation en dépend ;
  - diagnostic de layout release en erreur ;
  - cible runtime/déploiement introuvable.

### `app.getSelectedTargetContext()`
- **Responsabilité exacte** : centraliser la lecture de `release`, `profil`, `cible`, `DB` dans un seul helper.
- **But** : éviter que le bandeau, le pipeline et le prochain pas re-lisent chacun des champs différents.

### `app.invalidateTransverseViewModelCache()`
- **Responsabilité exacte** : invalider le cache de view-model après refresh overview, event temps réel ou switch de page.
- **Règle** : appelée depuis `dev-tools-realtime.js` et depuis tout setter transverse dans `dev-tools-shared.js`.

## 4.2 `shared/dev-tools-ui/dev-tools-layout.js`

### `app.renderTransverseLayout()`
- **Responsabilité exacte** : piloter le rendu complet des trois roots du lot 1.
- **Ordre** :
  1. `globalBannerRoot`
  2. `promotionPipelineRoot`
  3. `nextStepRoot`
- **Règle** : aucun calcul métier ici, uniquement récupération du view-model et rendu HTML.

### `app.renderDevToolsGlobalBanner(viewModel)`
- **Responsabilité exacte** : rendre les deux lignes du bandeau global.
- **Règle** : utiliser `viewModel.globalBanner` uniquement.

### `app.renderDevToolsPromotionPipeline(viewModel)`
- **Responsabilité exacte** : rendre le rail sticky du pipeline.
- **Règle** : marquer visuellement l’étape courante et les étapes bloquées.

### `app.renderDevToolsNextStepCard(viewModel)`
- **Responsabilité exacte** : rendre la carte `Prochain pas recommandé`.
- **Règle** : si `viewModel.nextStep.visible === false`, masquer le root proprement.

### `app.renderDevToolsStatusBadge(badge)`
- **Responsabilité exacte** : convertir un badge normalisé en HTML commun.
- **Règle** : source unique des classes CSS transverses.

### `app.renderBannerItems(items)`
- **Responsabilité exacte** : rendre une ligne de champs du bandeau.
- **Règle** : traiter les items dans l’ordre fourni par le view-model.

### `app.renderPipelineStep(step)`
- **Responsabilité exacte** : rendre une étape du pipeline.
- **Règle** : afficher : libellé, badge, note courte, lien éventuel vers la page cible.

## 4.3 `shared/dev-tools-ui/dev-tools-renderers.js`

### `app.renderOverview()`
- **Responsabilité exacte** : rester le seul point d’orchestration du rendu global.
- **Ordre cible du lot 1** :
  1. capturer le viewport ;
  2. `app.configurePageChrome()` ;
  3. `app.renderTransverseLayout()` ;
  4. renderers métier existants ;
  5. modales ;
  6. restauration viewport.
- **Règle** : le lot 1 ne rebranche pas les pages ; il insère seulement le transverse.

### `renderTransverseLayoutIfAvailable()` *(helper interne conseillé)*
- **Responsabilité exacte** : protéger `renderOverview()` tant que le lot 1 n’est pas encore complètement branché.
- **Règle** : appeler `app.renderTransverseLayout()` seulement si la fonction existe.

## 4.4 `shared/dev-tools-ui/dev-tools-shared.js`

### `app.state.uiSync`
- **Responsabilité exacte** : stocker l’état technique transverse, indépendant du DTO backend.
- **Forme cible minimale** :
  - `lastOverviewAt`
  - `lastOverviewSource`
  - `fallbackActive`
  - `initialOverviewLoaded`
  - `transverseCacheKey`

### `app.setOverviewSyncState(patch)`
- **Responsabilité exacte** : mettre à jour `app.state.uiSync` de manière uniforme.
- **Règle** : invalider le cache transverse à chaque mise à jour.

### `app.getOverviewSyncState()`
- **Responsabilité exacte** : fournir un accès stable à l’état de synchro.

### `app.getLastOverviewTimestamp()`
- **Responsabilité exacte** : retourner le timestamp de référence pour la fraîcheur.

### `app.statusClass(status)`
- **Rôle conservé** : compatibilité avec les renderers existants.
- **Décision** : ne pas le casser ; le transverse passera par `app.buildStatusBadgeViewModel()`.

### `app.taskStatusLabel(status)` / `app.activityLabel(activity)`
- **Rôle conservé** : alimenter certains sous-libellés du view-model transverse si nécessaire.

## 4.5 `shared/dev-tools-ui/dev-tools-realtime.js`

### `app.applyOverviewData(overview)`
- **Modification exacte** :
  - après écriture de `app.state.overview`, appeler `app.setOverviewSyncState({ lastOverviewAt, lastOverviewSource, fallbackActive: false, initialOverviewLoaded: true })` ;
  - invalider le cache transverse ;
  - lancer le rendu global.

### `app.fetchOverview()`
- **Modification exacte** :
  - marquer la source `http` sur succès ;
  - ne pas mettre de logique de rendu transverse ici.

### `app.scheduleOverviewFallback(delay)`
- **Modification exacte** :
  - marquer `fallbackActive: true` dès que le fallback devient la stratégie active ;
  - ne pas écraser l’état d’overview.

### `app.cancelOverviewFallback()`
- **Modification exacte** :
  - remettre `fallbackActive: false` si le WS est redevenu la voie normale.

### `app.connectDevToolsRealtime()`
- **Modification exacte** :
  - sur `connect`, écrire `lastOverviewSource: 'ws'`, `fallbackActive: false` ;
  - sur `disconnect`, conserver `realtimeConnected = false`, puis laisser le bandeau/pipeline refléter `reconnexion…`.

## 4.6 `src/modules/devTools/dev-tools.page.parts.ts`

### `renderDevToolsNavigation()`
- **Modification exacte** : garder la nav + `globalContextBar`, puis ajouter :
  - `<section id="globalBannerRoot" ...></section>`
  - `<section id="promotionPipelineRoot" ...></section>`
- **Décision de structure** :
  - `globalContextBar` reste l’endroit des sélecteurs ;
  - `globalBannerRoot` devient la couche de lecture transverse ;
  - `promotionPipelineRoot` vient juste avant la topbar page.

### `renderDevToolsPanels()`
- **Modification exacte** : ajouter `<section id="nextStepRoot" class="panel hidden"></section>`.
- **Placement tranché pour le lot 1** :
  - insérer `nextStepRoot` **après** le grand bloc principal existant et **avant** `tasksPanel`.
- **Pourquoi** : position stable, transverse, sans casser l’ordre actuel des panneaux.

### `renderDevToolsScripts()`
- **Modification exacte** : insérer les scripts dans cet ordre :
  1. `dev-tools-shared.js`
  2. `dev-tools-view-model.js`
  3. `dev-tools-realtime.js`
  4. `dev-tools-runtime.js`
  5. `dev-tools-runtime-view.js`
  6. `dev-tools-overview-page.js`
  7. `dev-tools-layout.js`
  8. `dev-tools-command-modal.js`
  9. `dev-tools-renderers.js`
  10. `dev-tools-app.js`
- **Règle** : `dev-tools-renderers.js` doit rester après tous les modules qui définissent des fonctions de rendu.

---

## 5. Contrat de données du view-model transverse

Décision de contrat :

- `overview` backend reste la **source brute** ;
- `getTransverseViewModel()` retourne la **source de vérité UI transverse** ;
- les renderers ne doivent consommer **que** ce contrat.

## 5.1 Objet racine

```text
{
  globalBanner: GlobalBannerViewModel,
  pipeline: PromotionPipelineViewModel,
  nextStep: NextStepViewModel,
  freshness: FreshnessViewModel,
  realtime: RealtimeViewModel,
  blocking: StatusBadgeViewModel | null
}
```

## 5.2 Contrat `globalBanner`

```text
{
  tone: "normal" | "warning" | "critique",
  line1: [
    { key: "targetRelease", label: "Release visée", value: string, tone: "normal" | "warning" | "critique" },
    { key: "remoteCurrentRelease", label: "Active distante", value: string, tone: "normal" | "warning" | "critique" },
    { key: "target", label: "Cible", value: string, tone: "normal" | "warning" | "critique" }
  ],
  line2: [
    { key: "profile", label: "Profil", value: string, tone: "normal" | "warning" | "critique" },
    { key: "database", label: "DB cible", value: string, tone: "normal" | "warning" | "critique" },
    { key: "freshness", label: "État", value: string, tone: "normal" | "warning" | "critique" },
    { key: "realtime", label: "Temps réel", value: string, tone: "normal" | "warning" | "critique" }
  ],
  blocking: {
    key: "blocking",
    label: "Blocage",
    value: string,
    tone: "critique"
  } | null
}
```

### Exemple concret

```text
{
  tone: "warning",
  line1: [
    { key: "targetRelease", label: "Release visée", value: "20260404-014000", tone: "normal" },
    { key: "remoteCurrentRelease", label: "Active distante", value: "20260403-231500", tone: "warning" },
    { key: "target", label: "Cible", value: "Préprod", tone: "normal" }
  ],
  line2: [
    { key: "profile", label: "Profil", value: "default", tone: "normal" },
    { key: "database", label: "DB cible", value: "ttm_api / stackmod-dev-mariadb", tone: "warning" },
    { key: "freshness", label: "État", value: "données à rafraîchir", tone: "warning" },
    { key: "realtime", label: "Temps réel", value: "fallback HTTP", tone: "warning" }
  ],
  blocking: null
}
```

## 5.3 Contrat `pipeline`

```text
{
  currentStepId: string | null,
  steps: [
    {
      id: "build" | "local-test" | "push" | "remote-check" | "database" | "preview" | "activation",
      label: string,
      pageId: "build" | "deploy" | "database" | "releases" | "runtime" | "profiles",
      href: string,
      state: "ready" | "pending" | "blocked" | "running" | "stale" | "not-required",
      badge: StatusBadgeViewModel,
      note: string | null,
      blockingReason: string | null,
      active: boolean
    }
  ]
}
```

### Exemple concret

```text
{
  currentStepId: "database",
  steps: [
    {
      id: "build",
      label: "Build",
      pageId: "build",
      href: "/dev/tools/build",
      state: "ready",
      badge: { kind: "ready", tone: "normal", label: "Prêt", className: "devtools-status is-normal" },
      note: "Artefacts locaux présents",
      blockingReason: null,
      active: false
    },
    {
      id: "local-test",
      label: "Test local",
      pageId: "runtime",
      href: "/dev/tools/runtime",
      state: "ready",
      badge: { kind: "ready", tone: "normal", label: "Prêt", className: "devtools-status is-normal" },
      note: "Runtime local joignable",
      blockingReason: null,
      active: false
    },
    {
      id: "push",
      label: "Push distant",
      pageId: "deploy",
      href: "/dev/tools/deploy",
      state: "ready",
      badge: { kind: "ready", tone: "normal", label: "Prêt", className: "devtools-status is-normal" },
      note: "Release présente côté distant",
      blockingReason: null,
      active: false
    },
    {
      id: "remote-check",
      label: "Validation distante",
      pageId: "runtime",
      href: "/dev/tools/runtime",
      state: "ready",
      badge: { kind: "ready", tone: "normal", label: "Prêt", className: "devtools-status is-normal" },
      note: "API distante vérifiée",
      blockingReason: null,
      active: false
    },
    {
      id: "database",
      label: "Base",
      pageId: "database",
      href: "/dev/tools/database",
      state: "blocked",
      badge: { kind: "blocked", tone: "critique", label: "Bloqué", className: "devtools-status is-critique" },
      note: "Migration Prisma en erreur",
      blockingReason: "Corriger la DB cible avant activation.",
      active: true
    },
    {
      id: "preview",
      label: "Preview",
      pageId: "releases",
      href: "/dev/tools/releases",
      state: "pending",
      badge: { kind: "pending", tone: "warning", label: "À faire", className: "devtools-status is-warning" },
      note: "Exposition preview non validée",
      blockingReason: null,
      active: false
    },
    {
      id: "activation",
      label: "Activation",
      pageId: "releases",
      href: "/dev/tools/releases",
      state: "blocked",
      badge: { kind: "blocked", tone: "critique", label: "Bloqué", className: "devtools-status is-critique" },
      note: "Étape DB non validée",
      blockingReason: "La chaîne de promotion n’est pas encore sûre.",
      active: false
    }
  ]
}
```

## 5.4 Contrat `nextStep`

```text
{
  visible: boolean,
  tone: "normal" | "warning" | "critique",
  title: "Prochain pas recommandé",
  summary: string,
  reason: string,
  blocked: boolean,
  blockingReason: string | null,
  cta: {
    type: "link" | "action" | "none",
    label: string,
    href: string | null,
    actionId: string | null
  }
}
```

### Exemple concret — cas non bloqué

```text
{
  visible: true,
  tone: "warning",
  title: "Prochain pas recommandé",
  summary: "Préparer la base cible",
  reason: "La release distante est prête mais la migration Prisma n’est pas encore validée.",
  blocked: false,
  blockingReason: null,
  cta: {
    type: "link",
    label: "Ouvrir Base de données",
    href: "/dev/tools/database",
    actionId: null
  }
}
```

### Exemple concret — cas bloqué

```text
{
  visible: true,
  tone: "critique",
  title: "Prochain pas recommandé",
  summary: "Débloquer la cible DB",
  reason: "La DB cible n’est pas suffisamment qualifiée pour lancer une migration sûre.",
  blocked: true,
  blockingReason: "DB cible non confirmée",
  cta: {
    type: "link",
    label: "Relire le diagnostic DB",
    href: "/dev/tools/database",
    actionId: null
  }
}
```

## 5.5 Contrat `badge d’état`

```text
{
  kind: "ready" | "pending" | "blocked" | "running" | "stale" | "warning" | "neutral" | "not-required",
  tone: "normal" | "warning" | "critique",
  label: string,
  className: string,
  detail: string | null
}
```

### Mapping figé du lot 1

| kind | label | tone |
|---|---|---|
| `ready` | `Prêt` | `normal` |
| `pending` | `À faire` | `warning` |
| `blocked` | `Bloqué` | `critique` |
| `running` | `En cours` | `warning` |
| `stale` | `Obsolète` | `warning` |
| `warning` | `À vérifier` | `warning` |
| `neutral` | `Neutre` | `normal` |
| `not-required` | `Non requis` | `normal` |

## 5.6 Contrat `fraîcheur / reconnexion`

```text
{
  freshness: {
    state: "fresh" | "stale" | "unknown",
    label: string,
    tone: "normal" | "warning" | "critique",
    lastSyncAt: string | null,
    source: "ws" | "http" | "boot" | "unknown"
  },
  realtime: {
    state: "connected" | "reconnecting" | "fallback",
    label: string,
    tone: "normal" | "warning" | "critique",
    note: string | null
  }
}
```

### Exemple concret

```text
{
  freshness: {
    state: "stale",
    label: "données à rafraîchir",
    tone: "warning",
    lastSyncAt: "2026-04-06T11:32:18.000Z",
    source: "http"
  },
  realtime: {
    state: "fallback",
    label: "fallback HTTP",
    tone: "warning",
    note: "Le socket est indisponible ; le refresh HTTP garde la page lisible."
  }
}
```

---

## 6. Ordre exact de codage

Séquence recommandée pour coder le lot 1 sans se bloquer :

1. **Ajouter les roots structurels dans `dev-tools.page.parts.ts`**
   - `globalBannerRoot`
   - `promotionPipelineRoot`
   - `nextStepRoot`
   - ordre de scripts mis à jour
2. **Ajouter le sous-état transverse dans `dev-tools-shared.js`**
   - `app.state.uiSync`
   - setters/getters de synchro
3. **Créer `dev-tools-view-model.js` avec les stubs de fonctions**
   - sans logique finale au premier commit
4. **Brancher `dev-tools-realtime.js` sur `uiSync`**
   - source `ws/http`
   - fallback actif
   - timestamp du dernier overview
5. **Implémenter les fonctions de calcul du view-model transverse**
   - bandeau
   - pipeline
   - prochain pas
   - badges
   - fraîcheur / reconnexion
6. **Créer `dev-tools-layout.js`**
   - renderers HTML purs
   - masquage propre si root vide
7. **Brancher `app.renderOverview()` dans `dev-tools-renderers.js`**
   - rendre le transverse avant les blocs métier
8. **Ajouter le CSS transverse dans `dev-tools.css`**
   - bandeau
   - pipeline sticky
   - bloc prochain pas
   - variantes `normal/warning/critique`
9. **Faire le nettoyage minimal des redondances**
   - uniquement si le nouveau bandeau entre en collision visuelle avec `pageContextSummary`
10. **Valider manuellement toutes les pages**
    - `build`
    - `deploy`
    - `releases`
    - `database`
    - `profiles`
    - `runtime`

### Ordre à ne pas inverser

- ne pas coder `dev-tools-layout.js` avant d’avoir figé le contrat de `dev-tools-view-model.js` ;
- ne pas toucher les renderers métier avant que le transverse fonctionne partout ;
- ne pas nettoyer `pageContextSummary` tant que le nouveau bandeau n’est pas visuellement validé.

---

## 7. Risques de régression

### 7.1 Collisions DOM

Points à surveiller :

- ids nouveaux trop proches des ids existants ;
- écrasement visuel de `globalContextBar` ;
- insertion de `nextStepRoot` dans une zone déjà manipulée par un renderer existant.

Garde-fou :

- utiliser uniquement :
  - `globalBannerRoot`
  - `promotionPipelineRoot`
  - `nextStepRoot`
- ne renommer aucun id existant.

### 7.2 Duplication d’état

Risque :

- lire la fraîcheur à la fois depuis `overview`, `lastRealtimeEventAt`, `getRuntimeRealtimeStatus()` et des timestamps métier non harmonisés.

Garde-fou :

- une seule source transverse : `app.state.uiSync` + helpers de `dev-tools-view-model.js`.

### 7.3 Sticky layout

Risque :

- le pipeline sticky casse le scroll ou chevauche la topbar.

Garde-fou :

- rendre le pipeline sticky dans sa propre classe CSS ;
- ne pas rendre sticky toute la `shell-header` ;
- tester sur pages longues (`runtime`, `database`).

### 7.4 Régression sur navigation

Risque :

- la nav active reste correcte mais le bandeau/pipeline pointe vers une mauvaise page ou un mauvais libellé.

Garde-fou :

- `pageId` du pipeline doit rester aligné avec les ids actuels :
  - `build`
  - `deploy`
  - `releases`
  - `database`
  - `runtime`
  - `profiles`

### 7.5 Régression sur temps réel

Risque :

- le bandeau affiche `fallback HTTP` alors que le WS est revenu ;
- le cache transverse n’est pas invalidé après un snapshot.

Garde-fou :

- invalider le cache transverse dans :
  - `applyOverviewData()`
  - `applyTaskUpdatedPayload()`
  - `applySummaryUpdatedPayload()`
  - `connect` / `disconnect`

### 7.6 Conflits avec les renderers existants

Risque :

- les renderers page existants continuent de rendre des résumés redondants ;
- `pageContextSummary` concurrence le nouveau bandeau.

Garde-fou :

- lot 1 = tolérance à la redondance **temporaire** ;
- si nécessaire, masquer uniquement `pageContextSummary`, pas les quick actions.

---

## 8. Critères de validation du lot 1

Checklist testable pour dire : **“le lot 1 est terminé”**.

### 8.1 Structure

- [ ] `globalBannerRoot` existe dans toutes les pages.
- [ ] `promotionPipelineRoot` existe dans toutes les pages.
- [ ] `nextStepRoot` existe dans toutes les pages.
- [ ] aucun id DOM existant n’a été renommé.

### 8.2 Rendu transverse

- [ ] le bandeau global s’affiche sur `build`, `deploy`, `releases`, `database`, `profiles`, `runtime`.
- [ ] le pipeline s’affiche sur toutes les pages.
- [ ] le bloc `Prochain pas recommandé` s’affiche ou se masque proprement selon le view-model.
- [ ] aucun de ces blocs ne dépend d’un renderer métier spécifique.

### 8.3 États UI

- [ ] les badges du transverse utilisent tous le même contrat `StatusBadgeViewModel`.
- [ ] `Prêt`, `À faire`, `Bloqué`, `En cours`, `Obsolète`, `Non requis` existent et sont rendus correctement.
- [ ] `normal`, `warning`, `critique` ont chacun un style CSS stable.

### 8.4 Fraîcheur / reconnexion / fallback

- [ ] avec socket connecté, le bandeau affiche `Temps réel : connecté`.
- [ ] en cas de `disconnect`, le bandeau affiche `Temps réel : reconnexion…`.
- [ ] quand le fallback HTTP prend le relais, le bandeau affiche `Temps réel : fallback HTTP`.
- [ ] la fraîcheur bascule en warning si l’état n’est plus considéré frais.

### 8.5 Pipeline

- [ ] les 7 étapes sont toujours affichées dans le même ordre.
- [ ] une étape bloquée rend visible sa raison courte.
- [ ] une étape active est visuellement identifiable.
- [ ] un clic sur une étape peut renvoyer vers la page existante correspondante.

### 8.6 Non-régression minimum

- [ ] la navigation existante continue de fonctionner.
- [ ] les sélecteurs `release` et `profil` continuent de fonctionner.
- [ ] `renderOverview()` ne jette pas d’erreur JS quand `overview` est vide.
- [ ] la page `runtime` conserve son comportement terminal/logs.
- [ ] les modales existantes fonctionnent encore.

---

## 9. Découpage en sous-tâches

### Sous-tâche 1 — Ajouter les roots HTML transverses
- **Fichier** : `src/modules/devTools/dev-tools.page.parts.ts`
- **Action** : insérer `globalBannerRoot`, `promotionPipelineRoot`, `nextStepRoot`.
- **Livrable** : shell prête sans rendu fonctionnel.

### Sous-tâche 2 — Ajouter l’ordre de chargement des scripts
- **Fichier** : `src/modules/devTools/dev-tools.page.parts.ts`
- **Action** : brancher `dev-tools-view-model.js` et `dev-tools-layout.js`.
- **Livrable** : ordre de dépendances stable.

### Sous-tâche 3 — Ajouter l’état de synchro transverse
- **Fichier** : `shared/dev-tools-ui/dev-tools-shared.js`
- **Action** : créer `app.state.uiSync`, getter/setter, invalidation cache.
- **Livrable** : stockage technique transverse prêt.

### Sous-tâche 4 — Brancher le temps réel sur `uiSync`
- **Fichier** : `shared/dev-tools-ui/dev-tools-realtime.js`
- **Action** : marquer `lastOverviewAt`, `lastOverviewSource`, `fallbackActive`.
- **Livrable** : fraîcheur / fallback calculables proprement.

### Sous-tâche 5 — Créer les stubs du view-model transverse
- **Fichier** : `shared/dev-tools-ui/dev-tools-view-model.js`
- **Action** : déclarer toutes les fonctions cibles avec retours sûrs par défaut.
- **Livrable** : fichier intégrable sans casser le rendu.

### Sous-tâche 6 — Implémenter le contrat `badge / fraîcheur / reconnexion`
- **Fichier** : `shared/dev-tools-ui/dev-tools-view-model.js`
- **Action** : coder les helpers de normalisation avant bandeau/pipeline.
- **Livrable** : briques de base stables.

### Sous-tâche 7 — Implémenter `globalBanner`
- **Fichier** : `shared/dev-tools-ui/dev-tools-view-model.js`
- **Action** : construire les 2 lignes + blocage.
- **Livrable** : VM bandeau prêt.

### Sous-tâche 8 — Implémenter `pipeline`
- **Fichier** : `shared/dev-tools-ui/dev-tools-view-model.js`
- **Action** : dériver les 7 étapes et leurs statuts.
- **Livrable** : VM pipeline prêt.

### Sous-tâche 9 — Implémenter `nextStep`
- **Fichier** : `shared/dev-tools-ui/dev-tools-view-model.js`
- **Action** : calculer le prochain pas unique du lot 1.
- **Livrable** : VM prochain pas prêt.

### Sous-tâche 10 — Créer les renderers transverses
- **Fichier** : `shared/dev-tools-ui/dev-tools-layout.js`
- **Action** : coder le rendu HTML du bandeau, pipeline, prochain pas et badge.
- **Livrable** : rendu autonome prêt à être branché.

### Sous-tâche 11 — Brancher le rendu transverse global
- **Fichier** : `shared/dev-tools-ui/dev-tools-renderers.js`
- **Action** : appeler `app.renderTransverseLayout()` dans `renderOverview()`.
- **Livrable** : transverse visible partout.

### Sous-tâche 12 — Ajouter le CSS transverse
- **Fichier** : `shared/dev-tools-ui/dev-tools.css`
- **Action** : styles des nouveaux blocs et variantes d’état.
- **Livrable** : rendu lisible et stable.

### Sous-tâche 13 — Nettoyage minimal des redondances
- **Fichier** : `shared/dev-tools-ui/dev-tools-overview-page.js` ou `shared/dev-tools-ui/dev-tools.css`
- **Action** : masquer uniquement ce qui gêne vraiment.
- **Livrable** : cohabitation acceptable entre ancien et nouveau socle.

### Sous-tâche 14 — Validation manuelle finale
- **Fichiers concernés** : ensemble du lot 1
- **Action** : test visuel et comportemental sur toutes les pages.
- **Livrable** : lot 1 validé.

---

## Conclusion exécutable

Pour démarrer le code immédiatement, l’ordre réel à suivre est :

1. `dev-tools.page.parts.ts`
2. `dev-tools-shared.js`
3. `dev-tools-realtime.js`
4. `dev-tools-view-model.js`
5. `dev-tools-layout.js`
6. `dev-tools-renderers.js`
7. `dev-tools.css`
8. nettoyage minimal si nécessaire

La règle la plus importante du lot 1 est simple :

- **un seul calcul transverse** dans `dev-tools-view-model.js` ;
- **un seul rendu transverse** dans `dev-tools-layout.js` ;
- **aucune logique métier de page déplacée trop tôt**.

C’est le choix le plus sûr pour commencer l’implémentation immédiatement sans casser l’UI actuelle.
