313 313
314Pour chaque session qui utilise git géré par Anthropic, le runner enregistre également une ligne `[runner:warn]` qui commence par `governed git: GIT_SSL_CAINFO is set` ou `governed git: GIT_SSL_NO_VERIFY is set`. La ligne dit ce que le runner a fait avec cette variable pour son propre git, pour git à l'intérieur de la session, et pour vos hooks de cycle de vie. Elle se termine par si vous devez changer quelque chose.314Pour chaque session qui utilise git géré par Anthropic, le runner enregistre également une ligne `[runner:warn]` qui commence par `governed git: GIT_SSL_CAINFO is set` ou `governed git: GIT_SSL_NO_VERIFY is set`. La ligne dit ce que le runner a fait avec cette variable pour son propre git, pour git à l'intérieur de la session, et pour vos hooks de cycle de vie. Elle se termine par si vous devez changer quelque chose.
315 315
316### Ajouter des dépôts github.com aux sessions de canal Claude Tag
317
318Si Claude ne parvient pas à ajouter un dépôt github.com à une session de canal Claude Tag, assurez-vous que le canal y a accès, même lorsque votre runner peut le cloner. Cet accès provient de paramètres sur GitHub et dans votre organisation Claude, et non des identifiants git de votre runner.
319
320**1. Installer la GitHub App Claude**
321
322Installez la [GitHub App Claude](https://github.com/apps/claude) sur l'organisation ou le compte GitHub propriétaire du dépôt, et incluez ce dépôt dans l'installation.
323
324**2. Lier l'organisation ou le compte GitHub**
325
326Dans [**Paramètres de l'organisation > Fournisseurs Git**](https://claude.ai/admin-settings/source-control), liez cette organisation ou ce compte à votre organisation Claude. Un Owner de votre organisation Claude qui est également propriétaire de l'organisation GitHub peut la lier. Consultez [Lier votre organisation GitHub](https://claude.com/docs/claude-tag/admins/configure-github#link-your-github-organization).
327
328**3. Accorder l'accès au dépôt**
329
330Dans les [paramètres d'administration de Claude Tag](https://claude.ai/admin-settings/claude-tag), accordez le dépôt au canal ou à une portée plus large qui inclut le canal : son espace de travail Slack ou l'ensemble de Slack. Une personne ayant le rôle Owner ou la permission Claude Tag Admin peut l'accorder. Un gestionnaire de canal peut également l'ajouter à son propre canal si son compte GitHub est administrateur du dépôt. Consultez [Accorder l'accès au dépôt](https://claude.com/docs/claude-tag/admins/configure-github#grant-repository-access).
331
332Démarrez ensuite un nouveau fil dans le canal et [nommez le dépôt](https://claude.com/docs/claude-tag/admins/configure-github#how-granted-repositories-reach-a-session) dans votre premier message.
333
334Si Claude ne parvient toujours pas à ajouter le dépôt, consultez [Si Claude ne peut pas atteindre un dépôt](https://claude.com/docs/claude-tag/admins/configure-github#if-claude-can%E2%80%99t-reach-a-repository) pour savoir où vérifier le lien GitHub et l'accès accordé au dépôt.
335
316### Réécrire les URL git pour les réseaux privés336### Réécrire les URL git pour les réseaux privés
317 337
318Les URL de dépôt arrivent du plan de contrôle en HTTPS, avec le nom d'hôte de votre hôte git ; pour GitHub Enterprise, c'est le nom d'hôte que vous avez configuré pour l'[intégration GitHub Enterprise](https://code.claude.com/docs/fr/github-enterprise-server.md) sur claude.ai. Deux flags répétables réécrivent ces URL avant le clone :338Les URL de dépôt arrivent du plan de contrôle en HTTPS, avec le nom d'hôte de votre hôte git ; pour GitHub Enterprise, c'est le nom d'hôte que vous avez configuré pour l'[intégration GitHub Enterprise](https://code.claude.com/docs/fr/github-enterprise-server.md) sur claude.ai. Deux flags répétables réécrivent ces URL avant le clone :
376 396
377Les builds et les tests constituent généralement la plus grande et la plus variable partie de la charge d'une session, donc exécutez un build représentatif de votre référentiel, mesurez son pic de CPU et de mémoire, et augmentez toute valeur de départ qui ne laisse pas de place au processus Claude Code en plus de ce pic.397Les builds et les tests constituent généralement la plus grande et la plus variable partie de la charge d'une session, donc exécutez un build représentatif de votre référentiel, mesurez son pic de CPU et de mémoire, et augmentez toute valeur de départ qui ne laisse pas de place au processus Claude Code en plus de ce pic.
378 398
379Le runner utilise `--capacity` pour limiter le nombre de sessions qu'il exécute à la fois. Il ne divise pas le CPU ou la mémoire entre elles, donc les sessions sur un runner partagent le CPU et la mémoire du conteneur. Pour limiter la part d'une session, appliquez les limites de votre [script wrapper](https://code.claude.com/docs/fr/self-hosted-environments-configuration.md#wrapper-scripts). Ce qu'il faut donner à un conteneur dépend donc du nombre de sessions qu'il dessert à la fois :399Le runner utilise `--capacity` pour limiter le nombre de sessions qu'il exécute à la fois. Il ne divise pas le CPU ou la mémoire entre elles, donc les sessions sur un runner partagent le CPU et la mémoire du conteneur. Pour éviter que la consommation de mémoire d'une session ne mette fin aux autres, exécutez une session par runner avec [sa propre limite de mémoire](#give-each-session-its-own-memory-limit).
380 400
381* **Une session par runner** : donnez à chaque conteneur les valeurs d'une session. Utilisez ce dimensionnement à `--capacity 1`, que la [section de durcissement](#harden-your-deployment) recommande, et pour les [runners à la demande](https://code.claude.com/docs/fr/self-hosted-environments-configuration.md#on-demand-runners), où vous définissez les valeurs sur la charge de travail que votre [hook `spawn-runner`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#the-spawn-runner-hook) soumet, comme un modèle de pod de Job Kubernetes.401* **Une session par runner** : donnez à chaque conteneur les valeurs d'une session. Utilisez ce dimensionnement à `--capacity 1`, que la [section de durcissement](#harden-your-deployment) recommande, et pour les [runners à la demande](https://code.claude.com/docs/fr/self-hosted-environments-configuration.md#on-demand-runners), où vous définissez les valeurs sur la charge de travail que votre [hook `spawn-runner`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#the-spawn-runner-hook) soumet, comme un modèle de pod de Job Kubernetes.
382* **Plusieurs sessions par runner** : à un `--capacity` supérieur à un, multipliez les valeurs d'une session par la capacité, car jusqu'à ce nombre de sessions peuvent s'exécuter dans le conteneur en même temps. Les recettes [Kubernetes](#kubernetes) et [Docker Compose](#docker-compose) exécutent `--capacity 4` sans limites de CPU ou de mémoire, donc ajoutez des limites dimensionnées pour la capacité que vous exécutez.402* **Plusieurs sessions par runner** : à un `--capacity` supérieur à un, multipliez les valeurs d'une session par la capacité, car jusqu'à ce nombre de sessions peuvent s'exécuter dans le conteneur en même temps. Les recettes [Kubernetes](#kubernetes) et [Docker Compose](#docker-compose) exécutent `--capacity 4` sans limites de CPU ou de mémoire, donc ajoutez des limites dimensionnées pour la capacité que vous exécutez.
403* **Plusieurs conteneurs de runner sur un même hôte** : dimensionnez l'hôte pour la somme des capacités des runners, car chaque runner ne décompte que ses propres sessions par rapport à `--capacity` :
404
405 * **Mémoire** : multipliez la mémoire d'une session par cette somme, puis ajoutez ce qu'utilisent les propres processus de l'hôte
406 * **CPU** : multipliez la demande de CPU d'une session par cette somme. Pour permettre à toutes les sessions de dépasser leur demande en même temps, multipliez plutôt sa limite de CPU.
407
408 Donnez à chaque runner son propre conteneur. Utilisez le [même chemin `--base-dir`](#keep-the-base-directory-and-capacity-identical-across-runners) dans chacun, sur un stockage qu'aucun autre conteneur de runner ne monte, afin que chaque runner dispose de [son propre répertoire de travail](#harden-your-deployment).
409
410### Donner à chaque session sa propre limite de mémoire
411
412Pour empêcher une session de mettre fin aux autres lorsque la mémoire vient à manquer, exécutez chaque runner à `--capacity 1` sous sa propre limite de mémoire, définie à la mémoire d'une session :
413
414* **Dans un conteneur** : exécutez un runner par conteneur, et définissez la limite de mémoire du conteneur, comme `limits.memory` sur Kubernetes, `--memory` sur Docker, ou `mem_limit` dans un fichier Compose. Gardez la somme des limites des conteneurs en dessous de la mémoire de l'hôte, car lorsque l'hôte lui-même manque de mémoire, le noyau peut tuer des processus n'importe où sur l'hôte.
415* **Sous systemd sans conteneur** : définissez `MemoryMax` sur l'unité du runner, et exécutez un runner par hôte, car les runners qui [utilisent le même `--base-dir`](#keep-the-base-directory-and-capacity-identical-across-runners) sur un même système de fichiers partagent les dépôts qui y sont clonés. Par exemple, à `--capacity 1` sans hook `checkout`, leurs sessions sur un même dépôt peuvent travailler simultanément dans le même checkout. Pour exécuter plusieurs runners sur un même hôte, donnez à chacun un conteneur.
416
417Lorsqu'un conteneur ou une unité atteint sa limite de mémoire, le noyau tue des processus uniquement à l'intérieur de celui-ci. Si vous définissez `--capacity` au-dessus de un, les sessions partagent une seule limite. Lorsqu'elles l'atteignent, le noyau ne choisit pas par session les processus à tuer, donc la consommation de mémoire d'une session peut mettre fin aux autres.
418
419Sur Kubernetes, vous ne pouvez séparer les sessions avec aucune des valeurs du paramètre `singleProcessOOMKill` du kubelet. Vous ne pouvez pas non plus plafonner la mémoire totale d'une session avec un `ulimit` de mémoire dans votre [script wrapper](https://code.claude.com/docs/fr/self-hosted-environments-configuration.md#wrapper-scripts). Le noyau applique une valeur comme `ulimit -v` à chaque processus séparément.
420
421### Laisser de la marge sous les limites de processus et de threads
422
423Pour dimensionner une limite de processus et de threads, mesurez ce dont une session a besoin et multipliez-le par le nombre de sessions qui partagent la limite. Lorsque ces sessions atteignent la limite, le noyau refuse les nouveaux processus et threads, de sorte que les commandes, les builds et les tests de n'importe laquelle d'entre elles peuvent échouer. L'appel refusé renvoie `EAGAIN`, dont le message standard est `Resource temporarily unavailable`.
424
425Supposons qu'une session atteigne un pic de 300 processus et threads, et que quatre sessions partagent la limite. Ensemble, elles ont besoin de 1 200, et avec une limite de 2 400, vous laissez de la marge pour des builds qui en nécessitent deux fois plus.
426
427#### Où définir chaque limite
428
429Chaque limite de ce tableau compte les threads ainsi que les processus, et `pids.max` est le fichier qui contient la limite d'un cgroup :
430
431| Limite | Où la définir | Ce qui la partage |
432| :- | :- | :- |
433| `pids.max` sur Kubernetes | `podPidsLimit` dans la configuration du kubelet des nœuds qui exécutent les runners. Vous ne pouvez pas la définir dans une spécification de Pod. | Les conteneurs d'un même Pod |
434| `pids.max` sur Docker | `--pids-limit`, ou `pids_limit` dans un fichier Compose | Un conteneur |
435| `pids.max` sous systemd | `TasksMax` sur l'unité du runner | Une unité |
436| `ulimit -u` | Le shell ou le service qui démarre le runner | Tout ce qui, sur l'hôte, s'exécute sous l'ID utilisateur du runner. Non appliquée pour root. |
437
438Lorsque vous ne définissez pas `TasksMax`, systemd applique `DefaultTasksMax`, qui vaut par défaut 15 % de la plus petite des valeurs parmi `kernel.pid_max`, `kernel.threads-max` et le `pids.max` du cgroup racine.
439
440#### Mesurer le pic d'une session
441
442Pour obtenir les chiffres de vos propres runners, mesurez une session sur son hôte, qui, sur Kubernetes, est le nœud :
443
444**1. Démarrer un runner seul**
445
446Démarrez un runner à `--capacity 1` sans rien d'autre dans son conteneur ou son unité systemd, seul sur son hôte et dans son environnement :
447
448* **Son hôte** : utilisez-en un qui n'exécute aucun autre runner. Vous pourrez alors repérer son processus à l'étape suivante.
449* **Son environnement** : faites-en le seul runner présent, par exemple dans un environnement que vous [créez pour le test](https://code.claude.com/docs/fr/self-hosted-environments-quickstart.md#set-up-manually). Les sessions sont acheminées vers un environnement et non vers un runner.
450
451Ne démarrez pas encore de session, car vous avez besoin du répertoire de l'étape suivante avant que le build ne commence.
452
453**2. Trouver le répertoire cgroup du runner**
454
455Sur l'hôte, et non à l'intérieur du conteneur, trouvez l'ID de processus du runner, par exemple avec `ps`. Lisez `/proc/<pid>/cgroup` pour cet ID :
456
457* **Une ligne contient `:pids:`** : l'hôte utilise cgroup v1, et le répertoire est `/sys/fs/cgroup/pids` suivi du texte qui suit `:pids:`
458* **La seule ligne commence par `0::`** : l'hôte utilise cgroup v2, et le répertoire est `/sys/fs/cgroup` suivi du texte qui suit `0::`
459
460**3. Exécuter un build représentatif et relever le pic**
461
462Faites passer une session par un build représentatif, et relevez le pic depuis le répertoire cgroup du runner :
463
464* **Le répertoire contient `pids.peak`** : lisez le fichier une fois le build terminé
465* **Le répertoire ne contient pas `pids.peak`** : lisez `pids.current` une fois par seconde pendant l'exécution du build, et conservez la valeur la plus élevée. Un pic survenant entre deux lectures passe inaperçu, considérez donc cette valeur comme une estimation basse.
466
467**4. Multiplier par les sessions qui partagent la limite**
468
469* **Le Pod, le conteneur ou l'unité d'un seul runner** : multipliez le pic par le `--capacity` du runner
470* **Une limite que plusieurs runners partagent** : multipliez le pic par la somme de leurs capacités
471
472Ajoutez ensuite le pic de tout ce qui partage également la limite, comme un autre conteneur du Pod.
473
474**5. Définir la limite au-dessus du résultat**
475
476Avec une limite plus élevée, vous laissez de la marge pour des builds plus lourds, et avec une limite plus basse, vous arrêtez plus tôt une fork bomb.
477
478#### Vérifier une limite `pids.max`
479
480* **Vérifier si `pids.max` a été atteinte** : recherchez dans le log du noyau de l'hôte, par exemple avec `dmesg`, une ligne contenant `cgroup: fork rejected by pids controller in` et le chemin d'un cgroup. Le noyau ne la consigne que la première fois qu'il refuse un processus ou un thread dans ce cgroup, recherchez donc dans l'ensemble du log.
481* **Voir la limite en vigueur** : lisez `pids.max` dans le répertoire cgroup du runner et dans chaque répertoire situé au-dessus. La valeur la plus basse est la limite en vigueur, et `max` signifie qu'il n'y a aucune limite à ce niveau.
383 482
384## Kubernetes483## Kubernetes
385 484
577 676
578Ce que le chemin de réutilisation fait et ne garantit pas :677Ce que le chemin de réutilisation fait et ne garantit pas :
579 678
580* **N'importe quelle forme de clone fonctionne** : un clone complet, peu profond, ou à branche unique au chemin est utilisé tel quel. Le runner ne passe jamais `--depth` lors de la récupération dans un clone existant, donc un pré-chauffage complet garde son historique complet et un peu profond reste peu profond. `CLAUDE_RUNNER_FETCH_DEPTH` (`full`, `0`, ou un nombre ; par défaut 50) contrôle uniquement le clone froid que le runner fait quand aucun clone n'existe encore.679* **Profondeur du clone** : créez le clone sans `--depth`, avec ou sans `--single-branch`. Le runner ne passe jamais `--depth` lorsqu'il récupère dans un clone doté d'un remote `origin`, même si vous définissez [`CLAUDE_RUNNER_FETCH_DEPTH`](https://code.claude.com/docs/fr/self-hosted-environments-reference#environment-variable-only-settings). Avec un clone complet, chaque session ne télécharge que les commits qui manquent au clone. Avec un clone peu profond, une session sur une branche qui a divergé avant le commit le plus ancien du clone télécharge tout l'historique de cette branche.
581* **Les changements suivis se réinitialisent, les fichiers non suivis persistent** : à `--capacity 1`, chaque session commence à partir d'une réinitialisation dur qui efface les modifications suivies de la session précédente, mais le runner ne lance jamais `git clean`, donc les fichiers non suivis des sessions antérieures du propriétaire verrouillé restent dans l'arbre.680* **Les changements suivis se réinitialisent, les fichiers non suivis persistent** : à `--capacity 1`, chaque session commence à partir d'une réinitialisation dur qui efface les modifications suivies de la session précédente, mais le runner ne lance jamais `git clean`, donc les fichiers non suivis des sessions antérieures du propriétaire verrouillé restent dans l'arbre.
582* **Les répertoires par session persistent aussi** : à côté du checkout, le runner crée des entrées par session sous `<base-dir>/_sessions/` pour chaque session qu'il exécute. Le répertoire de configuration Claude de la session contient une copie locale de la transcription de la conversation. À côté se trouvent les fichiers téléchargés de la session, quand la session en a. Le répertoire de session s'y trouve aussi : il contient tous les worktrees par session et les checkouts du hook `checkout` pendant que la session s'exécute, et il conserve tout ce que Claude a écrit dedans.681* **Les répertoires par session persistent aussi** : à côté du checkout, le runner crée des entrées par session sous `<base-dir>/_sessions/` pour chaque session qu'il exécute. Le répertoire de configuration Claude de la session contient une copie locale de la transcription de la conversation. À côté se trouvent les fichiers téléchargés de la session, quand la session en a. Le répertoire de session s'y trouve aussi : il contient tous les worktrees par session et les checkouts du hook `checkout` pendant que la session s'exécute, et il conserve tout ce que Claude a écrit dedans.
583 682
587* **Avec le proxy git, un pré-chauffage `reftable` perd ses refs** : quand le runner assainit `.git/` avant chaque session, il conserve `.git/refs/` et supprime le répertoire `.git/reftable/`. Un clone pré-chauffé au format de ref `reftable` de git perd ainsi ses refs ; pré-chauffez donc plutôt le clone au format de ref `files` de git.686* **Avec le proxy git, un pré-chauffage `reftable` perd ses refs** : quand le runner assainit `.git/` avant chaque session, il conserve `.git/refs/` et supprime le répertoire `.git/reftable/`. Un clone pré-chauffé au format de ref `reftable` de git perd ainsi ses refs ; pré-chauffez donc plutôt le clone au format de ref `files` de git.
588* **Les longs clones n'ont besoin d'aucune solution de contournement** : le runner limite chaque opération git avec un watchdog sans progrès de 120 secondes et un plafond dur de 30 minutes, pas un délai d'expiration plat, donc un clone froid lent qui continue de signaler le progrès se termine.687* **Les longs clones n'ont besoin d'aucune solution de contournement** : le runner limite chaque opération git avec un watchdog sans progrès de 120 secondes et un plafond dur de 30 minutes, pas un délai d'expiration plat, donc un clone froid lent qui continue de signaler le progrès se termine.
589 688
689### Confirmer que le runner utilise votre clone
690
691Pour confirmer que le runner utilise votre clone, démarrez-le avec [`--log-level debug`](https://code.claude.com/docs/fr/self-hosted-environments-reference#runner-cli-flags) et lancez une session sur le dépôt. Le runner consigne `Prefetched repo found at` et le chemin du clone. S'il consigne plutôt `No prefetched repo, fresh init at`, il n'a trouvé aucun clone utilisable au chemin indiqué dans cette ligne. Vérifiez que votre clone se trouve à ce chemin, qu'il appartient à l'utilisateur du runner et qu'il possède un remote nommé `origin`.
692
693Si le runner trouve votre clone et que les sessions démarrent toujours lentement, vérifiez si le clone est peu profond.
694
590## Épingler la version695## Épingler la version
591 696
592Par défaut, le processus enfant de chaque session exécute le binaire du runner lui-même, et le runner désactive la mise à jour automatique à l'intérieur des sessions qu'il génère. Avec ce comportement par défaut, chaque session exécute la version que vous avez installée sur l'hôte ou intégrée à l'image. Une mise à jour au niveau de l'hôte prend effet la prochaine fois que le runner démarre.697Par défaut, le processus enfant de chaque session exécute le binaire du runner lui-même, et le runner désactive la mise à jour automatique à l'intérieur des sessions qu'il génère. Avec ce comportement par défaut, chaque session exécute la version que vous avez installée sur l'hôte ou intégrée à l'image. Une mise à jour au niveau de l'hôte prend effet la prochaine fois que le runner démarre.
609* **Flotte fixe** : exécutez un ensemble statique de répliques runner et mettez à l'échelle sur les [métriques Prometheus](https://code.claude.com/docs/fr/self-hosted-environments-reference.md#prometheus-metrics) que chaque runner sert714* **Flotte fixe** : exécutez un ensemble statique de répliques runner et mettez à l'échelle sur les [métriques Prometheus](https://code.claude.com/docs/fr/self-hosted-environments-reference.md#prometheus-metrics) que chaque runner sert
610* **Runners à la demande** : exécutez la sous-commande `claude self-hosted-runner orchestrator`, qui interroge Anthropic pour les sessions en attente sans runner disponible et invoque votre hook `spawn-runner` pour en démarrer un par session. Consultez [Runners à la demande](https://code.claude.com/docs/fr/self-hosted-environments-configuration.md#on-demand-runners).715* **Runners à la demande** : exécutez la sous-commande `claude self-hosted-runner orchestrator`, qui interroge Anthropic pour les sessions en attente sans runner disponible et invoque votre hook `spawn-runner` pour en démarrer un par session. Consultez [Runners à la demande](https://code.claude.com/docs/fr/self-hosted-environments-configuration.md#on-demand-runners).
611 716
717### Libérer les sessions inactives sur une flotte fixe
718
719Si des sessions restent en attente sur une flotte fixe alors que chaque runner est en ligne, les sessions inactives peuvent en être la cause. Par défaut, une session conserve son emplacement une fois son tour terminé, et son runner reste verrouillé sur le [propriétaire](https://code.claude.com/docs/fr/self-hosted-environments#key-concepts) de cette session. Si la [tâche planifiée cloud](https://code.claude.com/docs/fr/routines.md) de votre équipe démarre une nouvelle session à chaque exécution, ses sessions inactives peuvent occuper tous les emplacements.
720
721Pour vérifier, lisez `claude_code_self_hosted_runner_session_idle_seconds` dans les [métriques Prometheus](https://code.claude.com/docs/fr/self-hosted-environments-reference.md#prometheus-metrics) de chaque runner. Chaque session détenue par un runner possède sa propre série : `0` tant que la session est occupée, sinon le nombre de secondes écoulées depuis qu'elle est devenue inactive. Choisissez la durée d'inactivité que vous autorisez : plus longue que les pauses que les gens font en cours de tâche, et suffisamment courte pour que chaque exécution soit libérée avant le démarrage de la suivante de la tâche planifiée. Comparez ensuite les séries à cette durée.
722
723* **Séries au-dessus de cette durée, comme `1800` pour 30 minutes** : des sessions inactives occupent ces emplacements, définissez donc `--release-idle-session-min`
724* **Toutes les séries en dessous** : aucune session n'est restée inactive aussi longtemps. Ajoutez des runners, ou définissez [`--kill-session-after-min`](https://code.claude.com/docs/fr/self-hosted-environments-reference#runner-cli-flags) pour les [sessions qui ne deviennent jamais inactives](#some-sessions-don’t-count-as-idle).
725* **Un runner sans aucune série** : aucune session ne l'occupe. Consultez **Sessions stay queued** sous [Dépannage](#troubleshooting).
726* **Un runner dont les métriques ne répondent pas** : son processus s'est peut-être arrêté. Consultez **An environment that looks healthy** sous [Quand le runner s'arrête](#when-the-runner-exits).
727
728Pour libérer les sessions inactives, démarrez chaque runner avec [`--release-idle-session-min`](https://code.claude.com/docs/fr/self-hosted-environments-reference#runner-cli-flags) suivi de cette durée en minutes, par exemple `--release-idle-session-min 30`. Le runner libère alors toute session qui reste inactive aussi longtemps, qu'elle ait été démarrée par une tâche planifiée ou par une personne.
729
730* **Travail non poussé** : une session libérée reprend à son prochain message, sans le travail qu'elle n'avait pas poussé. Pour conserver ses commits, consultez [Les sessions reprises perdent le travail non poussé](#additional-limitations).
731* **Arrêt du runner** : un runner qui libère sa dernière session entame un [arrêt normal](#when-the-runner-exits). Faites-le redémarrer par votre superviseur sur un système de fichiers vierge, comme le décrit [Renforcer votre déploiement](#harden-your-deployment).
732* **Sessions qui ne deviennent jamais inactives** : définissez également [`--kill-session-after-min`](https://code.claude.com/docs/fr/self-hosted-environments-reference#runner-cli-flags), car [certaines sessions ne comptent pas comme inactives](#some-sessions-don’t-count-as-idle)
733
612## Problèmes connus et limitations734## Problèmes connus et limitations
613 735
614Voici les limitations de cette version, avec des solutions de contournement où l'une existe.736Voici les limitations de cette version, avec des solutions de contournement où l'une existe.
642### Limitations supplémentaires764### Limitations supplémentaires
643 765
644* **Les sessions reprises perdent le travail non poussé** : un nouveau runner clone à nouveau le dépôt à partir de sa branche de démarrage, donc le travail que la session n'avait pas poussé est perdu.766* **Les sessions reprises perdent le travail non poussé** : un nouveau runner clone à nouveau le dépôt à partir de sa branche de démarrage, donc le travail que la session n'avait pas poussé est perdu.
645 * **Pour conserver le travail commité** : définissez [`--push-outcome-on-release`](https://code.claude.com/docs/fr/self-hosted-environments-reference#runner-cli-flags) sur chaque runner de l'environnement, car un runner sans ce flag reprend la session à partir de sa branche de démarrage. Un runner avec ce flag effectue, dans la mesure du possible, un push des branches de résultat de la session avant de la libérer, et la session reprise démarre à partir de ces commits. Le push utilise les propres identifiants git de l'hôte runner, y compris sur un runner qui utilise [git géré par Anthropic](#use-the-anthropic-git-proxy). Les modifications non commitées sont toujours perdues.767 * **Pour conserver le travail commité** : définissez [`--push-outcome-on-release`](https://code.claude.com/docs/fr/self-hosted-environments-reference#runner-cli-flags) sur chaque runner de l'environnement, car un runner sans ce flag reprend la session à partir de sa branche de démarrage. Un runner avec ce flag effectue, dans la mesure du possible, un push des branches de résultat de la session avant de la libérer, et la session reprise démarre à partir de ces commits. Le push utilise les propres identifiants git de l'hôte runner, y compris sur un runner qui utilise [git géré par Anthropic](#use-the-anthropic-git-proxy).
646 * **Avec un hook `checkout`** : les dépôts extraits via un [hook de cycle de vie `checkout`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#checkout) ne font pas l'objet d'un push. Créez plutôt un instantané de ces dépôts à partir du [hook `post-session`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#post-session).768 * **Avec un hook `checkout`** : les dépôts extraits via un [hook de cycle de vie `checkout`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#checkout) ne font pas l'objet d'un push. Créez plutôt un instantané de ces dépôts à partir du [hook `post-session`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#post-session).
647 * **Avant d'activer le flag** : limitez qui peut pousser vers les refs `claude/*` sur le dépôt distant source. À la reprise, le runner récupère la branche précédemment poussée sans vérifier qui l'a poussée.769 * **Avant d'activer le flag** : limitez qui peut pousser vers les refs `claude/*` sur le dépôt distant source. À la reprise, le runner récupère la branche précédemment poussée sans vérifier qui l'a poussée.
770 * **Les modifications non commitées sont toujours perdues** : pour demander à Claude de créer un commit et de pousser lorsqu'il a fini de répondre, consultez [Demander aux sessions de pousser leur travail](https://code.claude.com/docs/fr/self-hosted-environments-configuration.md#prompt-sessions-to-push-their-work)
648* **Le clonage d'un dépôt ajouté en milieu de session peut échouer** : Claude le clone avec `git clone` via HTTPS. Sur un runner sans [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), le clonage échoue avec une erreur d'authentification git si rien sur l'hôte ne peut lire le dépôt. Dans la mesure du possible, sélectionnez chaque dépôt dont la session a besoin quand vous la créez.771* **Le clonage d'un dépôt ajouté en milieu de session peut échouer** : Claude le clone avec `git clone` via HTTPS. Sur un runner sans [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), le clonage échoue avec une erreur d'authentification git si rien sur l'hôte ne peut lire le dépôt. Dans la mesure du possible, sélectionnez chaque dépôt dont la session a besoin quand vous la créez.
649* **Certains connecteurs n'apparaissent pas dans les sessions auto-hébergées** : un connecteur que vous n'avez pas encore connecté dans les paramètres claude.ai n'est pas listé dans une session auto-hébergée, et la session ne vous invitera pas à le connecter. Connectez-le d'abord dans les paramètres, puis démarrez une session fraîche. Ajouter un connecteur à une session déjà en cours d'exécution ne rend pas non plus ses outils disponibles à Claude ; démarrez une session fraîche pour récupérer un connecteur nouvellement ajouté.772* **Certains connecteurs n'apparaissent pas dans les sessions auto-hébergées** : un connecteur que vous n'avez pas encore connecté dans les paramètres claude.ai n'est pas listé dans une session auto-hébergée, et la session ne vous invitera pas à le connecter. Connectez-le d'abord dans les paramètres, puis démarrez une session fraîche. Ajouter un connecteur à une session déjà en cours d'exécution ne rend pas non plus ses outils disponibles à Claude ; démarrez une session fraîche pour récupérer un connecteur nouvellement ajouté.
650* **Quand quelqu'un archive ou supprime une session, ou libère son runner depuis le menu de la session** : ne comptez pas sur le push de [`--push-outcome-on-release`](https://code.claude.com/docs/fr/self-hosted-environments-reference#runner-cli-flags). Pour sauvegarder les commits dans ces cas, poussez `HEAD` vers une branche de secours à partir d'un [hook `post-session`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#post-session). Si vous partez de l'exemple qui s'y trouve, faites-le pousser même lorsqu'il n'y a aucune modification non commitée, car il ne pousse qu'après un commit d'instantané.773* **Quand quelqu'un archive ou supprime une session, ou libère son runner depuis le menu de la session** : ne comptez pas sur le push de [`--push-outcome-on-release`](https://code.claude.com/docs/fr/self-hosted-environments-reference#runner-cli-flags). Pour sauvegarder les commits dans ces cas, poussez `HEAD` vers une branche de secours à partir d'un [hook `post-session`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#post-session). Si vous partez de l'exemple qui s'y trouve, faites-le pousser même lorsqu'il n'y a aucune modification non commitée, car il ne pousse qu'après un commit d'instantané.
684* **Les sessions échouent immédiatement après leur prise en charge** : ouvrez la session dans claude.ai/code pour voir l'erreur. Si l'erreur indique que git n'a pas pu s'authentifier, vérifiez les [identifiants git](#configure-git) du runner. Sur un runner démarré avec `--use-anthropic-git-proxy`, consultez [Lorsque les sessions ne démarrent pas sur un runner avec le proxy git](#when-anthropic-doesnt-serve-a-session).807* **Les sessions échouent immédiatement après leur prise en charge** : ouvrez la session dans claude.ai/code pour voir l'erreur. Si l'erreur indique que git n'a pas pu s'authentifier, vérifiez les [identifiants git](#configure-git) du runner. Sur un runner démarré avec `--use-anthropic-git-proxy`, consultez [Lorsque les sessions ne démarrent pas sur un runner avec le proxy git](#when-anthropic-doesnt-serve-a-session).
685 808
686 Un répertoire de base non accessible en écriture arrête le runner au démarrage au lieu de faire échouer les sessions. Consultez l'entrée **Le runner s'arrête au démarrage avec `cannot create or write to base directory`** de cette liste.809 Un répertoire de base non accessible en écriture arrête le runner au démarrage au lieu de faire échouer les sessions. Consultez l'entrée **Le runner s'arrête au démarrage avec `cannot create or write to base directory`** de cette liste.
810* **Une session est prise en charge, mais Claude ne répond jamais à son premier message** : si vos runners joignent `api.anthropic.com` via un proxy, configurez-le pour qu'il transmette la réponse `/worker/events/stream` de la session au fur et à mesure de son arrivée. Augmenter les délais d'expiration du runner n'aide pas. Pour confirmer la cause dans le log du runner, consultez [Une session est prise en charge, mais Claude ne répond jamais à son premier message](#when-a-sessions-first-message-is-never-answered).
687* **Les sessions ne démarrent pas sur un runner ayant défini `--use-anthropic-git-proxy`** : cherchez dans le log du runner `access denied by the git proxy`, ou une erreur git mentionnant une adresse `api.anthropic.com` contenant `/git_proxy/`. Pour déterminer si Anthropic a servi la session et corriger la cause, consultez [Lorsque les sessions ne démarrent pas sur un runner avec le proxy git](#when-anthropic-doesnt-serve-a-session).811* **Les sessions ne démarrent pas sur un runner ayant défini `--use-anthropic-git-proxy`** : cherchez dans le log du runner `access denied by the git proxy`, ou une erreur git mentionnant une adresse `api.anthropic.com` contenant `/git_proxy/`. Pour déterminer si Anthropic a servi la session et corriger la cause, consultez [Lorsque les sessions ne démarrent pas sur un runner avec le proxy git](#when-anthropic-doesnt-serve-a-session).
688* **Les sessions ne peuvent pas accéder au réseau via un proxy de sortie avec authentification** : lorsque la source que vous avez définie avec [`--proxy-authorization-command` ou `--proxy-authorization-file`](#authenticate-to-an-egress-proxy) échoue, expire au bout de 30 secondes ou renvoie une valeur vide, le runner répond `502 Bad Gateway` à cette connexion et journalise la raison. Le runner masque le stderr de la commande dans ce log et ne journalise jamais la valeur de l'en-tête. Avec `--proxy-authorization-command`, exécutez vous-même la commande sur l'hôte pour vérifier qu'elle affiche la valeur complète de l'en-tête sur stdout. Si le runner s'arrête plutôt au démarrage avec `could not start the proxy-authorization listener`, il n'a pas pu ouvrir son listener de loopback.812* **Les sessions ne peuvent pas accéder au réseau via un proxy de sortie avec authentification** : lorsque la source que vous avez définie avec [`--proxy-authorization-command` ou `--proxy-authorization-file`](#authenticate-to-an-egress-proxy) échoue, expire au bout de 30 secondes ou renvoie une valeur vide, le runner répond `502 Bad Gateway` à cette connexion et journalise la raison. Le runner masque le stderr de la commande dans ce log et ne journalise jamais la valeur de l'en-tête. Avec `--proxy-authorization-command`, exécutez vous-même la commande sur l'hôte pour vérifier qu'elle affiche la valeur complète de l'en-tête sur stdout. Si le runner s'arrête plutôt au démarrage avec `could not start the proxy-authorization listener`, il n'a pas pu ouvrir son listener de loopback.
689* **Le runner journalise des lignes `Poll failed` contenant `rejecting the malformed poll response`** : le runner a reçu une réponse d'interrogation de travail dont le corps n'est pas le JSON attendu de la file d'attente. L'une des causes possibles est un proxy d'interception ou un portail captif qui répond avec sa propre page. Si le runner se connecte via un proxy, configurez le proxy pour qu'il transmette sans modification les réponses de `api.anthropic.com`.813* **Le runner journalise des lignes `Poll failed` contenant `rejecting the malformed poll response`** : le runner a reçu une réponse d'interrogation de travail dont le corps n'est pas le JSON attendu de la file d'attente. L'une des causes possibles est un proxy d'interception ou un portail captif qui répond avec sa propre page. Si le runner se connecte via un proxy, configurez le proxy pour qu'il transmette sans modification les réponses de `api.anthropic.com`.
708* **Une session échoue au démarrage avec `This runner couldn't read`** : le runner a demandé un dépôt à l'hôte git avec ses [identifiants git](#configure-git) et a reçu une réponse indiquant que le dépôt est introuvable. Le dépôt n'existe pas, ou ces identifiants ne permettent pas d'atteindre le dépôt.832* **Une session échoue au démarrage avec `This runner couldn't read`** : le runner a demandé un dépôt à l'hôte git avec ses [identifiants git](#configure-git) et a reçu une réponse indiquant que le dépôt est introuvable. Le dépôt n'existe pas, ou ces identifiants ne permettent pas d'atteindre le dépôt.
709 * **Si vous administrez le runner** : accordez à ses identifiants git un accès en lecture au dépôt, puis démarrez une nouvelle session.833 * **Si vous administrez le runner** : accordez à ses identifiants git un accès en lecture au dépôt, puis démarrez une nouvelle session.
710 * **Si vous avez démarré la session** : si le dépôt n'existe pas, démarrez une nouvelle session avec le bon dépôt. Si le runner ne peut pas atteindre le dépôt, demandez à l'administrateur du runner d'accorder l'accès, puis démarrez une nouvelle session.834 * **Si vous avez démarré la session** : si le dépôt n'existe pas, démarrez une nouvelle session avec le bon dépôt. Si le runner ne peut pas atteindre le dépôt, demandez à l'administrateur du runner d'accorder l'accès, puis démarrez une nouvelle session.
711* **Les sessions mettent plusieurs minutes à démarrer** : le clone initial en est généralement la cause principale. Surveillez la [métrique](https://code.claude.com/docs/fr/self-hosted-environments-reference.md#prometheus-metrics) `claude_code_self_hosted_runner_session_init_duration_seconds` pour le confirmer, et réduisez la durée du clone avec un [checkout préchauffé](#reuse-a-pre-warmed-checkout) ou une valeur de `CLAUDE_RUNNER_FETCH_DEPTH` plus petite.835* **Les sessions mettent plusieurs minutes à démarrer ou à reprendre** : l'une des causes est un clone initial. Sur une flotte qui démarre chaque runner sur un disque vide, sans [hook `checkout`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#checkout), le runner clone à nouveau les dépôts de la session avant de démarrer Claude Code. Une session qui reprend sur un disque vide attend de nouveau ce même clone.
836
837 Pour éviter le clone initial, fournissez un [checkout préchauffé](#reuse-a-pre-warmed-checkout). Si vous en fournissez déjà un, [vérifiez que le runner l'utilise](#confirm-that-the-runner-uses-your-clone). Lorsque le runner effectue lui-même le clone et s'arrête après une seule session, réduisez [`CLAUDE_RUNNER_FETCH_DEPTH`](https://code.claude.com/docs/fr/self-hosted-environments-reference#environment-variable-only-settings) par rapport à sa valeur par défaut de 50 commits afin de récupérer moins d'historique.
838
839 Si vous utilisez un hook, il s'exécute à la place du clone du runner ; examinez donc plutôt ce que fait votre hook. Surveillez la [métrique](https://code.claude.com/docs/fr/self-hosted-environments-reference.md#prometheus-metrics) `claude_code_self_hosted_runner_session_init_duration_seconds` pour connaître la durée des démarrages.
712* **Les tours échouent avec une erreur 401** : lorsqu'un tour se termine par une erreur 401 ou 403 de l'API Anthropic, le runner récupère un nouveau [`CLAUDE_CODE_OAUTH_TOKEN`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#wrapper-scripts) auprès d'Anthropic et le transmet à la session. Le tour échoué n'est pas réessayé. Ce jeton a une durée de vie courte, et le runner le renouvelle via le stdin de la session.840* **Les tours échouent avec une erreur 401** : lorsqu'un tour se termine par une erreur 401 ou 403 de l'API Anthropic, le runner récupère un nouveau [`CLAUDE_CODE_OAUTH_TOKEN`](https://code.claude.com/docs/fr/self-hosted-environments-configuration#wrapper-scripts) auprès d'Anthropic et le transmet à la session. Le tour échoué n'est pas réessayé. Ce jeton a une durée de vie courte, et le runner le renouvelle via le stdin de la session.
713 841
714 Lorsqu'une récupération échoue, le runner journalise une ligne `inference_token refresh failed` indiquant quand il réessaiera, et il continue de réessayer tant que la session s'exécute.842 Lorsqu'une récupération échoue, le runner journalise une ligne `inference_token refresh failed` indiquant quand il réessaiera, et il continue de réessayer tant que la session s'exécute.
788 916
789Pour un diagnostic guidé sur l'hôte du runner, exécutez la [sous-commande doctor](#troubleshooting).917Pour un diagnostic guidé sur l'hôte du runner, exécutez la [sous-commande doctor](#troubleshooting).
790 918
919### Une session est prise en charge, mais Claude ne répond jamais à son premier message
920
921Si un runner prend en charge une session mais que Claude ne répond jamais au premier message, et que vos runners joignent `api.anthropic.com` via un proxy, le proxy peut en être la cause. Anthropic envoie à une session ses messages via une unique réponse en streaming qui reste ouverte. Si votre proxy la met en tampon, ou y met fin avant d'avoir transmis quoi que ce soit, le premier message n'arrive jamais. Les autres requêtes de la session réussissent quand même.
922
923Pour corriger le problème, modifiez la façon dont le proxy traite cette requête :
924
925```text theme={null}
926GET https://api.anthropic.com/v1/code/sessions/<session-id>/worker/events/stream
927```
928
929* **Ne mettez pas la réponse en tampon** : transmettez-la au fur et à mesure de son arrivée. Son type de contenu est `text/event-stream`.
930* **Laissez la réponse ouverte** : elle peut rester ouverte jusqu'à une heure. Si le proxy a un délai d'inactivité, maintenez-le bien au-dessus de 15 secondes, car Anthropic envoie par défaut un keep-alive au moins aussi souvent.
931
932Démarrez ensuite une nouvelle session et vérifiez que Claude répond. Augmenter les délais d'expiration du runner n'aide pas : vous attendez seulement plus longtemps avant que le runner n'arrête la session.
933
934#### Confirmer la cause dans le log du runner
935
936Ces étapes concernent les runners sur Claude Code v2.1.287 ou ultérieure, avec le niveau par défaut `--log-level info`. Les runners plus anciens journalisent ces lignes plus tard, voire pas du tout.
937
9381. Attendez que [`--startup-timeout-min`](https://code.claude.com/docs/fr/self-hosted-environments-reference#runner-cli-flags) soit écoulé, 15 minutes par défaut. Le runner arrête alors la session et journalise une ligne qui contient `no child output for` et se termine par `— releasing`.
9392. Cherchez `SDKStartup: SSE` dans le log du runner. Ces lignes suivent une ligne `finished: interrupted`, quelques lignes après celle de l'étape 1. La plupart n'ont pas d'horodatage ; un collecteur de logs peut donc les afficher ailleurs.
9403. Trouvez la ligne dans ce tableau :
941
942| La ligne de log contient | Que faire |
943| :- | :- |
944| `SSE connect failed (no response in 30s)` ou `SSE liveness timeout` | Vérifiez que le proxy transmet la requête, qu'il transmet les en-têtes et les données de la réponse au fur et à mesure de leur arrivée, et qu'il ne coupe pas la connexion |
945| `SSE connect failed (HTTP` et un code de statut | Consultez le log du proxy lui-même. Si le proxy a répondu lui-même à la requête, laissez la requête le traverser. |
946| `SSE connect failed (` et tout autre texte | Le texte entre parenthèses est l'erreur. Cherchez la même connexion dans le log du proxy lui-même. |
947| `SSE stream ended by server`, encore et encore | Vérifiez la limite de durée du proxy pour les réponses de longue durée. Une seule ligne de ce type est normale. |
948| `SSE reconnected after` | Cherchez l'une des autres lignes après celle-ci. Cette ligne ne signifie pas que quoi que ce soit a atteint la session. |
949
950Si le log ne contient aucune de ces lignes, ou si le log du proxy ne montre aucune anomalie, [signalez un problème](#report-an-issue).
951
952#### Empêcher la session de réessayer
953
954La session conserve le message en attente, et un runner capable de servir le propriétaire de la session la reprend en charge. Tant que vous n'avez pas modifié le proxy, la session attend de la même manière à chaque fois. Pour y mettre fin, archivez la session dans claude.ai/code.
955
791## Prochaines étapes956## Prochaines étapes
792 957
793* [Personnaliser les sessions](https://code.claude.com/docs/fr/self-hosted-environments-configuration.md) : scripts wrapper, hooks de cycle de vie, runners à la demande, serveurs MCP, et permissions958* [Personnaliser les sessions](https://code.claude.com/docs/fr/self-hosted-environments-configuration.md) : scripts wrapper, hooks de cycle de vie, runners à la demande, serveurs MCP, et permissions