diff --git a/CHANGELOG.md b/CHANGELOG.md index fcf0418..2f3de98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,50 @@ # CHANGELOG — Set-OPS +## 2026-09-20 (7) — Une portée jugée trop haut ferme une séquence à qui elle appartient + +**83 preuves, `make test` à 0 échec (15 tests de runbooks).** Les assistants livrés à +l'entrée `(6)` pesaient la portée **au runbook**. Une console de locataire le montre en +une lecture : `locataire-deployer` et `machine-une` y étaient hors de portée — donc un +locataire ne pouvait pas déployer sa propre flotte, ce qui est exactement son métier. + +### Une seule étape qui matérialise fermait tout le reste + +`flotte-creer` et `creer-vm` engendrent des VM : ils exigent la fabric, que le locataire +n'a pas. Déclarés dans une séquence dont la portée valait pour l'ensemble, ils faisaient +basculer en `poste` les étapes voisines — `deployer-tout`, `valider`, `verifier-hote` — +qui ne demandent pourtant que ce que le locataire possède déjà. + +Mesuré sur la console de TechnoLibre, portée `tenant` : **6 runbooks conduisibles sur 17**, +et parmi les onze fermés, les deux qui décrivent son travail quotidien. + +La portée se pèse désormais **à l'étape**. Le runbook n'en donne que le défaut ; l'étape +qui exige davantage le déclare. Le locataire conduit donc sa séquence et **bute +précisément là où il faut** : sur la machine à engendrer, pas sur le déploiement qui suit. +La page ferme l'étape seule, en disant sa raison sur cette étape ; l'inspecteur compte ce +qui est hors de portée plutôt que de barrer l'ensemble. + +Côté serveur, la garde de `/api/runbook-etape` lit la portée de **l'étape visée par son +index**, et non celle du runbook : juger sur le runbook aurait refusé un geste que la +console avait le droit de faire, ou accepté l'inverse. + +### Ce que cette correction dit du reste + +**Un droit qui se calcule sur l'ensemble se trompe toujours dans le même sens** : il +refuse à quelqu'un ce qu'il a le droit de faire, et le refus paraît fondé puisqu'il +nomme un vrai manque. Il a fallu une console de locataire réelle pour le voir — sur le +poste, qui porte les deux liens, les dix-sept séquences s'affichaient conduisibles et +rien ne clochait. + +Quatre tests neufs refusent le retour du défaut, dont un qui nomme le cas exact : +`deployer-tout` et `valider` restent à portée d'un locataire, `flotte-creer` non. + +### Ce qui a été vérifié + +`python3 scripts/runbooks.py verifier` (0 écart), `make test` à 0 échec, P83 verte. La +correction est mesurée sur les trois consoles après déploiement. Aucun rôle, playbook ou +réglage d'infrastructure modifié. **P02 reste en échec, pour la raison antérieure déjà +consignée à l'entrée `(6)`.** + ## 2026-09-20 (6) — Cent trente-deux cibles, et aucune ne dit dans quel ordre **83 preuves (P83 est neuve), `make test` à 0 échec.** Le `Makefile` porte 132 cibles diff --git a/docs/runbooks-construction.yml b/docs/runbooks-construction.yml index d9f016f..7ed21f2 100644 --- a/docs/runbooks-construction.yml +++ b/docs/runbooks-construction.yml @@ -28,6 +28,13 @@ # site une fabric est montee (`underlay.yml`) : on materialise # poste les deux — l'atelier du mainteneur # toute ni l'un ni l'autre n'est requis +# +# LA PORTEE SE PESE A L'ETAPE (2026-09-20). Un runbook donne le defaut ; une etape qui +# exige davantage le declare avec son propre `portee:`. Mesure faite sur la console de +# TechnoLibre : declaree au seul runbook, une unique etape qui materialise (`flotte-creer`, +# `creer-vm`) faisait basculer TOUTE la sequence en `poste` — et un locataire ne pouvait +# plus deployer sa propre flotte, ce qui est exactement son metier. Le locataire conduit +# donc sa sequence, et bute precisement la ou il faut : sur la machine a engendrer. # CE QUE LA CONSOLE DOIT DEMANDER A L'EXPLOITANT. Une variable absente d'ici est refusee # par la garde : la console ne saurait pas quoi afficher, et un champ libre sans invite @@ -206,7 +213,7 @@ runbooks: # ───────────────────────────────────────────────────────────────────────────── - id: locataire-naitre titre: "Faire naitre un ecosysteme de locataire" - portee: poste + portee: tenant doc: docs/multi-instances.md but: >- Du modele au plan monte : creer le dossier de l'ecosysteme, le rendre actif, puis @@ -340,7 +347,7 @@ runbooks: # ───────────────────────────────────────────────────────────────────────────── - id: locataire-deployer titre: "Materialiser et deployer la flotte d'un locataire" - portee: poste + portee: tenant doc: docs/vm-lifecycle.md but: >- Des VM au service rendu : creer les machines manquantes, deployer couche par couche, @@ -348,6 +355,7 @@ runbooks: etapes: - cible: flotte-creer nature: ecriture + portee: poste variables: [PARALLELE] pourquoi: >- Les VM manquantes, clonees depuis le gabarit dore. VMID, IP et VLAN sont DERIVES @@ -384,7 +392,7 @@ runbooks: # ───────────────────────────────────────────────────────────────────────────── - id: machine-une titre: "Ajouter ou reprendre une seule machine" - portee: poste + portee: tenant doc: docs/vm-lifecycle.md but: >- Le cycle d'UNE machine, du plan au service : la voir derivee, la creer, la deployer, @@ -399,6 +407,7 @@ runbooks: qu'on va bien poser ce qu'on croit. - cible: creer-vm nature: ecriture + portee: poste variables: [HOTE] duree: "~4 min 30" pourquoi: "Cloner depuis le gabarit et ATTENDRE que la machine reponde." @@ -412,6 +421,7 @@ runbooks: pourquoi: "Le playbook de verification sur cette machine seule." - cible: cloner-vm nature: ecriture + portee: poste variables: [HOTE, VMID] facultative: true pourquoi: >- @@ -421,7 +431,7 @@ runbooks: # ───────────────────────────────────────────────────────────────────────────── - id: flotte-refaire titre: "Raser et reconstruire un ecosysteme" - portee: poste + portee: tenant doc: docs/vm-lifecycle.md but: >- La preuve la plus dure du moteur : detruire un ecosysteme et le refaire depuis le @@ -429,6 +439,7 @@ runbooks: etapes: - cible: raser nature: destructif + portee: poste variables: [INSTANCE] fixes: {CONFIRMER: "true"} pourquoi: >- @@ -436,6 +447,7 @@ runbooks: lettres : c'est le seul garde-fou qui resiste a un clic distrait. - cible: reconstruire nature: ecriture + portee: poste duree: "long" pourquoi: >- Refaire tout depuis zero : les VM, puis le deploiement complet. Si le code ne diff --git a/scripts/inventory_gui.py b/scripts/inventory_gui.py index 235dd4d..886caf5 100644 --- a/scripts/inventory_gui.py +++ b/scripts/inventory_gui.py @@ -3811,6 +3811,8 @@ HTML = r""" // etape qui MESURE reste toujours offerte : mesurer apres un echec est exactement ce // qu'on fait pour comprendre. Les etapes facultatives ne bloquent personne. function etapeBloquee(rb, i) { + // Hors de portee de CETTE console : l'etape se lit, elle ne se lance pas. + if (rb.etapes[i].conduisible === false) return true; if (rb.etapes[i].nature === 'mesure') return false; for (let j = i - 1; j >= 0; j--) { if (rb.etapes[j].facultative) continue; @@ -3840,7 +3842,7 @@ HTML = r""" if (rb) { corps = rb.etapes.map((e, i) => { const etat = rbEtat(rb.id, i); - const bloquee = etapeBloquee(rb, i) || !rb.conduisible; + const bloquee = etapeBloquee(rb, i); const cls = etat === 'ok' ? ' faite' : etat === 'echec' ? ' ratee' : bloquee ? ' bloquee' : ''; const vars = (e.variables || []).map(v => v.nom + '=…').join(' '); const fixes = Object.entries(e.fixes || {}).map(([k, v]) => k + '=' + v).join(' '); @@ -3856,6 +3858,8 @@ HTML = r""" + (meta ? '
' + echapper(meta) + '
' : '') + (etat === 'ok' ? '
✔ réussie dans cette session
' : '') + (etat === 'echec' ? '
✘ échouée — la suite reste bloquée
' : '') + + (e.conduisible === false ? '
⊘ hors de portée de cette console — ' + + echapper(e.raison || '') + '
' : '') + '' + '