Que fait Développement piloté par contraintes ?
Présentation
Les autres compétences de ce pack décrivent ce qui est bien. code-review-and-quality vous donne cinq axes. test-driven-development vous donne un cycle. security-and-hardening vous donne une liste de menaces. Tout cela vit dans une prose que l'agent lit et peut ou non suivre, et rien ne survit à la fin de la session.
Cette compétence produit quelque chose de différent : un enregistrement écrit du niveau de qualité de ce projet, avec des chiffres, qui survit à la conversation et peut être vérifié mécaniquement.
La raison est importante. Quand vous écriviez le code, le lire vous disait s'il était bon. Un agent écrit plus en un après-midi que vous n'en lirez cette semaine, donc le jugement passe de votre tête à des vérifications qui tournent en boucle. Ces vérifications doivent exister, elles doivent avoir des chiffres que vous avez réellement choisis, et elles doivent se déclencher suffisamment près du travail pour que l'agent corrige sa propre sortie.
Le développement piloté par spécifications dit quoi construire. Le développement piloté par tests prouve que ça marche. Le développement piloté par contraintes définit ce que « assez bon pour être livré » signifie, avant que quiconque ne discute dans une pull request.
Quand utiliser
Appliquez cette compétence lorsque :
- Vous démarrez un projet ou une fonctionnalité importante et aucun niveau de qualité n'est écrit
- L'utilisateur demande à « définir des contraintes », « ajouter des portes de qualité », « définir nos standards » ou « empêcher l'agent de livrer de la camelote »
- Un agent produit un volume que personne ne lit ligne par ligne
- L'intégration continue a des vérifications, mais personne ne sait lesquelles bloquent une fusion et lesquelles sont décoratives
- Les chiffres de couverture, performance ou accessibilité sont débattus par pull request au lieu d'être décidés une fois pour toutes
- Vous êtes sur le point d'exécuter
/build autoou toute boucle autonome, et la seule chose entre elle et la branche principale est une suite de tests que l'agent a également écrite
À NE PAS utiliser lorsque :
- Le projet a déjà un
CONSTRAINTS.mdet l'utilisateur ne le modifie pas — lisez-le et suivez-le à la place - Scripts uniques, expérimentations, prototypes jetables
- L'utilisateur veut une revue de code maintenant (
code-review-and-quality) ou une pipeline CI construite (ci-cd-and-automation) - Code pré-Product-Market-Fit avec une durée de vie estimée à deux semaines — le plancher en vaut encore la peine, le reste non
Chargement des contraintes
L'entretien nécessite un utilisateur en direct. Ne l'exécutez pas dans des contextes non interactifs (CI, /loop, exécutions autonomes). Si les contraintes sont manquantes et que vous êtes dans l'un de ces contextes, appliquez le plancher ci-dessous, notez que vous l'avez fait, et signalez le reste à un humain.
Le processus
Étape 1 : Détectez avant de demander
Ne demandez jamais ce que vous pouvez lire. Avant la première question, recueillez :
| Quoi | Où chercher |
|---|---|
| Langage et stack | package.json, pyproject.toml, go.mod, Cargo.toml |
| Exécuteur de tests | dépendances de développement, script test, fichiers de test existants |
| Linters existants | eslint.config.*, biome.json, .ruff.toml |
| Couverture actuelle | sortie coverage/, ou exécutez la suite une fois |
| CI | .github/workflows/, .gitlab-ci.yml |
| Infrastructure agent | .claude/, .codex/, AGENTS.md |
Signalez ce que vous avez trouvé en deux lignes, puis demandez seulement ce qui reste.
Étape 2 : Quatre questions, chacune avec une valeur par défaut
Suivez la discipline une question à la fois de interview-me, avec un changement : chaque question ici a une valeur par défaut, donc « Je ne sais pas » est une réponse complète qui produit toujours une configuration fonctionnelle.
Q1 : Au-delà du plancher, lesquelles de celles-ci voulez-vous appliquer ?
(a) Couverture de test sur le nouveau code
(b) Scan de sécurité
(c) Budgets de performance
(d) Accessibilité
(e) Frontières d'architecture
ESTIMATION : (a) et (b) — vous avez déjà un exécuteur de tests et vous traitez des entrées utilisateur.
DÉFAUT si incertain : (a) et (b).
Dites ce que chaque choix coûte : (c) et (d) nécessitent une URL en cours d'exécution, (e) nécessite un fichier de règles écrit.
Q2 : Lorsqu'une vérification échoue pendant que l'agent est en pleine tâche, doit-elle bloquer ou avertir ?
ESTIMATION : Bloquer. Vous exécutez des agents sans surveillance et un avertissement que personne ne lit est un avertissement.
DÉFAUT si incertain : Bloquer sur le plancher, avertir sur tout le reste pour les deux premières semaines.
Q3 : Avez-vous des chiffres cibles en tête, ou dois-je mesurer où vous en êtes aujourd'hui et maintenir cette ligne ?
ESTIMATION : Mesurer. La plupart des équipes n'ont pas de chiffre, et un chiffre inventé est ignoré.
DÉFAUT si incertain : Mesurer et maintenir. Voir « Cliquets » ci-dessous.
Q4 : Quelle est la vérification la plus lente que vous tolérerez avant que l'agent ne rende le travail ?
ESTIMATION : Environ 90 secondes. Plus long et vous arrêterez de l'exécuter.
DÉFAUT si incertain : 90 secondes à la fin de la tâche, illimité dans CI.
Arrêtez-vous à quatre. Un questionnaire de douze questions produit une configuration que personne ne comprend et un utilisateur qui regrette d'avoir commencé.
Étape 3 : Écrire CONSTRAINTS.md
Un fichier à la racine du dépôt. Tout agent sur toute infrastructure peut le lire, et une modification apparaît dans la revue là où elle doit être.
# Contraintes
Dernière révision : 2026-08-08 par @addy
## Plancher (toujours appliqué, sans configuration requise)
- Pas de nouveaux commentaires de suppression : `@ts-ignore`, `eslint-disable`, `# noqa`, `# type: ignore`
- Pas de stubs non implémentés : `throw new Error("Not implemented")`, `catch {}` vide
- Pas de tests ignorés ou supprimés sans raison dans le message de commit
- Pas de secrets dans le code source
- Ce fichier ne doit pas être affaibli pour faire passer une modification
## Appliqué avec des chiffres
| Dimension | Règle | Vérifié par | S'exécute à |
|-----------|-------|-------------|-------------|
| Types | Zéro erreur de type | `tsc --noEmit` | chaque édition |
| Lint | Zéro erreur de notre config | `biome check` | chaque édition |
| Secrets | Pas de secrets dans le source | `gitleaks detect --redact` | chaque édition |
| Couverture | Lignes modifiées ≥ 80 % couvertes | `vitest run --coverage` + git diff | fin de tâche, CI |
| Sécurité : code | Aucun résultat élevé | `semgrep scan --config p/default` | CI |
| Sécurité : dépendances | Rien à haut ou au-dessus | `osv-scanner scan source -r .` | CI |
| Accessibilité | Zéro critique ou sérieux | `axe $PREVIEW_URL --tags wcag2a,wcag2aa,wcag21aa` | déploiement de preview |
| Performance | LCP ≤ 2500ms, CLS ≤ 0.1 | `lighthouse $PREVIEW_URL --output=json` | déploiement de preview |
Chaque ligne nomme la commande qui produit le verdict. Une dimension avec un
chiffre et aucune commande dans cette colonne est une aspiration, pas une contrainte.
## Mesuré, pas encore appliqué
| Métrique | Aujourd'hui | Direction |
|----------|-------------|-----------|
| Couverture du projet | 62.4 % | ne doit pas baisser |
| Taille du bundle (main) | 184 kB | ne doit pas augmenter |
## Exceptions
| ID | Règle | Chemin | Raison | Propriétaire | Expire |
|----|-------|--------|--------|--------------|--------|
| W1 | `no-explicit-any` | `src/legacy/**` | Réécriture suivie dans ENG-441 | @addy | 2026-11-01 |
Ajoutez ensuite une ligne à AGENTS.md et CLAUDE.md : Lisez CONSTRAINTS.md avant d'écrire du code. Ne l'affaiblissez pas pour faire passer une modification.
Étape 4 : Installer ce dont chaque dimension a besoin
Choisir une dimension signifie installer quelque chose. Ne laissez pas l'utilisateur avec un chiffre et aucun mécanisme, et n'inventez pas votre propre vérificateur quand un outil de facto existe — ces outils sont listés parce que leurs formats de règles et seuils sont ce que tout le reste de l'écosystème cible, donc la configuration existante de l'équipe continue de fonctionner.
| Dimension | Outil | Installation | Exécution | Porte sur |
|---|---|---|---|---|
| Types (TS) | tsc | déjà présent | tsc --noEmit | toute erreur |
| Types (Python) | mypy | pip install mypy | mypy . | toute erreur |
| Lint | votre config existante | déjà présent | eslint . / biome check / ruff check | toute erreur |
| Couverture (JS) | votre exécuteur de tests | déjà présent | vitest run --coverage (ou jest --coverage) | couverture des lignes modifiées |
| Couverture (Python) | pytest-cov | pip install pytest-cov | pytest --cov --cov-report=lcov | idem |
| Sécurité : code | Semgrep | pipx install semgrep | semgrep scan --config p/default --config p/owasp-top-ten | tout résultat élevé |
| Sécurité : secrets | gitleaks | brew install gitleaks | gitleaks detect --redact --no-banner | tout résultat |
| Sécurité : dépendances | osv-scanner | brew install osv-scanner | osv-scanner scan source -r . | haut ou supérieur |
| Performance : page | Lighthouse | npm i -D lighthouse | lighthouse $URL --output=json --quiet | LCP, CLS, score de performance |
| Performance : bundle | size-limit | npm i -D size-limit | size-limit --json | budget par entrée |
| Accessibilité | axe-core | npm i -D @axe-core/cli | axe $URL --tags wcag2a,wcag2aa,wcag21aa | zéro critique ou sérieux |
| Architecture | dependency-cruiser | npm i -D dependency-cruiser | depcruise --validate src | toute violation |
| Qualité des assertions | Stryker | npm i -D @stryker-mutator/core | stryker run --mutate <fichiers modifiés> | score de mutation |
Cinq choses qui vous mordront si vous les sautez :
--redactsur gitleaks n'est pas optionnel. Sans lui, le secret correspondant atterrit dans le journal de l'agent, ce qui fait qu'une clé divulguée se retrouve dans un log, un résumé ou un message de commit. Signalez la règle et l'emplacement, jamais la valeur.- Lighthouse et axe ont besoin d'une URL. Ils ne fonctionnent que contre une application en cours d'exécution, donc ils appartiennent à l'étape d'exécution contre un déploiement de preview ou un serveur local que vous démarrez d'abord. Si le projet n'a pas d'URL à atteindre — un CLI, une bibliothèque, une application de bureau — dites-le et abandonnez la dimension plutôt que d'inventer une vérification qui ne peut pas s'exécuter.
- Limitez les vérifications coûteuses au diff.
stryker run --mutatesur l'ensemble du dépôt prend des heures et sera désactivé ; sur les fichiers touchés par un changement, cela prend moins d'une minute. Idem pour Semgrep, qui prend une liste de chemins. - La couverture n'a pas besoin d'une deuxième exécution de test. Lisez le lcov que votre suite écrit déjà et croisez-le avec
git diff. Exécuter la suite deux fois pour obtenir un chiffre est le moyen le plus rapide de faire détester cette approche. - Les règles du registre Semgrep sont gratuites à exécuter ; vérifiez la licence avant de les redistribuer.
opengrepest un fork compatible avec le même format de règles et une sortie JSON si cela compte pour votre équipe juridique.
Ajoutez chacune au script propre du projet pour qu'il soit reproductible sans agent :
{
"scripts": {
"check:fast": "tsc --noEmit && eslint . && gitleaks detect --redact --no-banner",
"check:task": "npm run check:fast && vitest run --coverage",
"check:full": "npm run check:task && semgrep scan --config p/default && osv-scanner scan source -r ."
}
}
Ce mappage est plus important que les outils. check:fast est ce qui s'exécute après une édition, check:task quand l'agent pense avoir terminé, check:full dans CI.
Les commandes vivent maintenant à deux endroits — la colonne Vérifié par dans CONSTRAINTS.md et ces scripts. CONSTRAINTS.md est la source canonique : elle porte la raison à côté de chaque commande et apparaît dans la revue. Les scripts sont des wrappers de commodité qui doivent la refléter, pas une seconde source de vérité ; s'ils divergent, le fichier gagne.
Étape 5 : Câbler au cycle de vie
La plus grosse erreur est de tout exécuter partout. Une vérification qui bloque l'agent est désactivée, et une porte désactivée est pire qu'aucune porte, car le niveau de qualité semble toujours exister.
| Phase | Commande | Ce qui s'exécute | Budget |
|---|---|---|---|
| BUILD | /build | Types, lint, secrets, le plancher | moins de 5s, fichier modifié seulement |
| VERIFY | /test | Tests associés, couverture sur les lignes modifiées | moins de 90s |
| REVIEW | /review | Tout, plus les gardes ci-dessous | minutes |
| SHIP | /ship | Vérifications de direction, pas de régressions | CI |
Deux règles qui rendent cela tolérable :
- Limitez au diff. Vérifiez les lignes que ce changement a touchées, pas l'ensemble du dépôt. La couverture des lignes modifiées est un chiffre que l'agent peut faire bouger ; la couverture du projet est un chiffre qu'il hérite.
- Le coût décide de l'emplacement. Tout ce qui dépasse quelques secondes sort de la boucle d'édition. Les tests de mutation sur un dépôt entier prennent des heures ; sur les fichiers touchés par un changement, cela prend moins d'une minute, ce qui fait la différence entre une vérification que les gens exécutent et une qu'ils ne font pas.
Étape 6 : Protéger le niveau de qualité lui-même
Quelqu'un fera remarquer que si l'agent écrit le code et les vérifications, les vérifications ne prouvent rien. À moitié vrai, et ça vaut la peine de l'ingénieriser.
Les agents ne créent pas de failles astucieuses. Ils rencontrent un échec rouge et prennent le chemin le moins cher pour passer au vert. Surveillez ces cinq mouvements dans le diff, au moment de la revue :
- Le seuil a bougé. Un budget réduit, une sévérité baissée, une vérification retirée de l'étape rapide. Comparez
CONSTRAINTS.mdà son état au point de branchement. - Un test est devenu plus facile.
.skipajouté, un fichier de test supprimé, des assertions retirées de tests qui sont restés. - Un vérificateur a été réduit au silence. Nouveau
@ts-ignoreoueslint-disable. Quatre suppressions méritent une attention particulière car elles désactivent une vérification sur laquelle vous comptez :istanbul ignoreretire du code de la couverture au lieu de le tester,Stryker disablecache un mutant survivant,nosemgrepetgitleaks:allowfont de même pour les résultats de sécurité. - Le travail est inachevé. Un stub qui lève une exception, un
catchvide qui transforme un échec en silence, unTODOà la place de l'implémentation. - Une exception est apparue. Une nouvelle ligne dans le tableau des Exceptions dont personne n'a parlé.
Aucun de ces éléments n'a besoin d'outillage au-delà de git diff. Renforcer le niveau de qualité devrait être silencieux ; l'affaiblir devrait être bruyant.
Contrairement aux dimensions numérotées, le plancher n'a pas d'outil de facto propre, donc un agent chargé de l'appliquer a tendance à écrire un vérificateur à partir de zéro, et deux agents en écrivent deux différents. Une implémentation de référence de ces cinq vérifications est livrée avec cette compétence dans references/floor-guard.md (limitée au diff, code de sortie 0/1/2, modèles adaptables par écosystème). Adaptez-la plutôt que de la réinventer, pour la même raison que chaque dimension nomme un outil de facto : pour que le mécanisme soit le même d'une exécution à l'autre et d'un stack à l'autre.
Toutes les vérifications ne sont pas également circulaires. Classez-les selon une question : l'agent peut-il les faire passer en écrivant du code qui ne fonctionne pas ?
- Externe — axe-core encode WCAG,
osv-scannerlit une base de données de vulnérabilités, Lighthouse mesure un vrai navigateur. L'agent ne peut pas discuter avec ceux-ci. - Projet — vos règles de lint, vos limites de couche. Un humain possède le fichier.
- Suite — vos propres tests. Le plus utile, et le seul vraiment circulaire.
Un bar