Skip to content

refactor: un fichier, une responsabilité — sans toucher au contrat externe - #49

Merged
lostmind84 merged 13 commits into
mainfrom
refactor/un-fichier-une-responsabilite
Aug 3, 2026
Merged

lostmind84 merged 13 commits into
mainfrom
refactor/un-fichier-une-responsabilite

Conversation

@lostmind84

Copy link
Copy Markdown
Owner

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. main n'a pas bougé depuis le point de
départ (merge-base == pointe de main), donc tout le diff vient d'ici.

Ce que la branche change

  • internal/domain : (*Config).Validate passe de 507 à 30 lignes, éclatée en 42
    fonctions sur 4 fichiers.
  • Les fichiers de plus de 500 lignes disparaissent : 8 → 0 en production, 22 → 0
    en test. Le plus gros fichier passe de 1 241 à 298 lignes.
  • Les pages d'administration sont découpées selon ce que l'utilisateur voit, et la
    logique sort des composants vers web/src/admin/lib/.
  • Un jeu de règles golangci-lint bloquant et vert entre au dépôt — il n'en existait
    aucun — avec, à côté de chaque linter écarté, le nombre de signalements qu'il
    produirait, pour que « pas activé » ne se lise jamais « sans valeur ».
  • La CI gagne trois étapes (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 :

Contrat Preuve
Surface Go extracteur AST, clé sur le chemin d'import : 2575 déclarations exportées de chaque côté, diff exit 0, md5 identique
Routes HTTP 42 littéraux, 61 couples route→handler + entrées guarded identiques
Formats JSON 725 tags identiques, comparés groupés par paquet — un multiensemble global aurait masqué un tag changeant de structure
Schéma SQLite 54 énoncés DDL identiques
Configuration 88 chemins de clés identiques : aucun poste installé ne casse
Constantes numériques diff vide, Go et front — aucun module de code-barres, cote, timeout ni plafond modifié
Corps des tests multiensemble par fonction hôte : 46 144 → 46 946 lignes, 0 perdue
Source du kiosque git diff main...HEAD -- web/src | grep -v admin/sortie vide

Deux 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 :

  • la suite de tests réorganisée de cette branche exécutée contre le code de
    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 Validate de main : 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.go est 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.svelte fusionne deux bascules qui avaient été dessinées à la main sur deux pages
et avaient dérivé. Sur l'écran d'administration, donc :

  • la puce technique de la page Catalogue passe de 1,0625 rem à 1 rem — les deux pages
    divergeaient déjà
    sur main (Catalogue avait une règle label, Rules non), et la
    fusion aligne Catalogue sur Rules ;
  • le hint passe d'inconditionnel à conditionnel, ce qui évite un <span> vide et sa
    piste 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 :

  • la déduplication ne concerne qu'un cas (~14 lignes, cmd/openscale/configwrite.go),
    pas le volume que le mot laissait attendre ;
  • les fonctions n'ont pas raccourci : médiane 11 → 11 lignes, fonctions de plus de 50
    lignes 72 → 71. .golangci.yml l'écrit lui-même — le découpage est mécanique et ne
    simplifie aucun corps de fonction. Config.Validate est l'exception ;
  • le front coûte +1 042 lignes nettes pour zéro test gagné : web/test/ a la même
    liste de fichiers et 521 it() des deux côtés. La testabilité que l'extraction rend
    possible reste à encaisser.

Vérification

make.ps1 vet      exit=0
make.ps1 lint     0 issues.                        exit=0
make.ps1 boundary les coupes ... sont respectées   exit=0
make.ps1 deps     6 dépendances ... à l'identique  exit=0
go test ./...     35 paquets ok, 0 FAIL
go test -race     35 paquets ok, 0 data race
vitest            38 fichiers, 1002 tests, 0 échec
svelte-check      373 fichiers, 0 erreur
prettier          All matched files use Prettier code style!
budget            80 551 / 112 640 o gzip (71,5 %)

Ce que rien ici ne couvre

Le rendu raster réel sur tête SATO WS408 : les bancs de conformité compilent sous
-tags hardware mais n'ont pas été exécutés, le banc physique étant absent. Un contrôle
d'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.svelte viennent de la lecture des règles
et de leur portée.

…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.
@lostmind84
lostmind84 merged commit 30cba1f into main Aug 3, 2026
15 of 16 checks passed
@lostmind84
lostmind84 deleted the refactor/un-fichier-une-responsabilite branch August 3, 2026 14:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant