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.

1

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.

Durée 10 à 15 minCoût quota de l’abonnementPrérequis Claude Code + abonnement

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, git et bun sur le poste — uv run adws/doctor.py le 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

.env
# 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.

adws/adw_config/factory.config.yaml (extrait)
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"
Attendu
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.

2

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.

Durée 10 à 15 minCoût quelques dizaines de centimes + quotaPrérequis scénario 1 + compte OpenRouter

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

.env
# 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

adws/adw_config/factory.config.yaml (extrait)
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"
Attendu
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.

3

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.

Durée 10 à 20 min (parallèle)Coût ~1 $ pour trois brasPrérequis Podman + scénario 4

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
Attendu
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>
Attendu
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.yaml et open-weights.config.yaml, pour quelques dizaines de centimes.

Quand ça casse

Un bras reste « en cours » beaucoup trop longtemps

just sandbox cmd tail -20 run.log 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.

4

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.

Durée ~20 s de montageCoût la gate : moins d’un centimePrérequis Podman rootless

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 :

ce que le roster déclare, ce que la boîte reçoit
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
Attendu
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>
Attendu
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.