# Feature Loop

> Use when the user wants a non-trivial feature implemented with autonomous, iterative, quality-gated delivery — best when quality matters more than raw speed. A mother agent sizes the difficulty tier and delegates to SEPARATE subagents (code writer ≠ test-writer ≠ blind reviewer; tests written from the spec). Objective gate (build/lint/typecheck/tests + test-must-go-red mutation check) BEFORE any LLM review; runnable features get a mandatory LIVE smoke test before any "tested" SUCCESS. Loops until all quality axes ≥ 8/10 with zero criticals (max 3 iters), keeps the BEST version, emits a markdown report. Subcommands: status (runs dashboard), learn (propose-only self-improvement). Flags: --fast, --paranoid, --worktree, --max-iter=N, --threshold=N, --judge, --issue=N (GitLab/GitHub issue as spec). NOT for quick edits (do them directly) nor review-only (use senior-review).

- Skill: `ohugonnot/feature-loop` (Agent Skill, multi-file: 15 files)
- Install (CLI): `npx skillmds@latest add ohugonnot/feature-loop`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ohugonnot/feature-loop/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: ohugonnot (https://skillmd.com/u/ohugonnot)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ohugonnot/feature-loop

---


# Feature Loop

**skill_version : 8.15.5** (historique : `CHANGELOG.md`). Implémentation itérative auto-notée d'une feature jusqu'à convergence sur un radar de qualité.

**Fichiers du skill (progressive disclosure)** : `scoring-rubric.md` (chargé par le reviewer), `lessons.md` (instantané publié, promu à la main — la mémoire de travail est hors dépôt dans `~/.claude/skill-memory/feature-loop-lessons.md`, chargée par la mère à l'init), `reference/subcommands.md` (lu au dispatch `status`/`learn`), `reference/report-template.md` (lu au §5.4), `reference/git-recipes.md` (recettes shell snapshot/restore/conflicts, lues aux §4.2/5.0/5.1bis), `reference/log-example.md` (trace de run illustrative), `reference/limitations.md` (lu si contexte concerné), `reference/references.md` (sources académiques, à la demande), `reference/stack-*.md` (packs spécialistes — symfony, golang, htmx, javascript, cqrs-es — chargés à l'Étape 2bis selon la stack détectée, combinables).

**Architecture** (patterns officiels Anthropic, *Building Effective Agents*) :
- **Orchestrator-workers** : une **mère** (Opus, haute réflexion) estime, décompose, délègue à des sous-agents spécialisés, puis synthétise. Elle reste le cerveau ; les workers sont les bras.
- **Evaluator-optimizer** : boucle générer → évaluer → raffiner, avec un évaluateur **distinct** du générateur. Valide quand les critères sont clairs et mesurables (notre radar).
- **Séparation stricte des rôles** : écrivain-code ≠ écrivain-tests ≠ relecteur. Le *self-preference bias* est prouvé (un modèle qui se juge se surnote). La mère orchestre, ne s'auto-juge jamais.
- **Effort proportionné à la difficulté** : multi-agents ≈ 15× les tokens d'un chat (Anthropic). On dimensionne modèle + profondeur à l'enjeu → rapide sur le facile, lourd sur le critique.

Sources détaillées (patterns Anthropic, LLM-as-judge, raffinement itératif, tests LLM) : `reference/references.md`.

```
PRE-FLIGHT (baseline projet : git clean, deps, build pass)
  ↓
INIT (clarifier + détecter scope/keywords-sensibles + extraire conventions + lint plugins
      + ESTIMER LA DIFFICULTÉ → tier + modèles (Étape 2bis)
      + charger insights projet + lessons cross-projet + branche in-place [ou worktree si --worktree])
  ↓
LOOP × max 3 (default, --max-iter ; --fast = chemin court si tier TRIVIAL) :
  PLAN (Sonnet) → MINI-REVIEW PLAN (Haiku)
        ↓
  IMPLEMENT CODE  (agent A — Sonnet/Opus selon tier)        ⟍ agents
  ÉCRIRE TESTS depuis la SPEC (agent B ≠ A, dès STANDARD)    ⟍ DISTINCTS
        ↓
  GATE OBJECTIF : build/lint/typecheck/tests + RED-CHECK (test critique doit pouvoir rougir)
       (lint plugins spécialisés + retry flaky 1×) — PAS de juge LLM si le gate casse
        ↓
  REVIEW BLIND (agent C ≠ A,B — Sonnet défaut / Opus si complexe-sensible)
       + écrit un test adversarial par finding   [+ DEVIL'S ADVOCATE si paranoid/auto]
        ↓
  ESCALADE SUR DOUTE (score borderline / désaccord juges / confiance basse / scope sensible
       → 2e juge, puis panel de 3, puis arbitre Opus)
        ↓
  VALIDATION DES PREUVES (anti-hallucination, file:line vérifiés)
        ↓
  DÉTECTION RÉGRESSION / PERSISTANCE / STAGNATION  → garder la MEILLEURE version (pas la dernière)
        ↓
  Convergence ?  → SUCCESS    Régression ? → ROLLBACK + contrainte
        ↓
       next iter (force notes_acknowledged)
  ↓
SMOKE TEST FINAL (offline build/tests) → SMOKE TEST LIVE (run réel + exercer le chemin) → CONFLICTS CHECK vs main → RAPPORT MARKDOWN → insights + lessons + runs-log + skill_version → user décide
```

## Principes non négociables

<!-- LOCKED: jamais d'édition auto par `learn` (propose-only, voir Étape 7). Modif humaine uniquement. -->

Fondements académiques de ces règles : `reference/references.md`.

- **Travail à deux, pas tête baissée** : ambiguïté → demander avant de coder. Stopper la boucle pour toute décision produit ou trade-off non technique.
- **Honnêteté > faux confort** : une solution simple aux limites documentées bat une solution complexe qui se prétend parfaite. Sur-ingénierie constatée à l'usage → reculer proprement (rollback simplificateur, 4.8), pas persister.
- **Solution minimale viable avant infrastructure** : JAMAIS de parser custom / framework / abstraction avant d'avoir essayé la solution simple. Si elle ne suffit pas, justifier par les cas concrets qu'elle rate.
- **Logs temps réel** : 1 ligne par sous-étape clé. Pas de silence > 3 min.
- **Review en aveugle** : le reviewer ne voit JAMAIS le prompt d'implémentation.
- **Séparation des rôles writer ≠ tester ≠ reviewer (non négociable)** : l'auteur du code ne l'évalue ni ne le teste JAMAIS (self-preference bias prouvé, refs). Donc (a) review = sous-agent au contexte vierge ; (b) tests = sous-agent dédié ≠ auteur **dès STANDARD**, écrits depuis la SPEC (pas en lisant l'impl) ; (c) sur TRIVIAL/express la mère peut coder ET tester, mais la **review reste déléguée** (auto-review interdite, toujours).
- **Pas de validation sans signal externe** : l'auto-correction LLM sans oracle externe dégrade (refs). Aucune itération SUCCESS sur la seule auto-critique : il faut le gate objectif vert (build/lint/tests) ET un juge séparé.
- **Le run réel est le signal ultime** : pour toute app *runnable*, gate offline + intg sur DB de test = nécessaires mais PAS suffisants (ils ne voient pas un serveur resté sur l'ancien binaire, une migration non rejouée sur la DB runtime, une erreur de câblage, une intégration que les mocks simulaient). Exécuter l'app réelle et exercer le chemin de bout en bout (smoke LIVE, §5.1b) avant tout SUCCESS « testé ». Live bloqué (auth/creds) → maximum faisable + dire explicitement ce qui reste à confirmer ; jamais « testé » sur la seule foi des mocks.
- **Tout test critique doit pouvoir rougir (red-check)** : 76 % des tests LLM ratent le fail-to-pass (refs). Avant de faire confiance à un test critique : muter sa ligne cible, vérifier qu'il ÉCHOUE, restaurer. Resté vert = vacant → réécrire. Périmètre : tests critiques seulement (§4.5b).
- **Effort proportionné à la difficulté** : multi-agents ≈ 15× les tokens d'un chat. Panel, devil's advocate, review Opus, itérations multiples seulement si l'enjeu le justifie. Trivial → mode express (2bis).
- **Doute → escalade (panel avant gros modèle)** : jugement incertain (score borderline près du seuil, désaccord juges ≥ 2 pts, confiance basse, scope sensible, preuves invalides) jamais accepté tel quel → 2ᵉ juge → panel mixte de 3 → arbitre Opus (§4.6c).
- **Garder la meilleure version, pas la dernière** : les gains plafonnent après 2-3 itérations et une itération peut régresser → snapshot du meilleur radar (`best_iter_sha`), restauré si la dernière est moins bonne.
- **Preuves obligatoires et vérifiées** : tout score < 10 cite `file:line` ; le skill VÉRIFIE que ces refs existent.
- **Build/lint/typecheck/tests doivent passer** : sinon score robustesse = 0.
- **Isolation par branche** : in-place sur branche dédiée par défaut (courante si feature branch, sinon `feature-loop/<slug>` créée depuis la courante si protégée — develop/main/master). Worktree UNIQUEMENT sur `--worktree`. `run_base_sha` + snapshots → rollback sûr. L'in-place rend les edits visibles live dans l'éditeur de l'user.
- **Pas de commit/push/merge automatique** sur la branche principale, ni sur la branche user en in-place au-delà des snapshots de mécanisme.
- **Prompt caching** : parties stables (rubrique, conventions, insights) en tête des prompts ; partie variable (diff, findings) en fin.
- **Discipline tokens/latence** — à qualité égale, le run le moins cher gagne : (a) le reviewer TIRE ses inputs (COMMANDES exactes `git diff <pre_impl_sha>..HEAD -- <scope>` + liste fermée de fichiers), la mère ne colle pas le contenu ; jamais les deux ; re-lecture limitée au scope ; (b) itération corrective → continuer le même juge (SendMessage), pas d'agent frais (§4.6) ; (c) Playwright sobre — snapshots ciblés, `curl` pour le non-visuel ; clic réel OBLIGATOIRE pour les contrôles UI nouveaux (§5.1b) ; (d) sorties shell tronquées (`tail`/`--filter` ; suite complète aux seuls gates) ; (e) validation de preuves = grep par la mère, jamais un agent ; (f) paralléliser l'indépendant (A∥B, tools groupés, panel parallèle).
- **Axes standards + extension** : les axes de base (lisibilité, robustesse, modularité, simplicité, YAGNI, tests + scope-specifics) sont obligatoires. +1-3 axes domain-specific possibles, jamais en substitution. Renommer/supprimer un axe standard = interdit.
- **Skip Sonnet refusé si paranoid actif** : mode paranoid actif → délégation de l'impl à Sonnet non négociable. Sécurité > overhead.
- **Mode spécialiste selon l'archi (auto, annoncé)** : l'Étape 2bis nomme la combinaison stack+archi et confère à l'impl, au test-writer ET au reviewer une persona d'expert senior + les invariants/pièges d'archi à respecter/vérifier. Écrire idiomatique = moins de bugs à la source (d'où l'application à l'impl, pas au seul reviewer). Auto-détecté, jamais une question de plus.
- **Recherche externe permise en cas de doute** : un agent qui doute d'une API/version/framework PEUT consulter le web (doc officielle d'abord) plutôt qu'halluciner une signature — mais l'oracle reste le gate objectif + red-check + smoke live, jamais la réponse web. En cas de doute seulement, pas par défaut.
- **Pas de "mergeable proprement" sans commit** : §5.2 refuse de logger `mergeable` si 0 commit applicatif ; le rapport force un commit final (5.1bis) avant le conflicts check.

## Parsing des arguments

**Sous-commandes** (premier token, pas de boucle d'implémentation) :
- `status` — affiche le tableau de bord des runs passés sur ce repo (lit le runs-log persistant). Voir Étape 6.
- `learn` — analyse les runs passés + complète `lessons.md`, et propose (sans appliquer en silence) des évolutions du SKILL.md. Voir Étape 7.

**Mode feature** (défaut) :
- `<description>` — description libre de la feature
- `--worktree` — force l'isolation worktree git au lieu de l'in-place. Cas d'usage : faire tourner deux runs en parallèle sur le même repo sans collision de branche. Sinon, défaut = in-place sur branche dédiée (voir Étape 3).
- `--paranoid` — force le 2e reviewer Opus en devil's advocate (sinon auto-activé sur keywords sensibles)
- `--no-paranoid` — désactive le devil's advocate même si keywords sensibles détectés
- `--max-iter=N` — override la limite par défaut (**3**). Augmenter au-delà de 5 nécessite justification : les iter 4-5 apportent peu en pratique (observé sur runs réels : converge en 2-3, ou MAX_ITERATIONS valide avec limites documentées).
- `--threshold=N` — override le seuil de SUCCESS par axe (8)
- `--fast` — force l'évaluation en **mode express** (Étape 2bis) et supprime les confirmations d'avant-boucle. Le mode express s'active DÉJÀ tout seul sur une tâche TRIVIALE sans keyword sensible (cf. 2bis) ; `--fast` ne fait que sauter la confirmation et l'imposer si l'estimation hésite. Refusé dès qu'un keyword sensible apparaît.
- `--judge=sonnet|opus` — override le modèle du reviewer par défaut (défaut : Sonnet, escalade auto sur doute). `--judge=opus` force Opus à chaque review (plus lent/cher, qualité de jugement max).
- `--no-redcheck` — désactive le red-check (4.5b) sur les tests critiques. Déconseillé : c'est le garde-fou anti tests vacants. Utile seulement sur un projet où la mutation est impraticable (build trop lent, pas d'exécution ciblée possible).
- `--issue=N` — charge l'issue GitLab/GitHub #N comme SPEC (`glab issue view N`, ou `gh issue view N` si le remote est GitHub : titre + description deviennent la description de la feature). Si une branche liée `N-*` existe (créée par `glab mr create --related-issue`, ex. via le skill projet `issue-mr`), elle devient la branche de travail (Étape 3). Voir Étape 1 pour le pont issue-mr quand la spec est vague.

## Logs utilisateur (principe)

Préfixes : `[preflight]`, `[scan]`, `[init]`, `[tier]`, `[iter N/max]`, `[plan]`, `[impl]`, `[tests]`, `[gate]`, `[redcheck]`, `[review]`, `[devil]`, `[escalade]`, `[evidence]`, `[converge]`, `[best]`, `[smoke]`, `[smoke-live]`, `[commit]`, `[conflicts]`, `[report]`, `[insights]`, `[lessons]`, `[runs]`, `[done]`. Sous-commandes : `[status]`, `[learn]`. Pas d'emojis. Style factuel.

Exemple complet d'une trace de run de bout en bout : `reference/log-example.md`.

## Étape 0 — Pre-flight check (baseline projet)

**Avant tout** vérifier que le projet est dans un état exploitable. Si non, le skill ne peut pas mesurer ses propres changements.

Log : `[preflight] vérification baseline...`

Checks dans le repo principal (avant tout choix de branche / worktree) :
1. **Git clean** : `git status --porcelain`. Si modifs non commitées → `AskUserQuestion` : "Le repo a des modifications non commitées. Options : 1) commit avant de continuer / 2) stash temporaire (récupéré à la fin) / 3) annuler".
2. **Deps installées** : présence de `node_modules/` (Node), `vendor/` (PHP), équivalent selon stack. Si pas installé → proposer `npm install` (ou équivalent) puis continuer.
3. **Baseline build pass** : lancer build + lint + typecheck + tests sur HEAD actuel — **la MÊME suite que le gate 4.5 exécutera** (tags d'intégration inclus, ex. `go test -tags intg`, si le gate les lancera). Une baseline partielle fait découvrir les échecs préexistants en pleine boucle → diagnostic stash coûteux (et un `git stash` sans `-u` laisse les fichiers untracked qui cassent la compile — toujours `-u`). Si ÉCHEC → `AskUserQuestion` : "Le projet ne passe pas son propre build/lint/tests sur HEAD. Options : 1) corriger d'abord (skill ne peut pas comparer un avant/après sur base cassée) / 2) continuer quand même — si l'échec est PRÉEXISTANT et hors de la zone touchée, le documenter et l'exclure du verdict de gate / 3) annuler".

