From d3a8124777aff7e10f58314afe3b2a6a9662aab8 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Sun, 20 Sep 2026 19:55:02 -0400 Subject: [PATCH] assistants : la portee se pese a l'etape, pas a la sequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mesure sur la console de TechnoLibre, portee tenant : 6 runbooks conduisibles sur 17, et parmi les onze fermes, locataire-deployer et machine-une — c'est-a-dire le travail quotidien d'un locataire. La cause : flotte-creer et creer-vm engendrent des VM et exigent la fabric, et une portee declaree pour toute la sequence faisait basculer avec eux des etapes voisines qui ne demandent que ce que le locataire possede deja. Le runbook ne donne plus que le defaut ; l'etape qui exige davantage le declare. Le locataire conduit sa sequence et bute precisement la ou il faut : sur la machine a engendrer, pas sur le deploiement qui suit. La page ferme l'etape seule avec sa raison, et la garde de la route lit la portee de l'etape visee par son index. Un droit calcule sur l'ensemble se trompe toujours dans le meme sens : il refuse a quelqu'un ce qu'il a le droit de faire, et le refus parait fonde puisqu'il nomme un vrai manque. Il a fallu une console de locataire reelle pour le voir — sur le poste, qui porte les deux liens, les dix-sept sequences s'affichaient conduisibles. Valide : runbooks.py verifier a 0 ecart, make test a 0 echec (4 tests neufs, dont un qui nomme le cas exact), P83 verte. P02 reste en echec pour la raison anterieure deja consignee. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 45 ++++++++++++++++++++++++ docs/runbooks-construction.yml | 20 ++++++++--- scripts/inventory_gui.py | 64 +++++++++++++++++++++++++--------- scripts/runbooks.py | 18 ++++++++-- scripts/tests/test_runbooks.py | 41 ++++++++++++++++++++++ 5 files changed, 165 insertions(+), 23 deletions(-) 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 || '') + '
' : '') + '' + '