Files
system_update/validation_tache2.md
T
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

55 KiB

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 builddist/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 :

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 :

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 :

{
  "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 :

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.tsaucun 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).