**Check piège vendor/node_modules symlink** : UNIQUEMENT en mode `--worktree`. APRÈS création du worktree (étape 3), vérifier :
```bash
for dep in vendor node_modules; do
  if [ -L "$WORKTREE/$dep" ]; then
    REAL=$(realpath "$WORKTREE/$dep")
    MAIN=$(realpath "$REPO_MAIN/$dep")
    [ "$REAL" = "$MAIN" ] && echo "WARN: $WORKTREE/$dep symlinked to main → autoloader/require résoudra vers /main/src, runtime test des modifs entités IMPOSSIBLE"
  fi
done
```
Si symlink détecté → logger `[preflight] WARN: <dep> symlinked to main — limitation runtime test (voir Limitations connues)`. Ne pas BLOQUER (juste avertir l'agent + reviewer pour qu'ils en tiennent compte).

**En mode in-place (défaut)** : ce piège n'existe pas — le repo principal utilise son vrai `vendor/`/`node_modules`, les tests runtime des modifs sont fiables. C'est un avantage concret de l'in-place sur les monorepos `go.work` + vendor.

**Détecter règle CLAUDE.md user "no auto-commit"** : lire `~/.claude/CLAUDE.md` (global user) ET `<repo>/CLAUDE.md` (projet) si présents. Chercher des patterns comme :
- `INTERDIT git add` / `interdit git commit`
- `sans permission explicite`
- `l'utilisateur valide` / `l'user décide`
- `ne commit pas` / `pas d'auto-commit`

