tooling.workspaces-dockerfile compte « pnpm install » dans les commentaires : faux positif bloquant, et l'ironie est qu'il se déclenche sur la documentation du défaut qu'il détecte #47

Closed
opened 2026-09-10 08:12:33 +00:00 by nicolas-fryder · 2 comments

Constaté le 2026-09-10 en montant le premier satellite réel en @eqs/* 0.8.1. Premier passage du contrôle en conditions réelles — il est né de notre ticket eqs-satellite-template#7, et il ne pouvait pas se charger en 0.8.0 (#44, corrigé, merci).

Bonne nouvelle d'abord : il se charge et il tourne. Mauvaise : il rend deux bloquants qui n'en sont pas.

Le symptôme

$ pnpm exec eqs doctor

Bloquants (2)
  [bloquant] tooling.workspaces-dockerfile — Dockerfile : Dockerfile copie
    apps/mcp/package.json 2 fois pour 3 étages qui installent
  [bloquant] tooling.workspaces-dockerfile — Dockerfile : Dockerfile copie
    apps/web/package.json 2 fois pour 3 étages qui installent

Or notre Dockerfile n'a que deux étages qui installent — deps et prod-deps — et chacun copie bien les quatre manifestes. Les images se construisent, avec Scope: all 4 workspace projects aux deux étages.

La cause

workspaces-dockerfile.js compte les étages ainsi :

const etages = compter(texte, 'pnpm install')

C'est un comptage textuel, sur le fichier entier. Il ne distingue pas une instruction d'un commentaire.

Chez nous :

$ grep -n 'pnpm install' Dockerfile
25: # `pnpm install --frozen-lockfile` ne rejette PAS le lockfile — pire : il
102:     pnpm install --frozen-lockfile
130:     pnpm install --frozen-lockfile --prod --filter=api...

Trois occurrences, dont une dans un commentaire. D'où 3 étages comptés pour 2 copies, et le refus.

L'ironie, qui est aussi l'argument

Le commentaire de la ligne 25 est celui que nous avons écrit pour documenter ce défaut précis, quand nous l'avons découvert :

pnpm install --frozen-lockfile ne rejette PAS le lockfile — pire : il installe le sous-ensemble qu'il voit et ne le dit pas.

Autrement dit : le contrôle censé empêcher l'installation partielle muette échoue sur la documentation de l'installation partielle muette.

Ce n'est pas qu'une anecdote. Un Dockerfile bien commenté — et celui du gabarit l'est remarquablement — est précisément celui qui a le plus de chances de mentionner pnpm install en prose. Le contrôle pénalise donc ce qu'on veut encourager.

Ce que je proposerais

Ignorer les commentaires avant de compter. Sur un Dockerfile, c'est peu coûteux : les lignes dont le premier caractère non blanc est #.

Deux remarques en plus, si elles sont utiles :

  1. Compter les FROM plutôt que les pnpm install serait peut-être plus juste sur le fond : ce que le contrôle veut savoir, c'est « combien d'étages lancent une installation », et un étage peut en lancer plusieurs, ou l'écrire sur plusieurs lignes avec des continuations. Mais je n'ai pas de cas réel sous la main où ça change le verdict — à votre appréciation.

  2. Le message est excellent et mérite d'être conservé tel quel : il dit quoi ajouter, où, et pourquoi (« pnpm installe le sous-ensemble qu'il voit et ne le dit pas : l'image se construit, amputée »). C'est exactement ce qu'il fallait. Le défaut est dans le comptage, pas dans ce qu'il en fait.

Ce qui est en place chez nous

Rien, et nous n'en voulons pas. Le contournement évident — reformuler notre commentaire pour ne plus écrire « pnpm install » — reviendrait à abîmer la documentation pour satisfaire un contrôle, ce qui est exactement le mauvais sens. eqs doctor reste donc en échec chez nous jusqu'à la correction ; pnpm check est vert, les 58 tests d'intégration passent, et les deux images se construisent.

Dites-moi si vous voulez que je vérifie la correction dès qu'elle sort — ce contrôle est né chez nous, autant qu'il y soit éprouvé.

Constaté le 2026-09-10 en montant le premier satellite réel en `@eqs/*` 0.8.1. **Premier passage du contrôle en conditions réelles** — il est né de notre ticket [eqs-satellite-template#7](https://git.equilibre-structures.fr/Infrastructure/eqs-satellite-template/issues/7), et il ne pouvait pas se charger en 0.8.0 ([#44](https://git.equilibre-structures.fr/Infrastructure/eqs-platform/issues/44), corrigé, merci). Bonne nouvelle d'abord : **il se charge et il tourne.** Mauvaise : il rend deux bloquants qui n'en sont pas. ## Le symptôme ``` $ pnpm exec eqs doctor Bloquants (2) [bloquant] tooling.workspaces-dockerfile — Dockerfile : Dockerfile copie apps/mcp/package.json 2 fois pour 3 étages qui installent [bloquant] tooling.workspaces-dockerfile — Dockerfile : Dockerfile copie apps/web/package.json 2 fois pour 3 étages qui installent ``` Or notre `Dockerfile` n'a que **deux** étages qui installent — `deps` et `prod-deps` — et chacun copie bien les quatre manifestes. Les images se construisent, avec `Scope: all 4 workspace projects` aux deux étages. ## La cause `workspaces-dockerfile.js` compte les étages ainsi : ```js const etages = compter(texte, 'pnpm install') ``` C'est un comptage **textuel**, sur le fichier entier. Il ne distingue pas une instruction d'un commentaire. Chez nous : ``` $ grep -n 'pnpm install' Dockerfile 25: # `pnpm install --frozen-lockfile` ne rejette PAS le lockfile — pire : il 102: pnpm install --frozen-lockfile 130: pnpm install --frozen-lockfile --prod --filter=api... ``` Trois occurrences, dont **une dans un commentaire**. D'où `3` étages comptés pour `2` copies, et le refus. ## L'ironie, qui est aussi l'argument Le commentaire de la ligne 25 est celui que **nous avons écrit pour documenter ce défaut précis**, quand nous l'avons découvert : > `pnpm install --frozen-lockfile` ne rejette PAS le lockfile — pire : il installe le sous-ensemble qu'il voit et ne le dit pas. Autrement dit : le contrôle censé empêcher l'installation partielle muette **échoue sur la documentation de l'installation partielle muette**. Ce n'est pas qu'une anecdote. Un `Dockerfile` bien commenté — et celui du gabarit l'est remarquablement — est précisément celui qui a le plus de chances de mentionner `pnpm install` en prose. Le contrôle pénalise donc ce qu'on veut encourager. ## Ce que je proposerais Ignorer les commentaires avant de compter. Sur un `Dockerfile`, c'est peu coûteux : les lignes dont le premier caractère non blanc est `#`. Deux remarques en plus, si elles sont utiles : 1. **Compter les `FROM` plutôt que les `pnpm install`** serait peut-être plus juste sur le fond : ce que le contrôle veut savoir, c'est « combien d'étages lancent une installation », et un étage peut en lancer plusieurs, ou l'écrire sur plusieurs lignes avec des continuations. Mais je n'ai pas de cas réel sous la main où ça change le verdict — à votre appréciation. 2. **Le message est excellent** et mérite d'être conservé tel quel : il dit quoi ajouter, où, et pourquoi (« pnpm installe le sous-ensemble qu'il voit et ne le dit pas : l'image se construit, amputée »). C'est exactement ce qu'il fallait. Le défaut est dans le comptage, pas dans ce qu'il en fait. ## Ce qui est en place chez nous Rien, et nous n'en voulons pas. Le contournement évident — reformuler notre commentaire pour ne plus écrire « pnpm install » — reviendrait à **abîmer la documentation pour satisfaire un contrôle**, ce qui est exactement le mauvais sens. `eqs doctor` reste donc en échec chez nous jusqu'à la correction ; `pnpm check` est vert, les 58 tests d'intégration passent, et les deux images se construisent. Dites-moi si vous voulez que je vérifie la correction dès qu'elle sort — ce contrôle est né chez nous, autant qu'il y soit éprouvé.
Author
Owner

Vrai, corrigé — et votre ticket en a fait tomber quatre autres que personne n'avait signalés.

Ce que vous avez trouvé. Comptage textuel sur le fichier entier : une ligne de prose qui nomme pnpm install valait un étage. Reproduit, et votre lecture de l'ironie est la bonne — le commentaire fautif était celui qui documentait l'installation partielle muette, c'est-à-dire le défaut que ce contrôle existe pour empêcher.

Ce que j'ai corrigé en plus, et qui compte davantage. Le sens inverse : un commentaire qui cite apps/x/package.json gonflait le nombre de copies vues, et pouvait donc masquer un manifeste réellement absent. Un « pensez à ajouter COPY apps/mcp/package.json » en commentaire aurait suffi. Un faux positif se voit ; un faux négatif dans une porte, non.

Votre remarque n°1, et pourquoi je ne l'ai pas suivie. Compter les FROM : le Dockerfile du gabarit en a cinq pour deux installations — base, deps, prod-deps, build, puis api et web qui n'installent rien. Ça surestimerait bien plus que le comptage actuel. Ce que le contrôle veut savoir n'est pas « combien d'étages » mais « combien d'étages installent », et là-dessus compter les pnpm install hors commentaires est juste. Votre instinct était bon, la mesure tranche autrement.

Votre remarque n°2 : message conservé mot pour mot.


La chasse, parce que c'était la deuxième fois.

J'avais corrigé exactement cette faute la veille sur config.compose-env, qui lisait les commentaires YAML et réclamait la variable citée dans l'explication du compose. Je ne l'avais pas reportée ailleurs. Votre ticket est la facture de cet oubli.

J'ai donc écrit une fixture qui ne fait que parler des choses interdites, et je l'ai passée à tous les contrôles qui scannent du texte :

http.fetch-brut              FAUX POSITIF -> src/notes.ts:1 appelle fetch directement
config.lecture-hors-module   FAUX POSITIF -> src/notes.ts:2 lit process.env
telemetry.console            FAUX POSITIF -> src/notes.ts:3 utilise console
config.env-public            FAUX POSITIF -> NEXT_PUBLIC_API_URL est lue par le front mais absente de .env.example

Quatre d'un coup, sur un fichier qui n'exécute rien. Un satellite qui écrit « ne jamais appeler fetch( directement » dans un commentaire se faisait refuser pour l'avoir écrit — et le contournement naturel, poser un eqs-http:autorise sur une ligne qui n'appelle rien, abîme le sens de l'échappatoire.

Le dernier est le plus pervers : il réclamait qu'on déclare dans .env.example une variable que personne ne lit, parce qu'un commentaire la mentionne. Je l'avais écrit la même nuit que les deux corrections.

Les six sont corrigés, vérifiés dans les deux sens : muets sur la prose, toujours rouges sur du vrai code.

Et une pratique en sort. reference/chasses.md consigne désormais les classes de défaut, leurs instances, et ce que chaque garde-fou ne couvre pas — pour que la question « où ailleurs ? » survive à la conversation qui l'a posée. AGENTS.md en fait une règle : un ticket ouvre une chasse. Quatre autres classes y sont déjà ouvertes, dont trois viennent de vos tickets de cette semaine — l'artefact publié qu'on ne vérifie jamais, le contrôle qui ne tourne nulle part, la surface documentée que personne n'a exercée.

Je laisse ce ticket ouvert jusqu'à la publication de la 0.8.2, et oui — je veux bien que vous l'éprouviez chez vous. C'est votre Dockerfile qui a le commentaire que mes fixtures n'avaient pas, et ça vaut mieux qu'une fixture que j'écris moi-même.

Vrai, corrigé — et votre ticket en a fait tomber **quatre autres** que personne n'avait signalés. **Ce que vous avez trouvé.** Comptage textuel sur le fichier entier : une ligne de prose qui nomme `pnpm install` valait un étage. Reproduit, et votre lecture de l'ironie est la bonne — le commentaire fautif était celui qui documentait l'installation partielle muette, c'est-à-dire le défaut que ce contrôle existe pour empêcher. **Ce que j'ai corrigé en plus, et qui compte davantage.** Le sens inverse : un commentaire qui **cite** `apps/x/package.json` gonflait le nombre de copies vues, et pouvait donc **masquer** un manifeste réellement absent. Un « pensez à ajouter `COPY apps/mcp/package.json` » en commentaire aurait suffi. Un faux positif se voit ; un faux négatif dans une porte, non. **Votre remarque n°1, et pourquoi je ne l'ai pas suivie.** Compter les `FROM` : le `Dockerfile` du gabarit en a cinq pour deux installations — `base`, `deps`, `prod-deps`, `build`, puis `api` et `web` qui n'installent rien. Ça surestimerait bien plus que le comptage actuel. Ce que le contrôle veut savoir n'est pas « combien d'étages » mais « combien d'étages **installent** », et là-dessus compter les `pnpm install` hors commentaires est juste. Votre instinct était bon, la mesure tranche autrement. **Votre remarque n°2** : message conservé mot pour mot. --- **La chasse, parce que c'était la deuxième fois.** J'avais corrigé exactement cette faute la veille sur `config.compose-env`, qui lisait les commentaires YAML et réclamait la variable citée dans l'explication du compose. Je ne l'avais pas reportée ailleurs. Votre ticket est la facture de cet oubli. J'ai donc écrit une fixture qui ne fait que **parler** des choses interdites, et je l'ai passée à tous les contrôles qui scannent du texte : ``` http.fetch-brut FAUX POSITIF -> src/notes.ts:1 appelle fetch directement config.lecture-hors-module FAUX POSITIF -> src/notes.ts:2 lit process.env telemetry.console FAUX POSITIF -> src/notes.ts:3 utilise console config.env-public FAUX POSITIF -> NEXT_PUBLIC_API_URL est lue par le front mais absente de .env.example ``` Quatre d'un coup, sur un fichier qui n'exécute rien. Un satellite qui écrit « ne jamais appeler `fetch(` directement » dans un commentaire se faisait refuser pour l'avoir écrit — et le contournement naturel, poser un `eqs-http:autorise` sur une ligne qui n'appelle rien, abîme le sens de l'échappatoire. Le dernier est le plus pervers : il réclamait qu'on déclare dans `.env.example` une variable que **personne ne lit**, parce qu'un commentaire la mentionne. Je l'avais écrit la même nuit que les deux corrections. Les six sont corrigés, vérifiés dans les deux sens : muets sur la prose, toujours rouges sur du vrai code. **Et une pratique en sort.** `reference/chasses.md` consigne désormais les classes de défaut, leurs instances, et ce que chaque garde-fou ne couvre pas — pour que la question « où ailleurs ? » survive à la conversation qui l'a posée. `AGENTS.md` en fait une règle : un ticket ouvre une chasse. Quatre autres classes y sont déjà ouvertes, dont trois viennent de vos tickets de cette semaine — l'artefact publié qu'on ne vérifie jamais, le contrôle qui ne tourne nulle part, la surface documentée que personne n'a exercée. Je laisse ce ticket **ouvert** jusqu'à la publication de la 0.8.2, et oui — je veux bien que vous l'éprouviez chez vous. C'est votre `Dockerfile` qui a le commentaire que mes fixtures n'avaient pas, et ça vaut mieux qu'une fixture que j'écris moi-même.
Author
Owner

Corrigé. Le scan ignore les commentaires : le garde-fou est repris dans les trois paquets concernés, et reference/chasses.md marque la chasse n°1 (« un scan textuel qui lit aussi les commentaires ») fermée le 2026-09-10, avec huit instances listées — quatre trouvées en cherchant, pas en attendant un rapport.

Fermé après vérification contre le code de dev le 2026-09-21. Ce ticket restait ouvert faute d'un Closes #47 dans le commit, pas faute de correctif.

Corrigé. Le scan ignore les commentaires : le garde-fou est repris dans les trois paquets concernés, et `reference/chasses.md` marque la chasse n°1 (« un scan textuel qui lit aussi les commentaires ») **fermée le 2026-09-10**, avec huit instances listées — quatre trouvées en cherchant, pas en attendant un rapport. Fermé après vérification contre le code de `dev` le 2026-09-21. Ce ticket restait ouvert faute d'un `Closes #47` dans le commit, pas faute de correctif.
Sign in to join this conversation.
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#47
No description provided.