porter() : trois depots croyaient linter leur domaine, aucun ne le faisait #102

Merged
nicolas-fryder merged 1 commit from nicolas-fryder/dev into main 2026-10-01 08:22:19 +00:00 AGit

@eqs/tooling/eslint exporte porter(prefixe, profil). Les trois copies locales retiraient le ** avant de prefixer : apps/api/*.ts ne matche rien sous src/, et ESLint rendait zero violation. Mesure sur le gabarit : 12 fichiers au lieu de 30. Cahier §5 amende, D-050. Corrige #100 pour sa premiere moitie.

@eqs/tooling/eslint exporte porter(prefixe, profil). Les trois copies locales retiraient le ** avant de prefixer : apps/api/*.ts ne matche rien sous src/, et ESLint rendait zero violation. Mesure sur le gabarit : 12 fichiers au lieu de 30. Cahier §5 amende, D-050. Corrige #100 pour sa premiere moitie.
`porter()` entre dans `@eqs/tooling/eslint`. Le geste est une concaténation de
préfixe, jamais une réécriture du motif — et cette phrase est toute la raison
d'être de cet export.

## Le défaut, et pourquoi il était invisible

Trois dépôts avaient recopié la même fonction locale pour restreindre un profil à
`apps/api`, et les trois portaient la même faute (#100) : un `.replace` qui
retire le `**` avant de préfixer.

`**/*.ts` devient `*.ts`, donc `apps/api/*.ts` après préfixage, et dans un glob
`*` **ne traverse pas** `/`. Aucun fichier de `apps/api/src/**` n'était analysé.

Reproduit sur le gabarit avant de toucher au socle :

    avec le .replace     12 fichiers analysés,  0 sous src/,  0 violation
    sans le .replace     30 fichiers analysés,  tout src/ inclus (main.ts compris)

Deux effets se combinaient, et chacun rassurait. ESLint 9 lint les `.mjs` par
défaut, donc la sortie n'avait pas l'air vide — ce sont eux, les 12. Et **zéro
violation se lit comme une réussite.** Même mode d'échec que #83 sur
`knipSatellite()`, même cause : un motif déduit qui ne couvre pas la forme réelle
du dépôt.

Le commentaire au-dessus de `porter()` expliquait longuement pourquoi il faut
PRÉFIXER et non remplacer. Il avait raison, et c'est le `.replace` juste en
dessous qui annulait ce qu'il défendait.

## Le test compte, il n'assure pas

Une assertion sur le motif passerait avec un motif juste et une sémantique mal
comprise — c'est exactement ce qui a manqué pendant trois recopies. Le test crée
un dépôt à deux niveaux et **compte les fichiers qu'ESLint atteint** :
`racine.ts` prouve le préfixe, `src/moyen.ts` et `src/profond/bas.ts` prouvent que
le `**` a survécu.

Éprouvé en réinjectant la faute dans `porter` — quatre refus, dont :

    porter : ESLint n'atteint pas moyen.ts — fichiers vus : ["racine.ts","eslint.config.mjs"]

`porter` refuse aussi un préfixe vide ou terminé par un slash, plutôt que de
produire des motifs faux en silence.

## Une règle amendée, pas contournée

Le cahier §5 disait « fichiers, pas de code » pendant que le paquet livrait déjà
`conformite`, `knip-satellite`, `orval` et `workspaces-dockerfile`. La règle était
donc fausse depuis quatre modules et personne ne l'avait amendée : on l'a
contournée quatre fois en silence, ce qui est précisément le signe décrit dans
`AGENTS.md`.

Elle porte maintenant son critère (D-050) : du code ici quand un satellite devrait
sinon connaître la sémantique d'un outil tiers pour écrire sa configuration, et se
tromper. Pas pour éviter trois lignes de JSON.

## Trouvé en commitant, et ça vise la même cible

Le `pre-commit` a refusé ma première tentative pour une variable morte dans mon
propre test. Il avait raison, et il a surtout montré que **`pnpm check` ne lint
pas `packages/*/scripts/`** : `lint:racine` ne couvre que `knip.ts` et `scripts/`
de la racine, et le `check` de `@eqs/tooling` joue son vérificateur sans se
linter. Le seul filet est l'ESLint large du `pre-commit`.

C'est la même forme que #100 — une porte qui n'atteint pas ce qu'on croit — et
elle reste ouverte ici.

## Ce que je n'ai pas fait

Les trois dépôts gardent leur copie fautive : leur correctif est d'importer
celui-ci, et il se fait chez eux, après publication. Le gabarit a été mesuré puis
remis d'aplomb sans être touché — le correctif d'une ligne y serait remplacé par
l'import dès la version suivante, et deux commits pour un défaut valent moins
qu'un.

**Pas fait non plus**, et c'est la seconde moitié de #100 : un contrôle qui refuse
une porte qui n'atteint rien. `tooling.eslint-etend` vérifie que la configuration
étend les profils, pas qu'elle touche des fichiers. Un `eslint --format json` qui
rend moins de fichiers que le dépôt n'en compte sous `src/` est un signal
mécanique, et il aurait attrapé celui-ci comme il aurait attrapé #83.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Infrastructure/eqs-platform!102
No description provided.