Mode d’emploi de l’usine
Quatre scénarios, du plus simple au plus outillé. Chacun donne les prérequis, les fichiers de configuration entiers, les commandes dans l’ordre, la sortie attendue, le coût — et quoi faire quand ça casse. Tout ce qui est écrit ici vient d’une pièce posée dans le livre.
Vous ne savez pas d’où viennent ces commandes ? Le schéma montre les phases et les fichiers en un regard, le glossaire donne les mots.
Une demande, sur votre poste, sans une seule clé
Le cas le plus simple : votre machine, votre abonnement Claude, aucun conteneur, aucune clé au jeton à provisionner. Une phrase entre, un cycle de développement complet en sort.
Quand l’utiliser
- Vous découvrez l’usine et vous voulez la voir tourner de bout en bout.
- Vous travaillez sur votre propre projet, seul, et personne d’autre ne lit vos secrets.
- Vous payez déjà un abonnement Claude et vous ne voulez pas ouvrir un compte de plus.
1 Ce qu’il faut avoir
plume-factoryà jour au moins jusqu’au chapitre 13 (le SDLC complet) et 17bis (le profil d’authentification).uv,just,gitetbunsur le poste —uv run adws/doctor.pyle vérifie pour vous.- Claude Code installé et connecté à votre abonnement (Pro, Max, Team ou Enterprise).
Un jeton d’un an remplace la session interactive — c’est lui que le port injectera dans chaque phase :
claude setup-token La commande ouvre votre navigateur, vous approuvez, elle imprime un jeton sk-ant-oat01-…. Copiez-le : il ne sera plus affiché.
2 Le fichier `.env` — deux lignes, jamais commitées
# le jeton d'un an de votre abonnement (claude setup-token)
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...
# Claude Code ne telephone que pour votre travail : ni telemetrie, ni mise a jour auto
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 Si ANTHROPIC_API_KEY traîne dans votre environnement, elle prend le pas sur l’abonnement et vous serez facturé au jeton. Retirez-la, ou sachez ce que vous faites.
3 Le roster — un seul profil, un seul harnais
Tous les agents passent par Claude Code, en route directe : le port n’ajoute aucun préfixe de passerelle, et le coffre .env ne laisse sortir que ce que le profil nomme.
auth:
claude-plan: # l'abonnement, expose derriere un jeton d'un an
regime: plan
route: direct # l'API Anthropic, par Claude Code
env: [CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC]
hosts: [api.anthropic.com] # ce que la porte du module 6 ouvrira, le jour venu
note: quota de l'abonnement, partage avec votre travail interactif
defaults:
harness: claude # l'adaptateur Claude Code du port (ch. 7)
auth: claude-plan # le profil de tous les agents (ch. 17bis)
model: anthropic/claude-opus-5 # l'adaptateur ne garde que l'id : claude-opus-5
thinking: medium
tools: [read, bash, grep, find, ls]
protected_files: [adws/adw_modules/, adws/adw_config/, adws/adw_*.py, PLAN.md]
data_dir: adws/adw_data Les cinq agents (scout, planner, builder, reviewer, documenter) restent ceux du chapitre 13 : seuls harness, auth et model changent. Sur un quota, un scout en frontier est un luxe — descendez-le d’un étage si votre fenêtre est serrée.
4 Les commandes, dans l’ordre
Une commande par ligne, depuis la racine de plume-factory. Les trois premières ne dépensent rien.
uv run adws/doctor.py
uv run --with pyyaml python -m adws.adw_modules.roster
just hello claude
uv run adws/adw_scout.py "Quelle commande lance les tests de apps/plume ?"
uv run adws/adw_sdlc.py "Ajoute un bouton pour effacer le texte de l'editeur de Plume" doctor : le tableau du préflight, tout en vert
roster : cinq agents, « auth : claude-plan (plan, route direct) », hôtes api.anthropic.com
hello : un aller-retour vers Claude Code, une sortie JSON validée
scout : une enveloppe de findings + « session … — cout … » (~1 min)
sdlc : 18 phases — spec posée, build (parfois une reprise « gates en echec »),
review approuvée, enveloppe du documenter, « SDLC complet » Puis regardez ce que le run a laissé :
just obs lanes
ls specs/ app_docs/
git diff --stat 5 Ce que vous avez obtenu
- Une spec dans
specs/: ce que le planner a décidé, avant d’écrire une ligne. - Du code testé dans
apps/plume: le builder a passé les gates, pas votre patience. - Un compte rendu dans
app_docs/: ce qui a changé et pourquoi. - Un run tracé dans
adws/adw_data/factory.db: chaque phase, sa durée, ses tentatives.
Rien n’est commité par l’usine : le git diff est à vous. C’est la règle du livre — un run vert n’est pas un run mergé.
Quand ça casse
claude a rendu 1 : … this workspace has not been trusted
Claude Code refuse un dépôt qu’aucun humain n’a approuvé. Lancez claude une fois à la racine de plume-factory, approuvez le dossier, ressortez — le réglage est enregistré dans ~/.claude.json.
Not logged in ou Invalid API key
Le jeton n’est pas arrivé jusqu’au nœud. Vérifiez que CLAUDE_CODE_OAUTH_TOKEN est bien dans .env et nommé dans env: du profil : sous profil, le coffre ferme tout le reste.
Le run affiche « Ignoring N permissions.allow entries » et rien d’autre
C’est un avertissement, pas la cause. Le port doit lire le JSON de la sortie standard avant stderr (_claude_evidence, chapitre A10) : avec la bonne version du harnais, le vrai motif s’affiche.
Des 429 ou « overloaded » en fin de journée
Vous touchez le quota de votre fenêtre. Le secours modèle du chapitre A10 ne vaut que pour pi ; côté Claude Code, descendez le scout et le documenter d’un étage, ou attendez la fenêtre suivante.
Deux régimes dans un même roster : abonnement et passerelle
Le jugement coûte cher, le repérage non. Ici, planner et reviewer restent sur votre abonnement Claude ; scout, builder et documenter passent par OpenRouter sur un modèle léger. Un roster, deux profils, deux harnais.
Quand l’utiliser
- Votre quota d’abonnement part trop vite sur des phases qui ne le méritent pas.
- Vous voulez comparer deux moteurs sur les mêmes phases, sur des runs réels.
- Vous voulez un chiffre en dollars sur ce que coûte chaque phase — un forfait ne le donne pas.
1 Le `.env` — les deux régimes côte à côte
# regime « plan », route directe : l'abonnement Claude (scenario 1)
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
# regime « key », route passerelle : une cle, tous les moteurs (ch. 15)
OPENROUTER_API_KEY=sk-or-... Le roster ne valide que les profils utilisés : tant qu’aucun agent ne référence gateway, OPENROUTER_API_KEY peut manquer sans faire échouer le chargement.
2 Le roster — un profil par agent
auth:
gateway: # une cle, tous les moteurs — revocable, plafonnable
regime: key
route: gateway # le port prefixe openrouter/ — ids du registre
env: [OPENROUTER_API_KEY]
hosts: [openrouter.ai]
claude-plan: # l'abonnement (scenario 1)
regime: plan
route: direct
env: [CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC]
hosts: [api.anthropic.com]
defaults:
harness: pi # le defaut redevient pi : polyglotte, par la passerelle
auth: gateway
model: deepseek/deepseek-v4-flash-0731 # leger — id verifie sur openrouter.ai le 2026-09-07
thinking: medium
tools: [read, bash, grep, find, ls]
protected_files: [adws/adw_modules/, adws/adw_config/, adws/adw_*.py, PLAN.md]
data_dir: adws/adw_data
agents:
- name: scout # herite : pi + passerelle + deepseek leger
purpose: Reperer ou vivent les choses dans le repo ; ne rien changer.
thinking: low
writes: []
tools: [read, bash, grep, find, ls, write]
- name: planner # le plan porte tout le run : il reste sur l'abonnement
purpose: Transformer une demande en plan que le builder implemente sans questions.
harness: claude
auth: claude-plan
model: anthropic/claude-opus-5
thinking: high
writes: [specs/]
tools: [read, bash, grep, find, ls, write]
- name: builder # le gros du volume : passerelle, modele leger
purpose: Implementer le plan, exactement ; declarer chaque fichier modifie.
thinking: high
tools: [read, bash, grep, find, ls, edit, write]
- name: reviewer # juger demande de reflechir : abonnement
purpose: Confirmer que ce qui est construit est ce qui etait demande ; ne rien changer.
harness: claude
auth: claude-plan
model: anthropic/claude-opus-5
thinking: high
writes: []
- name: documenter # rediger d'apres un diff : passerelle, modele leger
purpose: Rediger apres coup ce qui a change et pourquoi.
writes: [app_docs/]
tools: [read, bash, grep, find, ls, write] Deux vocabulaires cohabitent sans se mélanger : sous gateway, un identifiant du registre OpenRouter (deepseek/deepseek-v4-flash-0731) ; sous claude-plan, un identifiant que Claude Code accepte (l’adaptateur ne garde que ce qui suit le /). Les prix bougent : revérifiez l’identifiant et son étage le jour où vous l’écrivez.
3 Les commandes
uv run --with pyyaml python -m adws.adw_modules.roster
uv run adws/model_stack.py
uv run adws/adw_scout.py "Quelle commande lance les tests de apps/plume ?"
uv run adws/adw_sdlc.py "Ajoute un compteur de caracteres a l'editeur de Plume" roster : cinq agents, deux profils — « gateway (key, route gateway) » et
« claude-plan (plan, route direct) » ; hôtes openrouter.ai + api.anthropic.com
model_stack : « jauge : OK » — chaque moteur de la passerelle existe au registre,
prix du jour et étage à l'appui (les agents sous forfait ne sont pas jaugés)
scout : une enveloppe de findings, ~1 centime
sdlc : les 18 phases, en alternant les deux harnais sans que vous n'y pensiez Puis la question qui justifie tout ce chapitre — où va l’argent :
sqlite3 adws/adw_data/factory.db "SELECT name, ROUND(SUM(cost_usd),3) AS usd, COUNT(*) FROM phases GROUP BY name ORDER BY 2 DESC LIMIT 8;"
just obs lanes Les phases passées sur l’abonnement affichent 0,00 $ : elles ne sont pas gratuites, elles sont hors facture — payées par le quota. C’est exactement ce que dit la colonne, ni plus ni moins.
4 Faire varier le roster sans toucher au fichier
Chaque ADW accepte --config : les rosters des chapitres 16 et 17 sont des fichiers comme un autre, et la jauge se lance sur chacun.
uv run adws/model_stack.py --config adws/adw_config/eco.config.yaml
uv run adws/adw_sdlc.py "Ajoute un compteur de mots a Plume" --config adws/adw_config/eco.config.yaml
uv run adws/adw_bench.py "Ou vivent les tests de Plume ?" Quand ça casse
variable absente : OPENROUTER_API_KEY (profil gateway)
Le roster valide à zéro token, avant le moindre appel : un agent utilise gateway mais la variable n’est pas dans .env. Le message vous donne le nom exact — c’est le but.
Un modèle « no endpoints found » côté passerelle
L’identifiant a bougé, ou le modèle n’est plus routé. uv run adws/model_stack.py le dit avant le run ; corrigez la ligne du roster, pas le script.
Le builder est plus lent qu’avant
Un modèle léger fait plus de tours pour le même travail. Regardez just obs lanes : si le nombre de tentatives explose, l’étage est trop bas pour cette phase — remontez le builder, gardez le scout en bas.
Fan-out sur plusieurs rosters, puis moisson
La même demande, au même commit, confiée à trois rosters en parallèle — chacun dans sa boîte. Vous ne choisissez pas le meilleur moteur à l'avance : vous lisez les résultats, et vous gardez celui qui est vert et le moins cher.
Quand l’utiliser
- La tâche est ambiguë et vous ne savez pas quel étage de modèle suffit.
- Vous préparez un choix d’architecture et vous voulez trois propositions à comparer.
- Vous voulez chiffrer, une fois pour toutes, ce qu’un frontier apporte de plus qu’un workhorse sur *votre* code.
1 Avant de commencer
Le fan-out monte une boîte par bras : si vous n’avez jamais monté de boîte, faites d’abord le scénario 4, qui explique Podman, l’image et la porte. Ici, on suppose que just sandbox mount plume a déjà fonctionné une fois.
- Un arbre de travail propre : chaque bras part du même commit, celui de votre
HEAD. - Les trois rosters des chapitres 16 et 17 dans
adws/adw_config/. - De quoi payer trois runs en parallèle — les bras ne s’attendent pas.
2 Lancer les trois bras
Un nom de lot, une demande, puis autant de rosters que de bras. La commande rend la main tout de suite : les bras sont détachés.
uv run adws/sandbox_bestof.py --selftest
just sandbox fanout compteur "Ajoute un compteur de mots a Plume" adws/adw_config/eco.config.yaml adws/adw_config/open-weights.config.yaml adws/adw_config/factory.config.yaml sandbox_bestof OK — rapport lu, verdicts vert/rouge/en cours, classement
vert-le-moins-cher d'abord, lot relu
fanout : 3/3 bras lances, detaches, meme commit, meme demande
lot : compteur-<la-date>-<6 hex> 3 Attendre, puis moissonner
La moisson est une lecture : zéro token, non destructive, rejouable autant de fois que vous voulez pendant que les bras tournent.
just sandbox lots
just sandbox harvest compteur-<la-date>-<6 hex> un tableau, une ligne par bras : roster, verdict, coût, durée, phases, tentatives le relevé JSON du lot propose: <bras> — vert, le moins cher des verts puis la commande « discard » à lancer vous-même — jamais exécutée pour vous
Le classement est une règle, pas un avis : vert d’abord, puis le moins cher. Un bras rouge n’est jamais proposé, même s’il a coûté trois fois moins.
4 Garder un bras, jeter les autres
Rien ne se détruit sans votre --yes : la commande tourne à sec par défaut et vous montre ce qu’elle ferait.
just sandbox discard compteur-<la-date>-<6 hex> --keep <le-bras-garde>
just sandbox discard compteur-<la-date>-<6 hex> --keep <le-bras-garde> --yes
just sandbox cmd <le-bras-garde> git diff --stat
just sandbox teardown <le-bras-garde> Le diff du bras gardé se relit depuis l’extérieur avant le démontage — teardown détruit la boîte, et il n’y a pas de deuxième chance. C’est une décision, jamais un enchaînement automatique.
5 Ordre de grandeur
- Le lancement des trois bras : ~20 secondes chacun, moins d’un centime, sur Podman.
- Le travail : une dizaine de minutes, en parallèle — l’essentiel de la facture part sur le siège frontier du roster de production.
- Le tout, environ un dollar. Variante éco : ne passez que
eco.config.yamletopen-weights.config.yaml, pour quelques dizaines de centimes.
Quand ça casse
Un bras reste « en cours » beaucoup trop longtemps
just sandbox cmd lit son journal de l’extérieur, sans rien interrompre. Un agent muet, c’est presque toujours un modèle qui ne répond plus ou une gate qui boucle.
Tous les bras sont rouges
Ce n’est pas le moteur, c’est la demande. Relisez la spec produite par un bras : une demande ambiguë donne trois plans différents et trois échecs. Reformulez et relancez le lot.
La moisson propose un bras dont le diff ne vous plaît pas
Gardez-en un autre : --keep accepte n’importe quel bras. La moisson classe, elle ne décide pas — le dernier mot est humain, comme partout dans ce livre.
Une boîte jetable, du montage au démontage
Le hors-site minimal : un conteneur Podman sur un réseau interne, une porte qui n'ouvre que les hôtes nommés par vos profils, votre dépôt copié dedans, l'usine qui travaille — et rien qui touche votre poste.
Quand l’utiliser
- Vous laissez un agent écrire sans le surveiller, et vous ne voulez pas qu’il touche votre machine.
- Vous voulez que ce que l’usine emporte soit exactement ce que vous avez déclaré — ni plus, ni moins.
- Vous préparez un fan-out (scénario 3), qui monte une boîte par bras.
1 Installer Podman
Podman est sans démon et rootless : chaque commande est un processus ordinaire, sans sudo. Sous Linux, il vient de votre gestionnaire de paquets ; sous macOS et Windows, il pilote une petite machine virtuelle à créer une fois.
podman --version
podman machine init
podman machine start
uv run adws/sandbox_preflight.py Les deux commandes machine ne concernent que macOS et Windows. Le préflight vérifie rootless : oui, le backend réseau, l’image de base et l’outillage — lisez-le avant de monter quoi que ce soit.
2 Ce que la boîte emporte
La boîte n’hérite pas de votre .env. Elle emporte les variables des profils utilisés par le roster, écrites par l’entrée standard, et la porte n’ouvre que les hôtes de ces profils (plus l’outillage : pypi, npm). Avec le profil du scénario 1 :
auth:
claude-plan:
regime: plan
route: direct
env: [CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC]
hosts: [api.anthropic.com] # la porte n'ouvrira que celui-ci Un réglage qui doit atteindre le nœud passe par le profil : le coffre ne laisse rien d’autre sortir de .env. C’est l’erratum du chapitre 17bis reporté dans le cycle de vie.
3 Monter, travailler, observer
Les trois premières commandes tournent à sec, sans conteneur. La quatrième construit l’image du chapitre 21 (une fois, ~2 min).
uv run adws/sandbox_box.py
uv run adws/sandbox_gate.py --selftest
uv run adws/sandbox_lifecycle.py --selftest
just sandbox image
just sandbox mount plume mount déroule quatre phases — create → fill → setup → observe : provision : bun …, just …, claude approuve ok .env present (2 variable(s) de profil) ok porte : api.anthropic.com joignable ok porte : hote hors liste refuse gate verte puis l'id du run : plume-<la-date>-<6 hex>
La boîte est montée. Le travail se lance détaché, et s’observe de l’extérieur :
just sandbox execute plume-<la-date>-<6 hex> "Ajoute un compteur de caracteres a Plume"
just sandbox observe plume-<la-date>-<6 hex>
just sandbox cmd plume-<la-date>-<6 hex> tail -20 run.log
just sandbox egress plume-<la-date>-<6 hex> execute : adw_sdlc lance dans plume-box-… (pid 182) — detache
observe : le run en cours ou termine, les cinq derniers runs de factory.db,
et Plume servie sur 127.0.0.1 par la porte
egress : ce que la porte a laisse passer, et ce qu'elle a refuse 4 Démonter — une décision, jamais un enchaînement
just sandbox cmd plume-<la-date>-<6 hex> git diff --stat
just sandbox list
just sandbox teardown plume-<la-date>-<6 hex> Aucune phase ne détruit une boîte en cas d’échec : la preuve reste sur la boîte tant que vous n’avez pas lu. teardown relève la dépense, révoque ce qu’il y a à révoquer, puis détruit.
5 Et si vous travaillez par la passerelle
Avec le profil gateway, la boîte peut recevoir une clé jetable au lieu de la vôtre : frappée pour ce run, plafonnée en dollars, datée d’expiration, révoquée au démontage. Elle exige une clé de gestion, côté hôte seulement.
just sandbox mount plume --limit 5
just sandbox keys
just sandbox teardown plume-<la-date>-<6 hex> Un forfait ne se plafonne pas et ne se révoque pas par run : pour un travail sans humain devant l’écran, la clé au jeton reste le bon régime. Un abonnement dans une boîte, c’est pour votre propre travail, sur votre poste.
Quand ça casse
mount s’arrête sur « image absente »
Lancez just sandbox image : l’image de base du chapitre 21 se construit une fois, en deux minutes. Sur exe.dev, elle est inutile.
KO .env sans CLAUDE_CODE_OAUTH_TOKEN
La gate a raison : la variable n’est pas dans votre .env de l’hôte, ou pas nommée dans env: du profil. Les deux sont nécessaires — l’un porte la valeur, l’autre l’autorise à voyager.
KO api.anthropic.com injoignable depuis la boite
La porte ne connaît que les hôtes des profils utilisés. Ajoutez l’hôte au profil, remontez la boîte — ne désactivez pas la porte, c’est elle qui fait la valeur de la boîte.
Le scout de la gate sort en erreur, sans motif lisible
Vérifiez que la provision a bien affiché « claude approuve » : sans l’approbation du dépôt, Claude Code sort en code 1 dans une boîte où personne ne peut cliquer.