Kernel et pods
Alpha est un orchestrateur fin. Les pods sont des instances d'un agent CLI chargées d'une mission isolable. Cette page décrit le contrat de spawn, le cycle de vie, et les garde-fous de l'essaim.
Alpha : un orchestrateur fin, pas un travailleur
Le pod Alpha est l’instance d’un agent CLI qui reçoit le prompt utilisateur initial. Son rôle n’est pas d’exécuter le travail lui-même, mais de :
- Classifier l’intention via le routeur (
on_user_prompt.py). - Décomposer si nécessaire en hiérarchie
PROJ → JOB → TASK → SUB. - Spawner les pods workers spécialisés (researcher, coder, devops, analyst).
- Observer leur progression via le bus IPC et le feed entités.
- Synthétiser les résultats sans polluer son propre contexte.
Cette séparation existe pour une raison simple : la fenêtre de contexte d’Alpha est précieuse. Charger 200 kB de logs de scraping dans Alpha pour extraire une seule URL gaspille un budget que nous voulons réserver à la décision et à la synthèse.
Le pod : double récursivité
Un pod est une autre instance de l’agent CLI chargée avec ses propres primitives. Cela crée ce que nous appelons la double récursivité : Claude Code orchestrant l’agent CLI.
Chaque pod a :
- sa propre fenêtre de contexte (vide au départ) ;
- son propre
CLAUDE.md(souvent un sous-set ciblé) ; - ses propres hooks (lifecycle isolé) ;
- son propre set de tools / MCP servers ;
- son propre tmux pour permettre l’observabilité humaine.
Le backend (Kubernetes pod, local tmux, VPS) est un détail d’implémentation. L’invariant est : un pod = une instance d’un agent CLI = une mission.
Quand spawner un pod
Spawner un pod a un coût : initialisation, tokens de boot, overhead d’orchestration. La décision n’est pas par défaut. Spawner si au moins une des conditions ci-dessous est vraie :
- Le scope est isolable — objectif clair, contrat de sortie défini.
- Le profil d’outils diffère — le pod a besoin d’outils/MCP qu’Alpha n’utilise pas.
- La fenêtre de contexte bénéficie d’un reset — tâche indépendante du contexte Alpha.
- Parallélisation — plusieurs tâches indépendantes peuvent tourner en même temps.
- Récursivité utile — le pod doit lui-même orchestrer des sous-tâches.
À l’inverse, ne pas spawner si :
- La tâche est encore ambiguë — clarifier d’abord.
- Le contrat de sortie n’est pas défini (format attendu, critères de validation).
- Le coût d’orchestration dépasse la complexité de la tâche.
- Deux pods récupéreraient le même contexte — consolider en un seul.
- La tâche est triviale (< 5 outils inline).
Le contrat de spawn
Chaque pod doit recevoir, au moment du spawn, sept éléments :
| Champ | Contenu |
|---|---|
| Mission | Description de la tâche en une à trois phrases |
| Boundaries | Scope, contraintes, ce que le pod ne doit pas faire |
| Context minimal | Lineage parent + résultats des siblings via context_builder |
| Tools / MCP | Agent type infère le set : researcher, coder, devops, analyst, orchestrator |
| Output contract | Format JSON, channel bus, statuts valides |
| Failure policy | Retry count, escalation, timeout |
| Autonomy mode | Toujours execution — le pod ne demande jamais permission |
Une mission qui ne peut pas être formalisée en ces sept champs n’est pas encore prête à être spawnée. C’est un signal que l’orchestrateur doit clarifier la demande, pas déléguer son ambiguïté.
La primitive de spawn
L’unique entrée canonique pour spawner un pod est le script
spawn_pod_deterministic.sh. Il applique en dur, dans cet ordre :
- Acquisition de sémaphore (Redis) — hard-gate, exit 2 si pleine.
- Spawn tmux — fenêtre dédiée nommée d’après le pod.
- Hard-verify
/remote-control— 3 retries × 3 secondes ; exit 1 et libération du sémaphore si KO. - Publication de l’événement
createdsur le stream Redisnika:feed:entities, visible par le consumer groupcg:worker.
Aucun tmux send-keys "claude ..." hors de ce script n’est autorisé.
Cette discipline garantit que tous les pods passent par les mêmes garde-fous,
sont visibles dans le fleet, et libèrent proprement leurs ressources.
Cycle de vie d’un pod
Le cycle de vie standard d’un pod comporte cinq verbes :
flowchart LR
S["🚀 spawn"] --> I["⚡ invoke"]
I --> R["🔧 readjust"]
R --> I
I --> O["👁️ observe"]
O --> K["💀 kill"]
classDef start fill:#7DB5A5,color:#FDFBF8,stroke:#5E9384,stroke-width:2px;
classDef active fill:#F3EADE,color:#2C3E42,stroke:#7DB5A5,stroke-width:2px;
classDef mutate fill:#E99971,color:#FDFBF8,stroke:#C97A55,stroke-width:2px;
classDef terminal fill:#2C3E42,color:#FDFBF8,stroke:#1A262A,stroke-width:2px;
class S start;
class I,O active;
class R mutate;
class K terminal;
- spawn : création de l’instance, boot des hooks
SessionStart, injection du contexte. - invoke : exécution de la mission. Lifetime déterminé par le contrat.
- readjust : en cas d’échec partiel, mutation des primitives mutables (jamais du kernel) puis retry.
- observe : Alpha lit le stream Redis, le bus JSONL, et l’état de la hiérarchie YAML.
- kill :
SubagentStop→ publishpod_done+ relâche du sémaphore + ingestion du résumé en Qdrant.
Garde-fous de l’essaim
- Concurrence maximale : 6 pods simultanés (sémaphore Redis).
- Profondeur de récursion : 4 niveaux (
PROJ → JOB → TASK → SUB). - TTL backstop : 1 heure d’auto-release si un pod hang sans heartbeat.
- Stale pod policy :
task_watcher.pyscan toutes les 2 minutes. - Audit de handoff :
pod_handoff.audit_and_decide()produit un verdictDONE / PARTIAL / STUCK / DORMANTà chaque fin de pod.
Ces limites ne sont pas des suggestions. Elles existent parce qu’un essaim non borné finit par saturer ses propres ressources avant que l’opérateur s’en rende compte.