Si détecté → flag `no_auto_commit: true` dans le journal. Conséquence : l'étape 5.1bis ne **force pas** le commit, elle **propose** le message à exécuter manuellement. Voir 5.1bis pour le détail. Logger `[preflight] règle user "no auto-commit" détectée → mode proposition manuelle pour le commit final`.

Loguer le résultat de chaque check. Si tout ok : `[preflight] baseline OK`.

## Étape 1 — Clarifier la feature + détection sensibilité

Récupérer la description (args du skill ou demander).

**Spec depuis une issue (`--issue=N`)** : `glab issue view N` (GitLab) ou `gh issue view N` (GitHub, selon le remote) → le titre + la description de l'issue DEVIENNENT la description de la feature (une issue au format analyse — constat/pourquoi/périmètre/plan — passe le test de clarté d'office). Logger `[init] spec chargée depuis issue #N (<titre>)`. Échec glab/gh (issue inexistante, pas de remote) → le dire et retomber sur la description libre.

Test de clarté — la description doit répondre à :
1. **Quoi** : ce qui doit exister à la fin
2. **Qui** : utilisateur cible
3. **Pourquoi / contraintes** : exigences non triviales
4. **Périmètre** : ce qui est EXCLU

Si une réponse manque → `AskUserQuestion`.

