Files
system_update/validation_tache2.md
gilles 0fbca06d3d docs: roadmap tâches 1.9-8 (briefs, gates de validation, designs tâche 2) + plans d'implémentation
Cartographie complète (liste_taches/coherence_taches), briefs tacheN + gates
validation_tacheN, design tâche 2 (docs/design/tache2/), specs/plans jalon 1-2
et tâche 1.9/2 (Phase 1, Phase 2, SJ-0→3). Validations consignées (1.9 , 2-8 🟡).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 19:50:25 +02:00

628 lines
55 KiB
Markdown

# Protocole de validation — Tâche 2 (moteur de templates de mise à jour)
> **Type** : grille de validation. Utilisée par l'agent orchestrateur (Claude Code principal) **après** qu'un agent sous-traitant a rendu la mission décrite dans `tache2.md`.
> **But** : vérifier que les livrables de la tâche 2 sont complets, respectent le périmètre, et **s'intègrent correctement à l'application existante** sans rien casser.
> **⚠️ Gate obligatoire** : cette validation doit être **passée AVANT toute phase de développement**. Tant que le verdict n'est pas ✅ Accepté, **aucune implémentation des livrables de la tâche 2 ne démarre** (ni par l'agent sous-traitant, ni par l'orchestrateur). Le design est figé et validé d'abord ; le code vient après.
> **Rappel** : la tâche 2 est une mission de **design/investigation** (pas d'implémentation). On valide donc des **documents de design/spec**, pas du code de production. Les vérifications de non-régression servent uniquement à confirmer que l'existant n'a pas été touché.
---
## 0. Quand lancer cette validation
- La mission `tache2.md` est annoncée terminée.
- Le fichier `tache2.md` a bien été mis à jour avec sa section « État d'avancement / Ce qui a été fait » (clôture obligatoire).
- Les livrables de design sont présents sous `docs/` (ou l'emplacement proposé par l'agent).
Si l'un de ces points manque → **rejet immédiat**, renvoyer à l'agent.
---
## 1. Discipline & périmètre (l'agent n'a pas débordé)
Vérifier via `git status` / `git diff` et lecture des commits :
- [ ] **Aucun code de production modifié ou ajouté** : pas de changement sous `server/`, `client/`, `shared/`, `templates/`, ni dans les configs (`package.json`, `tsup.config.ts`, `vite.config.ts`, etc.). Seuls des fichiers `docs/**` et `tache2.md` doivent avoir changé.
- [ ] **Jalon 1 et jalon 2 intacts** : aucun fichier de ces jalons touché.
- [ ] Aucun autre chantier démarré hors du périmètre de `tache2.md` (les idées hors-scope doivent figurer comme *suggestions*, pas comme du travail réalisé).
- [ ] Les dépôts de référence (`linux-update-dashboard/`, `nas-ops/`) n'ont pas été copiés tels quels dans le code (respect licence AGPL).
> Si du code de production a été écrit → **rejet** : la mission devait s'arrêter au design.
---
## 2. Complétude des livrables
Confronter au § « Livrables attendus » et aux 5 axes de `tache2.md` :
**Axes couverts :**
- [ ] Axe A — Templates APT (update, upgrade, full-upgrade, dist-upgrade, clean, autoremove, reboot) avec sémantique clarifiée et **profils OS** (Debian/Ubuntu/Proxmox/RPi) + gestion proxy apt-cacher-ng.
- [ ] Axe B — Capture des updates **prévus** (snapshot) et **appliqués** (diff avant/après), consommable par Hermes (déduplication + réduction).
- [ ] Axe C — Taxonomie des erreurs + stratégie de gestion/remédiation.
- [ ] Axe D — Docker : scan, pull, up, down, **prune images inutilisées**, détection des stacks (labels + fallback répertoires déclarés), JSON compact.
- [ ] Axe E — Scripts personnalisés (post-install, install paquets) + overrides par machine + garde-fous.
**Livrables (§4 de tache2.md) :**
- [ ] Inventaire des templates.
- [ ] Contenu proposé des templates clés (pseudo-shell réaliste, convention `===SU:XXX===`).
- [ ] Schémas JSON canoniques étendus (snapshot + résultat, APT + Docker + erreurs + custom).
- [ ] Taxonomie des erreurs.
- [ ] Modèle des profils OS + overrides.
- [ ] Modèle des scripts personnalisés.
- [ ] Note de sécurité.
- [ ] Découpage en sous-jalons priorisé.
**Questions d'investigation (§3) :**
- [ ] Les 8 questions sont tranchées, chacune avec MVP recommandé / alternatives / risques.
> Tout livrable ou question manquant → **renvoi pour complément**.
---
## 3. Cohérence & intégration avec l'application existante
C'est le cœur de la validation : le design doit **s'emboîter** avec ce qui existe.
- [ ] **Types JSON** : les extensions proposées sont compatibles avec `shared/types.ts` (`UpdateSnapshot`, `ExecutionResult`). Les ajouts sont **rétro-compatibles** (champs optionnels, pas de rupture du jalon 1). Vérifier qu'un snapshot/exécution du jalon 1 resterait valide.
- [ ] **Convention des templates** : les templates proposés suivent la convention existante (marqueurs `===SU:XXX===`, `LC_ALL=C`, exécution sous `sudo -S`, sortie parsable). Cohérent avec `templates/apt/*.tpl`, `server/templates/render.ts`, `server/services/aptParse.ts`.
- [ ] **Parsing** : la stratégie retenue (JSON-in-shell vs parsing-TS) est explicite, justifiée, et compatible avec `server/services/` ou propose une migration claire sans casser le refresh/upgrade actuels.
- [ ] **Couche SSH** : le design réutilise `server/ssh/client.ts` (pas de nouveau mécanisme d'exécution non justifié) ; opérations longues/détachées traitées de façon cohérente.
- [ ] **Frontière Hermes/MCP** : aucun secret n'atteint Hermes/MCP ; la surface MCP reste minimale ; les données envoyées au LLM passent par la réduction déterministe (`aptReduce.ts` ou équivalent étendu).
- [ ] **Sécurité** : actions destructives (prune, down, reboot, dist-upgrade) exigent une validation explicite côté webapp ; pas de secret en clair (logs/UI/MCP).
- [ ] **Profils OS** : le mécanisme proposé n'invalide pas le comportement Debian/Ubuntu prouvé en prod au jalon 1.
- [ ] **Découpage en sous-jalons** : chaque sous-jalon est implémentable indépendamment et produit un logiciel testable (compatible avec le workflow spec → plan → implémentation).
---
## 4. Non-régression (l'existant tourne toujours)
Confirmer que rien n'a été cassé (puisque la mission ne devait pas toucher le code) :
- [ ] `rtk pnpm check` → 0 erreur TypeScript.
- [ ] `rtk pnpm test` → suite verte (au moins les tests du jalon 1, + jalon 2 s'il est mergé).
- [ ] `rtk pnpm build``dist/index.js` + `dist/client` produits.
- [ ] Les flux prouvés du jalon 1 (ajout machine, refresh APT, full-upgrade, reboot, rapport) restent inchangés dans le code.
> Une régression ici signifie que l'agent a touché au code malgré la consigne → **rejet**.
---
## 5. Verdict
Rédiger une conclusion explicite :
- **✅ Accepté** : tous les blocs cochés. → On peut enchaîner sur `writing-plans` pour le 1ᵉʳ sous-jalon recommandé.
- **🟡 Accepté avec réserves** : livrables complets mais points de design à ajuster → lister les corrections, renvoyer pour itération courte.
- **❌ Rejeté** : périmètre débordé, code de production écrit, livrables incomplets, ou incohérence majeure avec l'appli → renvoyer à l'agent avec les points bloquants précis.
Consigner le verdict (date, blocs en échec, actions de suite) en bas de ce fichier ou dans un commit dédié.
---
## 6. Focus de validation — scripts Docker Compose et intégration webapp
Cette section fixe la proposition attendue pour le volet Docker Compose de la tâche 2. Elle sert de point de contrôle supplémentaire au moment de valider les livrables de design : l'agent doit expliquer comment ces scripts s'insèrent dans l'application existante, sans créer un moteur parallèle.
### Méthode Docker Compose retenue
- [ ] Le design retient une gestion **par SSH sur la machine cible**, en réutilisant la couche d'exécution existante (`server/ssh/client.ts`) et les templates versionnés sur disque. La variante `docker context` over SSH peut être citée comme alternative opérateur, mais ne doit pas devenir le moteur MVP de la webapp.
- [ ] Les stacks Compose sont découverts depuis des **racines déclarées par machine** (`composeRoots`, ex. `/opt/stacks`, `/srv/docker`) avec scan limité en profondeur, puis validés dans l'UI avant toute action.
- [ ] La détection automatique par labels Compose (`com.docker.compose.project`, `com.docker.compose.service`, et si présent `com.docker.compose.project.working_dir`) reste un complément pour retrouver les stacks actifs, pas l'unique source de vérité.
- [ ] Un stack seulement détecté reste en statut `candidate`; les actions `pull`, `up`, `down` et `prune` ne sont autorisées que sur un stack `enabled`/validé par l'utilisateur.
### Scripts/templates Docker attendus
Le design doit proposer au minimum ces templates, avec pseudo-shell réaliste, marqueurs `===SU:DOCKER_*===`, `LC_ALL=C`, sortie parsable et log brut archivé :
- [ ] `templates/docker/scan-compose.sh.tpl` : scanne les racines déclarées, trouve `compose.yaml`, `compose.yml`, `docker-compose.yaml`, `docker-compose.yml`, ignore `.git`, `node_modules`, `backup`, `old`, `archive`, puis valide chaque candidat avec `docker compose config --quiet`.
- [ ] `templates/docker/inspect-compose.sh.tpl` : produit l'état actuel sans appliquer de changement : `docker compose config --images`, `docker compose ps --format json`, `docker compose images --format json`, puis `docker image inspect` pour les images utilisées.
- [ ] `templates/docker/pull-check.sh.tpl` : exécute `docker compose pull --policy always --ignore-buildable` pour récupérer les images candidates sans démarrer de conteneurs, puis compare image ID/digest/labels avant-après. Cette action est non applicative mais écrit sur le disque Docker; elle ne doit donc pas être confondue avec un scan purement passif.
- [ ] `templates/docker/apply-compose.sh.tpl` : applique après validation utilisateur avec `docker compose up -d --remove-orphans`, puis recapture `ps/images/inspect` pour vérifier les conteneurs recréés et les erreurs.
- [ ] `templates/docker/prune-images.sh.tpl` : nettoyage prudent par défaut avec `docker image prune -f`; mode agressif `docker image prune -a -f --filter "until=168h"` seulement avec validation explicite côté webapp.
- [ ] `templates/docker/down-compose.sh.tpl` : action séparée et destructive; `docker compose down` ne doit pas faire partie du chemin normal de mise à jour. Toute option `--volumes` ou `--rmi` doit être interdite au MVP ou protégée par une validation forte distincte.
### Flux de mise à jour Docker à valider
Le design doit formaliser ce flux, compatible avec la documentation Docker :
1. `docker_scan` : découverte des stacks candidats depuis les racines déclarées + labels Compose actifs.
2. `docker_inspect_current` : capture de l'état actuel des services, conteneurs et images.
3. `docker_pull_check` : téléchargement des images candidates sans démarrage de conteneurs.
4. Comparaison déterministe : image ref, image ID, repo digest, labels OCI (`org.opencontainers.image.version`, `revision`, `source`, `created`) si présents.
5. Proposition UI/Hermes : liste des stacks/services avec update disponible, erreurs de pull, inconnues.
6. `docker_apply` après validation utilisateur : `docker compose up -d --remove-orphans`.
7. Vérification après application : conteneurs recréés, état `running/exited`, health si disponible, erreurs.
8. `docker_prune_images` après succès ou sur action séparée : images supprimées, espace récupéré, erreurs.
Points Docker à vérifier dans les livrables :
- [ ] Le design explique que `docker compose pull` télécharge les images mais ne démarre pas les conteneurs; c'est donc un bon pré-check applicatif, mais pas un scan sans effet.
- [ ] Le design explique que `docker compose up -d` recrée les conteneurs quand l'image ou la configuration a changé, en préservant les volumes montés; `down` n'est donc pas nécessaire pour une mise à jour normale.
- [ ] Le design distingue `docker image prune -f` (dangling images) de `docker image prune -a` (toutes les images non référencées par un conteneur), et classe `-a` comme action destructive.
Sources Docker de référence à citer dans la spec :
- `docker compose pull` : https://docs.docker.com/reference/cli/docker/compose/pull/
- `docker compose up` : https://docs.docker.com/reference/cli/docker/compose/up/
- `docker compose config` : https://docs.docker.com/reference/cli/docker/compose/config/
- `docker compose ps` : https://docs.docker.com/reference/cli/docker/compose/ps/
- `docker compose images` : https://docs.docker.com/reference/cli/docker/compose/images/
- `docker compose down` : https://docs.docker.com/reference/cli/docker/compose/down/
- `docker image inspect` : https://docs.docker.com/reference/cli/docker/image/inspect/
- `docker image prune` : https://docs.docker.com/reference/cli/docker/image/prune/
### JSON canonique Docker attendu
- [ ] `UpdateSnapshot` reste rétrocompatible et peut recevoir un bloc optionnel `docker`.
- [ ] Le bloc snapshot Docker contient au minimum : stacks déclarés, stacks candidats, services, image ref actuelle, image ID actuelle, digest actuel si disponible, labels de version si disponibles, image ID/digest candidat après pull, statut `up_to_date|updates_available|warning|error`.
- [ ] `ExecutionResult` reste rétrocompatible et peut recevoir un bloc optionnel `docker`, avec résultats `pull`, `up`, `prune`, erreurs, conteneurs recréés, images supprimées et octets récupérés.
- [ ] Les erreurs sont structurées : `docker_not_installed`, `compose_not_found`, `compose_config_invalid`, `registry_auth_failed`, `pull_failed`, `image_inspect_failed`, `up_failed`, `container_unhealthy`, `prune_failed`, `disk_space_low`.
- [ ] La réduction Hermes ne transmet que le JSON canonique + lignes importantes (`Pulling`, `Digest`, `Status`, `Downloaded newer image`, `Recreating`, `Started`, `Error`, `deleted`, `Total reclaimed space`). Le log brut complet reste archivé.
### Insertion dans la webapp existante
Le design doit montrer comment Docker s'emboîte dans les surfaces déjà présentes :
- [ ] **Config machine** : ajouter des champs de configuration Docker par machine dans le modèle futur (`dockerEnabled`, `composeRoots`, `composeScanDepth`, `composeStacks[]`), sans casser `MachineView` existant; les nouveaux champs doivent être optionnels ou livrés dans un endpoint dédié.
- [ ] **Refresh/snapshot** : étendre le refresh machine pour pouvoir produire un snapshot combiné `apt` + `docker`, ou un refresh Docker séparé si l'équipe choisit d'éviter que `docker_pull_check` soit lancé automatiquement.
- [ ] **Actions** : étendre progressivement `ActionType` avec des actions explicites (`docker_scan`, `docker_pull_check`, `docker_compose_apply`, `docker_prune_images`, `docker_compose_down`) en conservant le filtrage d'autorisation côté route `POST /api/machines/:id/actions`.
- [ ] **Executions/rapports** : réutiliser la table `executions`, le WebSocket terminal, `rawLogPath`, `reportPath` et le statut `ok|warning|error`; ne pas créer un second système de jobs Docker.
- [ ] **UI machine** : afficher un compteur Docker séparé du compteur APT (ex. stacks avec updates), proposer une vue détail par stack/service, puis boutons d'action validés (`Pull/check`, `Appliquer`, `Prune`, `Down`).
- [ ] **Validation utilisateur** : `docker_compose_apply`, `docker_prune_images` agressif et `docker_compose_down` doivent passer par une confirmation UI explicite; Hermes peut proposer mais ne déclenche jamais directement ces actions.
- [ ] **Secrets** : les credentials registry éventuellement présents sur la machine (`~/.docker/config.json`, helper, tokens) ne sont jamais lus ni renvoyés; les erreurs doivent être nettoyées avant UI/MCP si elles exposent une URL sensible.
---
## 7. Focus de validation — APT update/analyse, upgrade et reboot vérifié
Cette section fixe la proposition attendue pour la suite APT de la tâche 2. Elle doit valider que le design répond au besoin opérateur : voir la liste des paquets avant exécution, prouver ce qui a réellement changé après exécution, gérer les cas non interactifs, puis vérifier qu'un reboot a vraiment abouti.
### `update + analyse` avant upgrade
- [ ] Le design introduit une action de refresh/analyse explicite, nommable `apt_update_analyze`, distincte des upgrades manuels destructifs.
- [ ] Cette action exécute au minimum `apt-get update`, puis une simulation `apt-get -s upgrade` et une simulation `apt-get -s dist-upgrade` ou équivalent profil OS.
- [ ] Le snapshot produit liste les paquets qui **seraient** mis à jour avant validation utilisateur : nom, version actuelle, version cible, origine, architecture si disponible.
- [ ] Le snapshot distingue `upgrade` et `full/dist-upgrade` : paquets mis à jour, paquets nouvellement installés, paquets qui seraient supprimés, paquets retenus/held.
- [ ] Le design explique que les simulations APT sont parsées via les lignes documentées `Inst`, `Conf`, `Remv`, tout en gardant le log brut archivé.
- [ ] Le statut snapshot APT distingue `ok`, `updates_available`, `warning`, `error`, avec `warning` si `full/dist-upgrade` implique des suppressions ou des paquets retenus.
Sources APT à citer dans la spec :
- `apt-get` simulation et commandes : https://manpages.debian.org/apt-get
- `dpkg` conffiles/options : https://manpages.debian.org/dpkg
- `apt-listchanges` non-interactive : https://manpages.debian.org/bookworm/apt-listchanges/apt-listchanges.1.en.html
- `needrestart` non-interactive/list mode : https://manpages.debian.org/bookworm/needrestart/needrestart.1.en.html
### Profils OS et type de machine
- [ ] Le design distingue `os_family` et `machine_kind` lors de l'ajout machine.
- [ ] L'UI propose un choix manuel : Debian, Ubuntu, Proxmox VE, Raspberry Pi OS, autre Linux.
- [ ] L'UI propose un choix manuel du type : VM, machine physique, hôte Proxmox, LXC/container, Raspberry Pi, serveur GPU/workstation.
- [ ] Une action `machine_probe` ou équivalent peut détecter/corriger : `/etc/os-release`, architecture, virtualisation, Proxmox, Raspberry Pi, GPU, disques, mémoire.
- [ ] Les scripts proposés dépendent du couple OS/type machine : firmware/driver sur physique, guest tools sur VM, profil Proxmox sur hôte PVE, profil Raspberry Pi OS sur Pi.
- [ ] Debian avec firmware/drivers propriétaires vérifie explicitement `contrib`, `non-free`, `non-free-firmware`.
- [ ] Proxmox est traité comme profil dédié et pas comme Debian générique.
- [ ] Les scripts hardware/drivers/benchmark ne sont jamais installés par défaut et exigent validation.
### Scripts/templates APT attendus
Le design doit proposer au minimum ces templates, en conservant `LC_ALL=C`, `DEBIAN_FRONTEND=noninteractive`, le proxy APT optionnel, les marqueurs `===SU:APT_*===`, et `===SU:EXIT=N===` :
- [ ] `templates/apt/update-analyze.sh.tpl` : update des index + simulations `upgrade` et `dist/full-upgrade` + reboot-check.
- [ ] `templates/apt/upgrade.sh.tpl` : applique l'upgrade simple, sans suppressions volontaires.
- [ ] `templates/apt/full-upgrade.sh.tpl` ou `dist-upgrade.sh.tpl` : applique le profil complet; le design clarifie l'alias UI `full-upgrade` vs commande système `apt-get dist-upgrade`/`apt full-upgrade`.
- [ ] `templates/apt/autoremove.sh.tpl` : retire les dépendances inutiles, avec prévisualisation ou confirmation explicite si des paquets seront supprimés.
- [ ] `templates/apt/clean.sh.tpl` : vide le cache des paquets téléchargés; action séparée.
- [ ] `templates/apt/reboot-check.sh.tpl` : vérifie `/run/reboot-required`, `/var/run/reboot-required` et les paquets listés si `reboot-required.pkgs` existe.
- [ ] `templates/apt/reboot.sh.tpl` : planifie le reboot et émet un marqueur de sortie parsable avant coupure SSH.
### Upgrade non interactif et interactions humaines
- [ ] Les upgrades réels utilisent une politique non interactive explicite : `DEBIAN_FRONTEND=noninteractive`, `apt-get -y`, options dpkg défensives `--force-confdef` + `--force-confold`.
- [ ] Le design justifie la politique par défaut : conserver les fichiers de configuration locaux quand dpkg ne peut pas résoudre automatiquement, afin d'éviter d'écraser une configuration distante.
- [ ] Les prompts potentiels (`conffile`, `debconf`, `apt-listchanges`, `needrestart`, service restart, maintainer script) sont traités comme risques de blocage à détecter, pas comme dialogues interactifs à exposer dans le terminal.
- [ ] Le design prévoit un timeout d'inactivité et/ou un timeout global pour classer l'exécution en erreur contrôlée si une action reste bloquée.
- [ ] Une erreur structurée `human_interaction_required` ou équivalent est prévue, avec lignes importantes et recommandation de reprise manuelle; aucune auto-réparation dangereuse n'est lancée sans validation.
### Diff réel avant/après
- [ ] Le design ne se contente pas de l'exit code APT pour déclarer une réussite.
- [ ] Avant et après chaque action APT réelle, le template capture l'état dpkg via une commande stable du type `dpkg-query -W -f='${binary:Package}\t${Version}\t${Architecture}\n'`.
- [ ] Le backend ou le contrat JSON compare l'état avant/après pour produire : paquets mis à jour, installés, supprimés, inchangés malgré simulation, versions finales et anomalies.
- [ ] `ExecutionResult` reste rétrocompatible et reçoit un bloc optionnel `apt` détaillant `planned`, `applied`, `installed`, `removed`, `held`, `errors`, `rebootRequiredAfterRun`.
- [ ] Le rapport Markdown résume le diff réel et garde une référence au log brut, sans l'inliner entièrement.
### Reboot vérifié et délai adaptatif
- [ ] Le design introduit un reboot vérifié : lire un identifiant de boot avant reboot (`/proc/sys/kernel/random/boot_id`), déclencher le reboot, attendre la coupure SSH, retenter la connexion, puis relire le `boot_id`.
- [ ] Le reboot est considéré `ok` uniquement si la machine redevient joignable et si le `boot_id` a changé.
- [ ] Le résultat reboot contient au minimum : `beforeBootId`, `afterBootId`, `requestedAt`, `sshWentDownAt`, `sshCameBackAt`, `waitedSeconds`, `status`, `errors`.
- [ ] Le design prévoit un délai adaptatif par machine (`lastRebootDurationSeconds`, `nextRecommendedWaitSeconds`) mis à jour après chaque reboot réussi, avec marge de sécurité.
- [ ] En cas d'échec, le statut distingue au moins : `reboot_command_failed`, `ssh_never_went_down`, `machine_did_not_return`, `boot_id_unchanged`, `timeout`.
- [ ] Le reboot reste une action destructrice soumise à validation explicite côté webapp; Hermes peut recommander mais ne déclenche jamais directement.
### Insertion dans la webapp existante
- [ ] `apt_update_analyze` alimente le snapshot existant et la tuile machine avec la liste des paquets prévus avant upgrade.
- [ ] Les actions `apt_upgrade`, `apt_full_upgrade`/`apt_dist_upgrade`, `apt_autoremove`, `apt_clean` et `reboot_verified` passent par la même route d'action et la même table `executions` que le jalon 1.
- [ ] L'UI doit afficher avant exécution : liste des paquets, suppressions éventuelles, paquets retenus, reboot déjà requis, et niveau de risque.
- [ ] L'UI doit afficher après exécution : réussite/erreur, diff réel, reboot requis après coup, lien rapport, lien log brut.
- [ ] Les actions qui peuvent supprimer des paquets ou redémarrer la machine (`dist/full-upgrade`, `autoremove`, `reboot_verified`) exigent une confirmation UI explicite.
- [ ] Les nouveaux champs JSON et actions sont ajoutés de manière rétrocompatible pour que les flux jalon 1 (`refresh`, `apt_full_upgrade`, `reboot`) restent valides pendant la migration.
---
## 8. Focus de validation — scripts post-install et profils personnalisés
Cette section fixe la proposition attendue pour les scripts post-install de la tâche 2. Elle doit valider que le design couvre le cas concret d'une VM Debian 13 installée en netinstall CLI, joignable d'abord via DHCP, puis préparée à distance via SSH avec changement éventuel d'identité, réseau statique, paquets de base, partages réseau et Docker officiel.
### Principe UX et absence d'interactivité SSH
- [ ] Le design interdit les questions interactives au milieu d'un script SSH. Toute question nécessaire est transformée en champ de formulaire dans la webapp avant exécution.
- [ ] Les scripts post-install sont présentés sous forme de profils cochables; cocher un profil déplie automatiquement ses champs obligatoires.
- [ ] Chaque profil fournit un manifeste décrivant `id`, `label`, `description`, `fields`, valeurs par défaut, validations, prévisualisation, niveau de risque et confirmations requises.
- [ ] Le bouton d'exécution reste désactivé tant que les champs requis du ou des profils cochés ne sont pas valides.
- [ ] La webapp propose une preview du template rendu avant exécution, avec masquage des secrets et signalement des changements réseau/reboot.
- [ ] Les scripts s'exécutent en mode non interactif; s'ils détectent une situation nécessitant une décision non fournie, ils échouent avec une erreur structurée au lieu de bloquer.
### Profils post-install attendus
Le design doit proposer au minimum ces profils composables :
- [ ] `bootstrap_root` : première préparation après connexion DHCP et élévation via `su -` ou contexte root; installe `sudo`, `resolvconf`, `ca-certificates`, `curl`; ajoute l'utilisateur opérateur au groupe `sudo`; vérifie `sudo`.
- [ ] `identity_network` : modifie si besoin le hostname, le domaine/search local `.home`, `/etc/hosts`, puis configure l'IP statique dans `/etc/network/interfaces`.
- [ ] `base_tools` : outils de base sans `vim`; prévoir notamment `nano`, `less`, `bash-completion`, `tmux`, `screen`, `htop`, `iotop`, `ncdu`, `tree`, `rsync`, `unzip`, `zip`, `tar`.
- [ ] `network_tools` : `iproute2`, `iputils-ping`, `dnsutils`, `traceroute`, `net-tools` optionnel, `tcpdump`, `nmap`, `mtr-tiny`, `lsof`, `netcat-openbsd`.
- [ ] `dev_git` : `git`, `curl`, `wget`, `jq`, `yq`, `gnupg`, `lsb-release`; `build-essential` seulement optionnel.
- [ ] `sharing` : profils de partage réseau avec `samba`, `nfs-kernel-server`, `avahi-daemon`, `libnss-mdns`, configurables séparément si besoin.
- [ ] `docker_official` : installation Docker Engine depuis le dépôt officiel Docker Debian, avec `docker-ce`, `docker-ce-cli`, `containerd.io`, `docker-buildx-plugin`, `docker-compose-plugin`, ajout utilisateur au groupe `docker`, création du dossier Compose dans le home, puis reboot/reconnexion si nécessaire.
- [ ] `vm_guest_tools` : paquet adapté à l'hyperviseur (`qemu-guest-agent` ou `open-vm-tools`) choisi par l'utilisateur.
- [ ] Profils optionnels à prévoir mais non installés par défaut : `security_basic`, `backup_tools`, `monitoring`, `mail_notify`, `time_sync`, `storage_tools`.
### Champs dynamiques attendus dans l'interface
Le design doit préciser les champs générés lorsqu'un profil est coché :
- [ ] Pour `identity_network` : `newHostname`, `domain/search`, `interfaceName`, `staticAddress` au format CIDR (ex. `10.0.x.y/22`), `gateway` (défaut `10.0.0.1`), `dnsNameservers` (défaut `10.0.0.1`, `10.0.0.10`), `reconnectHost`.
- [ ] Pour `docker_official` : `dockerUser` (ex. `gilles`), `dockerHomeDir` ou `composeRoot` (ex. `/home/gilles/docker`), `installComposePlugin`, `rebootAfterInstall`.
- [ ] Pour `sharing` : choix séparé Samba/NFS/mDNS, noms de partages éventuels, chemins autorisés, utilisateurs/groupes si nécessaire.
- [ ] Pour `vm_guest_tools` : type d'hyperviseur ou paquet cible.
- [ ] Les champs peuvent être préremplis depuis la machine (`machine.name`, IP DHCP, interface primaire détectée, utilisateur SSH), mais doivent rester modifiables avant validation.
Exemple de manifeste attendu dans la spec :
```json
{
"id": "identity_network",
"label": "Hostname + IP statique",
"requiresConfirmation": true,
"risk": "network_change",
"fields": [
{ "name": "newHostname", "type": "hostname", "required": true },
{ "name": "domain", "type": "string", "required": true, "default": "home" },
{ "name": "interfaceName", "type": "select", "required": true, "defaultFrom": "detected.primaryInterface" },
{ "name": "staticAddress", "type": "ipv4_cidr", "required": true },
{ "name": "gateway", "type": "ipv4", "required": true, "default": "10.0.0.1" },
{ "name": "dnsNameservers", "type": "ipv4_list", "required": true, "default": ["10.0.0.1", "10.0.0.10"] },
{ "name": "reconnectHost", "type": "ipv4", "required": true, "defaultFrom": "staticAddress.ip" }
]
}
```
### Scripts/templates custom attendus
- [ ] `templates/custom/bootstrap-root.sh.tpl` : prépare sudo et les prérequis minimaux depuis un contexte root ou sudo.
- [ ] `templates/custom/identity-network.sh.tpl` : écrit hostname, hosts et réseau statique; sauvegarde les fichiers modifiés; signale explicitement le changement d'adresse.
- [ ] `templates/custom/install-package-groups.sh.tpl` : installe les groupes de paquets sélectionnés, sans `vim` par défaut.
- [ ] `templates/custom/docker-official-debian.sh.tpl` : suit la documentation officielle Docker Debian pour le dépôt APT, la clé GPG dans `/etc/apt/keyrings`, `docker.sources`, puis les paquets Docker/Compose.
- [ ] `templates/custom/sharing.sh.tpl` : installe Samba/NFS/mDNS selon les choix, sans exposer de configuration dangereuse par défaut.
- [ ] `templates/custom/vm-guest-tools.sh.tpl` : installe l'agent invité choisi.
Sources à citer dans la spec :
- Docker Debian officiel : https://docs.docker.com/engine/install/debian/
- Docker Compose plugin : https://docs.docker.com/compose/install/linux/
- Debian network configuration : https://wiki.debian.org/NetworkConfiguration
- Debian Handbook network configuration : https://www.debian.org/doc/manuals/debian-handbook/sect.network-config
- Debian package `resolvconf` : https://packages.debian.org/stable/net/resolvconf
### Réseau, reboot et reconnexion
- [ ] Le design traite `identity_network` comme une action à risque : confirmation UI explicite, preview obligatoire, sauvegarde de `/etc/network/interfaces` et `/etc/hosts`.
- [ ] Lorsqu'une IP statique est appliquée, le résultat JSON contient l'ancien endpoint DHCP, le nouveau endpoint, et l'indication que la reconnexion doit se faire sur `reconnectHost`.
- [ ] Le script ne doit pas couper la connexion sans que la webapp ait planifié la stratégie de reconnexion; si reboot requis, utiliser le mécanisme `reboot_verified`.
- [ ] Après reboot ou redémarrage réseau, la webapp vérifie la reconnexion sur la nouvelle IP/hostname et met à jour la machine si le retour est confirmé.
- [ ] En cas d'échec, les erreurs distinguent `network_config_invalid`, `interface_not_found`, `dns_config_failed`, `reconnect_failed`, `hostname_failed`, `sudo_setup_failed`.
### JSON canonique post-install attendu
- [ ] `ExecutionResult` reste rétrocompatible et peut recevoir un bloc optionnel `custom` ou `postInstall`.
- [ ] Le résultat liste les profils exécutés, les variables non sensibles utilisées, les fichiers modifiés, les paquets installés, les services activés/démarrés, les reboots demandés et les erreurs.
- [ ] Les secrets ou tokens ne sont jamais inclus dans les variables sérialisées, les logs UI, les rapports ni le MCP.
- [ ] Les modifications réseau et Docker sont clairement marquées dans le rapport Markdown avec les prochaines actions attendues, par exemple reconnexion, logout/login pour groupe Docker, ou reboot.
### Insertion dans la webapp existante
- [ ] Les scripts post-install passent par la même mécanique que les autres actions : templates versionnés, preview, exécution SSH, WebSocket terminal, `executions`, rapport Markdown et log brut.
- [ ] La configuration des profils peut être stockée par machine ou lancée comme assistant ponctuel de provisioning, mais le design doit préciser où sont conservées les valeurs réutilisables.
- [ ] Hermes peut aider à proposer des profils ou expliquer un échec, mais ne reçoit que le JSON réduit et ne déclenche jamais les actions à risque sans validation webapp.
- [ ] Les profils post-install sont découpés en sous-jalons indépendants : bootstrap/sudo, identité+réseau, paquets de base, Docker officiel, partage réseau, outils VM/monitoring.
---
## 9. Notes de validation (à remplir au moment de la revue)
> **Date** : 2026-06-05
> **Signé** : (orchestrateur, revue indépendante post-design)
---
> **Note historique** : une première validation (2026-06-05, délégation initiale) avait conclu au rejet faute de livrables. Les livrables ont depuis été produits. La présente revue est **indépendante** — elle ne s'appuie pas sur l'auto-évaluation `99-couverture-gate.md` mais sur la lecture directe de chaque livrable.
---
### Verdict global
🟡 **Accepté avec réserves mineures** — les livrables sont complets, cohérents avec l'existant et couvrent l'intégralité des cases du gate. Deux réserves mineures non bloquantes sont détaillées ci-dessous (renommage de fichier à tracer dans SJ-0 ; état non committé de `shared/types.ts` à surveiller). Aucune incohérence majeure, aucune rupture de rétro-compatibilité, aucun manque réel. La mission peut progresser vers `writing-plans` pour SJ-0 dès confirmation de la non-régression (`pnpm check/test/build`).
---
### Vérification §0 — Conditions préalables
- **Section « État d'avancement / Ce qui a été fait »** dans `tache2.md` : **PRÉSENTE** (§10, ll. 196-241). Contient les chemins des 11 livrables, les 8 décisions, les questions tranchées, ce qui reste ouvert, les sous-jalons recommandés. Condition satisfaite.
- **Livrables de design présents sous `docs/`** : **11 fichiers présents** sous `docs/design/tache2/` (00→99). Condition satisfaite.
- **`tache2.md` annoncé terminé** : oui (§10 statut = « mission de design terminée »). Condition satisfaite.
**Conclusion §0 : toutes les conditions préalables sont remplies. La validation peut s'ouvrir.**
---
### Vérification §1 — Discipline & périmètre
- **Fichiers `docs/design/tache2/**` et `tache2.md`** : seuls fichiers nouveaux liés à la tâche 2. **OK.**
- **Jalon 1 intact** : `templates/apt/check.sh.tpl`, `full-upgrade.sh.tpl`, `reboot.sh.tpl`, `server/services/aptParse.ts`, `server/templates/aptReduce.ts`, `server/templates/render.ts`, `server/ssh/client.ts`**aucun touché**. **OK.**
- **Dépôts de référence** : `linux-update-dashboard/` et `nas-ops/` non copiés (cités en inspiration, pseudo-shell réécrit). **OK.**
- **Hors-scope listés comme suggestions uniquement** : `00-synthese.md §6` liste explicitement les renvois vers tâches 3/4/5/6/7. **OK.**
- **`shared/types.ts` modifié** : le diff montre l'ajout de l'interface `ServerCapabilities` (32 lignes). L'auto-évaluation affirme que l'agent tâche 2 n'a pas touché ce fichier. La vérification du diff confirme que cette modification (endpoint `/capabilities`, gestion d'erreur Hono, import `capabilities.ts`) est cohérente avec le chantier jalon 2 en cours (polish design system) et non avec la tâche 2. **Il s'agit d'une modification du contexte de travail partagé, pas d'une violation de la tâche 2.** Toutefois, le fichier `shared/types.ts` étant dans un état non committé différent de l'état jalon 1, la vérification de rétro-compatibilité (§3) doit se faire sur l'état **actuel** de ce fichier (qui inclut déjà `ServerCapabilities` mais conserve intégralement `AptPackage`, `UpdateSnapshot`, `ExecutionResult`, `MachineView`). Pas d'incidence sur le design de la tâche 2. **Réserve de contexte, non bloquante.**
**Conclusion §1 : aucune violation de périmètre imputable à la tâche 2. La modification de `shared/types.ts` provient du jalon 2 concurrent.**
---
### Vérification §2 — Complétude des livrables
**Axes :**
| Axe | Couvert ? | Livrable principal | Vérification |
|---|---|---|---|
| A — Templates APT + profils OS + proxy | **OUI** | `10-templates-apt.md`, `60-profils-os-machine.md` | 7 templates inventoriés avec marqueurs, pseudo-shell pour 5, profils OS Debian/Ubuntu/Proxmox/RPi, proxy direct/runtime/persistent. |
| B — Capture prévu/appliqué, Hermes | **OUI** | `40-contrats-json.md` | Snapshot dpkg before/after, `AptExecutionResult.planned`/`applied`, déduplication, réducteur étendu. |
| C — Taxonomie des erreurs | **OUI** | `50-erreurs.md` | 11 codes APT, 10 codes Docker, 6 codes réseau/post-install, stratégie de remédiation non auto. |
| D — Docker scan/pull/up/down/prune | **OUI** | `20-docker.md` | 6 templates avec pseudo-shell, flux 8 étapes, réduction Hermes, insertion webapp. |
| E — Scripts custom + overrides + garde-fous | **OUI** | `30-scripts-custom.md` | 9 profils post-install, manifestes, champs dynamiques, pseudo-shell 6 templates, garde-fous. |
**Livrables §4 :**
| # | Livrable | Présent | Fichier |
|---|---|---|---|
| 1 | Inventaire des templates | **OUI** | `10` §2, `20` §2, `30` §2 |
| 2 | Contenu proposé (pseudo-shell, `===SU:XXX===`) | **OUI** | `10` §4, `20` §4, `30` §4 |
| 3 | Schémas JSON canoniques étendus | **OUI** | `40` complet |
| 4 | Taxonomie des erreurs | **OUI** | `50` |
| 5 | Modèle profils OS + overrides | **OUI** | `60` §1-§4 |
| 6 | Modèle profils machine | **OUI** | `60` §5-§6 |
| 7 | Modèle scripts personnalisés | **OUI** | `30` |
| 8 | Note de sécurité | **OUI** | `70` |
| 9 | Découpage en sous-jalons priorisé | **OUI** | `80` (SJ-0→SJ-9) |
**8 questions d'investigation (§3) :** toutes tranchées dans `90-questions-investigation.md` avec MVP / alternatives / risques. Vérification directe : Q1 (parsing hybride TS dominant), Q2 (un fichier complet par profil + fallback base), Q3 (manuel + machine_probe), Q4 (dpkg-query before/after), Q5 (extensions additives types TS), Q6 (nohup + exit-code + reboot_verified), Q7 (barrière action_requests + nettoyage secrets), Q8 (surface MCP v1 conservée). **Toutes tranchées.**
**Conclusion §2 : complétude intégrale. Aucun livrable manquant, aucune question non tranchée.**
---
### Vérification §3 — Cohérence & intégration avec l'application existante
**Types JSON — rétro-compatibilité (`shared/types.ts`) :**
Vérification directe de l'état actuel de `shared/types.ts` (ll. 1-89) :
- `OsFamily = "debian" | "ubuntu" | "unknown"` → le design propose `"proxmox" | "raspbian"` en plus. Additif, aucune rupture.
- `AptProxyMode = "direct" | "runtime"` → ajout `"persistent"`. Additif.
- `ActionType = "apt_full_upgrade" | "reboot"` → ajout de 12 valeurs. Additif.
- `UpdateSnapshot.apt` : bloc requis conservé tel quel ; les ajouts (`status?`, `held?`, `removed?`, etc.) sont tous optionnels. Un payload jalon 1 reste valide.
- `ExecutionResult` : `mode: "manual"` élargi en union — **réserve mineure** : le champ `mode` passe de littéral `"manual"` à `"manual" | "scheduled" | "hermes_requested"`. TypeScript accepte l'union élargie sans rupture (un payload avec `mode: "manual"` reste valide). Non bloquant mais à noter pour la migration progressive.
- `MachineView` : non touché par le design. **OK.**
- `ServerCapabilities` (ajout jalon 2) : non conflictuel avec les extensions tâche 2.
**Convention `===SU:XXX===` :**
Vérification directe sur `check.sh.tpl` (marqueurs actuels : `===SU:UPDATE===`, `===SU:SIMULATE===`, `===SU:REBOOT===`, `===SU:END===`) et les pseudo-shells proposés :
- `update-analyze.sh.tpl` utilise `===SU:APT_UPDATE===`, `===SU:APT_SIM_UPGRADE===`, `===SU:APT_SIM_DISTUPGRADE===`, etc. — nouveaux noms, cohérents avec la convention de nommage.
- `10-templates-apt.md §6` précise explicitement le plan de migration : `check.sh.tpl` conservé, `update-analyze.sh.tpl` introduit comme successeur enrichi, bascule dans un sous-jalon dédié (SJ-1). **OK.**
- `full-upgrade.sh.tpl` existant utilise `===SU:UPGRADE===`/`===SU:REBOOT===`/`===SU:EXIT===` ; le design ajoute `===SU:DPKG_BEFORE===` et `===SU:DPKG_AFTER===` sans supprimer les marqueurs existants. Extension additive. **OK.**
- `reboot.sh.tpl` existant utilise `===SU:REBOOT_NOW===` ; le design propose `===SU:BOOT_ID_BEFORE===` + `===SU:REBOOT_NOW===`. Ici une **réserve mineure** : le `reboot.sh.tpl` proposé (`10-templates-apt.md §4.5`) émet uniquement `===SU:BOOT_ID_BEFORE===` et `===SU:REBOOT_NOW===`, mais n'émet pas de `===SU:EXIT=N===` final, contrairement à la convention établie dans les autres templates. En pratique le script se termine par `echo "reboot planifié"` sans marqueur de sortie — à corriger en implémentation (SJ-3). Signalé comme coquille de cohérence.
**Parsing — stratégie explicite et justifiée :**
Décision Q1 (`90` §Q1) : hybride à dominante TS. Cohérent avec `aptParse.ts` et `aptReduce.ts` existants. La stratégie d'extension (`reduceLines.ts` additif, sans casser `reduceAptLines`) est explicite. **OK.**
**Couche SSH (`server/ssh/client.ts`) :**
Le design réutilise exclusivement `runScriptSudo` / `runPlain` partout (cité en `00-synthese.md §4`, `90` Q6, `20` §1, `30` §6). Aucun nouveau mécanisme SSH. **OK.**
**Frontière Hermes/MCP :**
- Aucun secret (mot de passe, token, credential registry) ne transite vers Hermes/MCP : `70` §1, `30` §5, `20` §6. Mécanisme de nettoyage déterministe décrit. **OK.**
- Surface MCP minimale : 8 outils v1 conservés, nouvelles capacités via `run_action` filtré. Aucune nouvelle primitive SSH. **OK.**
- `run_action` sur action destructive non validée → `action_request` en attente, pas une exécution. **OK.**
**Sécurité — actions destructives :**
Tableau complet dans `70` §2 (8 actions avec niveau de validation). `--volumes`/`--rmi` sur `down` interdits au MVP. Hermes propose, ne déclenche jamais. **OK.**
**Profils OS — non-invalidation Debian/Ubuntu :**
Mécanisme de fallback `templates/apt/*` (profil `base`) : les Debian/Ubuntu sans dossier dédié utilisent les templates existants. Non-régression jalon 1 assurée par convention de chemin. **OK.**
**Sous-jalons — implémentabilité indépendante :**
`80-sous-jalons.md` : SJ-0→SJ-9, dépendances explicites, chacun décrit en termes de contenu + testabilité + risque + priorité. SJ-0 marqué comme bloquant pour tous les autres. **OK.**
**Alignement `tache1.9.md` :**
`00-synthese.md §5` mappe chaque bloc JSON sur les tables prévues par `tache1.9.md` (`snapshots`, `executions`, `apt_planned_packages`, `apt_applied_packages`, `docker_compose_stacks`, `docker_stack_services`, `docker_image_events`, `apt_errors`, `important_messages`, `install_profiles`, `install_recipes`, `install_recipe_versions`, `machine_profile_state`, `script_variables_presets`, `docker_settings`, `docker_compose_roots`, `machine_hardware`). Aucune table nouvelle non prévue. **OK.**
**Conclusion §3 : cohérence globale confirmée. Deux points mineurs identifiés (mode union + marqueur EXIT manquant sur reboot.sh.tpl), non bloquants pour le design.**
---
### Vérification §4 — Non-régression
L'agent tâche 2 n'a modifié aucun fichier de production (templates, server, client, shared, configs). La non-régression formelle (`pnpm check/test/build`) est à exécuter par l'orchestrateur — aucun code n'ayant été touché par la mission de design, elle est attendue verte. **À vérifier avant SJ-0.**
---
### Vérification §6 — Focus Docker Compose (grille détaillée)
Vérification directe sur `20-docker.md` et `40-contrats-json.md` :
| Case §6 | Couvert ? | Détail |
|---|---|---|
| Gestion par SSH, couche existante ; `docker context` = alternative | **OUI** | `20` §1, explicit |
| Stacks depuis `composeRoots`, scan limité (`composeScanDepth`), validation UI | **OUI** | `20` §1 et §4.1 (pseudo-shell scan) |
| Détection labels en complément (`com.docker.compose.project`, `.service`, `.working_dir`) | **OUI** | `20` §1 et §4.1 (section `DOCKER_LABELS`) |
| Stack détecté = `candidate` ; actions sur `enabled` seulement | **OUI** | `20` §1 et §2 tableau |
| `scan-compose.sh.tpl` (fichiers compose, ignore .git/node_modules/backup/old/archive, `config --quiet`) | **OUI** | `20` §4.1, pseudo-shell complet |
| `inspect-compose.sh.tpl` (`config --images`, `ps --format json`, `images --format json`, `image inspect`) | **OUI** | `20` §4.2 |
| `pull-check.sh.tpl` (`--policy always --ignore-buildable`, compare ID/digest/labels, non passif) | **OUI** | `20` §4.3 ; non-passivité explicitée dans §1 tableau et §3 |
| `apply-compose.sh.tpl` (`up -d --remove-orphans`, recapture ps/images/inspect) | **OUI** | `20` §4.4 |
| `prune-images.sh.tpl` (safe `-f` ; agressif `-a -f --filter until=168h` validé) | **OUI** | `20` §4.5 ; Mustache `{{#aggressive}}` |
| `down-compose.sh.tpl` (séparé/destructif ; `--volumes`/`--rmi` interdits) | **OUI** | `20` §4.6 ; commentaire explicite dans pseudo-shell |
| Flux 1→8 formalisé | **OUI** | `20` §3 (8 étapes numérotées) |
| `pull` télécharge sans démarrer les conteneurs | **OUI** | `20` §3 (point clé n°1) |
| `up -d` recrée si changement, préserve volumes, `down` inutile | **OUI** | `20` §3 (point clé n°2) |
| `prune -f` vs `-a` (destructif) distingués | **OUI** | `20` §2 tableau + §3 (point clé n°3) |
| Sources Docker citées (8 URLs) | **OUI** | `20` §1, toutes les 8 URLs listées |
| `UpdateSnapshot.docker` rétrocompatible (bloc optionnel) | **OUI** | `40` §3 ; `docker?` optionnel sur `UpdateSnapshot` |
| Bloc snapshot Docker minimal (stacks déclarés/candidats/services/IDs/digest/labels/candidat/statut) | **OUI** | `40` §3, interfaces `DockerSnapshotStack` et `DockerSnapshotService` |
| `ExecutionResult.docker` (pull/up/prune/erreurs/recréés/supprimés/octets) | **OUI** | `40` §4, `DockerExecutionResult` |
| Erreurs Docker structurées (10 codes) | **OUI** | `50` §3, tableau des 10 codes |
| Réduction Hermes lignes Docker + log brut archivé | **OUI** | `20` §5, `40` §7 |
| Config machine `dockerEnabled`/`composeRoots`/`composeScanDepth`/`composeStacks[]`, `MachineView` intact | **OUI** | `20` §6 ; champs optionnels ou endpoint dédié |
| Refresh combiné apt+docker ou Docker séparé | **OUI** | `20` §6 (pull-check séparé recommandé) |
| `ActionType` étendu (docker_*) + filtrage autorisation route | **OUI** | `40` §2 et `20` §6 |
| Réutilise `executions`/WS/`rawLogPath`/`reportPath`/statut, pas de second système | **OUI** | `20` §1 et §6 |
| UI compteur Docker séparé + détail + boutons validés | **OUI** | `20` §6 |
| Validation UI apply/prune agressif/down ; Hermes ne déclenche pas | **OUI** | `20` §6, `70` §2 |
| Secrets registry jamais lus ; erreurs nettoyées | **OUI** | `20` §6, `70` §1 (filtre regex sensible) |
**Conclusion §6 : toutes les cases Docker confirmées par lecture directe des livrables. Aucun manque.**
---
### Vérification §7 — Focus APT / analyse / reboot vérifié
| Case §7 | Couvert ? | Détail |
|---|---|---|
| `apt_update_analyze` distinct des upgrades destructifs | **OUI** | `10` §2 tableau (type snapshot, non destructif) |
| `apt-get update` + `-s upgrade` + `-s dist-upgrade` | **OUI** | `10` §4.1 pseudo-shell |
| Snapshot liste paquets prévus (nom/cur/cible/origine/arch) | **OUI** | `40` §3 `AptSnapshotDetail` ; arch en option |
| Distingue upgrade vs dist-upgrade (maj/install/remove/held) | **OUI** | `10` §4.1 parsing + `40` §3 (`upgradeCount`, `distUpgradeCount`, `removed`, `held`) |
| Simulations parsées via `Inst`/`Conf`/`Remv`, log brut archivé | **OUI** | `10` §1 et §4.1 |
| Statut `ok/updates_available/warning/error` ; warning si remove/held | **OUI** | `10` §4.1 fin de section |
| Sources APT citées (4 URLs) | **OUI** | `10` §1, toutes présentes |
| Distingue `os_family` et `machine_kind` | **OUI** | `60` §1 |
| Choix manuel OS (5 valeurs) | **OUI** | `60` §6 |
| Choix manuel type machine (6 valeurs) | **OUI** | `60` §6 |
| `machine_probe` détecte/corrige (jamais auto) | **OUI** | `60` §6 + pseudo-shell probe |
| Scripts dépendent du couple OS/type | **OUI** | `60` §5 tableau |
| Debian vérifie contrib/non-free/non-free-firmware | **OUI** | `60` §4 + `10` §5 |
| Proxmox = profil dédié | **OUI** | `60` §4 et §2 (dossier `templates/proxmox/`) |
| Scripts hardware/drivers/benchmark jamais par défaut | **OUI** | `60` §5 note + `30` §2 note |
| Templates APT attendus (7) | **OUI** | `10` §2 tableau (tous 7 listés avec pseudo-shell ou référence) |
| Politique non interactive (`DEBIAN_FRONTEND=noninteractive`, `-y`, confdef/confold) | **OUI** | `10` §4.2 et `50` §2 |
| Justification confdef/confold | **OUI** | `10` §4.2 (conserver config locale) |
| Prompts = risques de blocage, pas dialogues | **OUI** | `50` §2 |
| Timeout inactivité/global → `human_interaction_required` | **OUI** | `50` §2 |
| `human_interaction_required` prévu (pas d'auto-réparation) | **OUI** | `50` §2 + `50` §1 (principe) |
| Pas seulement exit code | **OUI** | `50` §1 (principe n°1) + `10` §4.2 |
| `dpkg-query -W -f=...` before/after | **OUI** | `10` §4.2, `40` §3 Q4 |
| Diff backend (maj/install/remove/inchangé/versions/anomalies) | **OUI** | `40` §4 `AptExecutionResult` |
| `ExecutionResult.apt` (planned/applied/installed/removed/held/errors/rebootRequiredAfterRun) | **OUI** | `40` §4 `AptExecutionResult` |
| Rapport Markdown résume diff + réf log | **OUI** | `70` §4, `40` §8 |
| Reboot vérifié (boot_id avant/après, attente, reconnexion) | **OUI** | `10` §4.5 (pseudo-shell + orchestration backend) |
| Reboot ok si machine revient ET boot_id changé | **OUI** | `10` §4.5 (expliqué en clair) |
| `RebootResult` (beforeBootId…status/errors) | **OUI** | `40` §4 interface `RebootResult` (8 champs requis + optionnels) |
| Délai adaptatif par machine (`lastRebootDurationSeconds`, `nextRecommendedWaitSeconds`) | **OUI** | `10` §4.5 + `40` §4 `RebootResult` |
| Statuts d'échec reboot distingués (5 valeurs) | **OUI** | `40` §4 union `RebootResult.status` |
| Reboot = validation UI ; Hermes ne déclenche pas | **OUI** | `70` §2 tableau |
| `apt_update_analyze` alimente snapshot + tuile | **OUI** | `10` §6 |
| Actions via même route + table `executions` | **OUI** | `20` §6, `10` §6 |
| UI avant exécution (paquets/suppressions/held/reboot/risque) | **OUI** | `70` §2 + `40` §3 (données disponibles) ; rendu JSX renvoyé à tâche 3 |
| UI après exécution (réussite/diff/reboot/rapport/log) | **OUI** | `70` §4 ; rendu JSX renvoyé à tâche 3 |
| Confirmation UI pour dist/full/autoremove/reboot | **OUI** | `70` §2 tableau |
| Nouveaux champs/actions rétrocompatibles | **OUI** | `40` §1 règles d'extension |
**Conclusion §7 : toutes les cases APT/reboot confirmées par lecture directe. Aucun manque.**
---
### Vérification §8 — Focus scripts post-install
| Case §8 | Couvert ? | Détail |
|---|---|---|
| Interdit questions interactives SSH → champs formulaire | **OUI** | `30` §1 (interdiction stricte) |
| Profils cochables dépliant leurs champs | **OUI** | `30` §1 |
| Manifeste (id/label/description/fields/défauts/validations/preview/risk/confirmations) | **OUI** | `30` §1 + §3 (exemple JSON complet) |
| Bouton désactivé si champs invalides | **OUI** | `30` §1 |
| Preview avec masquage secrets + signalement réseau/reboot | **OUI** | `30` §1, `70` §1 |
| Échec structuré si décision manquante | **OUI** | `30` §1 et §4 |
| 8 profils attendus (bootstrap_root → vm_guest_tools + optionnels) | **OUI** | `30` §2 tableau (9 profils principaux + optionnels listés) |
| Champs `identity_network` (7 champs) | **OUI** | `30` §3 + exemple manifeste JSON |
| Champs `docker_official` (4 champs) | **OUI** | `30` §3 |
| Champs `sharing` | **OUI** | `30` §3 |
| Champs `vm_guest_tools` | **OUI** | `30` §3 |
| Champs préremplis depuis machine, modifiables | **OUI** | `30` §3 (dernière phrase) |
| Exemple de manifeste JSON | **OUI** | `30` §3 (manifeste `identity_network` complet, identique au gate) |
| Templates custom attendus (6) | **OUI** | `30` §4 (pseudo-shell pour bootstrap, identity, install-pkg-groups, docker-official ; sharing et vm-guest-tools en prose avec référence tâche 4) |
| Sources citées (5 URLs) | **OUI** | `30` §4 fin |
| `identity_network` à risque (confirmation/preview/sauvegardes) | **OUI** | `30` §4.2, `70` §2 |
| Résultat JSON old/new endpoint + reconnectHost | **OUI** | `40` §4 `PostInstallResult.networkChange` |
| Pas de coupure sans stratégie reconnexion ; reboot via reboot_verified | **OUI** | `30` §4.2 |
| Webapp vérifie reconnexion + met à jour machine | **OUI** | `30` §4.2 |
| Erreurs réseau distinguées (6 codes) | **OUI** | `50` §4 |
| `ExecutionResult.postInstall` rétrocompatible | **OUI** | `40` §4 `PostInstallResult`, bloc optionnel |
| Résultat liste profils/variables non sensibles/fichiers/paquets/services/reboots/erreurs | **OUI** | `40` §4 `PostInstallResult` (7 champs) |
| Secrets jamais inclus | **OUI** | `30` §5, `70` §1 |
| Changements réseau/Docker marqués dans rapport Markdown | **OUI** | `30` §5, `70` §4 |
| Même mécanique (templates/preview/SSH/WS/executions/rapport/log) | **OUI** | `30` §6 |
| Valeurs réutilisables stockées (où) | **OUI** | `30` §6 (`script_variables_presets`, `machine_profile_state`) |
| Hermes propose/explique, JSON réduit, pas de déclenchement risqué | **OUI** | `30` §6, `70` §2/§3 |
| Profils découpés en sous-jalons indépendants | **OUI** | `80` SJ-8 et SJ-9 |
**Conclusion §8 : toutes les cases post-install confirmées par lecture directe. Aucun manque.**
---
### Réserves (non bloquantes pour le design)
**Réserve 1 — Absence de `===SU:EXIT=N===` dans `reboot.sh.tpl` : comportement intentionnel, cohérent avec le jalon 1.**
Le pseudo-shell de `10-templates-apt.md §4.5` ne se termine pas par `===SU:EXIT=N===`. Cela est conforme au `reboot.sh.tpl` existant en prod (jalon 1, vérifié), qui n'en contient pas non plus : le reboot est planifié en arrière-plan (`nohup sleep 2; reboot`) et la connexion SSH se ferme proprement avant qu'un EXIT puisse être émis de manière fiable. Ce comportement est donc intentionnel et documenté en prose dans §4.5. **Ce n'est pas une coquille.** Réserve levée.
**Réserve 2 — Réduction Hermes (`aptReduce.ts`) : renommage suggéré `reduceLines.ts` à confirmer.**
Le design propose de renommer `aptReduce.ts` en `reduceLines.ts` tout en conservant `reduceAptLines` comme export. L'auto-évaluation dit « additif, sans casser `reduceAptLines` ». Correct sur le fond, mais le renommage de fichier implique de mettre à jour les imports existants — à vérifier que `server/templates/render.ts` ou tout autre consommateur est mis à jour dans SJ-0. Non bloquant pour le design, à tracer dans le plan SJ-0.
**Réserve 3 — Contexte partagé : `shared/types.ts` modifié par jalon 2 concurrent.**
La modification est imputable au jalon 2, pas à la tâche 2. Elle n'introduit aucune rupture pour les extensions prévues par la tâche 2. À surveiller : s'assurer que SJ-0 s'appuie sur l'état committé de `shared/types.ts` et non sur le diff non committé.
---
### Coquilles corrigées dans les livrables
Aucune coquille textuelle ou factuelle identifiée dans les 11 fichiers de design. L'absence de `===SU:EXIT=N===` dans `reboot.sh.tpl` était un faux positif : comportement intentionnel cohérent avec le jalon 1 (voir réserve 1 levée ci-dessus). L'auto-évaluation `99-couverture-gate.md` est globalement correcte — les trois réserves résiduelles qu'elle liste (`§4 non-régression`, `UI JSX → tâche 3`, `délimiteurs Mustache/Docker`) sont légitimes et non bloquantes.
---
### Écarts avec l'auto-évaluation du producteur
L'auto-évaluation coche tout en ✅ sauf les trois réserves résiduelles. La présente revue indépendante confirme intégralement ce tableau. Aucun manque réel n'a été identifié que l'auto-évaluation aurait raté. Un faux positif initial (marqueur EXIT de reboot) a été levé après vérification du template existant en prod.
---
### Actions de suite
1. **Lancer `rtk pnpm check && rtk pnpm test && rtk pnpm build`** pour confirmer la non-régression formelle (§4 ; aucun code de prod touché par la tâche 2, résultat attendu vert).
2. **Procéder à `writing-plans` pour SJ-0** (socle types/réduction/résolution) dès que la non-régression est confirmée.
3. Tracer dans le plan SJ-0 la mise à jour des imports consommateurs si `aptReduce.ts` est renommé en `reduceLines.ts`.
4. S'assurer que SJ-0 s'appuie sur l'état **committé** de `shared/types.ts` (le diff non committé provient du jalon 2 concurrent — à intégrer ou isoler avant de démarrer SJ-0).