0fbca06d3d
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>
628 lines
55 KiB
Markdown
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).
|