**Pont issue-mr (spec vague)** : si le test de clarté échoue sur ≥ 2 points ET qu'un skill `issue-mr` est disponible (global depuis sa v2.0.0 — mode ANALYSE) → proposer via `AskUserQuestion` de l'invoquer MAINTENANT, avant la boucle : l'analyse explore le code, tranche la conception avec l'user et produit une issue structurée qui devient la SPEC ; la branche `<issue>-<slug>` créée devient la branche de travail (Étape 3). Bénéfice : le plan 4.3 converge plus vite et l'agent B (tests-depuis-la-spec) teste un vrai contrat. Contraintes : invocation par la mère en conversation principale UNIQUEMENT (un sous-agent ne peut pas invoquer de skill), et UNIQUEMENT ici — jamais au milieu de la boucle (l'interactivité d'issue-mr casserait l'autonomie). Refus user → clarifier par `AskUserQuestion` classiques.

**Détection de keywords sensibles** : la description et le scope contiennent-ils un de :
`auth`, `password`, `payment`, `payer`, `paiement`, `stripe`, `token`, `permission`, `role`, `crypto`, `hash`, `secret`, `pii`, `gdpr`, `rgpd`, `migration`, `sql`, `admin`, `audit`, `acl`, `oauth`, `jwt`, `session`, `csrf`, `xss` ?

Si oui ET pas de `--no-paranoid` → activer automatiquement le devil's advocate (`paranoid: true` dans le journal). Logger `[scan] keywords sensibles détectés (<liste>) → --paranoid auto-activé`.

**Calibrage `migration`/`sql`** : ces deux keywords sur-déclenchent — une migration purement additive (`ADD COLUMN ... DEFAULT`, index) est à bas risque et ne justifie pas SENSIBLE (review Opus + DA + panel ≈ 3-4× le coût). Si `migration`/`sql` sont les SEULS keywords détectés → ne pas forcer : qualifier le risque réel (additive vs ALTER destructif / UPDATE-DELETE de données / changement de type) et proposer le choix paranoid on/off via l'AskUserQuestion de l'Étape 2, avec cette qualification et une recommandation. Les autres keywords (auth, payment, secret, pii…) continuent de forcer l'auto-activation.

## Étape 2 — Auto-détection scope + axes + conventions + lint plugins

Log : `[scan] détection stack en cours...`

Scanner en parallèle :
- Stack : `package.json`, `Cargo.toml`, `pyproject.toml`, `go.mod`, etc.
- Front / Back / Tests / E2E / Conventions (voir liste détaillée plus bas)

**Framework de tests** : détection sur `vitest.config*`, `jest.config*`, `pytest.ini`, `playwright.config*`, etc.

Si **AUCUN framework de tests détecté avec confiance** → `AskUserQuestion` :
"Aucun framework de tests détecté. Options : 1) initialiser Vitest (ou équivalent cohérent avec la stack) / 2) framework personnalisé (à préciser) / 3) désactiver l'axe tests pour ce run".

Ne JAMAIS installer un framework non confirmé par l'user — éviter le mismatch (Vitest installé sur projet Jest, etc.).

**Lint plugins disponibles** : parser `package.json` (`devDependencies` + `dependencies`) à la recherche de :
- `eslint-plugin-jsx-a11y` → a11y métriques
- `eslint-plugin-security` → security métriques
- `eslint-plugin-react-hooks` → robustesse hooks
- `eslint-plugin-import` → cycles / imports
- `@typescript-eslint/eslint-plugin` → règles TS strictes
- équivalents Python (`bandit`, `pylint-security`) ou Go (`gosec`)

