refactor: un fichier, une responsabilité — sans toucher au contrat externe - #49
Merged
Merged
Conversation
…t gestes `config.go` portait 2408 lignes et sept sujets : les types des quatorze blocs, leurs codecs JSON, la validation, l'orthographe canonique d'un document, les empreintes, le caviardage et les clés retirées. `machine.go` en portait 1679 : les états, les événements, les effets, la transition et ses aides de cycle. Ils deviennent vingt-six fichiers, dont aucun ne dépasse 452 lignes. Rien ne change : mêmes symboles exportés, mêmes signatures, même comportement. Les déclarations de paquet sont comptées avant et après par `go/ast` — 343 contre 370, aucune perdue, les vingt-sept ajoutées étant les fonctions extraites de `Validate`. `Config.Validate` faisait 514 lignes d'une traite ; c'est maintenant vingt-deux appels dans l'ordre de §11.3 vers dix-huit fonctions de dix à soixante lignes. L'ordre d'évaluation ne bouge pas, et c'est ce qui compte : cette liste est ce qu'affichent `openscale doctor`, l'écran d'administration et `config validate`, et c'est ce qu'un bénévole lit au téléphone devant un poste en ERR-CFG-01. Vérifié en cassant la garantie plutôt qu'en la relisant. Cinq configurations fautives couvrant les quarante-huit contrôles, passées par `Validate` avant et après, comparées champ par champ : cinquante-cinq lignes de faute, identiques mot pour mot. La sonde a d'abord été validée en intervertissant deux groupes de contrôles — elle diverge alors sur douze lignes. La même comparaison mécanique a rattrapé une perte que la relecture avait laissée passer : le paragraphe justifiant le contrôle 43 (§11.5, ADR-026) avait disparu de `CheckPrice`. Restauré.
`doctor.go` portait 1481 lignes : un type, une boucle, et dix-sept contrôles indépendants avec leurs remèdes. Il en garde 292 — le type, `New`, `Run`, la lecture de configuration — et les contrôles partent par famille : ce qui dit si le poste est debout, ce qui le sépare de l'endroit où il écrit, le matériel, la machine dessous, ce qu'on lui a déclaré. `probes.go` 749 → 227, `archive.go` 601 → 282. Aucun fichier du paquet ne dépasse désormais 340 lignes. Côté `deploy`, `deploy_test.go` portait 1822 lignes et huit sujets ; il devient huit fichiers nommés par sujet et disparaît, parce qu'un fichier résiduel qui n'en garderait qu'un mentirait sur son contenu à côté de `bootstrap_test.go` et `release_workflow_test.go`, déjà nommés ainsi. Les tests suivent la découpe de production : qui ouvre `doctor_devices.go` trouve `doctor_devices_test.go` à côté. Comptés avant et après : 108 fonctions de test dans `internal/diag`, 70 dans `deploy`, aux mêmes noms, et zéro ligne de code perdue ou ajoutée. Aucun `t.Parallel()` introduit — il n'y en avait aucun, et la passe `-race` n'a pas à hériter d'une intermittence de ce lot. Deux commentaires citaient `deploy_test.go`, qui n'existe plus, dans des fichiers qu'aucune découpe n'avait touchés. Ils disent maintenant ce que ces fichiers font, sans nommer personne : la phrase survivra au prochain découpage.
`station.go` 1128 → 256 et `hub.go` 1097 → 341 : le cycle de vie, le câblage des drivers, le rechargement de §11.4, la scrutation du catalogue, l'arrêt de §13.4, la boucle, les abonnés, les effets et la diffusion prennent chacun leur fichier. `serve.go` 1079 → 528, `hardware.go` 664 → 112, `capture.go` 695 → 314, `config.go` 522 → 201 : une famille de sous-commandes par fichier, le fichier d'origine ne gardant que le câblage. `serve.go` reste au-dessus de la cible parce que `serve()` EST la racine de composition ; la découper est une réécriture, pas un rangement. LA SYNCHRONISATION N'A PAS ÉTÉ TOUCHÉE, et c'est vérifié plutôt qu'affirmé. Onze motifs comptés avant et après dans `internal/station` — `go`, `chan`, `select`, `sync.`, `atomic.`, `defer`, `<-`, `Lock`/`Unlock`, `make(chan`, `close(` — tous identiques. Le seul écart du périmètre est `context.` 46 → 45 dans `cmd/openscale`, et il est localisé à la ligne près : la seconde copie supprimée par `editConfigFile`, seule duplication extraite du lot. La surface publique de la ligne de commande est inchangée : six actions, onze drapeaux, trois sous-commandes, listés avant et après. `deploy/*/update.ps1` les appelle par leur nom. La comparaison du multiensemble de toutes les lignes non vides a trouvé une perte que la relecture du diff n'avait pas vue — le godoc de `Command` — et elle est restaurée. Sur du texte, la relecture ne remplace pas le comptage.
Les deux `conformance.go` sont des suites de cas nommés qu'un driver doit passer : 1134 → 273 pour celui des imprimantes, 502 → 189 pour celui des transports. Un fichier par famille, le fichier d'origine ne gardant que le harnais et la liste. `render.go`, `raster.go`, `service.go`, `sbpl/command.go` suivent la même règle. CE PAQUET FABRIQUE L'ÉTIQUETTE QUE LA CAISSE SCANNE, et la vérification en tient compte. Deux preuves plutôt qu'une relecture : L'ensemble des littéraux numériques est IDENTIQUE — rien de disparu, rien d'apparu — et les valeurs de géométrie (203, 280, 300, 100, 1000) ont le même nombre d'occurrences. Le seul écart porte sur des numéros de § cités dans les en-têtes des fichiers créés. Aucun module, aucun dpi, aucune marge, aucune hauteur de barre n'a bougé. Les cas de banc sont comptés dans LES DEUX SENS : vingt avant, vingt après aux mêmes noms pour les imprimantes, douze pour les transports, et aucune fonction `checkXxx` déclarée mais absente de la liste. Ce second sens est le seul qui voie un banc amputé — `conformance_test.go` porte vingt-neuf drivers cassés et passerait au vert en jouant moins de cas. Les tests suivent : 280 fonctions avant, 280 après, zéro ligne de code perdue. Les deux `conformance_test.go` ne sont PAS découpés — leurs tests pilotent la suite entière d'un seul point d'entrée, aucun ne vise un `check_*` en particulier, et découper là aurait inventé une frontière au lieu d'en lire une.
…lisent en vis-à-vis `webdav.go` 634 → 251 et `localdrop.go` 441 → 216 reçoivent LA MÊME découpe : ce qu'une configuration bâtit, le contrat `ports.CatalogSource`, et pour WebDAV le dialogue sur le fil. Les deux sources se lisent désormais côte à côte. CE QUI N'A PAS ÉTÉ FAIT COMPTE AUTANT. `Next`, `Wake`, `keep`, `take`, `isClosed`, `refuse` et `Close` ont la même forme des deux côtés, et la tentation était de les factoriser. Ça voulait dire remonter du code dans `catalog`, donc déplacer une décision d'une source vers le paquet qui décide — exactement ce qu'ADR-052 interdit et ce que la coupe 2 empêche. La découpe symétrique donne le bénéfice de lisibilité sans toucher à la frontière, et `catalog.Assemble` est inchangé. `kiosk/supervisor.go` 508 → 252, `store/catalog.go` 481 → 266, et le banc de conformité des balances 501 → 168, découpé dans SA PROPRE taxonomie : `checks()` écrit « identity first, then the two channels, then the exits, then what travels on them », et c'est cette phrase qui a servi de plan. Il reprend la convention `check_*.go` posée par le banc des imprimantes ; les deux se lisent pareil. `tools/boundary/main.go` 543 → 108, un fichier par coupe vérifiée. La copie de `repositoryRoot` que porte `tools/deps` est LAISSÉE INTACTE : son commentaire dit que c'est un choix assumé entre deux programmes autonomes. Aucun fichier de production de `serial` ni de `gramxfoc` n'a été ouvert : un octet, un terminateur, un délai sont des faits sur du matériel. Vérifié — 34 durées, zéro littéral hexadécimal, zéro terminateur, identiques. Les trois `example/` restent d'un seul tenant : `docs/07` et `docs/08` les donnent à copier, et leur valeur tient à ce qu'ils se lisent en entier.
…s ne pouvait s'appeler ainsi Cinq fichiers de 3291 lignes deviennent dix-huit, dont aucun ne dépasse 461. `admin.go` portait 746 lignes et six sujets sans lien ; il est SUPPRIMÉ et réparti en journal, imports, décisions, matériel et lecture de requête. Un fichier dont le nom ne dit rien est précisément ce que ce lot supprime. `config.go`, `server.go`, `session.go` et `health.go` suivent : la fenêtre en trois temps de §11.4, l'export et la restauration, la table de routes de §14.5, les quatre formes d'une réponse, les dix-neuf contrats déclarés côté consommateur, la cryptographie de l'administration, le tableau de bord et les deux sondes prennent chacun leur fichier. LES CONTRATS EXTERNES SONT INTACTS, prouvés par AST et non par grep : 61 routes identiques chemin et méthode, 353 balises `json:`, 152 codes de statut, 328 déclarations de paquet, zéro ligne de code perdue. Le front lit ces champs par leur nom — un renommage aurait cassé l'écran sans casser Go. L'EN-TÊTE D'`admin.go` ÉTAIT FAUX, et il n'a pas été propagé. Il affirmait « everything in it is READ except three routes » ; la vérification route par route contre la map `guarded` en donne six pour ce périmètre, la phrase datant d'avant qu'ADR-033 déplace la ligne. Plutôt que de recopier un compte faux dans cinq fichiers, chaque en-tête dit ce qui est vrai de SES routes. Le critère lui-même reste où il vit déjà, dans `routes.go`, qui revendique d'être « the only place that says it » — et c'est cette unicité qui manquait. `admin-wording.test.ts` lisait `internal/web/config.go` en dur pour y chercher `changedBlocks`, partie dans `configwrite.go`. Le chemin est corrigé et la dette écrite au-dessus : un banc qui épingle l'EMPLACEMENT d'une fonction juge la forme du code et non son comportement.
…utilisateur voit
Six pages passent de 7492 à 4963 lignes. `Rules.svelte` 1214 → 291,
`Station.svelte` 1149 → 751, `Journal.svelte` 863 → 716, `Hardware.svelte`
1462 → 1085, `Catalog.svelte` 2139 → 1502.
Douze composants, aucun au-dessus de 212 lignes, nommés par ce qu'ils MONTRENT
et non par leur rang dans le fichier d'origine : `CatalogSourcePanel`,
`FindingsPanel`, `DecisionsInForcePanel`, `ImportHistoryPanel`, `TierGrid`,
`SafeguardList`, `WaiverList`, `Toggle`, `StandingHeader`, `TechnicalLines`,
`ConfigDiffTable`, `ImportFaults`. `FindingsPanel` est écrit une fois et rendu
trois. Seize modules `lib/` sortent la logique non visuelle des `.svelte`, où
elle devient testable sans DOM.
Cinq duplications réelles supprimées : `faultOf` 4 → 1, `isCredentialRefusal`
3 → 1, `numberTyped` 3 → 1, `productNameOf` 3 → 1, `tally` 2 → 1. Les copies
étaient strictement identiques dans les cinq cas — vérifié, parce qu'une fusion
de copies divergentes fait un choix, et un choix se dit.
LE RENDU EST PROUVÉ IDENTIQUE, autrement qu'en interrogeant le DOM par
sélecteurs : les deux versions de chaque page sont montées côte à côte et leur
`innerHTML` comparé caractère par caractère, en ne neutralisant que le hachage
de portée Svelte et les marqueurs de fragment. Six pages, identiques. Et
`svelte-check` sort zéro avertissement, ce qui garantit gratuitement qu'aucun
sélecteur CSS n'est devenu orphelin sur les ~490 lignes de style déplacées.
Le bilan des branchements boucle : `{#if}` −27 des pages, +3 réécrits, +21 dans
les composants ; `{:else}` −18, +14. Les sept écarts sont deux fusions,
instances comptées. Aucun état d'écran ne cesse d'être rendu.
Aucun texte français n'a bougé d'une lettre, mesuré par deux sondes
indépendantes. La comparaison à la granularité de la PHRASE — comparer des
lignes ne dit rien d'un refactor, réindenter les recoupe — a rattrapé sept
justifications perdues, dont celle qui explique le piège du 409 nu, précisément
ce qui avait fait diverger les trois copies d'`isCredentialRefusal`.
Le budget de l'écran client ne bouge pas d'un octet : 80 551 o gzip, 71,5 %.
L'administration prend +1,4 ko, l'enveloppe des composants.
…dette nommée Le dépôt n'avait aucun linter au-delà de `go vet`, ni côté Go ni côté front. `make lint` est bloquante et VERTE, `make audit` active tout et ne bloque rien. LES DEUX VONT ENSEMBLE : un jeu qui rougit dès le premier jour n'est pas lu, il est contourné. `.golangci.yml` ne porte donc que ce que le dépôt tient aujourd'hui — et il écrit à côté de chaque linter écarté le nombre exact de signalements qu'il produirait, pour que « pas activé » ne se lise jamais « sans valeur » : errcheck 147, goconst 85, gosec 71, errorlint 30, revive 20. `gocognit` et `funlen` sont au seuil QUI MORD, avec les onze fonctions fautives exclues NOMMÉMENT et chiffrées. Poser le seuil au-dessus du pire cas — 70, la valeur de `Template.ValidateOn` — n'aurait rien interdit ; ne pas les activer aurait laissé la dette dans un commentaire que personne ne relit. Nommer les fautives fait les deux : aucun code nouveau ne peut dépasser, et la liste d'exclusions EST la liste de travail, qui rétrécit quand on la traite. Le cliquet est éprouvé en le cassant : une fonction jetable de complexité cognitive 55 déposée dans `internal/domain` fait sortir l'outil en 1, et 0 dès qu'on la retire. `cyclop` est écarté sur le fond — il compte chaque `case` d'un `switch` à égalité avec un `if` imbriqué, si bien que `domain.State.String()` y marque 17 en étant lisible d'un coup d'œil. `unconvert` l'est sur deux faux positifs gardés comme exemple : `error(ErrScriptExhausted)` donne à la variable le type statique `error`, la conversion n'est pas inutile. DEUX PIÈGES DE MESURE SONT ÉCRITS DANS LE FICHIER, parce qu'ils ont chacun produit des chiffres faux avant d'être vus. L'outil TRONQUE par défaut — trois signalements identiques, cinquante par linter : les premiers comptes étaient faux d'un facteur dix sur errcheck. Et il ne rend QU'UN signalement par ligne, si bien que `gocognit` masquait `funlen` sur les mêmes fonctions — c'est pourquoi cinq règles nomment les deux linters, et pourquoi l'inventaire désactive cette déduplication. Côté front : ESLint typé, deux régimes (strict sur `src`, recommandé sur `test`), et `max-lines` par groupe avec des plafonds mesurés — aucun preset ne contient de garde-fou de taille, et c'était l'objectif nommé. Prettier vérifie sans reformater : les cinquante-cinq fichiers en dette sont nommés un par un dans `.prettierignore`, même principe que les exclusions Go. `printWidth: 100` est mesuré et non choisi — élargir AUGMENTE l'écart, Prettier recollant des coupures voulues. Rien n'entre dans `go.mod` : l'outil s'installe hors module, et `make deps` compare go.mod aux tables de §17.1 dans les deux sens (ADR-039). La version est écrite à UN seul endroit, le Makefile, que la CI interroge.
…e retiré plutôt que corrigé Quatre étiquettes d'extraits de `docs/02-architecture.md` ne pointaient plus juste : `stationSpecificOptions` vit dans `redact.go`, `Hub.run` dans `loop.go`, `Options` dans `serial/options.go`, `deriveJobID` dans `domain/reading.go`. Les quatre numéros de ligne du tableau §17.1 sont RETIRÉS et non mis à jour. Ils désignaient des commentaires, donc aucun symbole ne pouvait les remplacer, et un chemin seul ne peut plus rouiller. Un numéro de ligne dans une citation est une dette : la rafraîchir la reporte, la retirer la solde. `docs/03-glossaire.md` n'est PAS touché, et c'était un piège. Sa colonne gauche cite `internal/noyau/machine.go`, `internal/balance/serie/boucle.go` : ce sont les anciens noms d'une table de renommage FR → EN, ils n'existent plus par construction et c'est leur rôle. Les corriger aurait détruit la table. Les quarante chemins de la colonne cible ont été vérifiés à la place : tous exacts. `docs/superpowers/plans/` et `specs/` restent périmés, délibérément. Ce sont des archives datées qui décrivent un état passé ; les corriger falsifierait l'historique. Même raison pour les numéros de ligne des ADR et des entrées de journal, qui parlent au passé. `SUIVI.md` reçoit l'entrée du lot, avec ce que la vérification a coûté et ce qu'elle a trouvé, et les trois choses qui restent ouvertes — les neuf fonctions au-dessus du seuil cognitif, l'ordre des fautes qui n'était couvert que par accident, et les bancs du front qui lisent le texte source des pages qu'ils éprouvent.
CE COMMIT DÉPASSE LE MANDAT DU LOT, qui était mécanique. Il est isolé pour être retirable d'un `git rm` sans toucher au reste. Les tests ne couvraient PAS l'ordre dans lequel `Config.Validate` rend ses fautes, et c'est démontré plutôt qu'affirmé : intervertir les contrôles 22-25 et 26-28 laisse `go test ./internal/domain/` au vert. Chaque test cherche sa faute par son champ, via `findFault`, et n'asserte que `len(faults) >= len(wanted)`. Or cet ordre est un contrat. C'est ce qu'affichent `openscale doctor`, l'écran d'administration et `config validate`, et c'est la liste qu'un bénévole lit au téléphone devant un poste en ERR-CFG-01. §11.3 nomme ses contrôles par leur numéro. Deux cas, choisis pour ce qu'ils prouvent. Trente champs cassés du contrôle 1 au contrôle 49, vingt-sept fautes attendues écrites en dur dans l'ordre, chacune annotée de son numéro. Puis les options et les chemins, où `printer.options.transport` est rapporté TROIS fois — par les contrôles 7, 8 et 42 — et où seule sa POSITION dit lequel parle : un `findFault` par champ ne peut structurellement pas tenir ce cas. Le test est vérifié en le cassant, et il s'arrête à la PREMIÈRE divergence : lister les vingt conséquences d'un contrôle déplacé enterrerait la ligne qui dit où la séquence a cassé. Son godoc explique pourquoi l'ordre est un contrat et non un détail, consigne la mesure du 03/08/2026, et se termine par une section « si vous lisez ceci parce que le test est rouge » qui tranche en deux cas : contrôle ajouté, on l'inscrit à sa place ; contrôle déplacé, c'est ce que le test existe pour arrêter.
Un fichier vide nommé `0` est versionné à la racine depuis ff1d56f. C'est la signature d'une redirection shell — `> 0` au lieu de `>&1` — pas un artefact volontaire. Isolé de tout le reste : un `git revert` le remet sans toucher au rangement.
…test Le Makefile écrit GOLANGCI_VERSION à un seul endroit et dit en commentaire que « la CI, make.ps1 et ce fichier la lisent tous d'ici ». La CI la lit vraiment, par `make -s golangci-version` ; make.ps1 était la seule des trois sources citées à ne pas le faire, et envoyait installer @latest. Un développeur Windows qui suivait ce message obtenait un autre jeu de règles que la CI — donc le rouge-là-où-la-CI-voit-vert que le commentaire du Makefile décrit, ou son inverse, qui est pire parce que personne ne cherche la cause d'un vert. Ce script ne peut pas appeler `make` : il existe pour les postes qui n'en ont pas. Il lit donc la ligne, et LÈVE si elle a disparu plutôt que de retomber sur une valeur par défaut — un numéro deviné ici rétablirait les deux sources de vérité que le Makefile cherche à empêcher. Relevé par l'audit de la branche, §7.1.
…equête Le découpage a sorti le fetch de Station.svelte vers config-file.ts, et le paragraphe qui le décrivait — la route, le hash de mot de passe de §11.5, l'anchor qui ne peut pas voir un refus — est parti avec lui SANS quitter la page. Il était donc aux deux endroits, dont l'un devant une fonction qui n'émet plus aucune requête. readExport le porte au bon endroit, à côté du fetch. Ce qui reste ici dit ce que exportConfig ajoute, et rien de plus : la protection, et les octets remis au navigateur. C'est le motif que Toggle.svelte a documenté dans le même lot — « a comment that travels can turn false without a word of it changing ». Ici il n'est pas devenu faux, seulement écrit deux fois, ce qui revient à décider lequel des deux on met à jour le jour où la route bouge. Relevé par l'audit de la branche, §7.3.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ranger l'arborescence sans toucher à ce que la caisse voit : découper les fichiers
devenus trop gros, séparer ce qui décide de ce qui branche, et laisser le contrat
externe strictement où il était.
13 commits, 334 fichiers, +46 726 / −39 925.
mainn'a pas bougé depuis le point dedépart (
merge-base== pointe demain), donc tout le diff vient d'ici.Ce que la branche change
internal/domain:(*Config).Validatepasse de 507 à 30 lignes, éclatée en 42fonctions sur 4 fichiers.
en test. Le plus gros fichier passe de 1 241 à 298 lignes.
logique sort des composants vers
web/src/admin/lib/.golangci-lintbloquant et vert entre au dépôt — il n'en existaitaucun — avec, à côté de chaque linter écarté, le nombre de signalements qu'il
produirait, pour que « pas activé » ne se lise jamais « sans valeur ».
make lint,eslint,prettier) et n'en perd aucune.La médiane de longueur de fichier reste à 89 lignes des deux côtés : le découpage a
visé la queue de distribution, pas tout le monde.
Ce que la branche ne change pas, et comment on le sait
Un audit contradictoire a été mené avant cette PR, cinq revues indépendantes en lecture
seule contre
main. Les vérifications qui portent :diffexit 0, md5 identiqueguardedidentiquesdiffvide, Go et front — aucun module de code-barres, cote, timeout ni plafond modifiégit diff main...HEAD -- web/src | grep -v admin/→ sortie videDeux vérifications croisées, parce qu'une suite verte ne prouve rien tant qu'on n'a pas
montré qu'elle n'a pas été réécrite pour l'être :
production intouché de
main: 22 paquets passent ;validate_order_test.go— le test ajouté ici pour figer l'ordre des fautes — exécutécontre le
Validatedemain: PASS. Les 49 contrôles sont dans le même ordre,donc le test épingle le comportement existant et n'en photographie pas un nouveau.
Tests : 3092 → 3095 exécutés, 0 échec des deux côtés. Couverture 84,3 % → 84,4 %.
deploy/deploy_test.goest supprimé mais ses 40 tests sont tous retrouvés par nom,répartis sur 8 fichiers de
deploy/.Le seul comportement observable qui bouge
Toggle.sveltefusionne deux bascules qui avaient été dessinées à la main sur deux pageset avaient dérivé. Sur l'écran d'administration, donc :
divergeaient déjà sur
main(Catalogue avait une règlelabel, Rules non), et lafusion aligne Catalogue sur Rules ;
hintpasse d'inconditionnel à conditionnel, ce qui évite un<span>vide et sapiste flex sous le garde-fou 3, dont l'appelant passe une chaîne vide.
Rien de tout cela ne touche l'étiquette, le code-barres, la balance, l'imprimante ou la
caisse.
Ce qui était annoncé et n'est pas livré
Autant le dire ici plutôt que de le laisser découvrir :
cmd/openscale/configwrite.go),pas le volume que le mot laissait attendre ;
lignes 72 → 71.
.golangci.ymll'écrit lui-même — le découpage est mécanique et nesimplifie aucun corps de fonction.
Config.Validateest l'exception ;web/test/a la mêmeliste de fichiers et 521
it()des deux côtés. La testabilité que l'extraction rendpossible reste à encaisser.
Vérification
Ce que rien ici ne couvre
Le rendu raster réel sur tête SATO WS408 : les bancs de conformité compilent sous
-tags hardwaremais n'ont pas été exécutés, le banc physique étant absent. Un contrôled'impression sur le poste reste la seule preuve du tracé. De même, rien ne photographie
le CSS des deux côtés : les constats sur
Toggle.svelteviennent de la lecture des règleset de leur portée.