Lister ceux dispos. Ils seront lancés à l'étape 4.5 et nourriront la review. **Pas d'install automatique** — si manquant, l'axe correspondant reste 100% qualitatif.

Log : `[init] N conventions extraites, M lint plugins dispos (<liste>)`.

Déduire :
- **Scope** : backend / frontend / fullstack
- **Axes activés** : voir `scoring-rubric.md` pour les barèmes à ancres concrètes.
  - **Standards (toujours présents)** : lisibilité, robustesse, modularité, simplicité, YAGNI, tests.
  - **Packs conditionnels (activer seulement les pertinents)** :
    - *Front* : UX/UI, contraste, aéré, responsive, doc-utilisateur, a11y (sur screenshots multi-viewport).
    - *Sécurité/OWASP* : si keywords sensibles OU sortie consommée par un tiers (rubrique = checklist OWASP).
    - *Performance*, *observabilité* : selon scope.
    - **Pack user-facing** (repris d'outil-factory, si la feature produit une UI/contenu vu par un utilisateur final, typiquement un produit public) : i18n (si multilingue), copywriting (si texte user), SEO (si page publique), CTA/conversion (si page à objectif de conversion — rare en interne). Une route API n'active AUCUN de ces axes.
- **Axes domain-specific** (optionnel) : l'agent peut proposer 1-3 axes complémentaires si la feature a une dimension propre (ex: "résilience anti-bot/WAF" pour scraping, "Compat couche pure" pour refacto avec API publique stable). **Chaque axe additionnel doit avoir une rubrique 0-10 explicite définie AVANT l'impl** et stockée dans `.feature-loop.json` au champ `axes_custom`. **Substitution interdite** : jamais retirer/renommer un axe standard. L'axe "valeur vs concurrence" (outil-factory) n'est disponible que sur demande explicite (produit public comparable), jamais d'office.
- **Conventions** : extraire 3-5 patterns concrets du codebase (composant, test, error handling, validation, styling)

Présenter via `AskUserQuestion` :
```
Scope détecté : <fullstack>
Stack : <...>
Axes activés (N) : <liste>
Conventions extraites : <paths>
Lint plugins dispos : <liste>
Mode paranoid : <on/off> (auto si keywords sensibles)
→ Confirmer / Ajuster ?
```

**Mode express (2bis)** : sauter cette confirmation — logger le récap (scope/stack/tier) et continuer directement. L'user interrompt s'il veut ajuster.

## Étape 2bis — Estimation de difficulté + dimensionnement des modèles

But : **proportionner l'effort à l'enjeu** (multi-agents ≈ 15× les tokens d'un chat — Anthropic). La mère estime AVANT d'agir, puis fixe le tier, les modèles et la profondeur. Règle : estimer → dimensionner → déléguer → vérifier → réajuster.

### Estimer sur 4 dimensions (basse/moyenne/haute)
1. **Complexité de raisonnement** : logique subtile, algo, archi vs CRUD/copie mécanique.
2. **Volume / contexte** : nombre de fichiers, lignes, surfaces touchées.
3. **Risque si erreur** : sécurité, données, paiement, migration, prod-facing vs cosmétique réversible.
4. **Parallélisable** : sous-tâches indépendantes (→ fan-out workers) vs séquentiel.

### Tier résultant (inscrit au journal : `difficulty_tier`)
| Tier | Signature | Profondeur boucle |
|---|---|---|
| **TRIVIAL** | mécanique, < 50 LOC, ≤ 2 fichiers, risque nul, 0 keyword sensible | **mode express d'office** (cf. ci-dessous) |
| **STANDARD** | feature normale, logique modérée, risque limité | boucle 1–2 itérations, 1 reviewer Sonnet |
| **COMPLEXE** | raisonnement lourd OU large surface OU archi transverse | boucle complète, reviewer Opus, escalade possible |
| **SENSIBLE** | keyword sensible (auth/paiement/crypto/migration/données…) OU risque élevé | boucle complète + devil's advocate + panel d'office ; jamais de chemin court |

`paranoid` (Étape 1) force SENSIBLE. Présenter le tier à l'user en Étape 2.

### Dimensionnement des modèles (rôle × tier)
La **mère** tourne toujours sur le meilleur modèle disponible (= le modèle de la session — Opus, Fable…, haute réflexion) — orchestration, arbitrages, synthèse, décision finale. Elle délègue les bras :

| Rôle (sous-agent) | TRIVIAL | STANDARD | COMPLEXE | SENSIBLE |
|---|---|---|---|---|
| Plan | (mère) | Sonnet | Sonnet | Sonnet |
| Mini-review plan | — | Haiku | Haiku | Haiku |
| **Impl code** (agent A) | mère\* | Sonnet | Sonnet/Opus | Sonnet/Opus |
| **Tests** (agent B ≠ A) | mère\* | Sonnet | Sonnet | Sonnet |
| **Review** (agent C ≠ A,B) | Sonnet\*\* | Sonnet | Opus | Opus |
| Devil's advocate / panel | — | si doute | si doute | **d'office** |

\* TRIVIAL : la mère peut coder ET écrire les tests, MAIS la review est obligatoirement déléguée (agent ≠ mère). Dès STANDARD, l'agent tests (B) est distinct de l'agent code (A).
\*\* même TRIVIAL passe par un reviewer distinct : on n'économise jamais la *séparation des rôles*, on économise sur le MODÈLE et la PROFONDEUR.

**Tiers sémantiques (robustesse aux générations de modèles)** : les noms de la table = mapping par défaut au moment d'écrire — lire Haiku = tier rapide/mécanique, Sonnet = tier standard, Opus = tier raisonnement max. Si la session tourne sur un modèle plus récent/capable (ex. Fable 5), la mère = le modèle de la session, et chaque rôle prend le meilleur modèle disponible de son tier (paramètre `model` du tool Agent).

`--judge=opus` force la review en Opus quel que soit le tier. Inversement le reviewer Sonnet reste le défaut (rapide), l'escalade (§4.6c) monte en puissance seulement sur doute.

### Reconnaissance d'archi → persona spécialiste
Le tier choisit *quel modèle* ; la persona choisit *quelle expertise*. À partir du `[scan]` (Étape 2), **nommer la combinaison stack + archi dominante** (CQRS/ES, hexagonal, event-driven, Shopify/Stripe, Next/tRPC…) et la consigner au journal (`specialist_stack`). Quand elle porte des invariants propres :
- **Auto-activé et annoncé**, sans question supplémentaire (l'Étape 2 a déjà son AskUserQuestion ; on n'en rajoute pas). Logger `[tier] mode spécialiste: <stack> → personas expertes (impl/tests/review)`.
- **Persona experte par rôle** : les briefs de l'implémenteur (4.4), du test-writer (4.4b) et du reviewer (4.6) reçoivent « expert senior 10+ ans de <stack> » + une **courte liste d'invariants/pièges** de l'archi à respecter/vérifier en priorité. Exemples : *CQRS/ES* → idempotence des commandes, immutabilité/rejouabilité des events, cohérence projection↔agrégat, sagas/effets rétroactifs ; *Shopify* → pagination/éviction, normalisation E.164, scopes, throttling, champs version-dépendants de l'Admin API ; *front* → hydratation, a11y, états de chargement. La mère établit cette liste (elle PEUT s'appuyer sur une recherche web ciblée si la stack lui est peu familière, cf. principe « recherche externe »).
- **Généraliste** si aucune archi marquante (CRUD simple) : on ne force pas une persona artificielle.
- **Packs spécialistes fournis** : quand un pack `reference/stack-<nom>.md` existe pour la stack détectée, la mère le LIT à ce moment et en injecte les sections par rôle dans les briefs (impl → invariants impl, tests → invariants tests, review → checklist, gate → commandes). Disponibles : `stack-symfony.md` (Symfony/Doctrine/Twig), `stack-golang.md` (Go 1.21+, erreurs silencieuses qui compilent), `stack-htmx.md` (API basse fréquence = hallucinations max), `stack-javascript.md` (vanilla front, erreurs silencieuses async/Unicode), `stack-cqrs-es.md` (pack d'ARCHITECTURE, se combine avec un pack langage — ex. Go+CQRS sur un même projet : charger les deux). Un pack prime sur la liste d'invariants improvisée par la mère ; il s'y AJOUTE des invariants projet (CLAUDE.md) sans les remplacer.

### Mode express (auto sur TRIVIAL, ou forcé par `--fast`)

But : ne pas sortir l'artillerie sur un ticket simpliste. **Déclenché d'office** quand le tier est TRIVIAL ET 0 keyword sensible — pas besoin de `--fast` (qui ne fait que sauter les confirmations et l'imposer si l'estimation hésite).

Chemin court : pas de confirmation Étape 2 (logger le scope+tier détectés et continuer ; axes = standards seuls) → pas de dimensionnement élaboré → **pas de phase PLAN ni mini-review Haiku** (surdimensionné pour < 50 LOC) → impl + tests **par la mère** si les critères d'auto-impl (4.4a) sont réunis, sinon agent A (code) + agent B (tests) → gate objectif (build/lint/tests + red-check sur LE test du comportement principal) → **UNE review déléguée en aveugle** → si gate+review verts : SUCCESS en 1 itération, pas de devil's advocate ni panel. Smoke offline obligatoire ; live seulement si runnable trivialement. Rapport court (statut + radar + fichiers + commit proposé), pas le template complet.

**Garde-fous JAMAIS sacrifiés en express** : séparation reviewer ≠ writer (auto-review interdite, même quand la mère code), gate objectif avant la review, preuves file:line, respect de `no_auto_commit`. **Bascule hors express** dès qu'un keyword sensible OU un overflow (`loc_real > loc_planned*1.5`) apparaît en cours → reprise en boucle standard. Logger `[tier] mode express (TRIVIAL) → review déléguée maintenue, reste allégé`.

### Escalade de modèle en cours de route (la mère peut changer d'avis)
Si un agent Sonnet rend un travail superficiel/faux, ou si une review est incertaine, relancer avec Opus ou un panel. L'estimation initiale n'est pas un contrat figé (cf. §4.6c et boucle adaptative). Logger `[iter N/max] escalade modèle : <rôle> Sonnet→Opus (raison)`.

## Étape 3 — Charger insights + lessons + espace de travail + journal

**Mémoire cross-runs (par projet)** : chercher `~/.claude/projects/<encoded-cwd>/memory/project_feature_loop_insights.md` (encoded-cwd = path absolu avec `/` → `-`).

Si existe : Read, parser, passer comme contexte aux prompts impl/review. Logger `[init] insights projet chargés (N patterns)`.

**Meta-leçons cross-projet** : lire `~/.claude/skill-memory/feature-loop-lessons.md` (hors dépôt) (créer avec un header minimal s'il n'existe pas). Ces leçons portent sur *comment piloter la boucle* (indépendant du projet) — les passer en tête des prompts impl/review (zone stable, cache-friendly). Logger `[init] lessons cross-projet chargées (N leçons)`.

### Espace de travail : in-place (défaut) ou worktree (`--worktree`)

Enregistrer `run_base_sha = $(git rev-parse HEAD)` AVANT toute modification (permet un reset propre au "jeter").

**Mode in-place (défaut)** — le repo principal EST l'espace de travail, les edits sont visibles live dans l'éditeur de l'user :
- Déterminer la branche de travail :
  - Si la spec vient d'une issue GitLab (`--issue` ou pont issue-mr, Étape 1) et qu'une branche liée `<issue>-*` existe (locale ou `origin/`) → `git fetch origin` puis checkout de cette branche : la convention projet (`<issue>-<slug>`, MR liée) prime sur `feature-loop/<slug>`. Logger `[init] in-place : branche issue <branch> checkout (base <sha-court>)`.
    - **Garde repo sale** : si le repo principal porte des modifs non commitées ÉTRANGÈRES à cette feature (WIP d'une autre session/branche), un checkout les embarque (ou échoue) et le diff/commit final mélangerait les deux travaux → proposer d'office le mode worktree. Worktree sur une branche EXISTANTE : `git worktree add .claude/worktrees/<slug> <branche-issue>` puis `EnterWorktree` avec `path:` (EnterWorktree seul crée une branche NEUVE depuis la base par défaut — il ne sait pas checkout une branche existante). Si la branche issue est déjà checkoutée dans le repo principal, l'y libérer d'abord en parquant le WIP sur une branche locale au même commit (`git switch -c wip-parking`, zéro modif du working tree) — jamais de stash du travail d'une autre session.
  - Si la branche courante est **protégée** (`develop`, `main`, `master`, ou la "Main branch" déclarée dans `<repo>/CLAUDE.md`) → `git checkout -b feature-loop/<slug>` depuis la courante. Logger `[init] in-place : branche <base> protégée → feature-loop/<slug> créée+checkout (base <sha-court>)`.
  - Sinon (déjà sur une feature branch) → rester dessus. Logger `[init] in-place sur branche courante <branch> (snapshots de mécanisme créés ici, squashables en fin de run)`.
- `$WORK` = racine du repo. Pas de `cd`.
- Rollback (4.8) = reset/revert vers le `pre_impl_sha` de l'iter, dans l'arbre vivant.

**Mode worktree (`--worktree`)** :
- `EnterWorktree(name: "feature-loop-<slug>")`. `$WORK` = path du worktree. Lancer ensuite le check symlink vendor/node_modules (Étape 0).
- Cas d'usage : deux runs en parallèle sur le même repo sans collision.

**Artefacts de run (les deux modes)** : `.feature-loop.json` (journal) et `feature-loop-report.md` (rapport) vivent sous `$WORK/.feature-loop/`. En in-place, s'assurer que `.feature-loop/` est ignoré par git (l'ajouter à `<repo>/.gitignore` s'il n'y est pas) pour ne PAS polluer les commits applicatifs. Le runs-log persistant, lui, vit hors arbre (Étape 6).

Créer `$WORK/.feature-loop/.feature-loop.json` :
```json
{
  "feature": "...",
  "started_at": "<ISO>",
  "scope": "...",
  "stack": { "lang": "...", "framework": "...", "tests": "...", "e2e": "...", "lint_plugins": ["..."] },
  "axes": ["..."],
  "threshold": 8,
  "max_iterations": 3,
  "difficulty_tier": "standard",
  "specialist_stack": "<ex: Shopify + CQRS/ES en Go | null si généraliste>",
  "judge_model": "sonnet",
  "paranoid": true,
  "paranoid_auto_triggered_by": ["auth", "token"],
  "best_iter_sha": null,
  "best_radar": null,
  "escalations": [],
  "work_mode": "in_place",
  "work_path": "<repo root ou worktree path>",
  "branch": "feature-loop/<slug> ou branche courante",
  "branch_created": true,
  "run_base_sha": "<sha HEAD avant toute modif>",
  "conventions": [{ "path": "...", "purpose": "..." }],
  "project_insights": "<contenu ou null>",
  "lessons_loaded": 5,
  "baseline": { "build": "pass", "lint_errors": 0, "tests_passed": 47 },
  "iterations": [],
  "rollback_counts": {},
  "persistent_critics": {},
  "final_status": "in_progress"
}
```

## Étape 4 — Boucle d'itération (max 3 par défaut, configurable via `--max-iter`)

### 4.1 Construire le prompt d'implémentation

**Structure cache-friendly** — placer en tête (stable) :
1. Description courte de la feature
2. Conventions extraites (paths + extraits)
3. Project insights (cross-runs)
4. Rubrique des axes (résumée)
5. Contraintes (CLAUDE.md)
6. Charte du code (bloc ci-dessous, verbatim)

**Charte du code** (les conventions du projet priment en cas de conflit) :
> - Le nom EST l'explication : teste-le sur 3 questions (pourquoi il existe / ce qu'il fait / comment l'utiliser) — si un commentaire décrit le *quoi*, renommer au lieu de commenter. Après une fusion/délégation qui fait gagner un nouvel appelant à une fonction existante, re-tester son nom contre TOUS ses appelants (pas seulement le premier) : un nom fidèle à un seul usage devient trompeur une fois partagé.
> - Fonction profonde : petite surface (un appel), beaucoup de travail caché. Découper sans exposer — le sur-découpage (helpers que l'appelant doit enchaîner, méthodes siamoises) est un défaut au même titre que la fonction-fleuve.
> - Commentaires, 3 genres seulement : le **pourquoi** (décision non évidente), l'**avertissement** (piège, ordre à ne pas casser), le **contrat** (ce que la fonction promet). Jamais de paraphrase ; 1 ligne par défaut ; dans le doute, ne pas commenter. Un commentaire long et pénible à écrire signale une abstraction ratée : reconcevoir plutôt que documenter.
> - Erreurs : quand la sémantique le permet, faire disparaître le cas d'erreur (borner, valeur par défaut, null object) au lieu d'imposer des checks à chaque appelant — tirer la complexité vers le bas, dans le module.
> - DRY = savoir, pas code : deux fragments identiques qui évolueront pour des raisons différentes ne sont PAS une duplication — ne pas les fusionner. Boussole ETC : « est-ce plus facile à changer après ? »
> - Boy-scout borné au périmètre : dans les zones touchées, supprimer les commentaires morts/paraphrases existants ; ne rien nettoyer hors scope.

Puis en queue (volatile) :
7. État spécifique à l'itération (critiques précédentes, notes_for_implementer, scores cibles)

Cette structure permet à Anthropic prompt cache de réutiliser le préambule sur toutes les itérations.

Itération 1 : prompt = préambule + exigences de base (tests, build pass, front → Playwright).

Itération N>1 : prompt = préambule + critiques classifiées du tour précédent + `notes_for_implementer` du tour précédent + contraintes anti-régression (axes ≥ N à préserver) + résumé des tentatives précédentes.

**Forçage notes_acknowledged au tour N>1** : ajouter explicitement au prompt :
> "Tu DOIS retourner un champ `notes_acknowledged` qui liste, pour CHAQUE note du tour précédent, comment tu l'as appliquée dans cette implémentation. Format : `[ { note: '...', applied_at: 'file:line', explanation: '...' } ]`. Si tu ne peux pas appliquer une note, dis-le explicitement avec la raison."

Si retour vide ou évasif (toutes `explanation` génériques) → re-prompt avec emphase. Si toujours évasif → flag dans le journal `notes_ignored: true`, à mentionner au reviewer.

Calculer `prompt_hash`. Si déjà vu → reformuler avec variation.

### 4.2 Snapshot pré-implem

Le snapshot permet le rollback/restore. Sa forme dépend de `no_auto_commit` (détecté au pre-flight) :

**Mode 1 — `no_auto_commit: false`** (défaut) : commit de mécanisme.
```bash
git add -A && git commit --allow-empty -m "feature-loop iter-N pre-impl" --no-verify
```
`--no-verify` est volontaire : un snapshot de mécanisme ne doit pas être bloqué par un pre-commit hook applicatif. Noter le SHA dans `pre_impl_sha`.

**Mode 2 — `no_auto_commit: true`** (règle user « pas d'auto-commit ») : ne pas polluer l'**historique de la branche de l'user**, mais le snapshot doit rester **sûr** (anti perte de données : capturer aussi l'untracked, survivre au `git gc`, marcher même sur arbre propre). On crée donc un commit-objet **sur une ref technique dédiée hors-branche** (jamais sur HEAD, jamais dans `git log` de la branche) — **commandes exactes : `reference/git-recipes.md` §4.2 Mode 2** (capture tracked+untracked via index temporaire → `commit-tree` → `update-ref refs/feature-loop/snap-iter-N`). Préféré à `git stash create` (non-ancré donc gc-able, rate l'untracked, chaîne vide sur arbre propre). La branche de l'user n'est PAS modifiée (HEAD, index réel, `git log` intacts).

Rollback (4.8) et restore best (5.0) sur ce snapshot = restaurer le tree de `pre_impl_sha`/`best_iter_sha` dans le working tree **après sauvegarde de l'état courant** (jamais de `checkout -- .` aveugle, cf. 5.0). Nettoyage en fin de run : `git for-each-ref refs/feature-loop/ | … git update-ref -d` (supprimer les refs techniques). Logger `[iter N/max] snapshot ref-technique (no_auto_commit) <sha-court>`.

### 4.3 Phase PLAN (Sonnet) + MINI-REVIEW (Haiku)

**Mode express (2bis)** : sauter 4.3 entièrement — pas de plan formel ni de mini-review Haiku (la mère implémente directement, 4.4a). Reprendre à 4.4.

Log : `[iter N/max] plan Sonnet...`

Sonnet retourne `{ plan, files, tests, risks, confidence }`.

Sanity check basique : paths cohérents, confidence ≥ 6.

**Puis mini-review Haiku** :
```
Agent(
  subagent_type: "general-purpose",
  model: "haiku",
  description: "feature-loop plan check iter N",
  prompt: <prompt court>
)
```

Le prompt :
- "Voici la feature demandée : <description en 1-2 phrases>"
- "Voici le plan proposé par un autre agent : <plan JSON>"
- "Voici 3 conventions du projet (extraits) : <conventions>"
- "Vérifie 3 points : (a) le plan adresse-t-il la feature complètement ? (b) les fichiers cités sont-ils les bons (pas de fichier random, paths cohérents) ? (c) le scope est-il minimal (pas de refacto opportuniste) ?"
- "Réponse JSON : `{ verdict: 'ok'|'concerns'|'reject', concerns: [...], blocking: boolean }`"

Si `verdict: 'reject'` ou `blocking: true` → renvoyer le plan à Sonnet avec les concerns Haiku, demander un plan révisé. Max 2 révisions de plan, après quoi escalation user.

Log : `[iter N/max] mini-review Haiku : <verdict>`.

Si OK → continuer.

### 4.4 Phase IMPLEMENT — agent A (code)

Log : `[iter N/max] impl code (agent <modèle>)...`

Modèle selon le tier (Étape 2bis) : Sonnet (STANDARD), Sonnet/Opus (COMPLEXE/SENSIBLE), mère (TRIVIAL seulement). Mêmes obligations qu'avant (build pass, conventions, scope minimal, STOP_NEED_CLARIFICATION possible).

**Persona + recherche** : si `specialist_stack` est défini (2bis), briefer l'agent A en « expert senior 10+ ans de <stack> » et lui passer la liste d'invariants/pièges d'archi à respecter (écrire idiomatique = moins de bugs à la source). En cas de doute sur une API/un flag/une version, une **recherche web ciblée** est permise (doc officielle d'abord) plutôt que deviner une signature — mais le gate objectif + les tests de B restent l'or

…(truncated)
